From 0aaf0de7fed8555114aca766e9cdc836dad763a3 Mon Sep 17 00:00:00 2001 From: Richard Guo Date: Tue, 10 Feb 2026 10:18:47 +0900 Subject: [PATCH] Optimize BooleanTest with non-nullable input 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 Reviewed-by: Tender Wang Discussion: https://postgr.es/m/CAMbWs49BMAOWvkdSHxpUDnniqJcEcGq3_8dd_5wTR4xrQY8urA@mail.gmail.com --- src/backend/optimizer/util/clauses.c | 31 ++++++ src/test/regress/expected/predicate.out | 119 ++++++++++++++++++++++++ src/test/regress/sql/predicate.sql | 54 +++++++++++ 3 files changed, 204 insertions(+) diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 71c79ae4aed..f73d6a68495 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -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; diff --git a/src/test/regress/expected/predicate.out b/src/test/regress/expected/predicate.out index a85234aebf6..75590edf1bc 100644 --- a/src/test/regress/expected/predicate.out +++ b/src/test/regress/expected/predicate.out @@ -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; diff --git a/src/test/regress/sql/predicate.sql b/src/test/regress/sql/predicate.sql index 3f61184c577..b2811f7f3f0 100644 --- a/src/test/regress/sql/predicate.sql +++ b/src/test/regress/sql/predicate.sql @@ -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; -- 2.47.3