mirror of
https://github.com/postgres/postgres.git
synced 2026-04-21 06:08:26 -04:00
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 <guofenglinux@gmail.com> Reviewed-by: Tender Wang <tndrwang@gmail.com> Discussion: https://postgr.es/m/CAMbWs49BMAOWvkdSHxpUDnniqJcEcGq3_8dd_5wTR4xrQY8urA@mail.gmail.com
This commit is contained in:
parent
0a37961254
commit
0aaf0de7fe
3 changed files with 204 additions and 0 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue