]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Optimize BooleanTest with non-nullable input
authorRichard Guo <rguo@postgresql.org>
Tue, 10 Feb 2026 01:18:47 +0000 (10:18 +0900)
committerRichard Guo <rguo@postgresql.org>
Tue, 10 Feb 2026 01:18:47 +0000 (10:18 +0900)
The BooleanTest construct (IS [NOT] TRUE/FALSE/UNKNOWN) treats a NULL
input as the logical value "unknown".  However, when the input is
proven to be non-nullable, this special handling becomes redundant.
In such cases, the construct can be simplified directly to a boolean
expression or a constant.

Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Tender Wang <tndrwang@gmail.com>
Discussion: https://postgr.es/m/CAMbWs49BMAOWvkdSHxpUDnniqJcEcGq3_8dd_5wTR4xrQY8urA@mail.gmail.com

src/backend/optimizer/util/clauses.c
src/test/regress/expected/predicate.out
src/test/regress/sql/predicate.sql

index 71c79ae4aed91b77dd7b73c7823835f4120a4bac..f73d6a68495fd2212b7e37db2ac2fce58c399809 100644 (file)
@@ -3686,6 +3686,9 @@ eval_const_expressions_mutator(Node *node,
                                                                                                         context);
                                if (arg && IsA(arg, Const))
                                {
+                                       /*
+                                        * If arg is Const, simplify to constant.
+                                        */
                                        Const      *carg = (Const *) arg;
                                        bool            result;
 
@@ -3722,6 +3725,34 @@ eval_const_expressions_mutator(Node *node,
 
                                        return makeBoolConst(result, false);
                                }
+                               if (arg && expr_is_nonnullable(context->root, (Expr *) arg, false))
+                               {
+                                       /*
+                                        * If arg is proven non-nullable, simplify to boolean
+                                        * expression or constant.
+                                        */
+                                       switch (btest->booltesttype)
+                                       {
+                                               case IS_TRUE:
+                                               case IS_NOT_FALSE:
+                                                       return arg;
+
+                                               case IS_FALSE:
+                                               case IS_NOT_TRUE:
+                                                       return (Node *) make_notclause((Expr *) arg);
+
+                                               case IS_UNKNOWN:
+                                                       return makeBoolConst(false, false);
+
+                                               case IS_NOT_UNKNOWN:
+                                                       return makeBoolConst(true, false);
+
+                                               default:
+                                                       elog(ERROR, "unrecognized booltesttype: %d",
+                                                                (int) btest->booltesttype);
+                                                       break;
+                                       }
+                               }
 
                                newbtest = makeNode(BooleanTest);
                                newbtest->arg = (Expr *) arg;
index a85234aebf6b0786bc47df7e3584863b26c40b5d..75590edf1bc9cb723aaabc4a9cde21d91d0b0374 100644 (file)
@@ -768,3 +768,122 @@ SELECT * FROM dist_tab t1 JOIN dist_tab t2 ON t1.val_nn IS NOT DISTINCT FROM t2.
 RESET enable_nestloop;
 DROP TABLE dist_tab;
 DROP TYPE dist_row_t;
+--
+-- Test optimization of BooleanTest (IS [NOT] TRUE/FALSE/UNKNOWN) on
+-- non-nullable input
+--
+CREATE TABLE bool_tab (id int, flag_nn boolean NOT NULL, flag_null boolean);
+INSERT INTO bool_tab VALUES (1, true,  true);
+INSERT INTO bool_tab VALUES (2, false, NULL);
+CREATE INDEX bool_tab_nn_idx ON bool_tab (flag_nn);
+ANALYZE bool_tab;
+-- Ensure that the predicate folds to constant FALSE
+EXPLAIN (COSTS OFF)
+SELECT id FROM bool_tab WHERE flag_nn IS UNKNOWN;
+          QUERY PLAN          
+------------------------------
+ Result
+   Replaces: Scan on bool_tab
+   One-Time Filter: false
+(3 rows)
+
+SELECT id FROM bool_tab WHERE flag_nn IS UNKNOWN;
+ id 
+----
+(0 rows)
+
+-- Ensure that the predicate folds to constant TRUE
+EXPLAIN (COSTS OFF)
+SELECT id FROM bool_tab WHERE flag_nn IS NOT UNKNOWN;
+      QUERY PLAN      
+----------------------
+ Seq Scan on bool_tab
+(1 row)
+
+SELECT id FROM bool_tab WHERE flag_nn IS NOT UNKNOWN;
+ id 
+----
+  1
+  2
+(2 rows)
+
+-- Ensure that the predicate folds to flag_nn
+EXPLAIN (COSTS OFF)
+SELECT id FROM bool_tab WHERE flag_nn IS TRUE;
+      QUERY PLAN      
+----------------------
+ Seq Scan on bool_tab
+   Filter: flag_nn
+(2 rows)
+
+SELECT id FROM bool_tab WHERE flag_nn IS TRUE;
+ id 
+----
+  1
+(1 row)
+
+-- Ensure that the predicate folds to flag_nn, and thus can use index scan
+SET enable_seqscan TO off;
+EXPLAIN (COSTS OFF)
+SELECT id FROM bool_tab WHERE flag_nn IS NOT FALSE;
+                  QUERY PLAN                  
+----------------------------------------------
+ Index Scan using bool_tab_nn_idx on bool_tab
+   Index Cond: (flag_nn = true)
+(2 rows)
+
+SELECT id FROM bool_tab WHERE flag_nn IS NOT FALSE;
+ id 
+----
+  1
+(1 row)
+
+RESET enable_seqscan;
+-- Ensure that the predicate folds to not flag_nn
+EXPLAIN (COSTS OFF)
+SELECT id FROM bool_tab WHERE flag_nn IS FALSE;
+       QUERY PLAN        
+-------------------------
+ Seq Scan on bool_tab
+   Filter: (NOT flag_nn)
+(2 rows)
+
+SELECT id FROM bool_tab WHERE flag_nn IS FALSE;
+ id 
+----
+  2
+(1 row)
+
+-- Ensure that the predicate folds to not flag_nn, and thus can use index scan
+SET enable_seqscan TO off;
+EXPLAIN (COSTS OFF)
+SELECT id FROM bool_tab WHERE flag_nn IS NOT TRUE;
+                  QUERY PLAN                  
+----------------------------------------------
+ Index Scan using bool_tab_nn_idx on bool_tab
+   Index Cond: (flag_nn = false)
+(2 rows)
+
+SELECT id FROM bool_tab WHERE flag_nn IS NOT TRUE;
+ id 
+----
+  2
+(1 row)
+
+RESET enable_seqscan;
+-- Ensure that the predicate is preserved as a BooleanTest
+EXPLAIN (COSTS OFF)
+SELECT id FROM bool_tab WHERE flag_null IS UNKNOWN;
+            QUERY PLAN            
+----------------------------------
+ Seq Scan on bool_tab
+   Filter: (flag_null IS UNKNOWN)
+(2 rows)
+
+SELECT id FROM bool_tab WHERE flag_null IS UNKNOWN;
+ id 
+----
+  2
+(1 row)
+
+DROP TABLE bool_tab;
index 3f61184c577e565b9a2dd8d62498967f0c6964a9..b2811f7f3f0050b5f5347fd16062ce181ed8d8d4 100644 (file)
@@ -369,3 +369,57 @@ RESET enable_nestloop;
 
 DROP TABLE dist_tab;
 DROP TYPE dist_row_t;
+
+--
+-- Test optimization of BooleanTest (IS [NOT] TRUE/FALSE/UNKNOWN) on
+-- non-nullable input
+--
+CREATE TABLE bool_tab (id int, flag_nn boolean NOT NULL, flag_null boolean);
+
+INSERT INTO bool_tab VALUES (1, true,  true);
+INSERT INTO bool_tab VALUES (2, false, NULL);
+
+CREATE INDEX bool_tab_nn_idx ON bool_tab (flag_nn);
+
+ANALYZE bool_tab;
+
+-- Ensure that the predicate folds to constant FALSE
+EXPLAIN (COSTS OFF)
+SELECT id FROM bool_tab WHERE flag_nn IS UNKNOWN;
+SELECT id FROM bool_tab WHERE flag_nn IS UNKNOWN;
+
+-- Ensure that the predicate folds to constant TRUE
+EXPLAIN (COSTS OFF)
+SELECT id FROM bool_tab WHERE flag_nn IS NOT UNKNOWN;
+SELECT id FROM bool_tab WHERE flag_nn IS NOT UNKNOWN;
+
+-- Ensure that the predicate folds to flag_nn
+EXPLAIN (COSTS OFF)
+SELECT id FROM bool_tab WHERE flag_nn IS TRUE;
+SELECT id FROM bool_tab WHERE flag_nn IS TRUE;
+
+-- Ensure that the predicate folds to flag_nn, and thus can use index scan
+SET enable_seqscan TO off;
+EXPLAIN (COSTS OFF)
+SELECT id FROM bool_tab WHERE flag_nn IS NOT FALSE;
+SELECT id FROM bool_tab WHERE flag_nn IS NOT FALSE;
+RESET enable_seqscan;
+
+-- Ensure that the predicate folds to not flag_nn
+EXPLAIN (COSTS OFF)
+SELECT id FROM bool_tab WHERE flag_nn IS FALSE;
+SELECT id FROM bool_tab WHERE flag_nn IS FALSE;
+
+-- Ensure that the predicate folds to not flag_nn, and thus can use index scan
+SET enable_seqscan TO off;
+EXPLAIN (COSTS OFF)
+SELECT id FROM bool_tab WHERE flag_nn IS NOT TRUE;
+SELECT id FROM bool_tab WHERE flag_nn IS NOT TRUE;
+RESET enable_seqscan;
+
+-- Ensure that the predicate is preserved as a BooleanTest
+EXPLAIN (COSTS OFF)
+SELECT id FROM bool_tab WHERE flag_null IS UNKNOWN;
+SELECT id FROM bool_tab WHERE flag_null IS UNKNOWN;
+
+DROP TABLE bool_tab;