From 13cd2db9ffcbb8482ea792fa1dc41aa4a234a278 Mon Sep 17 00:00:00 2001 From: Julien Tachoires Date: Tue, 2 Dec 2025 10:48:42 +0100 Subject: [PATCH 7/7] Push down IS/IS NOT NULL quals to table AMs This commit adds suuport for IS/IS NOT NULL where clauses push down to table AMs during table scan. --- src/backend/access/heap/heapam_valid.c | 16 ++- src/backend/executor/nodeSeqscan.c | 77 +++++++++-- src/backend/optimizer/plan/createplan.c | 30 +++++ src/test/regress/expected/qual_pushdown.out | 141 +++++++++++++------- src/test/regress/sql/qual_pushdown.sql | 7 + 5 files changed, 209 insertions(+), 62 deletions(-) diff --git a/src/backend/access/heap/heapam_valid.c b/src/backend/access/heap/heapam_valid.c index 7261723e378..a05738a9144 100644 --- a/src/backend/access/heap/heapam_valid.c +++ b/src/backend/access/heap/heapam_valid.c @@ -129,7 +129,12 @@ HeapKeyTest(HeapTuple tuple, TupleDesc tupdesc, int nkeys, ScanKey keys) bool isnull; Datum test; - if (cur_key->sk_flags & SK_ISNULL) + /* + * When the SK_ISNULL flag is set but we are not handling the IS/IS + * NOT NULL case + */ + if ((cur_key->sk_flags & SK_ISNULL) + && !(cur_key->sk_flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL))) return false; atp = heap_getattr(tuple, cur_key->sk_attno, tupdesc, &isnull); @@ -272,6 +277,15 @@ HeapKeyTest(HeapTuple tuple, TupleDesc tupdesc, int nkeys, ScanKey keys) return false; } + /* IS/IS NOT NULL case */ + else if ((cur_key->sk_flags & SK_ISNULL) + && (cur_key->sk_flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL))) + { + if ((cur_key->sk_flags & SK_SEARCHNULL) && !isnull) + return false; + if ((cur_key->sk_flags & SK_SEARCHNOTNULL) && isnull) + return false; + } else { if (isnull) diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index b5f0d8c23b9..30a9b092267 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -167,6 +167,18 @@ ExecSeqBuildScanKeys(PlanState *planstate, List *quals, int *numScanKeys, n_runtime_keys++; scanvalue = (Datum) 0; } + + n_scan_keys++; + + ScanKeyEntryInitialize(this_scan_key, + flags, + varattno, + InvalidStrategy, /* no strategy */ + InvalidOid, /* no subtype */ + collationid, + opfuncid, + scanvalue); + } /* ANY/ALL (array-expression) */ else if (IsA(clause, ScalarArrayOpExpr)) @@ -310,6 +322,58 @@ ExecSeqBuildScanKeys(PlanState *planstate, List *quals, int *numScanKeys, saop->inputcollid, NULL, NULL); + + n_scan_keys++; + + ScanKeyEntryInitialize(this_scan_key, + flags, + varattno, + InvalidStrategy, /* no strategy */ + InvalidOid, /* no subtype */ + collationid, + opfuncid, + scanvalue); + + ScanKeyEntrySetHashInfo(this_scan_key, skeyhashinfo); + + } + /* IS/IS NOT NULL */ + else if (IsA(clause, NullTest)) + { + NullTest *ntest = (NullTest *) clause; + + leftop = ntest->arg; + collationid = InvalidOid; + + varattno = ((Var *) leftop)->varattno; + + /* + * initialize the scan key's fields appropriately + */ + switch (ntest->nulltesttype) + { + case IS_NULL: + flags = SK_ISNULL | SK_SEARCHNULL; + break; + case IS_NOT_NULL: + flags = SK_ISNULL | SK_SEARCHNOTNULL; + break; + default: + elog(ERROR, "unrecognized nulltesttype: %d", + (int) ntest->nulltesttype); + break; + } + + n_scan_keys++; + + ScanKeyEntryInitialize(this_scan_key, + flags, + varattno, + InvalidStrategy, /* no strategy */ + InvalidOid, /* no subtype */ + InvalidOid, /* no collation */ + InvalidOid, /* no reg proc for this */ + (Datum) 0); /* constant */ } else { @@ -318,19 +382,6 @@ ExecSeqBuildScanKeys(PlanState *planstate, List *quals, int *numScanKeys, */ continue; } - - n_scan_keys++; - - ScanKeyEntryInitialize(this_scan_key, - flags, - varattno, - InvalidStrategy, /* no strategy */ - InvalidOid, /* no subtype */ - collationid, - opfuncid, - scanvalue); - - ScanKeyEntrySetHashInfo(this_scan_key, skeyhashinfo); } /* diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 982bdd08e74..1b6fba85ba7 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -5433,6 +5433,36 @@ fix_tablequal_references(PlannerInfo *root, Path *best_path, fixed_tablequals = lappend(fixed_tablequals, clause); break; } + + /* + * NullTest: IS/IS NOT NULL + */ + case T_NullTest: + { + NullTest *nt = (NullTest *) clause; + Expr *leftop; + + leftop = (Expr *) nt->arg; + + /* + * Handle relabeling and make sure our left part is a + * column name. + */ + if (leftop && IsA(leftop, RelabelType)) + leftop = ((RelabelType *) leftop)->arg; + + if (leftop == NULL) + continue; + + if (!(IsA(leftop, Var) && ((Var *) leftop)->varattno > 0)) + continue; + + /* Override Null test arg in case of relabeling */ + nt->arg = leftop; + + fixed_tablequals = lappend(fixed_tablequals, clause); + break; + } default: continue; } diff --git a/src/test/regress/expected/qual_pushdown.out b/src/test/regress/expected/qual_pushdown.out index 75fc9c93ad0..60baa3f4275 100644 --- a/src/test/regress/expected/qual_pushdown.out +++ b/src/test/regress/expected/qual_pushdown.out @@ -6,16 +6,17 @@ CREATE TABLE qa (i INTEGER, ii INTEGER); CREATE TABLE qb (j INTEGER); INSERT INTO qa SELECT n, n * n FROM generate_series(1, 1000) as n; INSERT INTO qb SELECT n FROM generate_series(1, 1000) as n; +INSERT INTO qa VALUES (1001, NULL); ANALYZE qa; ANALYZE qb; -- By default, the quals are not pushed down. The tuples are filtered out by -- the executor. EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE i = 100; - QUERY PLAN -------------------------------------------- + QUERY PLAN +-------------------------------------------- Seq Scan on qa (actual rows=1.00 loops=1) Filter: (i = 100) - Rows Removed In Executor by Filter: 999 + Rows Removed In Executor by Filter: 1000 (3 rows) EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE i < 10; @@ -23,15 +24,15 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO ------------------------------------------- Seq Scan on qa (actual rows=9.00 loops=1) Filter: (i < 10) - Rows Removed In Executor by Filter: 991 + Rows Removed In Executor by Filter: 992 (3 rows) EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE 100 = i; - QUERY PLAN -------------------------------------------- + QUERY PLAN +-------------------------------------------- Seq Scan on qa (actual rows=1.00 loops=1) Filter: (100 = i) - Rows Removed In Executor by Filter: 999 + Rows Removed In Executor by Filter: 1000 (3 rows) EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE 10 > i; @@ -39,23 +40,23 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO ------------------------------------------- Seq Scan on qa (actual rows=9.00 loops=1) Filter: (10 > i) - Rows Removed In Executor by Filter: 991 + Rows Removed In Executor by Filter: 992 (3 rows) EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE i = SQRT(25)::INT; - QUERY PLAN -------------------------------------------- + QUERY PLAN +-------------------------------------------- Seq Scan on qa (actual rows=1.00 loops=1) Filter: (i = 5) - Rows Removed In Executor by Filter: 999 + Rows Removed In Executor by Filter: 1000 (3 rows) EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE i = (SELECT 100); - QUERY PLAN -------------------------------------------- + QUERY PLAN +-------------------------------------------- Seq Scan on qa (actual rows=1.00 loops=1) Filter: (i = (InitPlan expr_1).col1) - Rows Removed In Executor by Filter: 999 + Rows Removed In Executor by Filter: 1000 InitPlan expr_1 -> Result (actual rows=1.00 loops=1) (5 rows) @@ -65,7 +66,7 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO --------------------------------------------------- Seq Scan on qa (actual rows=1.00 loops=1) Filter: (i = (InitPlan expr_1).col1) - Rows Removed In Executor by Filter: 999 + Rows Removed In Executor by Filter: 1000 InitPlan expr_1 -> Seq Scan on qb (actual rows=1.00 loops=1) Filter: (j = 100) @@ -73,23 +74,23 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO (7 rows) EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa JOIN qb ON (qa.i = qb.j) WHERE j = 100; - QUERY PLAN -------------------------------------------------- + QUERY PLAN +-------------------------------------------------- Nested Loop (actual rows=1.00 loops=1) -> Seq Scan on qa (actual rows=1.00 loops=1) Filter: (i = 100) - Rows Removed In Executor by Filter: 999 + Rows Removed In Executor by Filter: 1000 -> Seq Scan on qb (actual rows=1.00 loops=1) Filter: (j = 100) Rows Removed In Executor by Filter: 999 (7 rows) EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE i = ii AND ii < 10; - QUERY PLAN -------------------------------------------- + QUERY PLAN +-------------------------------------------- Seq Scan on qa (actual rows=1.00 loops=1) Filter: ((ii < 10) AND (i = ii)) - Rows Removed In Executor by Filter: 999 + Rows Removed In Executor by Filter: 1000 (3 rows) EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE i = ANY('{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}'::INT[]); @@ -97,7 +98,7 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO ----------------------------------------------------------- Seq Scan on qa (actual rows=10.00 loops=1) Filter: (i = ANY ('{1,2,3,4,5,6,7,8,9,10}'::integer[])) - Rows Removed In Executor by Filter: 990 + Rows Removed In Executor by Filter: 991 (3 rows) EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE i = ANY('{1, 2}'::INT[]); @@ -105,7 +106,7 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO ------------------------------------------- Seq Scan on qa (actual rows=2.00 loops=1) Filter: (i = ANY ('{1,2}'::integer[])) - Rows Removed In Executor by Filter: 998 + Rows Removed In Executor by Filter: 999 (3 rows) EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE NOT (i <> ALL('{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}'::INT[])); @@ -113,7 +114,7 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO ----------------------------------------------------------- Seq Scan on qa (actual rows=10.00 loops=1) Filter: (i = ANY ('{1,2,3,4,5,6,7,8,9,10}'::integer[])) - Rows Removed In Executor by Filter: 990 + Rows Removed In Executor by Filter: 991 (3 rows) EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE i = ANY((SELECT array_agg(j) FROM qb WHERE j > 50 AND j <= 60)::int[]); @@ -121,7 +122,7 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO ---------------------------------------------------------- Seq Scan on qa (actual rows=10.00 loops=1) Filter: (i = ANY ((InitPlan expr_1).col1)) - Rows Removed In Executor by Filter: 990 + Rows Removed In Executor by Filter: 991 InitPlan expr_1 -> Aggregate (actual rows=1.00 loops=1) -> Seq Scan on qb (actual rows=10.00 loops=1) @@ -129,16 +130,32 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO Rows Removed In Executor by Filter: 990 (8 rows) +EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT i, ii FROM qa WHERE ii IS NULL; + QUERY PLAN +-------------------------------------------- + Seq Scan on qa (actual rows=1.00 loops=1) + Filter: (ii IS NULL) + Rows Removed In Executor by Filter: 1000 +(3 rows) + +EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT i, ii FROM qa WHERE ii IS NOT NULL AND i >= 1000;; + QUERY PLAN +---------------------------------------------- + Seq Scan on qa (actual rows=1.00 loops=1) + Filter: ((ii IS NOT NULL) AND (i >= 1000)) + Rows Removed In Executor by Filter: 1000 +(3 rows) + -- Enable quals push down ALTER TABLE qa SET (quals_push_down=on); ALTER TABLE qb SET (quals_push_down=on); -- Now, we expect to see the tuples being filtered out by the table AM EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE i = 100; - QUERY PLAN -------------------------------------------- + QUERY PLAN +-------------------------------------------- Seq Scan on qa (actual rows=1.00 loops=1) Filter: (i = 100) - Rows Removed In Table AM by Filter: 999 + Rows Removed In Table AM by Filter: 1000 (3 rows) SELECT ii FROM qa WHERE i = 100; @@ -152,7 +169,7 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO ------------------------------------------- Seq Scan on qa (actual rows=9.00 loops=1) Filter: (i < 10) - Rows Removed In Table AM by Filter: 991 + Rows Removed In Table AM by Filter: 992 (3 rows) SELECT ii FROM qa WHERE i < 10; @@ -170,11 +187,11 @@ SELECT ii FROM qa WHERE i < 10; (9 rows) EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE 100 = i; - QUERY PLAN -------------------------------------------- + QUERY PLAN +-------------------------------------------- Seq Scan on qa (actual rows=1.00 loops=1) Filter: (100 = i) - Rows Removed In Table AM by Filter: 999 + Rows Removed In Table AM by Filter: 1000 (3 rows) SELECT ii FROM qa WHERE 100 = i; @@ -188,7 +205,7 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO ------------------------------------------- Seq Scan on qa (actual rows=9.00 loops=1) Filter: (10 > i) - Rows Removed In Table AM by Filter: 991 + Rows Removed In Table AM by Filter: 992 (3 rows) SELECT ii FROM qa WHERE 10 > i; @@ -206,11 +223,11 @@ SELECT ii FROM qa WHERE 10 > i; (9 rows) EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE i = SQRT(25)::INT; - QUERY PLAN -------------------------------------------- + QUERY PLAN +-------------------------------------------- Seq Scan on qa (actual rows=1.00 loops=1) Filter: (i = 5) - Rows Removed In Table AM by Filter: 999 + Rows Removed In Table AM by Filter: 1000 (3 rows) SELECT ii FROM qa WHERE i = SQRT(25)::INT; @@ -220,11 +237,11 @@ SELECT ii FROM qa WHERE i = SQRT(25)::INT; (1 row) EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE i = (SELECT 100); - QUERY PLAN -------------------------------------------- + QUERY PLAN +-------------------------------------------- Seq Scan on qa (actual rows=1.00 loops=1) Filter: (i = (InitPlan expr_1).col1) - Rows Removed In Table AM by Filter: 999 + Rows Removed In Table AM by Filter: 1000 InitPlan expr_1 -> Result (actual rows=1.00 loops=1) (5 rows) @@ -240,7 +257,7 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO --------------------------------------------------- Seq Scan on qa (actual rows=1.00 loops=1) Filter: (i = (InitPlan expr_1).col1) - Rows Removed In Table AM by Filter: 999 + Rows Removed In Table AM by Filter: 1000 InitPlan expr_1 -> Seq Scan on qb (actual rows=1.00 loops=1) Filter: (j = 100) @@ -254,12 +271,12 @@ SELECT ii FROM qa WHERE i = (SELECT SQRT(j)::INT FROM qb WHERE j = 100); (1 row) EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa JOIN qb ON (qa.i = qb.j) WHERE j = 100; - QUERY PLAN -------------------------------------------------- + QUERY PLAN +-------------------------------------------------- Nested Loop (actual rows=1.00 loops=1) -> Seq Scan on qa (actual rows=1.00 loops=1) Filter: (i = 100) - Rows Removed In Table AM by Filter: 999 + Rows Removed In Table AM by Filter: 1000 -> Seq Scan on qb (actual rows=1.00 loops=1) Filter: (j = 100) Rows Removed In Table AM by Filter: 999 @@ -276,7 +293,7 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO ------------------------------------------- Seq Scan on qa (actual rows=1.00 loops=1) Filter: ((ii < 10) AND (i = ii)) - Rows Removed In Table AM by Filter: 997 + Rows Removed In Table AM by Filter: 998 Rows Removed In Executor by Filter: 2 (4 rows) @@ -291,7 +308,7 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO ----------------------------------------------------------- Seq Scan on qa (actual rows=10.00 loops=1) Filter: (i = ANY ('{1,2,3,4,5,6,7,8,9,10}'::integer[])) - Rows Removed In Table AM by Filter: 990 + Rows Removed In Table AM by Filter: 991 (3 rows) SELECT ii FROM qa WHERE i = ANY('{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}'::INT[]); @@ -314,7 +331,7 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO ------------------------------------------- Seq Scan on qa (actual rows=2.00 loops=1) Filter: (i = ANY ('{1,2}'::integer[])) - Rows Removed In Table AM by Filter: 998 + Rows Removed In Table AM by Filter: 999 (3 rows) SELECT ii FROM qa WHERE i = ANY('{1, 2}'::INT[]); @@ -329,7 +346,7 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO ----------------------------------------------------------- Seq Scan on qa (actual rows=10.00 loops=1) Filter: (i = ANY ('{1,2,3,4,5,6,7,8,9,10}'::integer[])) - Rows Removed In Table AM by Filter: 990 + Rows Removed In Table AM by Filter: 991 (3 rows) SELECT ii FROM qa WHERE NOT (i <> ALL('{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}'::INT[])); @@ -352,7 +369,7 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO ---------------------------------------------------------- Seq Scan on qa (actual rows=10.00 loops=1) Filter: (i = ANY ((InitPlan expr_1).col1)) - Rows Removed In Table AM by Filter: 990 + Rows Removed In Table AM by Filter: 991 InitPlan expr_1 -> Aggregate (actual rows=1.00 loops=1) -> Seq Scan on qb (actual rows=10.00 loops=1) @@ -375,5 +392,33 @@ SELECT ii FROM qa WHERE i = ANY((SELECT array_agg(j) FROM qb WHERE j > 50 AND j 3600 (10 rows) +EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT i, ii FROM qa WHERE ii IS NULL; + QUERY PLAN +-------------------------------------------- + Seq Scan on qa (actual rows=1.00 loops=1) + Filter: (ii IS NULL) + Rows Removed In Table AM by Filter: 1000 +(3 rows) + +SELECT i, ii FROM qa WHERE ii IS NULL; + i | ii +------+---- + 1001 | +(1 row) + +EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT i, ii FROM qa WHERE ii IS NOT NULL AND i >= 1000;; + QUERY PLAN +---------------------------------------------- + Seq Scan on qa (actual rows=1.00 loops=1) + Filter: ((ii IS NOT NULL) AND (i >= 1000)) + Rows Removed In Table AM by Filter: 1000 +(3 rows) + +SELECT i, ii FROM qa WHERE ii IS NOT NULL AND i >= 1000;; + i | ii +------+--------- + 1000 | 1000000 +(1 row) + DROP TABLE IF EXISTS qa; DROP TABLE IF EXISTS qb; diff --git a/src/test/regress/sql/qual_pushdown.sql b/src/test/regress/sql/qual_pushdown.sql index 38e88a50c33..50d6f9b316a 100644 --- a/src/test/regress/sql/qual_pushdown.sql +++ b/src/test/regress/sql/qual_pushdown.sql @@ -5,6 +5,7 @@ CREATE TABLE qa (i INTEGER, ii INTEGER); CREATE TABLE qb (j INTEGER); INSERT INTO qa SELECT n, n * n FROM generate_series(1, 1000) as n; INSERT INTO qb SELECT n FROM generate_series(1, 1000) as n; +INSERT INTO qa VALUES (1001, NULL); ANALYZE qa; ANALYZE qb; @@ -23,6 +24,8 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE i = ANY('{1, 2}'::INT[]); EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE NOT (i <> ALL('{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}'::INT[])); EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE i = ANY((SELECT array_agg(j) FROM qb WHERE j > 50 AND j <= 60)::int[]); +EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT i, ii FROM qa WHERE ii IS NULL; +EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT i, ii FROM qa WHERE ii IS NOT NULL AND i >= 1000;; -- Enable quals push down ALTER TABLE qa SET (quals_push_down=on); @@ -55,6 +58,10 @@ EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FRO SELECT ii FROM qa WHERE NOT (i <> ALL('{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}'::INT[])); EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT ii FROM qa WHERE i = ANY((SELECT array_agg(j) FROM qb WHERE j > 50 AND j <= 60)::INT[]); SELECT ii FROM qa WHERE i = ANY((SELECT array_agg(j) FROM qb WHERE j > 50 AND j <= 60)::INT[]); +EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT i, ii FROM qa WHERE ii IS NULL; +SELECT i, ii FROM qa WHERE ii IS NULL; +EXPLAIN (ANALYZE, COSTS off, TIMING off, SUMMARY off, BUFFERS off) SELECT i, ii FROM qa WHERE ii IS NOT NULL AND i >= 1000;; +SELECT i, ii FROM qa WHERE ii IS NOT NULL AND i >= 1000;; DROP TABLE IF EXISTS qa; DROP TABLE IF EXISTS qb; -- 2.39.5