Re: Teach predtest about IS [NOT] proofs

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: Teach predtest about IS [NOT] proofs
Дата
Msg-id 2703176.1711403586@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: Teach predtest about IS [NOT] proofs  (James Coleman <jtc331@gmail.com>)
Ответы Re: Teach predtest about IS [NOT] proofs  (Tom Lane <tgl@sss.pgh.pa.us>)
Re: Teach predtest about IS [NOT] proofs  (James Coleman <jtc331@gmail.com>)
Список pgsql-hackers
James Coleman <jtc331@gmail.com> writes:
> [ v6 patchset ]

I went ahead and committed 0001 after one more round of review

statements; my bad).  I also added the changes in test_predtest.c from
0002.  I attach a rebased version of 0002, as well as 0003 which isn't
changed, mainly to keep the cfbot happy.

I'm still not happy with what you did in predicate_refuted_by_recurse:
it feels wrong and rather expensively so.  There has to be a better
way.  Maybe strong vs. weak isn't quite the right formulation for
refutation tests?

            regards, tom lane

diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c
index 6e3b376f3d..5bb5bb4f0e 100644
--- a/src/backend/optimizer/util/predtest.c
+++ b/src/backend/optimizer/util/predtest.c
@@ -99,6 +99,8 @@ static bool predicate_implied_by_simple_clause(Expr *predicate, Node *clause,
                                                bool weak);
 static bool predicate_refuted_by_simple_clause(Expr *predicate, Node *clause,
                                                bool weak);
+static bool predicate_implied_not_null_by_clause(Expr *predicate, Node *clause,
+                                                 bool weak);
 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, bool allow_false);
@@ -198,6 +200,11 @@ predicate_implied_by(List *predicate_list, List *clause_list,
  * (i.e., B must yield false or NULL).  We use this to detect mutually
  * contradictory WHERE clauses.
  *
+ * A notable difference between implication and refutation proofs is that
+ * strong/weak refutations don't vary the input of A (both must be true) but
+ * vary the allowed outcomes of B (false vs. non-truth), while for implications
+ * we vary both A (truth vs. non-falsity) and B (truth vs. non-falsity).
+ *
  * Weak refutation can be proven in some cases where strong refutation doesn't
  * hold, so it's useful to use it when possible.  We don't currently have
  * support for disproving one CHECK constraint based on another one, nor for
@@ -740,6 +747,16 @@ predicate_refuted_by_recurse(Node *clause, Node *predicate,
                                              !weak))
                 return true;

+            /*
+             * Because weak refutation expands the allowed outcomes for B
+             * from "false" to "false or null", we can additionally prove
+             * weak refutation in the case that strong refutation is proven.
+             */
+            if (weak && not_arg &&
+                predicate_implied_by_recurse(predicate, not_arg,
+                                             true))
+                return true;
+
             switch (pclass)
             {
                 case CLASS_AND:
@@ -1137,21 +1154,27 @@ predicate_implied_by_simple_clause(Expr *predicate, Node *clause,

                     Assert(list_length(op->args) == 2);
                     rightop = lsecond(op->args);
-                    /* We might never see null Consts here, but better check */
-                    if (rightop && IsA(rightop, Const) &&
-                        !((Const *) rightop)->constisnull)
+                    if (rightop && IsA(rightop, Const))
                     {
+                        Const    *constexpr = (Const *) rightop;
                         Node       *leftop = linitial(op->args);

-                        if (DatumGetBool(((Const *) rightop)->constvalue))
+                        /*
+                         * We might never see a null Const here, but better
+                         * check anyway.
+                         */
+                        if (constexpr->constisnull)
+                            return false;
+
+                        if (DatumGetBool(constexpr->constvalue))
                         {
-                            /* X = true implies X */
+                            /* x = true implies x */
                             if (equal(predicate, leftop))
                                 return true;
                         }
                         else
                         {
-                            /* X = false implies NOT X */
+                            /* x = false implies NOT x */
                             if (is_notclause(predicate) &&
                                 equal(get_notclausearg(predicate), leftop))
                                 return true;
@@ -1160,6 +1183,97 @@ predicate_implied_by_simple_clause(Expr *predicate, Node *clause,
                 }
             }
             break;
+        case T_NullTest:
+            {
+                NullTest *clausentest = (NullTest *) clause;
+
+                /*
+                 * row IS NOT NULL does not act in the simple way we have in
+                 * mind
+                 */
+                if (clausentest->argisrow)
+                    return false;
+
+                switch (clausentest->nulltesttype)
+                {
+                    case IS_NULL:
+                        /*
+                         * A clause in the form "foo IS NULL" implies a
+                         * predicate "NOT foo" that is strict for "foo", but
+                         * only weakly since "foo" being null will result in
+                         * the clause evaluating to true while the predicate
+                         * will evaluate to null.
+                         */
+                        if (weak && is_notclause(predicate) &&
+                            clause_is_strict_for((Node *) get_notclausearg(predicate), (Node *) clausentest->arg,
true))
+                            return true;
+
+                        break;
+                    case IS_NOT_NULL:
+                        break;
+                }
+            }
+            break;
+        case T_BooleanTest:
+            {
+                BooleanTest    *clausebtest = (BooleanTest *) clause;
+
+                switch (clausebtest->booltesttype)
+                {
+                    case IS_TRUE:
+                        /* x IS TRUE implies x */
+                        if (equal(predicate, clausebtest->arg))
+                            return true;
+                        break;
+                    case IS_FALSE:
+                        /* x IS FALSE implies NOT x */
+                        if (is_notclause(predicate) &&
+                            equal(get_notclausearg(predicate), clausebtest->arg))
+                            return true;
+                        break;
+                    case IS_NOT_TRUE:
+                        /*
+                         * A clause in the form "foo IS NOT TRUE" implies a
+                         * predicate "NOT foo", but only weakly since "foo"
+                         * being null will result in the clause evaluating to
+                         * true while the predicate will evaluate to null.
+                         */
+                        if (weak && is_notclause(predicate) &&
+                            equal(get_notclausearg(predicate), clausebtest->arg))
+                            return true;
+                        break;
+                    case IS_NOT_FALSE:
+                        /*
+                         * A clause in the form "foo IS NOT FALSE" implies a
+                         * predicate "foo", but only weakly since "foo" being
+                         * null will result in the clause evaluating to true
+                         * when the predicate is null.
+                         */
+                        if (weak && equal(predicate, clausebtest->arg))
+                            return true;
+                        break;
+                    case IS_UNKNOWN:
+                        /*
+                         * A clause in the form "foo IS UNKNOWN" implies a
+                         * predicate "NOT foo" that is strict for "foo", but
+                         * only weakly since "foo" being null will result in
+                         * the clause evaluating to true while the predicate
+                         * will evaluate to null.
+                         */
+                        if (weak && is_notclause(predicate) &&
+                            clause_is_strict_for((Node *) get_notclausearg(predicate), (Node *) clausebtest->arg,
true))
+                            return true;
+                        break;
+                    case IS_NOT_UNKNOWN:
+                        /*
+                         * "foo IS NOT UKNOWN" implies "foo IS NOT NULL", but
+                         * we handle that in the predicate-type-specific cases
+                         * below.
+                         * */
+                        break;
+                }
+            }
+            break;
         default:
             break;
     }
@@ -1171,30 +1285,124 @@ predicate_implied_by_simple_clause(Expr *predicate, Node *clause,
             {
                 NullTest   *predntest = (NullTest *) predicate;

+                /*
+                 * row IS NOT NULL does not act in the simple way we have in
+                 * mind
+                 */
+                if (predntest->argisrow)
+                    return false;
+
                 switch (predntest->nulltesttype)
                 {
                     case IS_NOT_NULL:
-
                         /*
-                         * If the predicate is of the form "foo IS NOT NULL",
-                         * and we are considering strong implication, we can
-                         * conclude that the predicate is implied if the
-                         * clause is strict for "foo", i.e., it must yield
-                         * false or NULL when "foo" is NULL.  In that case
-                         * truth of the clause ensures that "foo" isn't NULL.
-                         * (Again, this is a safe conclusion because "foo"
-                         * must be immutable.)  This doesn't work for weak
-                         * implication, though.  Also, "row IS NOT NULL" does
-                         * not act in the simple way we have in mind.
+                         * When the predicate is of the form "foo IS NOT NULL",
+                         * we can conclude that the predicate is implied if
+                         * truth of the clause would imply that the predicate
+                         * must be not null.
                          */
-                        if (!weak &&
-                            !predntest->argisrow &&
-                            clause_is_strict_for(clause,
-                                                 (Node *) predntest->arg,
-                                                 true))
+                        if (predicate_implied_not_null_by_clause(predntest->arg,
+                                                                 clause, weak))
                             return true;
                         break;
                     case IS_NULL:
+                        if (IsA(clause, BooleanTest))
+                        {
+                            BooleanTest* clausebtest = (BooleanTest *) clause;
+
+                            /* x IS NULL is implied by x IS UNKNOWN */
+                            if (clausebtest->booltesttype == IS_UNKNOWN &&
+                                equal(predntest->arg, clausebtest->arg))
+                                return true;
+                        }
+                        break;
+                }
+            }
+            break;
+        case T_BooleanTest:
+            {
+                BooleanTest    *predbtest = (BooleanTest *) predicate;
+
+                switch (predbtest->booltesttype)
+                {
+                    case IS_TRUE:
+                        /*
+                         * x implies x is true
+                         *
+                         * We can only prove strong implication here since
+                         * "null is true" is false rather than null.
+                         */
+                        if (!weak && equal(clause, predbtest->arg))
+                            return true;
+                        break;
+                    case IS_FALSE:
+                        /*
+                         * NOT x implies x is false
+                         *
+                         * We can only prove strong implication here since "not
+                         * null" is null rather than true.
+                         */
+                        if (!weak && is_notclause(clause) &&
+                            equal(get_notclausearg(clause), predbtest->arg))
+                            return true;
+                        break;
+                    case IS_NOT_TRUE:
+                        {
+                            /* NOT x implies x is not true */
+                            if (is_notclause(clause) &&
+                                equal(get_notclausearg(clause), predbtest->arg))
+                                return true;
+
+                            if (IsA(clause, BooleanTest))
+                            {
+                                BooleanTest *clausebtest = (BooleanTest *) clause;
+
+                                /* x is unknown implies x is not true */
+                                if (clausebtest->booltesttype == IS_UNKNOWN &&
+                                    equal(clausebtest->arg, predbtest->arg))
+                                    return true;
+                            }
+                        }
+                        break;
+                    case IS_NOT_FALSE:
+                        {
+                            /* x implies x is not false*/
+                            if (equal(clause, predbtest->arg))
+                                return true;
+
+                            if (IsA(clause, BooleanTest))
+                            {
+                                BooleanTest *clausebtest = (BooleanTest *) clause;
+
+                                /* x is unknown implies x is not false */
+                                if (clausebtest->booltesttype == IS_UNKNOWN &&
+                                    equal(clausebtest->arg, predbtest->arg))
+                                    return true;
+                            }
+                        }
+                        break;
+                    case IS_UNKNOWN:
+                        if (IsA(clause, NullTest))
+                        {
+                            NullTest   *clausentest = (NullTest *) clause;
+
+                            /* x IS NULL implies x is unknown */
+                            if (clausentest->nulltesttype == IS_NULL &&
+                                equal(clausentest->arg, predbtest->arg))
+                                return true;
+                        }
+                        break;
+                    case IS_NOT_UNKNOWN:
+                        /*
+                         * Since "foo IS NOT UNKNOWN" has the same meaning
+                         * as "foo is NOT NULL" (assuming "foo" is a boolean)
+                         * we can prove the same things as we did earlier for
+                         * for NullTest's IS_NOT_NULL case.
+                         *
+                         * For example: truth of x implies x is not unknown.
+                         */
+                        if (predicate_implied_not_null_by_clause(predbtest->arg, clause, weak))
+                            return true;
                         break;
                 }
             }
@@ -1211,6 +1419,110 @@ predicate_implied_by_simple_clause(Expr *predicate, Node *clause,
     return operator_predicate_proof(predicate, clause, false, weak);
 }

+/*
+ * predicate_implied_not_null_by_clause
+ *      Tests a "simple clause" predicate to see if truth of the "simple clause"
+ *      restriction implies that that predicate is not null.
+ *
+ * We return true if able to prove the implication, false if not. It is expected
+ * that the predicate argument to this function has already been reduced to the
+ * argument of any BooleanTest or NullTest predicate expressions.
+ *
+ * This function encapsulates a specific subcase of
+ * predicate_implied_by_simple_clause cases which is useful in several cases of
+ * both refutation and implication.
+ *
+ * Proving implication of "NOT NULL" is particularly useful for proving it's
+ * safe to use partial indexes defined with a "foo NOT NULL" condition.
+ *
+ * In several of the cases below (e.g., BooleanTest and NullTest) we could
+ * recurse into the argument of those expressions. For example, if the argument
+ * in a BooleanTest is itself a BooleanTest or a NullTest, then if the argument
+ * to that nested test expression matches the clause's subexpression we can
+ * trivially prove implication of "NOT NULL" since BooleanTest and NullTest
+ * always evaluate to true or false. However that doesn't seem useful to expend
+ * cycles on at this point.
+ */
+static bool
+predicate_implied_not_null_by_clause(Expr *predicate, Node *clause, bool weak)
+{
+    switch (nodeTag(clause))
+    {
+        case T_BooleanTest:
+            {
+                BooleanTest *clausebtest = (BooleanTest *) clause;
+
+                /*
+                 * Because the obvious case of "foo IS NOT UNKNOWN" proving
+                 * "foo" isn't null, we can also prove "foo" isn't null if we
+                 * know that it has to be true or false.
+                 */
+                switch (clausebtest->booltesttype)
+                {
+                    case IS_TRUE:
+                    case IS_FALSE:
+                    case IS_NOT_UNKNOWN:
+                        if (equal(clausebtest->arg, predicate))
+                            return true;
+                        break;
+                    default:
+                        break;
+                }
+            }
+            break;
+        case T_NullTest:
+            {
+                NullTest *clausentest = (NullTest *) clause;
+
+                /*
+                 * row IS NULL does not act in the simple way we have in mind
+                 */
+                if (clausentest->argisrow)
+                    return false;
+
+                /*
+                 * It's self-evident that "foo IS NOT NULL" implies "foo"
+                 * isn't NULL.
+                 */
+                if (clausentest->nulltesttype == IS_NOT_NULL &&
+                    equal(predicate, clausentest->arg))
+                    return true;
+            }
+            break;
+        case T_DistinctExpr:
+            /*
+             * If both of the two arguments to IS [NOT] DISTINCT FROM separately
+             * imply that the predicate is not null or are strict for the
+             * predicate, then we could prove implication that the predicate is
+             * not null. But it's not obvious that it's worth expending time
+             * on that check since having the predicate in the expression on
+             * both sides of the distinct expression is likely uncommon.
+             */
+            break;
+        case T_Const:
+            /*
+             * We don't currently have to consider Const expressions because constant
+             * folding would have eliminated the node types we consider here.
+             */
+            break;
+        default:
+            break;
+    }
+
+    /*
+     * We can conclude that a predicate "foo" is not null if the clause is
+     * strict for "foo", i.e., it must yield false or NULL when "foo" is NULL.
+     * In that case truth of the clause ensures that "foo" isn't NULL.  (Again,
+     * this is a safe conclusion because "foo" must be immutable.) This doesn't
+     * work for weak implication, though, since the clause yielding the
+     * non-false value NULL means the predicate will evaluate to false.
+     */
+    if (!weak && clause_is_strict_for(clause, (Node *) predicate, true))
+        return true;
+
+    return false;
+}
+
 /*
  * predicate_refuted_by_simple_clause
  *      Does the predicate refutation test for a "simple clause" predicate
@@ -1254,32 +1566,20 @@ predicate_refuted_by_simple_clause(Expr *predicate, Node *clause,
                 {
                     case IS_NULL:
                         {
-                            switch (nodeTag(predicate))
-                            {
-                                case T_NullTest:
-                                    {
-                                        NullTest   *predntest = (NullTest *) predicate;
-
-                                        /*
-                                         * row IS NULL does not act in the
-                                         * simple way we have in mind
-                                         */
-                                        if (predntest->argisrow)
-                                            return false;
-
-                                        /*
-                                         * foo IS NULL refutes foo IS NOT
-                                         * NULL, at least in the non-row case,
-                                         * for both strong and weak refutation
-                                         */
-                                        if (predntest->nulltesttype == IS_NOT_NULL &&
-                                            equal(predntest->arg, clausentest->arg))
-                                            return true;
-                                    }
-                                    break;
-                                default:
-                                    break;
-                            }
+                            /*
+                             * A clause in the form "foo IS NULL" refutes any
+                             * predicate that is itself implied not null, but
+                             * we have to exclude cases where we'd allow false
+                             * in strictness checking so we always pass
+                             * weak=true here. This is because we aren't
+                             * assuming anything about the form of the
+                             * predicate in that case, and, for example, we
+                             * might have a predicate of simply "foo", but "foo
+                             * = false" would mean both our clause and our
+                             * predicate would evaluate to "false".
+                             */
+                            if (predicate_implied_not_null_by_clause(clausentest->arg, (Node *) predicate, true))
+                                return true;

                             /*
                              * foo IS NULL weakly refutes any predicate that
@@ -1288,15 +1588,111 @@ predicate_refuted_by_simple_clause(Expr *predicate, Node *clause,
                              * in the predicate, it's known immutable).
                              */
                             if (weak &&
-                                clause_is_strict_for((Node *) predicate,
-                                                     (Node *) clausentest->arg,
-                                                     true))
+                                clause_is_strict_for((Node *) predicate, (Node *) clausentest->arg, true))
                                 return true;

                             return false;    /* we can't succeed below... */
                         }
                         break;
                     case IS_NOT_NULL:
+                        /*
+                         * "foo IS NOT NULL" refutes both "foo IS NULL"
+                         * and "foo IS UNKNOWN", but we handle that in the
+                         * predicate switch statement.
+                         */
+                        break;
+                }
+            }
+            break;
+        case T_BooleanTest:
+            {
+                BooleanTest     *clausebtest = (BooleanTest *) clause;
+
+                switch (clausebtest->booltesttype)
+                {
+                    case IS_TRUE:
+                        /*
+                         * We've already previously checked for the clause
+                         * being a NOT arg and determined refutation based on
+                         * implication of the predicate by that arg. That check
+                         * handles the case "foos IS TRUE" refuting "NOT foo",
+                         * so we don't have to do anything special here.
+                         */
+                        break;
+                    case IS_NOT_TRUE:
+                        {
+                            if (IsA(predicate, BooleanTest))
+                            {
+                                BooleanTest    *predbtest = (BooleanTest *) predicate;
+
+                                /* foo IS NOT TRUE refutes foo IS TRUE */
+                                if (predbtest->booltesttype == IS_TRUE &&
+                                    equal(predbtest->arg, clausebtest->arg))
+                                    return true;
+                            }
+
+                            /* foo IS NOT TRUE weakly refutes foo */
+                            if (weak && equal(predicate, clausebtest->arg))
+                                return true;
+
+                        }
+                        break;
+                    case IS_FALSE:
+                        if (IsA(predicate, BooleanTest))
+                        {
+                            BooleanTest    *predbtest = (BooleanTest *) predicate;
+
+                            /* foo IS UNKNOWN refutes foo IS TRUE */
+                            /* foo IS UNKNOWN refutes foo IS NOT UNKNOWN */
+                            if (predbtest->booltesttype == IS_UNKNOWN &&
+                                equal(predbtest->arg, clausebtest->arg))
+                                return true;
+                        }
+                        break;
+                    case IS_NOT_FALSE:
+                        break;
+                    case IS_UNKNOWN:
+                        {
+                            /*
+                             * A clause in the form "foo IS UNKNOWN" refutes any
+                             * predicate that is itself implied not null, but
+                             * we have to exclude cases where we'd allow false
+                             * in strictness checking so we always pass
+                             * weak=true here. This is because we aren't
+                             * assuming anything about the form of the
+                             * predicate in that case, and, for example, we
+                             * might have a predicate of simply "foo", but "foo
+                             * = false" would mean both our clause and our
+                             * predicate would evaluate to "false".
+                             */
+                            if (predicate_implied_not_null_by_clause(clausebtest->arg, (Node *) predicate, true))
+                                return true;
+
+                            /*
+                             * A clause in the form "foo IS UNKNOWN" implies
+                             * that "foo" is null, so if the predicate is
+                             * strict for "foo" then that clause weakly refutes
+                             * the predicate since "foo" being null will cause
+                             * the predicate to evaluate to non-true, therefore
+                             * proving weak refutation.
+                             *
+                             * This doesn't work for strong refutation, however,
+                             * since evaluating the predicate with "foo" set to
+                             * null may result in "null" rather than "false".
+                             */
+                            if (weak &&
+                                clause_is_strict_for((Node *) predicate, (Node *) clausebtest->arg, true))
+                                return true;
+
+                            return false;            /* we can't succeed below... */
+                        }
+                        break;
+                    case IS_NOT_UNKNOWN:
+                        /*
+                         * "foo IS NOT UNKNOWN" refutes both "foo IS UNKNOWN"
+                         * and "foo IS NULL", but we handle that in the
+                         * predicate switch statement.
+                         */
                         break;
                 }
             }
@@ -1320,42 +1716,19 @@ predicate_refuted_by_simple_clause(Expr *predicate, Node *clause,
                 {
                     case IS_NULL:
                         {
-                            switch (nodeTag(clause))
-                            {
-                                case T_NullTest:
-                                    {
-                                        NullTest   *clausentest = (NullTest *) clause;
-
-                                        /*
-                                         * row IS NULL does not act in the
-                                         * simple way we have in mind
-                                         */
-                                        if (clausentest->argisrow)
-                                            return false;
-
-                                        /*
-                                         * foo IS NOT NULL refutes foo IS NULL
-                                         * for both strong and weak refutation
-                                         */
-                                        if (clausentest->nulltesttype == IS_NOT_NULL &&
-                                            equal(clausentest->arg, predntest->arg))
-                                            return true;
-                                    }
-                                    break;
-                                default:
-                                    break;
-                            }
-
                             /*
-                             * When the predicate is of the form "foo IS
-                             * NULL", we can conclude that the predicate is
-                             * refuted if the clause is strict for "foo" (see
-                             * notes for implication case).  That works for
-                             * either strong or weak refutation.
+                             * A predicate in the form "foo IS NULL" is refuted
+                             * if truth of the clause would imply that the
+                             * predicate must be not null.
+                             *
+                             * We always pass weak=false here because there are
+                             * some cases where we can only prove strong
+                             * implication of "foo is not null", but that will
+                             * also prove weak refutation since strong
+                             * refutation is a strict superset of weak
+                             * refutation.
                              */
-                            if (clause_is_strict_for(clause,
-                                                     (Node *) predntest->arg,
-                                                     true))
+                            if (predicate_implied_not_null_by_clause(predntest->arg, clause, false))
                                 return true;
                         }
                         break;
@@ -1366,6 +1739,37 @@ predicate_refuted_by_simple_clause(Expr *predicate, Node *clause,
                 return false;    /* we can't succeed below... */
             }
             break;
+        case T_BooleanTest:
+            {
+                BooleanTest     *predbtest = (BooleanTest *) predicate;
+
+                switch (predbtest->booltesttype)
+                {
+                    case IS_UNKNOWN:
+                        {
+                            /*
+                             * A predicate in the form "foo IS UNKNOWN" is refuted
+                             * if truth of the clause would imply that the
+                             * predicate must be not null.
+                             *
+                             * We always pass weak=false here because there are
+                             * some cases where we can only prove strong
+                             * implication of "foo is not null", but that will
+                             * also prove weak refutation since strong
+                             * refutation is a strict superset of weak
+                             * refutation.
+                             */
+                            if (predicate_implied_not_null_by_clause(predbtest->arg, clause, false))
+                                return true;
+
+                            return false;            /* we can't succeed below... */
+                        }
+                        break;
+                    default:
+                        break;
+                }
+            }
+            break;
         default:
             break;
     }
@@ -1506,6 +1910,17 @@ clause_is_strict_for(Node *clause, Node *subexpr, bool allow_false)
         return false;
     }

+    /*
+     * We can recurse into "not foo" without any additional processing because
+     * the "not" operator is itself strict, evaluating to null when its
+     * argument is null. We can't pass along allow_false=true, and instead
+     * always pass allow_false=false since "not (false)" is true rather than
+     * null.
+     */
+    if (is_notclause(clause) &&
+        clause_is_strict_for((Node *) get_notclausearg(clause), subexpr, false))
+        return true;
+
     /*
      * CoerceViaIO is strict (whether or not the I/O functions it calls are).
      * Likewise, ArrayCoerceExpr is strict for its array argument (regardless
@@ -1593,6 +2008,33 @@ clause_is_strict_for(Node *clause, Node *subexpr, bool allow_false)
         return clause_is_strict_for(arraynode, subexpr, false);
     }

+    /*
+     * BooleanTest expressions never evaluate to null so we can't do anything
+     * if allow_false isn't true.
+     */
+    if (allow_false && IsA(clause, BooleanTest))
+    {
+        BooleanTest *test = (BooleanTest *) clause;
+
+        switch (test->booltesttype)
+        {
+            case IS_TRUE:
+            case IS_FALSE:
+            case IS_NOT_UNKNOWN:
+                return clause_is_strict_for((Node *) test->arg, subexpr, false);
+                break;
+            /*
+             * null is not true, null is not false, and null is unknown are
+             * true, hence we know they can't be strict.
+             */
+            case IS_NOT_TRUE:
+            case IS_NOT_FALSE:
+            case IS_UNKNOWN:
+                return false;
+                break;
+        }
+    }
+
     /*
      * When recursing into an expression, we might find a NULL constant.
      * That's certainly NULL, whether it matches subexpr or not.
diff --git a/src/test/modules/test_predtest/expected/test_predtest.out
b/src/test/modules/test_predtest/expected/test_predtest.out
index 6d21bcd603..7a01fe1252 100644
--- a/src/test/modules/test_predtest/expected/test_predtest.out
+++ b/src/test/modules/test_predtest/expected/test_predtest.out
@@ -93,6 +93,48 @@ w_i_holds         | f
 s_r_holds         | f
 w_r_holds         | f

+select * from test_predtest($$
+select x is not unknown, x
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | t
+weak_implied_by   | f
+strong_refuted_by | f
+weak_refuted_by   | f
+s_i_holds         | t
+w_i_holds         | f
+s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select x is not unknown, x is not null
+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 x is not null, x is not unknown
+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 x is not null, x is null
 from integers
@@ -143,12 +185,222 @@ $$);
 strong_implied_by | f
 weak_implied_by   | f
 strong_refuted_by | f
+weak_refuted_by   | t
+s_i_holds         | f
+w_i_holds         | f
+s_r_holds         | f
+w_r_holds         | t
+
+select * from test_predtest($$
+select x is not true, not x
+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 not x, x is not true
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | f
+weak_implied_by   | t
+strong_refuted_by | f
+weak_refuted_by   | f
+s_i_holds         | f
+w_i_holds         | t
+s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select x is not true, x is unknown
+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 x is unknown, x is not true
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | f
+weak_implied_by   | f
+strong_refuted_by | f
+weak_refuted_by   | f
+s_i_holds         | f
+w_i_holds         | f
+s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select x is not unknown, x is not true
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | f
+weak_implied_by   | f
+strong_refuted_by | f
 weak_refuted_by   | f
 s_i_holds         | f
 w_i_holds         | f
 s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select x is not unknown, x is not false
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | f
+weak_implied_by   | f
+strong_refuted_by | f
+weak_refuted_by   | f
+s_i_holds         | f
+w_i_holds         | f
+s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select x is true, x
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | t
+weak_implied_by   | f
+strong_refuted_by | f
+weak_refuted_by   | f
+s_i_holds         | t
+w_i_holds         | f
+s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select x, x is true
+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 not x, x is true
+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 x is true, not 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 x is not true, x is true
+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 x is false, x is true
+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 x is unknown, x is true
+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 x is false, not x
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | t
+weak_implied_by   | f
+strong_refuted_by | f
+weak_refuted_by   | f
+s_i_holds         | t
+w_i_holds         | f
+s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select not x, x is false
+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 x is false, x
 from booleans
@@ -164,21 +416,316 @@ s_r_holds         | t
 w_r_holds         | t

 select * from test_predtest($$
-select x, x is false
+select x, x is false
+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 x is not false, x
+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 x, x is not false
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | f
+weak_implied_by   | t
+strong_refuted_by | f
+weak_refuted_by   | f
+s_i_holds         | f
+w_i_holds         | t
+s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select x is not false, x is unknown
+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 x is unknown, x is not false
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | f
+weak_implied_by   | f
+strong_refuted_by | f
+weak_refuted_by   | f
+s_i_holds         | f
+w_i_holds         | f
+s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select x is unknown, 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 x, x is unknown
+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 unknown, x is unknown
+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 x is unknown, x is not unknown
+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) is unknown, x is not unknown
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | f
+weak_implied_by   | f
+strong_refuted_by | f
+weak_refuted_by   | f
+s_i_holds         | f
+w_i_holds         | f
+s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select strictf(x, y) is unknown, strictf(x, y) is not unknown
+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 x is unknown, strictf(x, y) is not unknown
+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 x is unknown, (x is true) is not unknown
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | f
+weak_implied_by   | f
+strong_refuted_by | f
+weak_refuted_by   | f
+s_i_holds         | f
+w_i_holds         | f
+s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select x is null, x is unknown
+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 not strictf(x, y), x is unknown
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | f
+weak_implied_by   | t
+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 not strictf(x, y), x is null
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | f
+weak_implied_by   | t
+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 null, not strictf(x, y)
+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 x is not null, not strictf(x, y)
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | t
+weak_implied_by   | f
+strong_refuted_by | f
+weak_refuted_by   | f
+s_i_holds         | t
+w_i_holds         | f
+s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select x is not unknown, not strictf(x, y)
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | t
+weak_implied_by   | f
+strong_refuted_by | f
+weak_refuted_by   | f
+s_i_holds         | t
+w_i_holds         | f
+s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select x is unknown, not strictf(x, y)
+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 x is not null, x is true
 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
+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 x is unknown, x
+select x is not null, x is false
+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
+
+-- Assorted not-so-trivial refutation rules
+select * from test_predtest($$
+select x is null, x
 from booleans
 $$);
 -[ RECORD 1 ]-----+--
@@ -192,22 +739,21 @@ s_r_holds         | t
 w_r_holds         | t

 select * from test_predtest($$
-select x, x is unknown
+select x, x is null
 from booleans
 $$);
 -[ RECORD 1 ]-----+--
 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

--- Assorted not-so-trivial refutation rules
 select * from test_predtest($$
-select x is null, x
+select x is null, x is not unknown
 from booleans
 $$);
 -[ RECORD 1 ]-----+--
@@ -221,17 +767,17 @@ s_r_holds         | t
 w_r_holds         | t

 select * from test_predtest($$
-select x, x is null
+select x is not unknown, x is null
 from booleans
 $$);
 -[ RECORD 1 ]-----+--
 strong_implied_by | f
 weak_implied_by   | f
-strong_refuted_by | f
+strong_refuted_by | t
 weak_refuted_by   | t
 s_i_holds         | f
-w_i_holds         | t
-s_r_holds         | f
+w_i_holds         | f
+s_r_holds         | t
 w_r_holds         | t

 select * from test_predtest($$
@@ -1094,3 +1640,231 @@ w_i_holds         | t
 s_r_holds         | f
 w_r_holds         | t

+-- More BooleanTest proofs
+-- I'm wondering if we should standardize a location for all of them, and
+-- potentially updated test_predtest to run both directions rather than
+-- needing to duplicate queries (with the two clauses swapped).
+select * from test_predtest($$
+select x is true, x = true
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | t
+weak_implied_by   | f
+strong_refuted_by | f
+weak_refuted_by   | f
+s_i_holds         | t
+w_i_holds         | f
+s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select x is false, x = false
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | t
+weak_implied_by   | f
+strong_refuted_by | f
+weak_refuted_by   | f
+s_i_holds         | t
+w_i_holds         | f
+s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select x, x is false
+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 unknown
+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 strictf(x, y), x is not unknown
+from booleans
+$$);
+-[ RECORD 1 ]-----+--
+strong_implied_by | f
+weak_implied_by   | f
+strong_refuted_by | f
+weak_refuted_by   | f
+s_i_holds         | f
+w_i_holds         | f
+s_r_holds         | f
+w_r_holds         | f
+
+select * from test_predtest($$
+select not x, x is true
+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 x, x is not true
+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         | f
+s_r_holds         | f
+w_r_holds         | t
+
+select * from test_predtest($$
+select x is true, x is not true
+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 x is true, x is false
+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 x is true, x is unknown
+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 x is true, x is null
+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 x is false, x is unknown
+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 x is false, x is null
+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 x is unknown, x is false
+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 x is unknown, x is null
+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 x is unknown, x is not null
+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
+
diff --git a/src/test/modules/test_predtest/sql/test_predtest.sql
b/src/test/modules/test_predtest/sql/test_predtest.sql
index 072eb5b0d5..6a3c01dbf3 100644
--- a/src/test/modules/test_predtest/sql/test_predtest.sql
+++ b/src/test/modules/test_predtest/sql/test_predtest.sql
@@ -56,6 +56,21 @@ select x is not null, x
 from booleans
 $$);

+select * from test_predtest($$
+select x is not unknown, x
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is not unknown, x is not null
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is not null, x is not unknown
+from booleans
+$$);
+
 select * from test_predtest($$
 select x is not null, x is null
 from integers
@@ -76,6 +91,81 @@ select x, x is not true
 from booleans
 $$);

+select * from test_predtest($$
+select x is not true, not x
+from booleans
+$$);
+
+select * from test_predtest($$
+select not x, x is not true
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is not true, x is unknown
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is unknown, x is not true
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is not unknown, x is not true
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is not unknown, x is not false
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is true, x
+from booleans
+$$);
+
+select * from test_predtest($$
+select x, x is true
+from booleans
+$$);
+
+select * from test_predtest($$
+select not x, x is true
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is true, not x
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is not true, x is true
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is false, x is true
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is unknown, x is true
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is false, not x
+from booleans
+$$);
+
+select * from test_predtest($$
+select not x, x is false
+from booleans
+$$);
+
 select * from test_predtest($$
 select x is false, x
 from booleans
@@ -86,6 +176,26 @@ select x, x is false
 from booleans
 $$);

+select * from test_predtest($$
+select x is not false, x
+from booleans
+$$);
+
+select * from test_predtest($$
+select x, x is not false
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is not false, x is unknown
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is unknown, x is not false
+from booleans
+$$);
+
 select * from test_predtest($$
 select x is unknown, x
 from booleans
@@ -96,6 +206,81 @@ select x, x is unknown
 from booleans
 $$);

+select * from test_predtest($$
+select x is not unknown, x is unknown
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is unknown, x is not unknown
+from booleans
+$$);
+
+select * from test_predtest($$
+select strictf(x, y) is unknown, x is not unknown
+from booleans
+$$);
+
+select * from test_predtest($$
+select strictf(x, y) is unknown, strictf(x, y) is not unknown
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is unknown, strictf(x, y) is not unknown
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is unknown, (x is true) is not unknown
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is null, x is unknown
+from booleans
+$$);
+
+select * from test_predtest($$
+select not strictf(x, y), x is unknown
+from booleans
+$$);
+
+select * from test_predtest($$
+select not strictf(x, y), x is null
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is null, not strictf(x, y)
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is not null, not strictf(x, y)
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is not unknown, not strictf(x, y)
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is unknown, not strictf(x, y)
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is not null, x is true
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is not null, x is false
+from booleans
+$$);
+
 -- Assorted not-so-trivial refutation rules

 select * from test_predtest($$
@@ -108,6 +293,16 @@ select x, x is null
 from booleans
 $$);

+select * from test_predtest($$
+select x is null, x is not unknown
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is not unknown, x is null
+from booleans
+$$);
+
 select * from test_predtest($$
 select strictf(x,y), x is null
 from booleans
@@ -440,3 +635,88 @@ select * from test_predtest($$
 select x = all(opaque_array(array[1])), x is null
 from integers
 $$);
+
+-- More BooleanTest proofs
+-- I'm wondering if we should standardize a location for all of them, and
+-- potentially updated test_predtest to run both directions rather than
+-- needing to duplicate queries (with the two clauses swapped).
+
+select * from test_predtest($$
+select x is true, x = true
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is false, x = false
+from booleans
+$$);
+
+select * from test_predtest($$
+select x, x is false
+from booleans
+$$);
+
+select * from test_predtest($$
+select strictf(x, y), x is unknown
+from booleans
+$$);
+
+select * from test_predtest($$
+select strictf(x, y), x is not unknown
+from booleans
+$$);
+
+select * from test_predtest($$
+select not x, x is true
+from booleans
+$$);
+
+select * from test_predtest($$
+select x, x is not true
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is true, x is not true
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is true, x is false
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is true, x is unknown
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is true, x is null
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is false, x is unknown
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is false, x is null
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is unknown, x is false
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is unknown, x is null
+from booleans
+$$);
+
+select * from test_predtest($$
+select x is unknown, x is not null
+from booleans
+$$);
From efa4da4428010271ba5f2eecf96ceec614bd6a86 Mon Sep 17 00:00:00 2001
From: jcoleman <jtc331@gmail.com>
Date: Mon, 15 Jan 2024 09:00:14 -0500
Subject: [PATCH v6 3/3] Add temporary 'all permutations' test

---
 src/test/modules/test_predtest/Makefile       |   2 +-
 .../expected/test_all_permutations.out        | 226 ++++++++++++++++++
 .../sql/test_all_permutations.sql             |  26 ++
 3 files changed, 253 insertions(+), 1 deletion(-)
 create mode 100644 src/test/modules/test_predtest/expected/test_all_permutations.out
 create mode 100644 src/test/modules/test_predtest/sql/test_all_permutations.sql

diff --git a/src/test/modules/test_predtest/Makefile b/src/test/modules/test_predtest/Makefile
index a235e2aac9..5416350844 100644
--- a/src/test/modules/test_predtest/Makefile
+++ b/src/test/modules/test_predtest/Makefile
@@ -9,7 +9,7 @@ PGFILEDESC = "test_predtest - test code for optimizer/util/predtest.c"
 EXTENSION = test_predtest
 DATA = test_predtest--1.0.sql

-REGRESS = test_predtest
+REGRESS = test_predtest test_all_permutations

 ifdef USE_PGXS
 PG_CONFIG = pg_config
diff --git a/src/test/modules/test_predtest/expected/test_all_permutations.out
b/src/test/modules/test_predtest/expected/test_all_permutations.out
new file mode 100644
index 0000000000..a27b2cfcff
--- /dev/null
+++ b/src/test/modules/test_predtest/expected/test_all_permutations.out
@@ -0,0 +1,226 @@
+with clauses(expr) as (
+  values
+    ('x'),
+    ('not x'),
+    ('strictf(x, y)'),
+    ('not strictf(x, y)'),
+    ('x is null'),
+    ('x is not null'),
+    ('x is true'),
+    ('x is not true'),
+    ('x is false'),
+    ('x is not false'),
+    ('x is unknown'),
+    ('x is not unknown'),
+    ('x = true'),
+    ('x = false')
+)
+select p.expr predicate, c.expr clause, t.*
+from clauses p, clauses c
+join lateral (
+  select *
+  from test_predtest(
+    'select ' || p.expr || ', ' || c.expr ||
+    ' from booleans'
+  )
+) t on true;
+     predicate     |      clause       | strong_implied_by | weak_implied_by | strong_refuted_by | weak_refuted_by |
s_i_holds| w_i_holds | s_r_holds | w_r_holds  

+-------------------+-------------------+-------------------+-----------------+-------------------+-----------------+-----------+-----------+-----------+-----------
+ x                 | x                 | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ not x             | x                 | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ strictf(x, y)     | x                 | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ not strictf(x, y) | x                 | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is null         | x                 | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not null     | x                 | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x is true         | x                 | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x is not true     | x                 | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is false        | x                 | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not false    | x                 | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is unknown      | x                 | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not unknown  | x                 | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x = true          | x                 | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x = false         | x                 | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x                 | not x             | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ not x             | not x             | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ strictf(x, y)     | not x             | f                 | f               | f                 | f               | f
       | f         | f         | t 
+ not strictf(x, y) | not x             | f                 | f               | f                 | f               | f
       | t         | f         | f 
+ x is null         | not x             | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not null     | not x             | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x is true         | not x             | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not true     | not x             | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is false        | not x             | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x is not false    | not x             | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is unknown      | not x             | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not unknown  | not x             | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x = true          | not x             | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x = false         | not x             | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x                 | strictf(x, y)     | f                 | f               | f                 | f               | t
       | f         | f         | f 
+ not x             | strictf(x, y)     | f                 | f               | f                 | f               | f
       | f         | t         | t 
+ strictf(x, y)     | strictf(x, y)     | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ not strictf(x, y) | strictf(x, y)     | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is null         | strictf(x, y)     | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not null     | strictf(x, y)     | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x is true         | strictf(x, y)     | f                 | f               | f                 | f               | t
       | f         | f         | f 
+ x is not true     | strictf(x, y)     | f                 | f               | f                 | f               | f
       | f         | t         | t 
+ x is false        | strictf(x, y)     | f                 | f               | f                 | f               | f
       | f         | t         | t 
+ x is not false    | strictf(x, y)     | f                 | f               | f                 | f               | t
       | f         | f         | f 
+ x is unknown      | strictf(x, y)     | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not unknown  | strictf(x, y)     | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x = true          | strictf(x, y)     | f                 | f               | f                 | f               | t
       | f         | f         | f 
+ x = false         | strictf(x, y)     | f                 | f               | f                 | f               | f
       | f         | t         | t 
+ x                 | not strictf(x, y) | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ not x             | not strictf(x, y) | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ strictf(x, y)     | not strictf(x, y) | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ not strictf(x, y) | not strictf(x, y) | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is null         | not strictf(x, y) | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not null     | not strictf(x, y) | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x is true         | not strictf(x, y) | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is not true     | not strictf(x, y) | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is false        | not strictf(x, y) | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is not false    | not strictf(x, y) | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is unknown      | not strictf(x, y) | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not unknown  | not strictf(x, y) | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x = true          | not strictf(x, y) | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x = false         | not strictf(x, y) | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x                 | x is null         | f                 | f               | f                 | t               | f
       | t         | f         | t 
+ not x             | x is null         | f                 | t               | f                 | t               | f
       | t         | f         | t 
+ strictf(x, y)     | x is null         | f                 | f               | f                 | t               | f
       | t         | f         | t 
+ not strictf(x, y) | x is null         | f                 | t               | f                 | t               | f
       | t         | f         | t 
+ x is null         | x is null         | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is not null     | x is null         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is true         | x is null         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not true     | x is null         | f                 | f               | f                 | f               | t
       | t         | f         | f 
+ x is false        | x is null         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not false    | x is null         | f                 | f               | f                 | f               | t
       | t         | f         | f 
+ x is unknown      | x is null         | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is not unknown  | x is null         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x = true          | x is null         | f                 | f               | f                 | t               | f
       | t         | f         | t 
+ x = false         | x is null         | f                 | t               | f                 | t               | f
       | t         | f         | t 
+ x                 | x is not null     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ not x             | x is not null     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ strictf(x, y)     | x is not null     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ not strictf(x, y) | x is not null     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is null         | x is not null     | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not null     | x is not null     | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is true         | x is not null     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is not true     | x is not null     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is false        | x is not null     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is not false    | x is not null     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is unknown      | x is not null     | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not unknown  | x is not null     | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x = true          | x is not null     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x = false         | x is not null     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x                 | x is true         | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ not x             | x is true         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ strictf(x, y)     | x is true         | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ not strictf(x, y) | x is true         | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is null         | x is true         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not null     | x is true         | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is true         | x is true         | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is not true     | x is true         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is false        | x is true         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not false    | x is true         | f                 | f               | f                 | f               | t
       | t         | f         | f 
+ x is unknown      | x is true         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not unknown  | x is true         | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x = true          | x is true         | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x = false         | x is true         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x                 | x is not true     | f                 | f               | f                 | t               | f
       | f         | f         | t 
+ not x             | x is not true     | f                 | t               | f                 | f               | f
       | t         | f         | f 
+ strictf(x, y)     | x is not true     | f                 | f               | f                 | f               | f
       | f         | f         | t 
+ not strictf(x, y) | x is not true     | f                 | f               | f                 | f               | f
       | t         | f         | f 
+ x is null         | x is not true     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is not null     | x is not true     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is true         | x is not true     | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not true     | x is not true     | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is false        | x is not true     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is not false    | x is not true     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is unknown      | x is not true     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is not unknown  | x is not true     | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x = true          | x is not true     | f                 | f               | f                 | t               | f
       | f         | f         | t 
+ x = false         | x is not true     | f                 | t               | f                 | f               | f
       | t         | f         | f 
+ x                 | x is false        | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ not x             | x is false        | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ strictf(x, y)     | x is false        | f                 | f               | f                 | f               | f
       | f         | f         | t 
+ not strictf(x, y) | x is false        | f                 | f               | f                 | f               | f
       | t         | f         | f 
+ x is null         | x is false        | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not null     | x is false        | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is true         | x is false        | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not true     | x is false        | f                 | f               | f                 | f               | t
       | t         | f         | f 
+ x is false        | x is false        | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is not false    | x is false        | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is unknown      | x is false        | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not unknown  | x is false        | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x = true          | x is false        | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x = false         | x is false        | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x                 | x is not false    | f                 | t               | f                 | f               | f
       | t         | f         | f 
+ not x             | x is not false    | f                 | f               | f                 | f               | f
       | f         | f         | t 
+ strictf(x, y)     | x is not false    | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ not strictf(x, y) | x is not false    | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is null         | x is not false    | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is not null     | x is not false    | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is true         | x is not false    | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is not true     | x is not false    | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is false        | x is not false    | f                 | f               | f                 | f               | f
       | f         | t         | t 
+ x is not false    | x is not false    | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is unknown      | x is not false    | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is not unknown  | x is not false    | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x = true          | x is not false    | f                 | t               | f                 | f               | f
       | t         | f         | f 
+ x = false         | x is not false    | f                 | f               | f                 | f               | f
       | f         | f         | t 
+ x                 | x is unknown      | f                 | f               | f                 | t               | f
       | t         | f         | t 
+ not x             | x is unknown      | f                 | t               | f                 | t               | f
       | t         | f         | t 
+ strictf(x, y)     | x is unknown      | f                 | f               | f                 | t               | f
       | t         | f         | t 
+ not strictf(x, y) | x is unknown      | f                 | t               | f                 | t               | f
       | t         | f         | t 
+ x is null         | x is unknown      | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is not null     | x is unknown      | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is true         | x is unknown      | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not true     | x is unknown      | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is false        | x is unknown      | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not false    | x is unknown      | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is unknown      | x is unknown      | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is not unknown  | x is unknown      | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x = true          | x is unknown      | f                 | f               | f                 | t               | f
       | t         | f         | t 
+ x = false         | x is unknown      | f                 | t               | f                 | t               | f
       | t         | f         | t 
+ x                 | x is not unknown  | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ not x             | x is not unknown  | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ strictf(x, y)     | x is not unknown  | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ not strictf(x, y) | x is not unknown  | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is null         | x is not unknown  | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not null     | x is not unknown  | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is true         | x is not unknown  | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is not true     | x is not unknown  | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is false        | x is not unknown  | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is not false    | x is not unknown  | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is unknown      | x is not unknown  | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not unknown  | x is not unknown  | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x = true          | x is not unknown  | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x = false         | x is not unknown  | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x                 | x = true          | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ not x             | x = true          | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ strictf(x, y)     | x = true          | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ not strictf(x, y) | x = true          | f                 | f               | f                 | f               | f
       | f         | f         | f 
+ x is null         | x = true          | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not null     | x = true          | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x is true         | x = true          | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x is not true     | x = true          | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is false        | x = true          | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not false    | x = true          | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is unknown      | x = true          | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not unknown  | x = true          | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x = true          | x = true          | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x = false         | x = true          | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x                 | x = false         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ not x             | x = false         | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ strictf(x, y)     | x = false         | f                 | f               | f                 | f               | f
       | f         | f         | t 
+ not strictf(x, y) | x = false         | f                 | f               | f                 | f               | f
       | t         | f         | f 
+ x is null         | x = false         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not null     | x = false         | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x is true         | x = false         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not true     | x = false         | t                 | t               | f                 | f               | t
       | t         | f         | f 
+ x is false        | x = false         | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x is not false    | x = false         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is unknown      | x = false         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x is not unknown  | x = false         | t                 | f               | f                 | f               | t
       | f         | f         | f 
+ x = true          | x = false         | f                 | f               | t                 | t               | f
       | f         | t         | t 
+ x = false         | x = false         | t                 | t               | f                 | f               | t
       | t         | f         | f 
+(196 rows)
+
diff --git a/src/test/modules/test_predtest/sql/test_all_permutations.sql
b/src/test/modules/test_predtest/sql/test_all_permutations.sql
new file mode 100644
index 0000000000..aa57e98498
--- /dev/null
+++ b/src/test/modules/test_predtest/sql/test_all_permutations.sql
@@ -0,0 +1,26 @@
+with clauses(expr) as (
+  values
+    ('x'),
+    ('not x'),
+    ('strictf(x, y)'),
+    ('not strictf(x, y)'),
+    ('x is null'),
+    ('x is not null'),
+    ('x is true'),
+    ('x is not true'),
+    ('x is false'),
+    ('x is not false'),
+    ('x is unknown'),
+    ('x is not unknown'),
+    ('x = true'),
+    ('x = false')
+)
+select p.expr predicate, c.expr clause, t.*
+from clauses p, clauses c
+join lateral (
+  select *
+  from test_predtest(
+    'select ' || p.expr || ', ' || c.expr ||
+    ' from booleans'
+  )
+) t on true;
--
2.39.3 (Apple Git-145)


В списке pgsql-hackers по дате отправления:

Предыдущее
От: Tom Lane
Дата:
Сообщение: Re: Add bump memory context type and use it for tuplesorts
Следующее
От: Jacob Champion
Дата:
Сообщение: Re: WIP Incremental JSON Parser