summaryrefslogtreecommitdiffstats
path: root/src/test/regress/expected/partition_prune.out
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:17:33 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:17:33 +0000
commit5e45211a64149b3c659b90ff2de6fa982a5a93ed (patch)
tree739caf8c461053357daa9f162bef34516c7bf452 /src/test/regress/expected/partition_prune.out
parentInitial commit. (diff)
downloadpostgresql-15-5e45211a64149b3c659b90ff2de6fa982a5a93ed.tar.xz
postgresql-15-5e45211a64149b3c659b90ff2de6fa982a5a93ed.zip
Adding upstream version 15.5.upstream/15.5
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/regress/expected/partition_prune.out')
-rw-r--r--src/test/regress/expected/partition_prune.out4287
1 files changed, 4287 insertions, 0 deletions
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
new file mode 100644
index 0000000..c5bc6c3
--- /dev/null
+++ b/src/test/regress/expected/partition_prune.out
@@ -0,0 +1,4287 @@
+--
+-- Test partitioning planner code
+--
+-- Force generic plans to be used for all prepared statements in this file.
+set plan_cache_mode = force_generic_plan;
+create table lp (a char) partition by list (a);
+create table lp_default partition of lp default;
+create table lp_ef partition of lp for values in ('e', 'f');
+create table lp_ad partition of lp for values in ('a', 'd');
+create table lp_bc partition of lp for values in ('b', 'c');
+create table lp_g partition of lp for values in ('g');
+create table lp_null partition of lp for values in (null);
+explain (costs off) select * from lp;
+ QUERY PLAN
+-----------------------------------
+ Append
+ -> Seq Scan on lp_ad lp_1
+ -> Seq Scan on lp_bc lp_2
+ -> Seq Scan on lp_ef lp_3
+ -> Seq Scan on lp_g lp_4
+ -> Seq Scan on lp_null lp_5
+ -> Seq Scan on lp_default lp_6
+(7 rows)
+
+explain (costs off) select * from lp where a > 'a' and a < 'd';
+ QUERY PLAN
+-----------------------------------------------------------
+ Append
+ -> Seq Scan on lp_bc lp_1
+ Filter: ((a > 'a'::bpchar) AND (a < 'd'::bpchar))
+ -> Seq Scan on lp_default lp_2
+ Filter: ((a > 'a'::bpchar) AND (a < 'd'::bpchar))
+(5 rows)
+
+explain (costs off) select * from lp where a > 'a' and a <= 'd';
+ QUERY PLAN
+------------------------------------------------------------
+ Append
+ -> Seq Scan on lp_ad lp_1
+ Filter: ((a > 'a'::bpchar) AND (a <= 'd'::bpchar))
+ -> Seq Scan on lp_bc lp_2
+ Filter: ((a > 'a'::bpchar) AND (a <= 'd'::bpchar))
+ -> Seq Scan on lp_default lp_3
+ Filter: ((a > 'a'::bpchar) AND (a <= 'd'::bpchar))
+(7 rows)
+
+explain (costs off) select * from lp where a = 'a';
+ QUERY PLAN
+-----------------------------
+ Seq Scan on lp_ad lp
+ Filter: (a = 'a'::bpchar)
+(2 rows)
+
+explain (costs off) select * from lp where 'a' = a; /* commuted */
+ QUERY PLAN
+-----------------------------
+ Seq Scan on lp_ad lp
+ Filter: ('a'::bpchar = a)
+(2 rows)
+
+explain (costs off) select * from lp where a is not null;
+ QUERY PLAN
+-----------------------------------
+ Append
+ -> Seq Scan on lp_ad lp_1
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on lp_bc lp_2
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on lp_ef lp_3
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on lp_g lp_4
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on lp_default lp_5
+ Filter: (a IS NOT NULL)
+(11 rows)
+
+explain (costs off) select * from lp where a is null;
+ QUERY PLAN
+------------------------
+ Seq Scan on lp_null lp
+ Filter: (a IS NULL)
+(2 rows)
+
+explain (costs off) select * from lp where a = 'a' or a = 'c';
+ QUERY PLAN
+----------------------------------------------------------
+ Append
+ -> Seq Scan on lp_ad lp_1
+ Filter: ((a = 'a'::bpchar) OR (a = 'c'::bpchar))
+ -> Seq Scan on lp_bc lp_2
+ Filter: ((a = 'a'::bpchar) OR (a = 'c'::bpchar))
+(5 rows)
+
+explain (costs off) select * from lp where a is not null and (a = 'a' or a = 'c');
+ QUERY PLAN
+--------------------------------------------------------------------------------
+ Append
+ -> Seq Scan on lp_ad lp_1
+ Filter: ((a IS NOT NULL) AND ((a = 'a'::bpchar) OR (a = 'c'::bpchar)))
+ -> Seq Scan on lp_bc lp_2
+ Filter: ((a IS NOT NULL) AND ((a = 'a'::bpchar) OR (a = 'c'::bpchar)))
+(5 rows)
+
+explain (costs off) select * from lp where a <> 'g';
+ QUERY PLAN
+------------------------------------
+ Append
+ -> Seq Scan on lp_ad lp_1
+ Filter: (a <> 'g'::bpchar)
+ -> Seq Scan on lp_bc lp_2
+ Filter: (a <> 'g'::bpchar)
+ -> Seq Scan on lp_ef lp_3
+ Filter: (a <> 'g'::bpchar)
+ -> Seq Scan on lp_default lp_4
+ Filter: (a <> 'g'::bpchar)
+(9 rows)
+
+explain (costs off) select * from lp where a <> 'a' and a <> 'd';
+ QUERY PLAN
+-------------------------------------------------------------
+ Append
+ -> Seq Scan on lp_bc lp_1
+ Filter: ((a <> 'a'::bpchar) AND (a <> 'd'::bpchar))
+ -> Seq Scan on lp_ef lp_2
+ Filter: ((a <> 'a'::bpchar) AND (a <> 'd'::bpchar))
+ -> Seq Scan on lp_g lp_3
+ Filter: ((a <> 'a'::bpchar) AND (a <> 'd'::bpchar))
+ -> Seq Scan on lp_default lp_4
+ Filter: ((a <> 'a'::bpchar) AND (a <> 'd'::bpchar))
+(9 rows)
+
+explain (costs off) select * from lp where a not in ('a', 'd');
+ QUERY PLAN
+------------------------------------------------
+ Append
+ -> Seq Scan on lp_bc lp_1
+ Filter: (a <> ALL ('{a,d}'::bpchar[]))
+ -> Seq Scan on lp_ef lp_2
+ Filter: (a <> ALL ('{a,d}'::bpchar[]))
+ -> Seq Scan on lp_g lp_3
+ Filter: (a <> ALL ('{a,d}'::bpchar[]))
+ -> Seq Scan on lp_default lp_4
+ Filter: (a <> ALL ('{a,d}'::bpchar[]))
+(9 rows)
+
+-- collation matches the partitioning collation, pruning works
+create table coll_pruning (a text collate "C") partition by list (a);
+create table coll_pruning_a partition of coll_pruning for values in ('a');
+create table coll_pruning_b partition of coll_pruning for values in ('b');
+create table coll_pruning_def partition of coll_pruning default;
+explain (costs off) select * from coll_pruning where a collate "C" = 'a' collate "C";
+ QUERY PLAN
+-----------------------------------------
+ Seq Scan on coll_pruning_a coll_pruning
+ Filter: (a = 'a'::text COLLATE "C")
+(2 rows)
+
+-- collation doesn't match the partitioning collation, no pruning occurs
+explain (costs off) select * from coll_pruning where a collate "POSIX" = 'a' collate "POSIX";
+ QUERY PLAN
+---------------------------------------------------------
+ Append
+ -> Seq Scan on coll_pruning_a coll_pruning_1
+ Filter: ((a)::text = 'a'::text COLLATE "POSIX")
+ -> Seq Scan on coll_pruning_b coll_pruning_2
+ Filter: ((a)::text = 'a'::text COLLATE "POSIX")
+ -> Seq Scan on coll_pruning_def coll_pruning_3
+ Filter: ((a)::text = 'a'::text COLLATE "POSIX")
+(7 rows)
+
+create table rlp (a int, b varchar) partition by range (a);
+create table rlp_default partition of rlp default partition by list (a);
+create table rlp_default_default partition of rlp_default default;
+create table rlp_default_10 partition of rlp_default for values in (10);
+create table rlp_default_30 partition of rlp_default for values in (30);
+create table rlp_default_null partition of rlp_default for values in (null);
+create table rlp1 partition of rlp for values from (minvalue) to (1);
+create table rlp2 partition of rlp for values from (1) to (10);
+create table rlp3 (b varchar, a int) partition by list (b varchar_ops);
+create table rlp3_default partition of rlp3 default;
+create table rlp3abcd partition of rlp3 for values in ('ab', 'cd');
+create table rlp3efgh partition of rlp3 for values in ('ef', 'gh');
+create table rlp3nullxy partition of rlp3 for values in (null, 'xy');
+alter table rlp attach partition rlp3 for values from (15) to (20);
+create table rlp4 partition of rlp for values from (20) to (30) partition by range (a);
+create table rlp4_default partition of rlp4 default;
+create table rlp4_1 partition of rlp4 for values from (20) to (25);
+create table rlp4_2 partition of rlp4 for values from (25) to (29);
+create table rlp5 partition of rlp for values from (31) to (maxvalue) partition by range (a);
+create table rlp5_default partition of rlp5 default;
+create table rlp5_1 partition of rlp5 for values from (31) to (40);
+explain (costs off) select * from rlp where a < 1;
+ QUERY PLAN
+----------------------
+ Seq Scan on rlp1 rlp
+ Filter: (a < 1)
+(2 rows)
+
+explain (costs off) select * from rlp where 1 > a; /* commuted */
+ QUERY PLAN
+----------------------
+ Seq Scan on rlp1 rlp
+ Filter: (1 > a)
+(2 rows)
+
+explain (costs off) select * from rlp where a <= 1;
+ QUERY PLAN
+------------------------------
+ Append
+ -> Seq Scan on rlp1 rlp_1
+ Filter: (a <= 1)
+ -> Seq Scan on rlp2 rlp_2
+ Filter: (a <= 1)
+(5 rows)
+
+explain (costs off) select * from rlp where a = 1;
+ QUERY PLAN
+----------------------
+ Seq Scan on rlp2 rlp
+ Filter: (a = 1)
+(2 rows)
+
+explain (costs off) select * from rlp where a = 1::bigint; /* same as above */
+ QUERY PLAN
+-----------------------------
+ Seq Scan on rlp2 rlp
+ Filter: (a = '1'::bigint)
+(2 rows)
+
+explain (costs off) select * from rlp where a = 1::numeric; /* no pruning */
+ QUERY PLAN
+-----------------------------------------------
+ Append
+ -> Seq Scan on rlp1 rlp_1
+ Filter: ((a)::numeric = '1'::numeric)
+ -> Seq Scan on rlp2 rlp_2
+ Filter: ((a)::numeric = '1'::numeric)
+ -> Seq Scan on rlp3abcd rlp_3
+ Filter: ((a)::numeric = '1'::numeric)
+ -> Seq Scan on rlp3efgh rlp_4
+ Filter: ((a)::numeric = '1'::numeric)
+ -> Seq Scan on rlp3nullxy rlp_5
+ Filter: ((a)::numeric = '1'::numeric)
+ -> Seq Scan on rlp3_default rlp_6
+ Filter: ((a)::numeric = '1'::numeric)
+ -> Seq Scan on rlp4_1 rlp_7
+ Filter: ((a)::numeric = '1'::numeric)
+ -> Seq Scan on rlp4_2 rlp_8
+ Filter: ((a)::numeric = '1'::numeric)
+ -> Seq Scan on rlp4_default rlp_9
+ Filter: ((a)::numeric = '1'::numeric)
+ -> Seq Scan on rlp5_1 rlp_10
+ Filter: ((a)::numeric = '1'::numeric)
+ -> Seq Scan on rlp5_default rlp_11
+ Filter: ((a)::numeric = '1'::numeric)
+ -> Seq Scan on rlp_default_10 rlp_12
+ Filter: ((a)::numeric = '1'::numeric)
+ -> Seq Scan on rlp_default_30 rlp_13
+ Filter: ((a)::numeric = '1'::numeric)
+ -> Seq Scan on rlp_default_null rlp_14
+ Filter: ((a)::numeric = '1'::numeric)
+ -> Seq Scan on rlp_default_default rlp_15
+ Filter: ((a)::numeric = '1'::numeric)
+(31 rows)
+
+explain (costs off) select * from rlp where a <= 10;
+ QUERY PLAN
+---------------------------------------------
+ Append
+ -> Seq Scan on rlp1 rlp_1
+ Filter: (a <= 10)
+ -> Seq Scan on rlp2 rlp_2
+ Filter: (a <= 10)
+ -> Seq Scan on rlp_default_10 rlp_3
+ Filter: (a <= 10)
+ -> Seq Scan on rlp_default_default rlp_4
+ Filter: (a <= 10)
+(9 rows)
+
+explain (costs off) select * from rlp where a > 10;
+ QUERY PLAN
+----------------------------------------------
+ Append
+ -> Seq Scan on rlp3abcd rlp_1
+ Filter: (a > 10)
+ -> Seq Scan on rlp3efgh rlp_2
+ Filter: (a > 10)
+ -> Seq Scan on rlp3nullxy rlp_3
+ Filter: (a > 10)
+ -> Seq Scan on rlp3_default rlp_4
+ Filter: (a > 10)
+ -> Seq Scan on rlp4_1 rlp_5
+ Filter: (a > 10)
+ -> Seq Scan on rlp4_2 rlp_6
+ Filter: (a > 10)
+ -> Seq Scan on rlp4_default rlp_7
+ Filter: (a > 10)
+ -> Seq Scan on rlp5_1 rlp_8
+ Filter: (a > 10)
+ -> Seq Scan on rlp5_default rlp_9
+ Filter: (a > 10)
+ -> Seq Scan on rlp_default_30 rlp_10
+ Filter: (a > 10)
+ -> Seq Scan on rlp_default_default rlp_11
+ Filter: (a > 10)
+(23 rows)
+
+explain (costs off) select * from rlp where a < 15;
+ QUERY PLAN
+---------------------------------------------
+ Append
+ -> Seq Scan on rlp1 rlp_1
+ Filter: (a < 15)
+ -> Seq Scan on rlp2 rlp_2
+ Filter: (a < 15)
+ -> Seq Scan on rlp_default_10 rlp_3
+ Filter: (a < 15)
+ -> Seq Scan on rlp_default_default rlp_4
+ Filter: (a < 15)
+(9 rows)
+
+explain (costs off) select * from rlp where a <= 15;
+ QUERY PLAN
+---------------------------------------------
+ Append
+ -> Seq Scan on rlp1 rlp_1
+ Filter: (a <= 15)
+ -> Seq Scan on rlp2 rlp_2
+ Filter: (a <= 15)
+ -> Seq Scan on rlp3abcd rlp_3
+ Filter: (a <= 15)
+ -> Seq Scan on rlp3efgh rlp_4
+ Filter: (a <= 15)
+ -> Seq Scan on rlp3nullxy rlp_5
+ Filter: (a <= 15)
+ -> Seq Scan on rlp3_default rlp_6
+ Filter: (a <= 15)
+ -> Seq Scan on rlp_default_10 rlp_7
+ Filter: (a <= 15)
+ -> Seq Scan on rlp_default_default rlp_8
+ Filter: (a <= 15)
+(17 rows)
+
+explain (costs off) select * from rlp where a > 15 and b = 'ab';
+ QUERY PLAN
+---------------------------------------------------------
+ Append
+ -> Seq Scan on rlp3abcd rlp_1
+ Filter: ((a > 15) AND ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp4_1 rlp_2
+ Filter: ((a > 15) AND ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp4_2 rlp_3
+ Filter: ((a > 15) AND ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp4_default rlp_4
+ Filter: ((a > 15) AND ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp5_1 rlp_5
+ Filter: ((a > 15) AND ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp5_default rlp_6
+ Filter: ((a > 15) AND ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp_default_30 rlp_7
+ Filter: ((a > 15) AND ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp_default_default rlp_8
+ Filter: ((a > 15) AND ((b)::text = 'ab'::text))
+(17 rows)
+
+explain (costs off) select * from rlp where a = 16;
+ QUERY PLAN
+--------------------------------------
+ Append
+ -> Seq Scan on rlp3abcd rlp_1
+ Filter: (a = 16)
+ -> Seq Scan on rlp3efgh rlp_2
+ Filter: (a = 16)
+ -> Seq Scan on rlp3nullxy rlp_3
+ Filter: (a = 16)
+ -> Seq Scan on rlp3_default rlp_4
+ Filter: (a = 16)
+(9 rows)
+
+explain (costs off) select * from rlp where a = 16 and b in ('not', 'in', 'here');
+ QUERY PLAN
+----------------------------------------------------------------------
+ Seq Scan on rlp3_default rlp
+ Filter: ((a = 16) AND ((b)::text = ANY ('{not,in,here}'::text[])))
+(2 rows)
+
+explain (costs off) select * from rlp where a = 16 and b < 'ab';
+ QUERY PLAN
+---------------------------------------------------
+ Seq Scan on rlp3_default rlp
+ Filter: (((b)::text < 'ab'::text) AND (a = 16))
+(2 rows)
+
+explain (costs off) select * from rlp where a = 16 and b <= 'ab';
+ QUERY PLAN
+----------------------------------------------------------
+ Append
+ -> Seq Scan on rlp3abcd rlp_1
+ Filter: (((b)::text <= 'ab'::text) AND (a = 16))
+ -> Seq Scan on rlp3_default rlp_2
+ Filter: (((b)::text <= 'ab'::text) AND (a = 16))
+(5 rows)
+
+explain (costs off) select * from rlp where a = 16 and b is null;
+ QUERY PLAN
+--------------------------------------
+ Seq Scan on rlp3nullxy rlp
+ Filter: ((b IS NULL) AND (a = 16))
+(2 rows)
+
+explain (costs off) select * from rlp where a = 16 and b is not null;
+ QUERY PLAN
+------------------------------------------------
+ Append
+ -> Seq Scan on rlp3abcd rlp_1
+ Filter: ((b IS NOT NULL) AND (a = 16))
+ -> Seq Scan on rlp3efgh rlp_2
+ Filter: ((b IS NOT NULL) AND (a = 16))
+ -> Seq Scan on rlp3nullxy rlp_3
+ Filter: ((b IS NOT NULL) AND (a = 16))
+ -> Seq Scan on rlp3_default rlp_4
+ Filter: ((b IS NOT NULL) AND (a = 16))
+(9 rows)
+
+explain (costs off) select * from rlp where a is null;
+ QUERY PLAN
+----------------------------------
+ Seq Scan on rlp_default_null rlp
+ Filter: (a IS NULL)
+(2 rows)
+
+explain (costs off) select * from rlp where a is not null;
+ QUERY PLAN
+----------------------------------------------
+ Append
+ -> Seq Scan on rlp1 rlp_1
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on rlp2 rlp_2
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on rlp3abcd rlp_3
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on rlp3efgh rlp_4
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on rlp3nullxy rlp_5
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on rlp3_default rlp_6
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on rlp4_1 rlp_7
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on rlp4_2 rlp_8
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on rlp4_default rlp_9
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on rlp5_1 rlp_10
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on rlp5_default rlp_11
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on rlp_default_10 rlp_12
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on rlp_default_30 rlp_13
+ Filter: (a IS NOT NULL)
+ -> Seq Scan on rlp_default_default rlp_14
+ Filter: (a IS NOT NULL)
+(29 rows)
+
+explain (costs off) select * from rlp where a > 30;
+ QUERY PLAN
+---------------------------------------------
+ Append
+ -> Seq Scan on rlp5_1 rlp_1
+ Filter: (a > 30)
+ -> Seq Scan on rlp5_default rlp_2
+ Filter: (a > 30)
+ -> Seq Scan on rlp_default_default rlp_3
+ Filter: (a > 30)
+(7 rows)
+
+explain (costs off) select * from rlp where a = 30; /* only default is scanned */
+ QUERY PLAN
+--------------------------------
+ Seq Scan on rlp_default_30 rlp
+ Filter: (a = 30)
+(2 rows)
+
+explain (costs off) select * from rlp where a <= 31;
+ QUERY PLAN
+----------------------------------------------
+ Append
+ -> Seq Scan on rlp1 rlp_1
+ Filter: (a <= 31)
+ -> Seq Scan on rlp2 rlp_2
+ Filter: (a <= 31)
+ -> Seq Scan on rlp3abcd rlp_3
+ Filter: (a <= 31)
+ -> Seq Scan on rlp3efgh rlp_4
+ Filter: (a <= 31)
+ -> Seq Scan on rlp3nullxy rlp_5
+ Filter: (a <= 31)
+ -> Seq Scan on rlp3_default rlp_6
+ Filter: (a <= 31)
+ -> Seq Scan on rlp4_1 rlp_7
+ Filter: (a <= 31)
+ -> Seq Scan on rlp4_2 rlp_8
+ Filter: (a <= 31)
+ -> Seq Scan on rlp4_default rlp_9
+ Filter: (a <= 31)
+ -> Seq Scan on rlp5_1 rlp_10
+ Filter: (a <= 31)
+ -> Seq Scan on rlp_default_10 rlp_11
+ Filter: (a <= 31)
+ -> Seq Scan on rlp_default_30 rlp_12
+ Filter: (a <= 31)
+ -> Seq Scan on rlp_default_default rlp_13
+ Filter: (a <= 31)
+(27 rows)
+
+explain (costs off) select * from rlp where a = 1 or a = 7;
+ QUERY PLAN
+--------------------------------
+ Seq Scan on rlp2 rlp
+ Filter: ((a = 1) OR (a = 7))
+(2 rows)
+
+explain (costs off) select * from rlp where a = 1 or b = 'ab';
+ QUERY PLAN
+-------------------------------------------------------
+ Append
+ -> Seq Scan on rlp1 rlp_1
+ Filter: ((a = 1) OR ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp2 rlp_2
+ Filter: ((a = 1) OR ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp3abcd rlp_3
+ Filter: ((a = 1) OR ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp4_1 rlp_4
+ Filter: ((a = 1) OR ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp4_2 rlp_5
+ Filter: ((a = 1) OR ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp4_default rlp_6
+ Filter: ((a = 1) OR ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp5_1 rlp_7
+ Filter: ((a = 1) OR ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp5_default rlp_8
+ Filter: ((a = 1) OR ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp_default_10 rlp_9
+ Filter: ((a = 1) OR ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp_default_30 rlp_10
+ Filter: ((a = 1) OR ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp_default_null rlp_11
+ Filter: ((a = 1) OR ((b)::text = 'ab'::text))
+ -> Seq Scan on rlp_default_default rlp_12
+ Filter: ((a = 1) OR ((b)::text = 'ab'::text))
+(25 rows)
+
+explain (costs off) select * from rlp where a > 20 and a < 27;
+ QUERY PLAN
+-----------------------------------------
+ Append
+ -> Seq Scan on rlp4_1 rlp_1
+ Filter: ((a > 20) AND (a < 27))
+ -> Seq Scan on rlp4_2 rlp_2
+ Filter: ((a > 20) AND (a < 27))
+(5 rows)
+
+explain (costs off) select * from rlp where a = 29;
+ QUERY PLAN
+------------------------------
+ Seq Scan on rlp4_default rlp
+ Filter: (a = 29)
+(2 rows)
+
+explain (costs off) select * from rlp where a >= 29;
+ QUERY PLAN
+---------------------------------------------
+ Append
+ -> Seq Scan on rlp4_default rlp_1
+ Filter: (a >= 29)
+ -> Seq Scan on rlp5_1 rlp_2
+ Filter: (a >= 29)
+ -> Seq Scan on rlp5_default rlp_3
+ Filter: (a >= 29)
+ -> Seq Scan on rlp_default_30 rlp_4
+ Filter: (a >= 29)
+ -> Seq Scan on rlp_default_default rlp_5
+ Filter: (a >= 29)
+(11 rows)
+
+explain (costs off) select * from rlp where a < 1 or (a > 20 and a < 25);
+ QUERY PLAN
+------------------------------------------------------
+ Append
+ -> Seq Scan on rlp1 rlp_1
+ Filter: ((a < 1) OR ((a > 20) AND (a < 25)))
+ -> Seq Scan on rlp4_1 rlp_2
+ Filter: ((a < 1) OR ((a > 20) AND (a < 25)))
+(5 rows)
+
+-- where clause contradicts sub-partition's constraint
+explain (costs off) select * from rlp where a = 20 or a = 40;
+ QUERY PLAN
+----------------------------------------
+ Append
+ -> Seq Scan on rlp4_1 rlp_1
+ Filter: ((a = 20) OR (a = 40))
+ -> Seq Scan on rlp5_default rlp_2
+ Filter: ((a = 20) OR (a = 40))
+(5 rows)
+
+explain (costs off) select * from rlp3 where a = 20; /* empty */
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+-- redundant clauses are eliminated
+explain (costs off) select * from rlp where a > 1 and a = 10; /* only default */
+ QUERY PLAN
+----------------------------------
+ Seq Scan on rlp_default_10 rlp
+ Filter: ((a > 1) AND (a = 10))
+(2 rows)
+
+explain (costs off) select * from rlp where a > 1 and a >=15; /* rlp3 onwards, including default */
+ QUERY PLAN
+----------------------------------------------
+ Append
+ -> Seq Scan on rlp3abcd rlp_1
+ Filter: ((a > 1) AND (a >= 15))
+ -> Seq Scan on rlp3efgh rlp_2
+ Filter: ((a > 1) AND (a >= 15))
+ -> Seq Scan on rlp3nullxy rlp_3
+ Filter: ((a > 1) AND (a >= 15))
+ -> Seq Scan on rlp3_default rlp_4
+ Filter: ((a > 1) AND (a >= 15))
+ -> Seq Scan on rlp4_1 rlp_5
+ Filter: ((a > 1) AND (a >= 15))
+ -> Seq Scan on rlp4_2 rlp_6
+ Filter: ((a > 1) AND (a >= 15))
+ -> Seq Scan on rlp4_default rlp_7
+ Filter: ((a > 1) AND (a >= 15))
+ -> Seq Scan on rlp5_1 rlp_8
+ Filter: ((a > 1) AND (a >= 15))
+ -> Seq Scan on rlp5_default rlp_9
+ Filter: ((a > 1) AND (a >= 15))
+ -> Seq Scan on rlp_default_30 rlp_10
+ Filter: ((a > 1) AND (a >= 15))
+ -> Seq Scan on rlp_default_default rlp_11
+ Filter: ((a > 1) AND (a >= 15))
+(23 rows)
+
+explain (costs off) select * from rlp where a = 1 and a = 3; /* empty */
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+explain (costs off) select * from rlp where (a = 1 and a = 3) or (a > 1 and a = 15);
+ QUERY PLAN
+-------------------------------------------------------------------
+ Append
+ -> Seq Scan on rlp2 rlp_1
+ Filter: (((a = 1) AND (a = 3)) OR ((a > 1) AND (a = 15)))
+ -> Seq Scan on rlp3abcd rlp_2
+ Filter: (((a = 1) AND (a = 3)) OR ((a > 1) AND (a = 15)))
+ -> Seq Scan on rlp3efgh rlp_3
+ Filter: (((a = 1) AND (a = 3)) OR ((a > 1) AND (a = 15)))
+ -> Seq Scan on rlp3nullxy rlp_4
+ Filter: (((a = 1) AND (a = 3)) OR ((a > 1) AND (a = 15)))
+ -> Seq Scan on rlp3_default rlp_5
+ Filter: (((a = 1) AND (a = 3)) OR ((a > 1) AND (a = 15)))
+(11 rows)
+
+-- multi-column keys
+create table mc3p (a int, b int, c int) partition by range (a, abs(b), c);
+create table mc3p_default partition of mc3p default;
+create table mc3p0 partition of mc3p for values from (minvalue, minvalue, minvalue) to (1, 1, 1);
+create table mc3p1 partition of mc3p for values from (1, 1, 1) to (10, 5, 10);
+create table mc3p2 partition of mc3p for values from (10, 5, 10) to (10, 10, 10);
+create table mc3p3 partition of mc3p for values from (10, 10, 10) to (10, 10, 20);
+create table mc3p4 partition of mc3p for values from (10, 10, 20) to (10, maxvalue, maxvalue);
+create table mc3p5 partition of mc3p for values from (11, 1, 1) to (20, 10, 10);
+create table mc3p6 partition of mc3p for values from (20, 10, 10) to (20, 20, 20);
+create table mc3p7 partition of mc3p for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue);
+explain (costs off) select * from mc3p where a = 1;
+ QUERY PLAN
+---------------------------------------
+ Append
+ -> Seq Scan on mc3p0 mc3p_1
+ Filter: (a = 1)
+ -> Seq Scan on mc3p1 mc3p_2
+ Filter: (a = 1)
+ -> Seq Scan on mc3p_default mc3p_3
+ Filter: (a = 1)
+(7 rows)
+
+explain (costs off) select * from mc3p where a = 1 and abs(b) < 1;
+ QUERY PLAN
+--------------------------------------------
+ Append
+ -> Seq Scan on mc3p0 mc3p_1
+ Filter: ((a = 1) AND (abs(b) < 1))
+ -> Seq Scan on mc3p_default mc3p_2
+ Filter: ((a = 1) AND (abs(b) < 1))
+(5 rows)
+
+explain (costs off) select * from mc3p where a = 1 and abs(b) = 1;
+ QUERY PLAN
+--------------------------------------------
+ Append
+ -> Seq Scan on mc3p0 mc3p_1
+ Filter: ((a = 1) AND (abs(b) = 1))
+ -> Seq Scan on mc3p1 mc3p_2
+ Filter: ((a = 1) AND (abs(b) = 1))
+ -> Seq Scan on mc3p_default mc3p_3
+ Filter: ((a = 1) AND (abs(b) = 1))
+(7 rows)
+
+explain (costs off) select * from mc3p where a = 1 and abs(b) = 1 and c < 8;
+ QUERY PLAN
+--------------------------------------------------------
+ Append
+ -> Seq Scan on mc3p0 mc3p_1
+ Filter: ((c < 8) AND (a = 1) AND (abs(b) = 1))
+ -> Seq Scan on mc3p1 mc3p_2
+ Filter: ((c < 8) AND (a = 1) AND (abs(b) = 1))
+(5 rows)
+
+explain (costs off) select * from mc3p where a = 10 and abs(b) between 5 and 35;
+ QUERY PLAN
+-----------------------------------------------------------------
+ Append
+ -> Seq Scan on mc3p1 mc3p_1
+ Filter: ((a = 10) AND (abs(b) >= 5) AND (abs(b) <= 35))
+ -> Seq Scan on mc3p2 mc3p_2
+ Filter: ((a = 10) AND (abs(b) >= 5) AND (abs(b) <= 35))
+ -> Seq Scan on mc3p3 mc3p_3
+ Filter: ((a = 10) AND (abs(b) >= 5) AND (abs(b) <= 35))
+ -> Seq Scan on mc3p4 mc3p_4
+ Filter: ((a = 10) AND (abs(b) >= 5) AND (abs(b) <= 35))
+ -> Seq Scan on mc3p_default mc3p_5
+ Filter: ((a = 10) AND (abs(b) >= 5) AND (abs(b) <= 35))
+(11 rows)
+
+explain (costs off) select * from mc3p where a > 10;
+ QUERY PLAN
+---------------------------------------
+ Append
+ -> Seq Scan on mc3p5 mc3p_1
+ Filter: (a > 10)
+ -> Seq Scan on mc3p6 mc3p_2
+ Filter: (a > 10)
+ -> Seq Scan on mc3p7 mc3p_3
+ Filter: (a > 10)
+ -> Seq Scan on mc3p_default mc3p_4
+ Filter: (a > 10)
+(9 rows)
+
+explain (costs off) select * from mc3p where a >= 10;
+ QUERY PLAN
+---------------------------------------
+ Append
+ -> Seq Scan on mc3p1 mc3p_1
+ Filter: (a >= 10)
+ -> Seq Scan on mc3p2 mc3p_2
+ Filter: (a >= 10)
+ -> Seq Scan on mc3p3 mc3p_3
+ Filter: (a >= 10)
+ -> Seq Scan on mc3p4 mc3p_4
+ Filter: (a >= 10)
+ -> Seq Scan on mc3p5 mc3p_5
+ Filter: (a >= 10)
+ -> Seq Scan on mc3p6 mc3p_6
+ Filter: (a >= 10)
+ -> Seq Scan on mc3p7 mc3p_7
+ Filter: (a >= 10)
+ -> Seq Scan on mc3p_default mc3p_8
+ Filter: (a >= 10)
+(17 rows)
+
+explain (costs off) select * from mc3p where a < 10;
+ QUERY PLAN
+---------------------------------------
+ Append
+ -> Seq Scan on mc3p0 mc3p_1
+ Filter: (a < 10)
+ -> Seq Scan on mc3p1 mc3p_2
+ Filter: (a < 10)
+ -> Seq Scan on mc3p_default mc3p_3
+ Filter: (a < 10)
+(7 rows)
+
+explain (costs off) select * from mc3p where a <= 10 and abs(b) < 10;
+ QUERY PLAN
+-----------------------------------------------
+ Append
+ -> Seq Scan on mc3p0 mc3p_1
+ Filter: ((a <= 10) AND (abs(b) < 10))
+ -> Seq Scan on mc3p1 mc3p_2
+ Filter: ((a <= 10) AND (abs(b) < 10))
+ -> Seq Scan on mc3p2 mc3p_3
+ Filter: ((a <= 10) AND (abs(b) < 10))
+ -> Seq Scan on mc3p_default mc3p_4
+ Filter: ((a <= 10) AND (abs(b) < 10))
+(9 rows)
+
+explain (costs off) select * from mc3p where a = 11 and abs(b) = 0;
+ QUERY PLAN
+---------------------------------------
+ Seq Scan on mc3p_default mc3p
+ Filter: ((a = 11) AND (abs(b) = 0))
+(2 rows)
+
+explain (costs off) select * from mc3p where a = 20 and abs(b) = 10 and c = 100;
+ QUERY PLAN
+------------------------------------------------------
+ Seq Scan on mc3p6 mc3p
+ Filter: ((a = 20) AND (c = 100) AND (abs(b) = 10))
+(2 rows)
+
+explain (costs off) select * from mc3p where a > 20;
+ QUERY PLAN
+---------------------------------------
+ Append
+ -> Seq Scan on mc3p7 mc3p_1
+ Filter: (a > 20)
+ -> Seq Scan on mc3p_default mc3p_2
+ Filter: (a > 20)
+(5 rows)
+
+explain (costs off) select * from mc3p where a >= 20;
+ QUERY PLAN
+---------------------------------------
+ Append
+ -> Seq Scan on mc3p5 mc3p_1
+ Filter: (a >= 20)
+ -> Seq Scan on mc3p6 mc3p_2
+ Filter: (a >= 20)
+ -> Seq Scan on mc3p7 mc3p_3
+ Filter: (a >= 20)
+ -> Seq Scan on mc3p_default mc3p_4
+ Filter: (a >= 20)
+(9 rows)
+
+explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1 and c = 1) or (a = 10 and abs(b) = 5 and c = 10) or (a > 11 and a < 20);
+ QUERY PLAN
+---------------------------------------------------------------------------------------------------------------------------------
+ Append
+ -> Seq Scan on mc3p1 mc3p_1
+ Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)))
+ -> Seq Scan on mc3p2 mc3p_2
+ Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)))
+ -> Seq Scan on mc3p5 mc3p_3
+ Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)))
+ -> Seq Scan on mc3p_default mc3p_4
+ Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)))
+(9 rows)
+
+explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1 and c = 1) or (a = 10 and abs(b) = 5 and c = 10) or (a > 11 and a < 20) or a < 1;
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------------------------------------------
+ Append
+ -> Seq Scan on mc3p0 mc3p_1
+ Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1))
+ -> Seq Scan on mc3p1 mc3p_2
+ Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1))
+ -> Seq Scan on mc3p2 mc3p_3
+ Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1))
+ -> Seq Scan on mc3p5 mc3p_4
+ Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1))
+ -> Seq Scan on mc3p_default mc3p_5
+ Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1))
+(11 rows)
+
+explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1 and c = 1) or (a = 10 and abs(b) = 5 and c = 10) or (a > 11 and a < 20) or a < 1 or a = 1;
+ QUERY PLAN
+-------------------------------------------------------------------------------------------------------------------------------------------------------
+ Append
+ -> Seq Scan on mc3p0 mc3p_1
+ Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1) OR (a = 1))
+ -> Seq Scan on mc3p1 mc3p_2
+ Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1) OR (a = 1))
+ -> Seq Scan on mc3p2 mc3p_3
+ Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1) OR (a = 1))
+ -> Seq Scan on mc3p5 mc3p_4
+ Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1) OR (a = 1))
+ -> Seq Scan on mc3p_default mc3p_5
+ Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1) OR (a = 1))
+(11 rows)
+
+explain (costs off) select * from mc3p where a = 1 or abs(b) = 1 or c = 1;
+ QUERY PLAN
+------------------------------------------------------
+ Append
+ -> Seq Scan on mc3p0 mc3p_1
+ Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1))
+ -> Seq Scan on mc3p1 mc3p_2
+ Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1))
+ -> Seq Scan on mc3p2 mc3p_3
+ Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1))
+ -> Seq Scan on mc3p3 mc3p_4
+ Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1))
+ -> Seq Scan on mc3p4 mc3p_5
+ Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1))
+ -> Seq Scan on mc3p5 mc3p_6
+ Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1))
+ -> Seq Scan on mc3p6 mc3p_7
+ Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1))
+ -> Seq Scan on mc3p7 mc3p_8
+ Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1))
+ -> Seq Scan on mc3p_default mc3p_9
+ Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1))
+(19 rows)
+
+explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1) or (a = 10 and abs(b) = 10);
+ QUERY PLAN
+------------------------------------------------------------------------------
+ Append
+ -> Seq Scan on mc3p0 mc3p_1
+ Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 10)))
+ -> Seq Scan on mc3p1 mc3p_2
+ Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 10)))
+ -> Seq Scan on mc3p2 mc3p_3
+ Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 10)))
+ -> Seq Scan on mc3p3 mc3p_4
+ Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 10)))
+ -> Seq Scan on mc3p4 mc3p_5
+ Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 10)))
+ -> Seq Scan on mc3p_default mc3p_6
+ Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 10)))
+(13 rows)
+
+explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1) or (a = 10 and abs(b) = 9);
+ QUERY PLAN
+-----------------------------------------------------------------------------
+ Append
+ -> Seq Scan on mc3p0 mc3p_1
+ Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 9)))
+ -> Seq Scan on mc3p1 mc3p_2
+ Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 9)))
+ -> Seq Scan on mc3p2 mc3p_3
+ Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 9)))
+ -> Seq Scan on mc3p_default mc3p_4
+ Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 9)))
+(9 rows)
+
+-- a simpler multi-column keys case
+create table mc2p (a int, b int) partition by range (a, b);
+create table mc2p_default partition of mc2p default;
+create table mc2p0 partition of mc2p for values from (minvalue, minvalue) to (1, minvalue);
+create table mc2p1 partition of mc2p for values from (1, minvalue) to (1, 1);
+create table mc2p2 partition of mc2p for values from (1, 1) to (2, minvalue);
+create table mc2p3 partition of mc2p for values from (2, minvalue) to (2, 1);
+create table mc2p4 partition of mc2p for values from (2, 1) to (2, maxvalue);
+create table mc2p5 partition of mc2p for values from (2, maxvalue) to (maxvalue, maxvalue);
+explain (costs off) select * from mc2p where a < 2;
+ QUERY PLAN
+---------------------------------------
+ Append
+ -> Seq Scan on mc2p0 mc2p_1
+ Filter: (a < 2)
+ -> Seq Scan on mc2p1 mc2p_2
+ Filter: (a < 2)
+ -> Seq Scan on mc2p2 mc2p_3
+ Filter: (a < 2)
+ -> Seq Scan on mc2p_default mc2p_4
+ Filter: (a < 2)
+(9 rows)
+
+explain (costs off) select * from mc2p where a = 2 and b < 1;
+ QUERY PLAN
+---------------------------------
+ Seq Scan on mc2p3 mc2p
+ Filter: ((b < 1) AND (a = 2))
+(2 rows)
+
+explain (costs off) select * from mc2p where a > 1;
+ QUERY PLAN
+---------------------------------------
+ Append
+ -> Seq Scan on mc2p2 mc2p_1
+ Filter: (a > 1)
+ -> Seq Scan on mc2p3 mc2p_2
+ Filter: (a > 1)
+ -> Seq Scan on mc2p4 mc2p_3
+ Filter: (a > 1)
+ -> Seq Scan on mc2p5 mc2p_4
+ Filter: (a > 1)
+ -> Seq Scan on mc2p_default mc2p_5
+ Filter: (a > 1)
+(11 rows)
+
+explain (costs off) select * from mc2p where a = 1 and b > 1;
+ QUERY PLAN
+---------------------------------
+ Seq Scan on mc2p2 mc2p
+ Filter: ((b > 1) AND (a = 1))
+(2 rows)
+
+-- all partitions but the default one should be pruned
+explain (costs off) select * from mc2p where a = 1 and b is null;
+ QUERY PLAN
+-------------------------------------
+ Seq Scan on mc2p_default mc2p
+ Filter: ((b IS NULL) AND (a = 1))
+(2 rows)
+
+explain (costs off) select * from mc2p where a is null and b is null;
+ QUERY PLAN
+-----------------------------------------
+ Seq Scan on mc2p_default mc2p
+ Filter: ((a IS NULL) AND (b IS NULL))
+(2 rows)
+
+explain (costs off) select * from mc2p where a is null and b = 1;
+ QUERY PLAN
+-------------------------------------
+ Seq Scan on mc2p_default mc2p
+ Filter: ((a IS NULL) AND (b = 1))
+(2 rows)
+
+explain (costs off) select * from mc2p where a is null;
+ QUERY PLAN
+-------------------------------
+ Seq Scan on mc2p_default mc2p
+ Filter: (a IS NULL)
+(2 rows)
+
+explain (costs off) select * from mc2p where b is null;
+ QUERY PLAN
+-------------------------------
+ Seq Scan on mc2p_default mc2p
+ Filter: (b IS NULL)
+(2 rows)
+
+-- boolean partitioning
+create table boolpart (a bool) partition by list (a);
+create table boolpart_default partition of boolpart default;
+create table boolpart_t partition of boolpart for values in ('true');
+create table boolpart_f partition of boolpart for values in ('false');
+insert into boolpart values (true), (false), (null);
+explain (costs off) select * from boolpart where a in (true, false);
+ QUERY PLAN
+------------------------------------------------
+ Append
+ -> Seq Scan on boolpart_f boolpart_1
+ Filter: (a = ANY ('{t,f}'::boolean[]))
+ -> Seq Scan on boolpart_t boolpart_2
+ Filter: (a = ANY ('{t,f}'::boolean[]))
+(5 rows)
+
+explain (costs off) select * from boolpart where a = false;
+ QUERY PLAN
+---------------------------------
+ Seq Scan on boolpart_f boolpart
+ Filter: (NOT a)
+(2 rows)
+
+explain (costs off) select * from boolpart where not a = false;
+ QUERY PLAN
+---------------------------------
+ Seq Scan on boolpart_t boolpart
+ Filter: a
+(2 rows)
+
+explain (costs off) select * from boolpart where a is true or a is not true;
+ QUERY PLAN
+--------------------------------------------------
+ Append
+ -> Seq Scan on boolpart_f boolpart_1
+ Filter: ((a IS TRUE) OR (a IS NOT TRUE))
+ -> Seq Scan on boolpart_t boolpart_2
+ Filter: ((a IS TRUE) OR (a IS NOT TRUE))
+ -> Seq Scan on boolpart_default boolpart_3
+ Filter: ((a IS TRUE) OR (a IS NOT TRUE))
+(7 rows)
+
+explain (costs off) select * from boolpart where a is not true;
+ QUERY PLAN
+-----------------------------------------------
+ Append
+ -> Seq Scan on boolpart_f boolpart_1
+ Filter: (a IS NOT TRUE)
+ -> Seq Scan on boolpart_default boolpart_2
+ Filter: (a IS NOT TRUE)
+(5 rows)
+
+explain (costs off) select * from boolpart where a is not true and a is not false;
+ QUERY PLAN
+--------------------------------------------------
+ Seq Scan on boolpart_default boolpart
+ Filter: ((a IS NOT TRUE) AND (a IS NOT FALSE))
+(2 rows)
+
+explain (costs off) select * from boolpart where a is unknown;
+ QUERY PLAN
+-----------------------------------------------
+ Append
+ -> Seq Scan on boolpart_f boolpart_1
+ Filter: (a IS UNKNOWN)
+ -> Seq Scan on boolpart_t boolpart_2
+ Filter: (a IS UNKNOWN)
+ -> Seq Scan on boolpart_default boolpart_3
+ Filter: (a IS UNKNOWN)
+(7 rows)
+
+explain (costs off) select * from boolpart where a is not unknown;
+ QUERY PLAN
+-----------------------------------------------
+ Append
+ -> Seq Scan on boolpart_f boolpart_1
+ Filter: (a IS NOT UNKNOWN)
+ -> Seq Scan on boolpart_t boolpart_2
+ Filter: (a IS NOT UNKNOWN)
+ -> Seq Scan on boolpart_default boolpart_3
+ Filter: (a IS NOT UNKNOWN)
+(7 rows)
+
+select * from boolpart where a in (true, false);
+ a
+---
+ f
+ t
+(2 rows)
+
+select * from boolpart where a = false;
+ a
+---
+ f
+(1 row)
+
+select * from boolpart where not a = false;
+ a
+---
+ t
+(1 row)
+
+select * from boolpart where a is true or a is not true;
+ a
+---
+ f
+ t
+
+(3 rows)
+
+select * from boolpart where a is not true;
+ a
+---
+ f
+
+(2 rows)
+
+select * from boolpart where a is not true and a is not false;
+ a
+---
+
+(1 row)
+
+select * from boolpart where a is unknown;
+ a
+---
+
+(1 row)
+
+select * from boolpart where a is not unknown;
+ a
+---
+ f
+ t
+(2 rows)
+
+-- inverse boolean partitioning - a seemingly unlikely design, but we've got
+-- code for it, so we'd better test it.
+create table iboolpart (a bool) partition by list ((not a));
+create table iboolpart_default partition of iboolpart default;
+create table iboolpart_f partition of iboolpart for values in ('true');
+create table iboolpart_t partition of iboolpart for values in ('false');
+insert into iboolpart values (true), (false), (null);
+explain (costs off) select * from iboolpart where a in (true, false);
+ QUERY PLAN
+-------------------------------------------------
+ Append
+ -> Seq Scan on iboolpart_t iboolpart_1
+ Filter: (a = ANY ('{t,f}'::boolean[]))
+ -> Seq Scan on iboolpart_f iboolpart_2
+ Filter: (a = ANY ('{t,f}'::boolean[]))
+ -> Seq Scan on iboolpart_default iboolpart_3
+ Filter: (a = ANY ('{t,f}'::boolean[]))
+(7 rows)
+
+explain (costs off) select * from iboolpart where a = false;
+ QUERY PLAN
+-----------------------------------
+ Seq Scan on iboolpart_f iboolpart
+ Filter: (NOT a)
+(2 rows)
+
+explain (costs off) select * from iboolpart where not a = false;
+ QUERY PLAN
+-----------------------------------
+ Seq Scan on iboolpart_t iboolpart
+ Filter: a
+(2 rows)
+
+explain (costs off) select * from iboolpart where a is true or a is not true;
+ QUERY PLAN
+--------------------------------------------------
+ Append
+ -> Seq Scan on iboolpart_t iboolpart_1
+ Filter: ((a IS TRUE) OR (a IS NOT TRUE))
+ -> Seq Scan on iboolpart_f iboolpart_2
+ Filter: ((a IS TRUE) OR (a IS NOT TRUE))
+ -> Seq Scan on iboolpart_default iboolpart_3
+ Filter: ((a IS TRUE) OR (a IS NOT TRUE))
+(7 rows)
+
+explain (costs off) select * from iboolpart where a is not true;
+ QUERY PLAN
+-------------------------------------------------
+ Append
+ -> Seq Scan on iboolpart_t iboolpart_1
+ Filter: (a IS NOT TRUE)
+ -> Seq Scan on iboolpart_f iboolpart_2
+ Filter: (a IS NOT TRUE)
+ -> Seq Scan on iboolpart_default iboolpart_3
+ Filter: (a IS NOT TRUE)
+(7 rows)
+
+explain (costs off) select * from iboolpart where a is not true and a is not false;
+ QUERY PLAN
+--------------------------------------------------------
+ Append
+ -> Seq Scan on iboolpart_t iboolpart_1
+ Filter: ((a IS NOT TRUE) AND (a IS NOT FALSE))
+ -> Seq Scan on iboolpart_f iboolpart_2
+ Filter: ((a IS NOT TRUE) AND (a IS NOT FALSE))
+ -> Seq Scan on iboolpart_default iboolpart_3
+ Filter: ((a IS NOT TRUE) AND (a IS NOT FALSE))
+(7 rows)
+
+explain (costs off) select * from iboolpart where a is unknown;
+ QUERY PLAN
+-------------------------------------------------
+ Append
+ -> Seq Scan on iboolpart_t iboolpart_1
+ Filter: (a IS UNKNOWN)
+ -> Seq Scan on iboolpart_f iboolpart_2
+ Filter: (a IS UNKNOWN)
+ -> Seq Scan on iboolpart_default iboolpart_3
+ Filter: (a IS UNKNOWN)
+(7 rows)
+
+explain (costs off) select * from iboolpart where a is not unknown;
+ QUERY PLAN
+-------------------------------------------------
+ Append
+ -> Seq Scan on iboolpart_t iboolpart_1
+ Filter: (a IS NOT UNKNOWN)
+ -> Seq Scan on iboolpart_f iboolpart_2
+ Filter: (a IS NOT UNKNOWN)
+ -> Seq Scan on iboolpart_default iboolpart_3
+ Filter: (a IS NOT UNKNOWN)
+(7 rows)
+
+select * from iboolpart where a in (true, false);
+ a
+---
+ t
+ f
+(2 rows)
+
+select * from iboolpart where a = false;
+ a
+---
+ f
+(1 row)
+
+select * from iboolpart where not a = false;
+ a
+---
+ t
+(1 row)
+
+select * from iboolpart where a is true or a is not true;
+ a
+---
+ t
+ f
+
+(3 rows)
+
+select * from iboolpart where a is not true;
+ a
+---
+ f
+
+(2 rows)
+
+select * from iboolpart where a is not true and a is not false;
+ a
+---
+
+(1 row)
+
+select * from iboolpart where a is unknown;
+ a
+---
+
+(1 row)
+
+select * from iboolpart where a is not unknown;
+ a
+---
+ t
+ f
+(2 rows)
+
+create table boolrangep (a bool, b bool, c int) partition by range (a,b,c);
+create table boolrangep_tf partition of boolrangep for values from ('true', 'false', 0) to ('true', 'false', 100);
+create table boolrangep_ft partition of boolrangep for values from ('false', 'true', 0) to ('false', 'true', 100);
+create table boolrangep_ff1 partition of boolrangep for values from ('false', 'false', 0) to ('false', 'false', 50);
+create table boolrangep_ff2 partition of boolrangep for values from ('false', 'false', 50) to ('false', 'false', 100);
+-- try a more complex case that's been known to trip up pruning in the past
+explain (costs off) select * from boolrangep where not a and not b and c = 25;
+ QUERY PLAN
+----------------------------------------------
+ Seq Scan on boolrangep_ff1 boolrangep
+ Filter: ((NOT a) AND (NOT b) AND (c = 25))
+(2 rows)
+
+-- test scalar-to-array operators
+create table coercepart (a varchar) partition by list (a);
+create table coercepart_ab partition of coercepart for values in ('ab');
+create table coercepart_bc partition of coercepart for values in ('bc');
+create table coercepart_cd partition of coercepart for values in ('cd');
+explain (costs off) select * from coercepart where a in ('ab', to_char(125, '999'));
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------------------
+ Append
+ -> Seq Scan on coercepart_ab coercepart_1
+ Filter: ((a)::text = ANY ((ARRAY['ab'::character varying, (to_char(125, '999'::text))::character varying])::text[]))
+ -> Seq Scan on coercepart_bc coercepart_2
+ Filter: ((a)::text = ANY ((ARRAY['ab'::character varying, (to_char(125, '999'::text))::character varying])::text[]))
+ -> Seq Scan on coercepart_cd coercepart_3
+ Filter: ((a)::text = ANY ((ARRAY['ab'::character varying, (to_char(125, '999'::text))::character varying])::text[]))
+(7 rows)
+
+explain (costs off) select * from coercepart where a ~ any ('{ab}');
+ QUERY PLAN
+----------------------------------------------------
+ Append
+ -> Seq Scan on coercepart_ab coercepart_1
+ Filter: ((a)::text ~ ANY ('{ab}'::text[]))
+ -> Seq Scan on coercepart_bc coercepart_2
+ Filter: ((a)::text ~ ANY ('{ab}'::text[]))
+ -> Seq Scan on coercepart_cd coercepart_3
+ Filter: ((a)::text ~ ANY ('{ab}'::text[]))
+(7 rows)
+
+explain (costs off) select * from coercepart where a !~ all ('{ab}');
+ QUERY PLAN
+-----------------------------------------------------
+ Append
+ -> Seq Scan on coercepart_ab coercepart_1
+ Filter: ((a)::text !~ ALL ('{ab}'::text[]))
+ -> Seq Scan on coercepart_bc coercepart_2
+ Filter: ((a)::text !~ ALL ('{ab}'::text[]))
+ -> Seq Scan on coercepart_cd coercepart_3
+ Filter: ((a)::text !~ ALL ('{ab}'::text[]))
+(7 rows)
+
+explain (costs off) select * from coercepart where a ~ any ('{ab,bc}');
+ QUERY PLAN
+-------------------------------------------------------
+ Append
+ -> Seq Scan on coercepart_ab coercepart_1
+ Filter: ((a)::text ~ ANY ('{ab,bc}'::text[]))
+ -> Seq Scan on coercepart_bc coercepart_2
+ Filter: ((a)::text ~ ANY ('{ab,bc}'::text[]))
+ -> Seq Scan on coercepart_cd coercepart_3
+ Filter: ((a)::text ~ ANY ('{ab,bc}'::text[]))
+(7 rows)
+
+explain (costs off) select * from coercepart where a !~ all ('{ab,bc}');
+ QUERY PLAN
+--------------------------------------------------------
+ Append
+ -> Seq Scan on coercepart_ab coercepart_1
+ Filter: ((a)::text !~ ALL ('{ab,bc}'::text[]))
+ -> Seq Scan on coercepart_bc coercepart_2
+ Filter: ((a)::text !~ ALL ('{ab,bc}'::text[]))
+ -> Seq Scan on coercepart_cd coercepart_3
+ Filter: ((a)::text !~ ALL ('{ab,bc}'::text[]))
+(7 rows)
+
+explain (costs off) select * from coercepart where a = any ('{ab,bc}');
+ QUERY PLAN
+-------------------------------------------------------
+ Append
+ -> Seq Scan on coercepart_ab coercepart_1
+ Filter: ((a)::text = ANY ('{ab,bc}'::text[]))
+ -> Seq Scan on coercepart_bc coercepart_2
+ Filter: ((a)::text = ANY ('{ab,bc}'::text[]))
+(5 rows)
+
+explain (costs off) select * from coercepart where a = any ('{ab,null}');
+ QUERY PLAN
+---------------------------------------------------
+ Seq Scan on coercepart_ab coercepart
+ Filter: ((a)::text = ANY ('{ab,NULL}'::text[]))
+(2 rows)
+
+explain (costs off) select * from coercepart where a = any (null::text[]);
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+explain (costs off) select * from coercepart where a = all ('{ab}');
+ QUERY PLAN
+----------------------------------------------
+ Seq Scan on coercepart_ab coercepart
+ Filter: ((a)::text = ALL ('{ab}'::text[]))
+(2 rows)
+
+explain (costs off) select * from coercepart where a = all ('{ab,bc}');
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+explain (costs off) select * from coercepart where a = all ('{ab,null}');
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+explain (costs off) select * from coercepart where a = all (null::text[]);
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+drop table coercepart;
+CREATE TABLE part (a INT, b INT) PARTITION BY LIST (a);
+CREATE TABLE part_p1 PARTITION OF part FOR VALUES IN (-2,-1,0,1,2);
+CREATE TABLE part_p2 PARTITION OF part DEFAULT PARTITION BY RANGE(a);
+CREATE TABLE part_p2_p1 PARTITION OF part_p2 DEFAULT;
+CREATE TABLE part_rev (b INT, c INT, a INT);
+ALTER TABLE part ATTACH PARTITION part_rev FOR VALUES IN (3); -- fail
+ERROR: table "part_rev" contains column "c" not found in parent "part"
+DETAIL: The new partition may contain only the columns present in parent.
+ALTER TABLE part_rev DROP COLUMN c;
+ALTER TABLE part ATTACH PARTITION part_rev FOR VALUES IN (3); -- now it's ok
+INSERT INTO part VALUES (-1,-1), (1,1), (2,NULL), (NULL,-2),(NULL,NULL);
+EXPLAIN (COSTS OFF) SELECT tableoid::regclass as part, a, b FROM part WHERE a IS NULL ORDER BY 1, 2, 3;
+ QUERY PLAN
+---------------------------------------------------------
+ Sort
+ Sort Key: ((part.tableoid)::regclass), part.a, part.b
+ -> Seq Scan on part_p2_p1 part
+ Filter: (a IS NULL)
+(4 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM part p(x) ORDER BY x;
+ QUERY PLAN
+-----------------------------------------------
+ Sort
+ Output: p.x, p.b
+ Sort Key: p.x
+ -> Append
+ -> Seq Scan on public.part_p1 p_1
+ Output: p_1.x, p_1.b
+ -> Seq Scan on public.part_rev p_2
+ Output: p_2.x, p_2.b
+ -> Seq Scan on public.part_p2_p1 p_3
+ Output: p_3.x, p_3.b
+(10 rows)
+
+--
+-- some more cases
+--
+--
+-- pruning for partitioned table appearing inside a sub-query
+--
+-- pruning won't work for mc3p, because some keys are Params
+explain (costs off) select * from mc2p t1, lateral (select count(*) from mc3p t2 where t2.a = t1.b and abs(t2.b) = 1 and t2.c = 1) s where t1.a = 1;
+ QUERY PLAN
+-----------------------------------------------------------------------
+ Nested Loop
+ -> Append
+ -> Seq Scan on mc2p1 t1_1
+ Filter: (a = 1)
+ -> Seq Scan on mc2p2 t1_2
+ Filter: (a = 1)
+ -> Seq Scan on mc2p_default t1_3
+ Filter: (a = 1)
+ -> Aggregate
+ -> Append
+ -> Seq Scan on mc3p0 t2_1
+ Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1))
+ -> Seq Scan on mc3p1 t2_2
+ Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1))
+ -> Seq Scan on mc3p2 t2_3
+ Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1))
+ -> Seq Scan on mc3p3 t2_4
+ Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1))
+ -> Seq Scan on mc3p4 t2_5
+ Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1))
+ -> Seq Scan on mc3p5 t2_6
+ Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1))
+ -> Seq Scan on mc3p6 t2_7
+ Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1))
+ -> Seq Scan on mc3p7 t2_8
+ Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1))
+ -> Seq Scan on mc3p_default t2_9
+ Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1))
+(28 rows)
+
+-- pruning should work fine, because values for a prefix of keys (a, b) are
+-- available
+explain (costs off) select * from mc2p t1, lateral (select count(*) from mc3p t2 where t2.c = t1.b and abs(t2.b) = 1 and t2.a = 1) s where t1.a = 1;
+ QUERY PLAN
+-----------------------------------------------------------------------
+ Nested Loop
+ -> Append
+ -> Seq Scan on mc2p1 t1_1
+ Filter: (a = 1)
+ -> Seq Scan on mc2p2 t1_2
+ Filter: (a = 1)
+ -> Seq Scan on mc2p_default t1_3
+ Filter: (a = 1)
+ -> Aggregate
+ -> Append
+ -> Seq Scan on mc3p0 t2_1
+ Filter: ((c = t1.b) AND (a = 1) AND (abs(b) = 1))
+ -> Seq Scan on mc3p1 t2_2
+ Filter: ((c = t1.b) AND (a = 1) AND (abs(b) = 1))
+ -> Seq Scan on mc3p_default t2_3
+ Filter: ((c = t1.b) AND (a = 1) AND (abs(b) = 1))
+(16 rows)
+
+-- also here, because values for all keys are provided
+explain (costs off) select * from mc2p t1, lateral (select count(*) from mc3p t2 where t2.a = 1 and abs(t2.b) = 1 and t2.c = 1) s where t1.a = 1;
+ QUERY PLAN
+--------------------------------------------------------------
+ Nested Loop
+ -> Aggregate
+ -> Seq Scan on mc3p1 t2
+ Filter: ((a = 1) AND (c = 1) AND (abs(b) = 1))
+ -> Append
+ -> Seq Scan on mc2p1 t1_1
+ Filter: (a = 1)
+ -> Seq Scan on mc2p2 t1_2
+ Filter: (a = 1)
+ -> Seq Scan on mc2p_default t1_3
+ Filter: (a = 1)
+(11 rows)
+
+--
+-- pruning with clauses containing <> operator
+--
+-- doesn't prune range partitions
+create table rp (a int) partition by range (a);
+create table rp0 partition of rp for values from (minvalue) to (1);
+create table rp1 partition of rp for values from (1) to (2);
+create table rp2 partition of rp for values from (2) to (maxvalue);
+explain (costs off) select * from rp where a <> 1;
+ QUERY PLAN
+----------------------------
+ Append
+ -> Seq Scan on rp0 rp_1
+ Filter: (a <> 1)
+ -> Seq Scan on rp1 rp_2
+ Filter: (a <> 1)
+ -> Seq Scan on rp2 rp_3
+ Filter: (a <> 1)
+(7 rows)
+
+explain (costs off) select * from rp where a <> 1 and a <> 2;
+ QUERY PLAN
+-----------------------------------------
+ Append
+ -> Seq Scan on rp0 rp_1
+ Filter: ((a <> 1) AND (a <> 2))
+ -> Seq Scan on rp1 rp_2
+ Filter: ((a <> 1) AND (a <> 2))
+ -> Seq Scan on rp2 rp_3
+ Filter: ((a <> 1) AND (a <> 2))
+(7 rows)
+
+-- null partition should be eliminated due to strict <> clause.
+explain (costs off) select * from lp where a <> 'a';
+ QUERY PLAN
+------------------------------------
+ Append
+ -> Seq Scan on lp_ad lp_1
+ Filter: (a <> 'a'::bpchar)
+ -> Seq Scan on lp_bc lp_2
+ Filter: (a <> 'a'::bpchar)
+ -> Seq Scan on lp_ef lp_3
+ Filter: (a <> 'a'::bpchar)
+ -> Seq Scan on lp_g lp_4
+ Filter: (a <> 'a'::bpchar)
+ -> Seq Scan on lp_default lp_5
+ Filter: (a <> 'a'::bpchar)
+(11 rows)
+
+-- ensure we detect contradictions in clauses; a can't be NULL and NOT NULL.
+explain (costs off) select * from lp where a <> 'a' and a is null;
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+explain (costs off) select * from lp where (a <> 'a' and a <> 'd') or a is null;
+ QUERY PLAN
+------------------------------------------------------------------------------
+ Append
+ -> Seq Scan on lp_bc lp_1
+ Filter: (((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) OR (a IS NULL))
+ -> Seq Scan on lp_ef lp_2
+ Filter: (((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) OR (a IS NULL))
+ -> Seq Scan on lp_g lp_3
+ Filter: (((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) OR (a IS NULL))
+ -> Seq Scan on lp_null lp_4
+ Filter: (((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) OR (a IS NULL))
+ -> Seq Scan on lp_default lp_5
+ Filter: (((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) OR (a IS NULL))
+(11 rows)
+
+-- check that it also works for a partitioned table that's not root,
+-- which in this case are partitions of rlp that are themselves
+-- list-partitioned on b
+explain (costs off) select * from rlp where a = 15 and b <> 'ab' and b <> 'cd' and b <> 'xy' and b is not null;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------------------------------
+ Append
+ -> Seq Scan on rlp3efgh rlp_1
+ Filter: ((b IS NOT NULL) AND ((b)::text <> 'ab'::text) AND ((b)::text <> 'cd'::text) AND ((b)::text <> 'xy'::text) AND (a = 15))
+ -> Seq Scan on rlp3_default rlp_2
+ Filter: ((b IS NOT NULL) AND ((b)::text <> 'ab'::text) AND ((b)::text <> 'cd'::text) AND ((b)::text <> 'xy'::text) AND (a = 15))
+(5 rows)
+
+--
+-- different collations for different keys with same expression
+--
+create table coll_pruning_multi (a text) partition by range (substr(a, 1) collate "POSIX", substr(a, 1) collate "C");
+create table coll_pruning_multi1 partition of coll_pruning_multi for values from ('a', 'a') to ('a', 'e');
+create table coll_pruning_multi2 partition of coll_pruning_multi for values from ('a', 'e') to ('a', 'z');
+create table coll_pruning_multi3 partition of coll_pruning_multi for values from ('b', 'a') to ('b', 'e');
+-- no pruning, because no value for the leading key
+explain (costs off) select * from coll_pruning_multi where substr(a, 1) = 'e' collate "C";
+ QUERY PLAN
+------------------------------------------------------------
+ Append
+ -> Seq Scan on coll_pruning_multi1 coll_pruning_multi_1
+ Filter: (substr(a, 1) = 'e'::text COLLATE "C")
+ -> Seq Scan on coll_pruning_multi2 coll_pruning_multi_2
+ Filter: (substr(a, 1) = 'e'::text COLLATE "C")
+ -> Seq Scan on coll_pruning_multi3 coll_pruning_multi_3
+ Filter: (substr(a, 1) = 'e'::text COLLATE "C")
+(7 rows)
+
+-- pruning, with a value provided for the leading key
+explain (costs off) select * from coll_pruning_multi where substr(a, 1) = 'a' collate "POSIX";
+ QUERY PLAN
+------------------------------------------------------------
+ Append
+ -> Seq Scan on coll_pruning_multi1 coll_pruning_multi_1
+ Filter: (substr(a, 1) = 'a'::text COLLATE "POSIX")
+ -> Seq Scan on coll_pruning_multi2 coll_pruning_multi_2
+ Filter: (substr(a, 1) = 'a'::text COLLATE "POSIX")
+(5 rows)
+
+-- pruning, with values provided for both keys
+explain (costs off) select * from coll_pruning_multi where substr(a, 1) = 'e' collate "C" and substr(a, 1) = 'a' collate "POSIX";
+ QUERY PLAN
+---------------------------------------------------------------------------------------------------
+ Seq Scan on coll_pruning_multi2 coll_pruning_multi
+ Filter: ((substr(a, 1) = 'e'::text COLLATE "C") AND (substr(a, 1) = 'a'::text COLLATE "POSIX"))
+(2 rows)
+
+--
+-- LIKE operators don't prune
+--
+create table like_op_noprune (a text) partition by list (a);
+create table like_op_noprune1 partition of like_op_noprune for values in ('ABC');
+create table like_op_noprune2 partition of like_op_noprune for values in ('BCD');
+explain (costs off) select * from like_op_noprune where a like '%BC';
+ QUERY PLAN
+------------------------------------------------------
+ Append
+ -> Seq Scan on like_op_noprune1 like_op_noprune_1
+ Filter: (a ~~ '%BC'::text)
+ -> Seq Scan on like_op_noprune2 like_op_noprune_2
+ Filter: (a ~~ '%BC'::text)
+(5 rows)
+
+--
+-- tests wherein clause value requires a cross-type comparison function
+--
+create table lparted_by_int2 (a smallint) partition by list (a);
+create table lparted_by_int2_1 partition of lparted_by_int2 for values in (1);
+create table lparted_by_int2_16384 partition of lparted_by_int2 for values in (16384);
+explain (costs off) select * from lparted_by_int2 where a = 100000000000000;
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+create table rparted_by_int2 (a smallint) partition by range (a);
+create table rparted_by_int2_1 partition of rparted_by_int2 for values from (1) to (10);
+create table rparted_by_int2_16384 partition of rparted_by_int2 for values from (10) to (16384);
+-- all partitions pruned
+explain (costs off) select * from rparted_by_int2 where a > 100000000000000;
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+create table rparted_by_int2_maxvalue partition of rparted_by_int2 for values from (16384) to (maxvalue);
+-- all partitions but rparted_by_int2_maxvalue pruned
+explain (costs off) select * from rparted_by_int2 where a > 100000000000000;
+ QUERY PLAN
+------------------------------------------------------
+ Seq Scan on rparted_by_int2_maxvalue rparted_by_int2
+ Filter: (a > '100000000000000'::bigint)
+(2 rows)
+
+drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, iboolpart, boolrangep, rp, coll_pruning_multi, like_op_noprune, lparted_by_int2, rparted_by_int2;
+--
+-- Test Partition pruning for HASH partitioning
+--
+-- Use hand-rolled hash functions and operator classes to get predictable
+-- result on different machines. See the definitions of
+-- part_part_test_int4_ops and part_test_text_ops in insert.sql.
+--
+create table hp (a int, b text, c int)
+ partition by hash (a part_test_int4_ops, b part_test_text_ops);
+create table hp0 partition of hp for values with (modulus 4, remainder 0);
+create table hp3 partition of hp for values with (modulus 4, remainder 3);
+create table hp1 partition of hp for values with (modulus 4, remainder 1);
+create table hp2 partition of hp for values with (modulus 4, remainder 2);
+insert into hp values (null, null, 0);
+insert into hp values (1, null, 1);
+insert into hp values (1, 'xxx', 2);
+insert into hp values (null, 'xxx', 3);
+insert into hp values (2, 'xxx', 4);
+insert into hp values (1, 'abcde', 5);
+select tableoid::regclass, * from hp order by c;
+ tableoid | a | b | c
+----------+---+-------+---
+ hp0 | | | 0
+ hp1 | 1 | | 1
+ hp0 | 1 | xxx | 2
+ hp2 | | xxx | 3
+ hp3 | 2 | xxx | 4
+ hp2 | 1 | abcde | 5
+(6 rows)
+
+-- partial keys won't prune, nor would non-equality conditions
+explain (costs off) select * from hp where a = 1;
+ QUERY PLAN
+----------------------------
+ Append
+ -> Seq Scan on hp0 hp_1
+ Filter: (a = 1)
+ -> Seq Scan on hp1 hp_2
+ Filter: (a = 1)
+ -> Seq Scan on hp2 hp_3
+ Filter: (a = 1)
+ -> Seq Scan on hp3 hp_4
+ Filter: (a = 1)
+(9 rows)
+
+explain (costs off) select * from hp where b = 'xxx';
+ QUERY PLAN
+-----------------------------------
+ Append
+ -> Seq Scan on hp0 hp_1
+ Filter: (b = 'xxx'::text)
+ -> Seq Scan on hp1 hp_2
+ Filter: (b = 'xxx'::text)
+ -> Seq Scan on hp2 hp_3
+ Filter: (b = 'xxx'::text)
+ -> Seq Scan on hp3 hp_4
+ Filter: (b = 'xxx'::text)
+(9 rows)
+
+explain (costs off) select * from hp where a is null;
+ QUERY PLAN
+-----------------------------
+ Append
+ -> Seq Scan on hp0 hp_1
+ Filter: (a IS NULL)
+ -> Seq Scan on hp1 hp_2
+ Filter: (a IS NULL)
+ -> Seq Scan on hp2 hp_3
+ Filter: (a IS NULL)
+ -> Seq Scan on hp3 hp_4
+ Filter: (a IS NULL)
+(9 rows)
+
+explain (costs off) select * from hp where b is null;
+ QUERY PLAN
+-----------------------------
+ Append
+ -> Seq Scan on hp0 hp_1
+ Filter: (b IS NULL)
+ -> Seq Scan on hp1 hp_2
+ Filter: (b IS NULL)
+ -> Seq Scan on hp2 hp_3
+ Filter: (b IS NULL)
+ -> Seq Scan on hp3 hp_4
+ Filter: (b IS NULL)
+(9 rows)
+
+explain (costs off) select * from hp where a < 1 and b = 'xxx';
+ QUERY PLAN
+-------------------------------------------------
+ Append
+ -> Seq Scan on hp0 hp_1
+ Filter: ((a < 1) AND (b = 'xxx'::text))
+ -> Seq Scan on hp1 hp_2
+ Filter: ((a < 1) AND (b = 'xxx'::text))
+ -> Seq Scan on hp2 hp_3
+ Filter: ((a < 1) AND (b = 'xxx'::text))
+ -> Seq Scan on hp3 hp_4
+ Filter: ((a < 1) AND (b = 'xxx'::text))
+(9 rows)
+
+explain (costs off) select * from hp where a <> 1 and b = 'yyy';
+ QUERY PLAN
+--------------------------------------------------
+ Append
+ -> Seq Scan on hp0 hp_1
+ Filter: ((a <> 1) AND (b = 'yyy'::text))
+ -> Seq Scan on hp1 hp_2
+ Filter: ((a <> 1) AND (b = 'yyy'::text))
+ -> Seq Scan on hp2 hp_3
+ Filter: ((a <> 1) AND (b = 'yyy'::text))
+ -> Seq Scan on hp3 hp_4
+ Filter: ((a <> 1) AND (b = 'yyy'::text))
+(9 rows)
+
+explain (costs off) select * from hp where a <> 1 and b <> 'xxx';
+ QUERY PLAN
+---------------------------------------------------
+ Append
+ -> Seq Scan on hp0 hp_1
+ Filter: ((a <> 1) AND (b <> 'xxx'::text))
+ -> Seq Scan on hp1 hp_2
+ Filter: ((a <> 1) AND (b <> 'xxx'::text))
+ -> Seq Scan on hp2 hp_3
+ Filter: ((a <> 1) AND (b <> 'xxx'::text))
+ -> Seq Scan on hp3 hp_4
+ Filter: ((a <> 1) AND (b <> 'xxx'::text))
+(9 rows)
+
+-- pruning should work if either a value or a IS NULL clause is provided for
+-- each of the keys
+explain (costs off) select * from hp where a is null and b is null;
+ QUERY PLAN
+-----------------------------------------
+ Seq Scan on hp0 hp
+ Filter: ((a IS NULL) AND (b IS NULL))
+(2 rows)
+
+explain (costs off) select * from hp where a = 1 and b is null;
+ QUERY PLAN
+-------------------------------------
+ Seq Scan on hp1 hp
+ Filter: ((b IS NULL) AND (a = 1))
+(2 rows)
+
+explain (costs off) select * from hp where a = 1 and b = 'xxx';
+ QUERY PLAN
+-------------------------------------------
+ Seq Scan on hp0 hp
+ Filter: ((a = 1) AND (b = 'xxx'::text))
+(2 rows)
+
+explain (costs off) select * from hp where a is null and b = 'xxx';
+ QUERY PLAN
+-----------------------------------------------
+ Seq Scan on hp2 hp
+ Filter: ((a IS NULL) AND (b = 'xxx'::text))
+(2 rows)
+
+explain (costs off) select * from hp where a = 2 and b = 'xxx';
+ QUERY PLAN
+-------------------------------------------
+ Seq Scan on hp3 hp
+ Filter: ((a = 2) AND (b = 'xxx'::text))
+(2 rows)
+
+explain (costs off) select * from hp where a = 1 and b = 'abcde';
+ QUERY PLAN
+---------------------------------------------
+ Seq Scan on hp2 hp
+ Filter: ((a = 1) AND (b = 'abcde'::text))
+(2 rows)
+
+explain (costs off) select * from hp where (a = 1 and b = 'abcde') or (a = 2 and b = 'xxx') or (a is null and b is null);
+ QUERY PLAN
+-------------------------------------------------------------------------------------------------------------------------
+ Append
+ -> Seq Scan on hp0 hp_1
+ Filter: (((a = 1) AND (b = 'abcde'::text)) OR ((a = 2) AND (b = 'xxx'::text)) OR ((a IS NULL) AND (b IS NULL)))
+ -> Seq Scan on hp2 hp_2
+ Filter: (((a = 1) AND (b = 'abcde'::text)) OR ((a = 2) AND (b = 'xxx'::text)) OR ((a IS NULL) AND (b IS NULL)))
+ -> Seq Scan on hp3 hp_3
+ Filter: (((a = 1) AND (b = 'abcde'::text)) OR ((a = 2) AND (b = 'xxx'::text)) OR ((a IS NULL) AND (b IS NULL)))
+(7 rows)
+
+-- test pruning when not all the partitions exist
+drop table hp1;
+drop table hp3;
+explain (costs off) select * from hp where a = 1 and b = 'abcde';
+ QUERY PLAN
+---------------------------------------------
+ Seq Scan on hp2 hp
+ Filter: ((a = 1) AND (b = 'abcde'::text))
+(2 rows)
+
+explain (costs off) select * from hp where a = 1 and b = 'abcde' and
+ (c = 2 or c = 3);
+ QUERY PLAN
+----------------------------------------------------------------------
+ Seq Scan on hp2 hp
+ Filter: ((a = 1) AND (b = 'abcde'::text) AND ((c = 2) OR (c = 3)))
+(2 rows)
+
+drop table hp2;
+explain (costs off) select * from hp where a = 1 and b = 'abcde' and
+ (c = 2 or c = 3);
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+--
+-- Test runtime partition pruning
+--
+create table ab (a int not null, b int not null) partition by list (a);
+create table ab_a2 partition of ab for values in(2) partition by list (b);
+create table ab_a2_b1 partition of ab_a2 for values in (1);
+create table ab_a2_b2 partition of ab_a2 for values in (2);
+create table ab_a2_b3 partition of ab_a2 for values in (3);
+create table ab_a1 partition of ab for values in(1) partition by list (b);
+create table ab_a1_b1 partition of ab_a1 for values in (1);
+create table ab_a1_b2 partition of ab_a1 for values in (2);
+create table ab_a1_b3 partition of ab_a1 for values in (3);
+create table ab_a3 partition of ab for values in(3) partition by list (b);
+create table ab_a3_b1 partition of ab_a3 for values in (1);
+create table ab_a3_b2 partition of ab_a3 for values in (2);
+create table ab_a3_b3 partition of ab_a3 for values in (3);
+-- Disallow index only scans as concurrent transactions may stop visibility
+-- bits being set causing "Heap Fetches" to be unstable in the EXPLAIN ANALYZE
+-- output.
+set enable_indexonlyscan = off;
+prepare ab_q1 (int, int, int) as
+select * from ab where a between $1 and $2 and b <= $3;
+explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 2, 3);
+ QUERY PLAN
+---------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 6
+ -> Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
+ -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
+ -> Seq Scan on ab_a2_b3 ab_3 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
+(8 rows)
+
+explain (analyze, costs off, summary off, timing off) execute ab_q1 (1, 2, 3);
+ QUERY PLAN
+---------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 3
+ -> Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
+ -> Seq Scan on ab_a1_b2 ab_2 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
+ -> Seq Scan on ab_a1_b3 ab_3 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
+ -> Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
+ -> Seq Scan on ab_a2_b2 ab_5 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
+ -> Seq Scan on ab_a2_b3 ab_6 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
+(14 rows)
+
+deallocate ab_q1;
+-- Runtime pruning after optimizer pruning
+prepare ab_q1 (int, int) as
+select a from ab where a between $1 and $2 and b < 3;
+explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 2);
+ QUERY PLAN
+---------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 4
+ -> Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b < 3))
+ -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b < 3))
+(6 rows)
+
+explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 4);
+ QUERY PLAN
+---------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 2
+ -> Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b < 3))
+ -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b < 3))
+ -> Seq Scan on ab_a3_b1 ab_3 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b < 3))
+ -> Seq Scan on ab_a3_b2 ab_4 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b < 3))
+(10 rows)
+
+-- Ensure a mix of PARAM_EXTERN and PARAM_EXEC Params work together at
+-- different levels of partitioning.
+prepare ab_q2 (int, int) as
+select a from ab where a between $1 and $2 and b < (select 3);
+explain (analyze, costs off, summary off, timing off) execute ab_q2 (2, 2);
+ QUERY PLAN
+---------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 6
+ InitPlan 1 (returns $0)
+ -> Result (actual rows=1 loops=1)
+ -> Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b < $0))
+ -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1)
+ Filter: ((a >= $1) AND (a <= $2) AND (b < $0))
+ -> Seq Scan on ab_a2_b3 ab_3 (never executed)
+ Filter: ((a >= $1) AND (a <= $2) AND (b < $0))
+(10 rows)
+
+-- As above, but swap the PARAM_EXEC Param to the first partition level
+prepare ab_q3 (int, int) as
+select a from ab where b between $1 and $2 and a < (select 3);
+explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2);
+ QUERY PLAN
+---------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 6
+ InitPlan 1 (returns $0)
+ -> Result (actual rows=1 loops=1)
+ -> Seq Scan on ab_a1_b2 ab_1 (actual rows=0 loops=1)
+ Filter: ((b >= $1) AND (b <= $2) AND (a < $0))
+ -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1)
+ Filter: ((b >= $1) AND (b <= $2) AND (a < $0))
+ -> Seq Scan on ab_a3_b2 ab_3 (never executed)
+ Filter: ((b >= $1) AND (b <= $2) AND (a < $0))
+(10 rows)
+
+--
+-- Test runtime pruning with hash partitioned tables
+--
+-- recreate partitions dropped above
+create table hp1 partition of hp for values with (modulus 4, remainder 1);
+create table hp2 partition of hp for values with (modulus 4, remainder 2);
+create table hp3 partition of hp for values with (modulus 4, remainder 3);
+-- Ensure we correctly prune unneeded partitions when there is an IS NULL qual
+prepare hp_q1 (text) as
+select * from hp where a is null and b = $1;
+explain (costs off) execute hp_q1('xxx');
+ QUERY PLAN
+--------------------------------------------
+ Append
+ Subplans Removed: 3
+ -> Seq Scan on hp2 hp_1
+ Filter: ((a IS NULL) AND (b = $1))
+(4 rows)
+
+deallocate hp_q1;
+drop table hp;
+-- Test a backwards Append scan
+create table list_part (a int) partition by list (a);
+create table list_part1 partition of list_part for values in (1);
+create table list_part2 partition of list_part for values in (2);
+create table list_part3 partition of list_part for values in (3);
+create table list_part4 partition of list_part for values in (4);
+insert into list_part select generate_series(1,4);
+begin;
+-- Don't select an actual value out of the table as the order of the Append's
+-- subnodes may not be stable.
+declare cur SCROLL CURSOR for select 1 from list_part where a > (select 1) and a < (select 4);
+-- move beyond the final row
+move 3 from cur;
+-- Ensure we get two rows.
+fetch backward all from cur;
+ ?column?
+----------
+ 1
+ 1
+(2 rows)
+
+commit;
+begin;
+-- Test run-time pruning using stable functions
+create function list_part_fn(int) returns int as $$ begin return $1; end;$$ language plpgsql stable;
+-- Ensure pruning works using a stable function containing no Vars
+explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(1);
+ QUERY PLAN
+------------------------------------------------------------------
+ Append (actual rows=1 loops=1)
+ Subplans Removed: 3
+ -> Seq Scan on list_part1 list_part_1 (actual rows=1 loops=1)
+ Filter: (a = list_part_fn(1))
+(4 rows)
+
+-- Ensure pruning does not take place when the function has a Var parameter
+explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(a);
+ QUERY PLAN
+------------------------------------------------------------------
+ Append (actual rows=4 loops=1)
+ -> Seq Scan on list_part1 list_part_1 (actual rows=1 loops=1)
+ Filter: (a = list_part_fn(a))
+ -> Seq Scan on list_part2 list_part_2 (actual rows=1 loops=1)
+ Filter: (a = list_part_fn(a))
+ -> Seq Scan on list_part3 list_part_3 (actual rows=1 loops=1)
+ Filter: (a = list_part_fn(a))
+ -> Seq Scan on list_part4 list_part_4 (actual rows=1 loops=1)
+ Filter: (a = list_part_fn(a))
+(9 rows)
+
+-- Ensure pruning does not take place when the expression contains a Var.
+explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(1) + a;
+ QUERY PLAN
+------------------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ -> Seq Scan on list_part1 list_part_1 (actual rows=0 loops=1)
+ Filter: (a = (list_part_fn(1) + a))
+ Rows Removed by Filter: 1
+ -> Seq Scan on list_part2 list_part_2 (actual rows=0 loops=1)
+ Filter: (a = (list_part_fn(1) + a))
+ Rows Removed by Filter: 1
+ -> Seq Scan on list_part3 list_part_3 (actual rows=0 loops=1)
+ Filter: (a = (list_part_fn(1) + a))
+ Rows Removed by Filter: 1
+ -> Seq Scan on list_part4 list_part_4 (actual rows=0 loops=1)
+ Filter: (a = (list_part_fn(1) + a))
+ Rows Removed by Filter: 1
+(13 rows)
+
+rollback;
+drop table list_part;
+-- Parallel append
+-- Parallel queries won't necessarily get as many workers as the planner
+-- asked for. This affects not only the "Workers Launched:" field of EXPLAIN
+-- results, but also row counts and loop counts for parallel scans, Gathers,
+-- and everything in between. This function filters out the values we can't
+-- rely on to be stable.
+-- This removes enough info that you might wonder why bother with EXPLAIN
+-- ANALYZE at all. The answer is that we need to see '(never executed)'
+-- notations because that's the only way to verify runtime pruning.
+create function explain_parallel_append(text) returns setof text
+language plpgsql as
+$$
+declare
+ ln text;
+begin
+ for ln in
+ execute format('explain (analyze, costs off, summary off, timing off) %s',
+ $1)
+ loop
+ ln := regexp_replace(ln, 'Workers Launched: \d+', 'Workers Launched: N');
+ ln := regexp_replace(ln, 'actual rows=\d+ loops=\d+', 'actual rows=N loops=N');
+ ln := regexp_replace(ln, 'Rows Removed by Filter: \d+', 'Rows Removed by Filter: N');
+ return next ln;
+ end loop;
+end;
+$$;
+prepare ab_q4 (int, int) as
+select avg(a) from ab where a between $1 and $2 and b < 4;
+-- Encourage use of parallel plans
+set parallel_setup_cost = 0;
+set parallel_tuple_cost = 0;
+set min_parallel_table_scan_size = 0;
+set max_parallel_workers_per_gather = 2;
+select explain_parallel_append('execute ab_q4 (2, 2)');
+ explain_parallel_append
+------------------------------------------------------------------------------------
+ Finalize Aggregate (actual rows=N loops=N)
+ -> Gather (actual rows=N loops=N)
+ Workers Planned: 2
+ Workers Launched: N
+ -> Partial Aggregate (actual rows=N loops=N)
+ -> Parallel Append (actual rows=N loops=N)
+ Subplans Removed: 6
+ -> Parallel Seq Scan on ab_a2_b1 ab_1 (actual rows=N loops=N)
+ Filter: ((a >= $1) AND (a <= $2) AND (b < 4))
+ -> Parallel Seq Scan on ab_a2_b2 ab_2 (actual rows=N loops=N)
+ Filter: ((a >= $1) AND (a <= $2) AND (b < 4))
+ -> Parallel Seq Scan on ab_a2_b3 ab_3 (actual rows=N loops=N)
+ Filter: ((a >= $1) AND (a <= $2) AND (b < 4))
+(13 rows)
+
+-- Test run-time pruning with IN lists.
+prepare ab_q5 (int, int, int) as
+select avg(a) from ab where a in($1,$2,$3) and b < 4;
+select explain_parallel_append('execute ab_q5 (1, 1, 1)');
+ explain_parallel_append
+------------------------------------------------------------------------------------
+ Finalize Aggregate (actual rows=N loops=N)
+ -> Gather (actual rows=N loops=N)
+ Workers Planned: 2
+ Workers Launched: N
+ -> Partial Aggregate (actual rows=N loops=N)
+ -> Parallel Append (actual rows=N loops=N)
+ Subplans Removed: 6
+ -> Parallel Seq Scan on ab_a1_b1 ab_1 (actual rows=N loops=N)
+ Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3])))
+ -> Parallel Seq Scan on ab_a1_b2 ab_2 (actual rows=N loops=N)
+ Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3])))
+ -> Parallel Seq Scan on ab_a1_b3 ab_3 (actual rows=N loops=N)
+ Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3])))
+(13 rows)
+
+select explain_parallel_append('execute ab_q5 (2, 3, 3)');
+ explain_parallel_append
+------------------------------------------------------------------------------------
+ Finalize Aggregate (actual rows=N loops=N)
+ -> Gather (actual rows=N loops=N)
+ Workers Planned: 2
+ Workers Launched: N
+ -> Partial Aggregate (actual rows=N loops=N)
+ -> Parallel Append (actual rows=N loops=N)
+ Subplans Removed: 3
+ -> Parallel Seq Scan on ab_a2_b1 ab_1 (actual rows=N loops=N)
+ Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3])))
+ -> Parallel Seq Scan on ab_a2_b2 ab_2 (actual rows=N loops=N)
+ Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3])))
+ -> Parallel Seq Scan on ab_a2_b3 ab_3 (actual rows=N loops=N)
+ Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3])))
+ -> Parallel Seq Scan on ab_a3_b1 ab_4 (actual rows=N loops=N)
+ Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3])))
+ -> Parallel Seq Scan on ab_a3_b2 ab_5 (actual rows=N loops=N)
+ Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3])))
+ -> Parallel Seq Scan on ab_a3_b3 ab_6 (actual rows=N loops=N)
+ Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3])))
+(19 rows)
+
+-- Try some params whose values do not belong to any partition.
+select explain_parallel_append('execute ab_q5 (33, 44, 55)');
+ explain_parallel_append
+-----------------------------------------------------------
+ Finalize Aggregate (actual rows=N loops=N)
+ -> Gather (actual rows=N loops=N)
+ Workers Planned: 2
+ Workers Launched: N
+ -> Partial Aggregate (actual rows=N loops=N)
+ -> Parallel Append (actual rows=N loops=N)
+ Subplans Removed: 9
+(7 rows)
+
+-- Test Parallel Append with PARAM_EXEC Params
+select explain_parallel_append('select count(*) from ab where (a = (select 1) or a = (select 3)) and b = 2');
+ explain_parallel_append
+------------------------------------------------------------------------------
+ Aggregate (actual rows=N loops=N)
+ InitPlan 1 (returns $0)
+ -> Result (actual rows=N loops=N)
+ InitPlan 2 (returns $1)
+ -> Result (actual rows=N loops=N)
+ -> Gather (actual rows=N loops=N)
+ Workers Planned: 2
+ Params Evaluated: $0, $1
+ Workers Launched: N
+ -> Parallel Append (actual rows=N loops=N)
+ -> Parallel Seq Scan on ab_a1_b2 ab_1 (actual rows=N loops=N)
+ Filter: ((b = 2) AND ((a = $0) OR (a = $1)))
+ -> Parallel Seq Scan on ab_a2_b2 ab_2 (never executed)
+ Filter: ((b = 2) AND ((a = $0) OR (a = $1)))
+ -> Parallel Seq Scan on ab_a3_b2 ab_3 (actual rows=N loops=N)
+ Filter: ((b = 2) AND ((a = $0) OR (a = $1)))
+(16 rows)
+
+-- Test pruning during parallel nested loop query
+create table lprt_a (a int not null);
+-- Insert some values we won't find in ab
+insert into lprt_a select 0 from generate_series(1,100);
+-- and insert some values that we should find.
+insert into lprt_a values(1),(1);
+analyze lprt_a;
+create index ab_a2_b1_a_idx on ab_a2_b1 (a);
+create index ab_a2_b2_a_idx on ab_a2_b2 (a);
+create index ab_a2_b3_a_idx on ab_a2_b3 (a);
+create index ab_a1_b1_a_idx on ab_a1_b1 (a);
+create index ab_a1_b2_a_idx on ab_a1_b2 (a);
+create index ab_a1_b3_a_idx on ab_a1_b3 (a);
+create index ab_a3_b1_a_idx on ab_a3_b1 (a);
+create index ab_a3_b2_a_idx on ab_a3_b2 (a);
+create index ab_a3_b3_a_idx on ab_a3_b3 (a);
+set enable_hashjoin = 0;
+set enable_mergejoin = 0;
+set enable_memoize = 0;
+select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(0, 0, 1)');
+ explain_parallel_append
+--------------------------------------------------------------------------------------------------------
+ Finalize Aggregate (actual rows=N loops=N)
+ -> Gather (actual rows=N loops=N)
+ Workers Planned: 1
+ Workers Launched: N
+ -> Partial Aggregate (actual rows=N loops=N)
+ -> Nested Loop (actual rows=N loops=N)
+ -> Parallel Seq Scan on lprt_a a (actual rows=N loops=N)
+ Filter: (a = ANY ('{0,0,1}'::integer[]))
+ -> Append (actual rows=N loops=N)
+ -> Index Scan using ab_a1_b1_a_idx on ab_a1_b1 ab_1 (actual rows=N loops=N)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a1_b2_a_idx on ab_a1_b2 ab_2 (actual rows=N loops=N)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a1_b3_a_idx on ab_a1_b3 ab_3 (actual rows=N loops=N)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a2_b1_a_idx on ab_a2_b1 ab_4 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a2_b2_a_idx on ab_a2_b2 ab_5 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a2_b3_a_idx on ab_a2_b3 ab_6 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a3_b1_a_idx on ab_a3_b1 ab_7 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a3_b2_a_idx on ab_a3_b2 ab_8 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a3_b3_a_idx on ab_a3_b3 ab_9 (never executed)
+ Index Cond: (a = a.a)
+(27 rows)
+
+-- Ensure the same partitions are pruned when we make the nested loop
+-- parameter an Expr rather than a plain Param.
+select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a + 0 where a.a in(0, 0, 1)');
+ explain_parallel_append
+--------------------------------------------------------------------------------------------------------
+ Finalize Aggregate (actual rows=N loops=N)
+ -> Gather (actual rows=N loops=N)
+ Workers Planned: 1
+ Workers Launched: N
+ -> Partial Aggregate (actual rows=N loops=N)
+ -> Nested Loop (actual rows=N loops=N)
+ -> Parallel Seq Scan on lprt_a a (actual rows=N loops=N)
+ Filter: (a = ANY ('{0,0,1}'::integer[]))
+ -> Append (actual rows=N loops=N)
+ -> Index Scan using ab_a1_b1_a_idx on ab_a1_b1 ab_1 (actual rows=N loops=N)
+ Index Cond: (a = (a.a + 0))
+ -> Index Scan using ab_a1_b2_a_idx on ab_a1_b2 ab_2 (actual rows=N loops=N)
+ Index Cond: (a = (a.a + 0))
+ -> Index Scan using ab_a1_b3_a_idx on ab_a1_b3 ab_3 (actual rows=N loops=N)
+ Index Cond: (a = (a.a + 0))
+ -> Index Scan using ab_a2_b1_a_idx on ab_a2_b1 ab_4 (never executed)
+ Index Cond: (a = (a.a + 0))
+ -> Index Scan using ab_a2_b2_a_idx on ab_a2_b2 ab_5 (never executed)
+ Index Cond: (a = (a.a + 0))
+ -> Index Scan using ab_a2_b3_a_idx on ab_a2_b3 ab_6 (never executed)
+ Index Cond: (a = (a.a + 0))
+ -> Index Scan using ab_a3_b1_a_idx on ab_a3_b1 ab_7 (never executed)
+ Index Cond: (a = (a.a + 0))
+ -> Index Scan using ab_a3_b2_a_idx on ab_a3_b2 ab_8 (never executed)
+ Index Cond: (a = (a.a + 0))
+ -> Index Scan using ab_a3_b3_a_idx on ab_a3_b3 ab_9 (never executed)
+ Index Cond: (a = (a.a + 0))
+(27 rows)
+
+insert into lprt_a values(3),(3);
+select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 3)');
+ explain_parallel_append
+--------------------------------------------------------------------------------------------------------
+ Finalize Aggregate (actual rows=N loops=N)
+ -> Gather (actual rows=N loops=N)
+ Workers Planned: 1
+ Workers Launched: N
+ -> Partial Aggregate (actual rows=N loops=N)
+ -> Nested Loop (actual rows=N loops=N)
+ -> Parallel Seq Scan on lprt_a a (actual rows=N loops=N)
+ Filter: (a = ANY ('{1,0,3}'::integer[]))
+ -> Append (actual rows=N loops=N)
+ -> Index Scan using ab_a1_b1_a_idx on ab_a1_b1 ab_1 (actual rows=N loops=N)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a1_b2_a_idx on ab_a1_b2 ab_2 (actual rows=N loops=N)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a1_b3_a_idx on ab_a1_b3 ab_3 (actual rows=N loops=N)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a2_b1_a_idx on ab_a2_b1 ab_4 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a2_b2_a_idx on ab_a2_b2 ab_5 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a2_b3_a_idx on ab_a2_b3 ab_6 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a3_b1_a_idx on ab_a3_b1 ab_7 (actual rows=N loops=N)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a3_b2_a_idx on ab_a3_b2 ab_8 (actual rows=N loops=N)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a3_b3_a_idx on ab_a3_b3 ab_9 (actual rows=N loops=N)
+ Index Cond: (a = a.a)
+(27 rows)
+
+select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0)');
+ explain_parallel_append
+--------------------------------------------------------------------------------------------------------
+ Finalize Aggregate (actual rows=N loops=N)
+ -> Gather (actual rows=N loops=N)
+ Workers Planned: 1
+ Workers Launched: N
+ -> Partial Aggregate (actual rows=N loops=N)
+ -> Nested Loop (actual rows=N loops=N)
+ -> Parallel Seq Scan on lprt_a a (actual rows=N loops=N)
+ Filter: (a = ANY ('{1,0,0}'::integer[]))
+ Rows Removed by Filter: N
+ -> Append (actual rows=N loops=N)
+ -> Index Scan using ab_a1_b1_a_idx on ab_a1_b1 ab_1 (actual rows=N loops=N)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a1_b2_a_idx on ab_a1_b2 ab_2 (actual rows=N loops=N)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a1_b3_a_idx on ab_a1_b3 ab_3 (actual rows=N loops=N)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a2_b1_a_idx on ab_a2_b1 ab_4 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a2_b2_a_idx on ab_a2_b2 ab_5 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a2_b3_a_idx on ab_a2_b3 ab_6 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a3_b1_a_idx on ab_a3_b1 ab_7 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a3_b2_a_idx on ab_a3_b2 ab_8 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a3_b3_a_idx on ab_a3_b3 ab_9 (never executed)
+ Index Cond: (a = a.a)
+(28 rows)
+
+delete from lprt_a where a = 1;
+select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0)');
+ explain_parallel_append
+-------------------------------------------------------------------------------------------------
+ Finalize Aggregate (actual rows=N loops=N)
+ -> Gather (actual rows=N loops=N)
+ Workers Planned: 1
+ Workers Launched: N
+ -> Partial Aggregate (actual rows=N loops=N)
+ -> Nested Loop (actual rows=N loops=N)
+ -> Parallel Seq Scan on lprt_a a (actual rows=N loops=N)
+ Filter: (a = ANY ('{1,0,0}'::integer[]))
+ Rows Removed by Filter: N
+ -> Append (actual rows=N loops=N)
+ -> Index Scan using ab_a1_b1_a_idx on ab_a1_b1 ab_1 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a1_b2_a_idx on ab_a1_b2 ab_2 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a1_b3_a_idx on ab_a1_b3 ab_3 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a2_b1_a_idx on ab_a2_b1 ab_4 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a2_b2_a_idx on ab_a2_b2 ab_5 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a2_b3_a_idx on ab_a2_b3 ab_6 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a3_b1_a_idx on ab_a3_b1 ab_7 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a3_b2_a_idx on ab_a3_b2 ab_8 (never executed)
+ Index Cond: (a = a.a)
+ -> Index Scan using ab_a3_b3_a_idx on ab_a3_b3 ab_9 (never executed)
+ Index Cond: (a = a.a)
+(28 rows)
+
+reset enable_hashjoin;
+reset enable_mergejoin;
+reset enable_memoize;
+reset parallel_setup_cost;
+reset parallel_tuple_cost;
+reset min_parallel_table_scan_size;
+reset max_parallel_workers_per_gather;
+-- Test run-time partition pruning with an initplan
+explain (analyze, costs off, summary off, timing off)
+select * from ab where a = (select max(a) from lprt_a) and b = (select max(a)-1 from lprt_a);
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ InitPlan 1 (returns $0)
+ -> Aggregate (actual rows=1 loops=1)
+ -> Seq Scan on lprt_a (actual rows=102 loops=1)
+ InitPlan 2 (returns $1)
+ -> Aggregate (actual rows=1 loops=1)
+ -> Seq Scan on lprt_a lprt_a_1 (actual rows=102 loops=1)
+ -> Bitmap Heap Scan on ab_a1_b1 ab_1 (never executed)
+ Recheck Cond: (a = $0)
+ Filter: (b = $1)
+ -> Bitmap Index Scan on ab_a1_b1_a_idx (never executed)
+ Index Cond: (a = $0)
+ -> Bitmap Heap Scan on ab_a1_b2 ab_2 (never executed)
+ Recheck Cond: (a = $0)
+ Filter: (b = $1)
+ -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed)
+ Index Cond: (a = $0)
+ -> Bitmap Heap Scan on ab_a1_b3 ab_3 (never executed)
+ Recheck Cond: (a = $0)
+ Filter: (b = $1)
+ -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed)
+ Index Cond: (a = $0)
+ -> Bitmap Heap Scan on ab_a2_b1 ab_4 (never executed)
+ Recheck Cond: (a = $0)
+ Filter: (b = $1)
+ -> Bitmap Index Scan on ab_a2_b1_a_idx (never executed)
+ Index Cond: (a = $0)
+ -> Bitmap Heap Scan on ab_a2_b2 ab_5 (never executed)
+ Recheck Cond: (a = $0)
+ Filter: (b = $1)
+ -> Bitmap Index Scan on ab_a2_b2_a_idx (never executed)
+ Index Cond: (a = $0)
+ -> Bitmap Heap Scan on ab_a2_b3 ab_6 (never executed)
+ Recheck Cond: (a = $0)
+ Filter: (b = $1)
+ -> Bitmap Index Scan on ab_a2_b3_a_idx (never executed)
+ Index Cond: (a = $0)
+ -> Bitmap Heap Scan on ab_a3_b1 ab_7 (never executed)
+ Recheck Cond: (a = $0)
+ Filter: (b = $1)
+ -> Bitmap Index Scan on ab_a3_b1_a_idx (never executed)
+ Index Cond: (a = $0)
+ -> Bitmap Heap Scan on ab_a3_b2 ab_8 (actual rows=0 loops=1)
+ Recheck Cond: (a = $0)
+ Filter: (b = $1)
+ -> Bitmap Index Scan on ab_a3_b2_a_idx (actual rows=0 loops=1)
+ Index Cond: (a = $0)
+ -> Bitmap Heap Scan on ab_a3_b3 ab_9 (never executed)
+ Recheck Cond: (a = $0)
+ Filter: (b = $1)
+ -> Bitmap Index Scan on ab_a3_b3_a_idx (never executed)
+ Index Cond: (a = $0)
+(52 rows)
+
+-- Test run-time partition pruning with UNION ALL parents
+explain (analyze, costs off, summary off, timing off)
+select * from (select * from ab where a = 1 union all select * from ab) ab where b = (select 1);
+ QUERY PLAN
+-------------------------------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ InitPlan 1 (returns $0)
+ -> Result (actual rows=1 loops=1)
+ -> Append (actual rows=0 loops=1)
+ -> Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0 loops=1)
+ Recheck Cond: (a = 1)
+ Filter: (b = $0)
+ -> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1)
+ Index Cond: (a = 1)
+ -> Bitmap Heap Scan on ab_a1_b2 ab_12 (never executed)
+ Recheck Cond: (a = 1)
+ Filter: (b = $0)
+ -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed)
+ Index Cond: (a = 1)
+ -> Bitmap Heap Scan on ab_a1_b3 ab_13 (never executed)
+ Recheck Cond: (a = 1)
+ Filter: (b = $0)
+ -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed)
+ Index Cond: (a = 1)
+ -> Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a1_b2 ab_2 (never executed)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a1_b3 ab_3 (never executed)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a2_b2 ab_5 (never executed)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a2_b3 ab_6 (never executed)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a3_b1 ab_7 (actual rows=0 loops=1)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a3_b2 ab_8 (never executed)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a3_b3 ab_9 (never executed)
+ Filter: (b = $0)
+(37 rows)
+
+-- A case containing a UNION ALL with a non-partitioned child.
+explain (analyze, costs off, summary off, timing off)
+select * from (select * from ab where a = 1 union all (values(10,5)) union all select * from ab) ab where b = (select 1);
+ QUERY PLAN
+-------------------------------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ InitPlan 1 (returns $0)
+ -> Result (actual rows=1 loops=1)
+ -> Append (actual rows=0 loops=1)
+ -> Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0 loops=1)
+ Recheck Cond: (a = 1)
+ Filter: (b = $0)
+ -> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1)
+ Index Cond: (a = 1)
+ -> Bitmap Heap Scan on ab_a1_b2 ab_12 (never executed)
+ Recheck Cond: (a = 1)
+ Filter: (b = $0)
+ -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed)
+ Index Cond: (a = 1)
+ -> Bitmap Heap Scan on ab_a1_b3 ab_13 (never executed)
+ Recheck Cond: (a = 1)
+ Filter: (b = $0)
+ -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed)
+ Index Cond: (a = 1)
+ -> Result (actual rows=0 loops=1)
+ One-Time Filter: (5 = $0)
+ -> Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a1_b2 ab_2 (never executed)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a1_b3 ab_3 (never executed)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a2_b2 ab_5 (never executed)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a2_b3 ab_6 (never executed)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a3_b1 ab_7 (actual rows=0 loops=1)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a3_b2 ab_8 (never executed)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a3_b3 ab_9 (never executed)
+ Filter: (b = $0)
+(39 rows)
+
+-- Another UNION ALL test, but containing a mix of exec init and exec run-time pruning.
+create table xy_1 (x int, y int);
+insert into xy_1 values(100,-10);
+set enable_bitmapscan = 0;
+set enable_indexscan = 0;
+prepare ab_q6 as
+select * from (
+ select tableoid::regclass,a,b from ab
+union all
+ select tableoid::regclass,x,y from xy_1
+union all
+ select tableoid::regclass,a,b from ab
+) ab where a = $1 and b = (select -10);
+-- Ensure the xy_1 subplan is not pruned.
+explain (analyze, costs off, summary off, timing off) execute ab_q6(1);
+ QUERY PLAN
+--------------------------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 12
+ InitPlan 1 (returns $0)
+ -> Result (actual rows=1 loops=1)
+ -> Seq Scan on ab_a1_b1 ab_1 (never executed)
+ Filter: ((a = $1) AND (b = $0))
+ -> Seq Scan on ab_a1_b2 ab_2 (never executed)
+ Filter: ((a = $1) AND (b = $0))
+ -> Seq Scan on ab_a1_b3 ab_3 (never executed)
+ Filter: ((a = $1) AND (b = $0))
+ -> Seq Scan on xy_1 (actual rows=0 loops=1)
+ Filter: ((x = $1) AND (y = $0))
+ Rows Removed by Filter: 1
+ -> Seq Scan on ab_a1_b1 ab_4 (never executed)
+ Filter: ((a = $1) AND (b = $0))
+ -> Seq Scan on ab_a1_b2 ab_5 (never executed)
+ Filter: ((a = $1) AND (b = $0))
+ -> Seq Scan on ab_a1_b3 ab_6 (never executed)
+ Filter: ((a = $1) AND (b = $0))
+(19 rows)
+
+-- Ensure we see just the xy_1 row.
+execute ab_q6(100);
+ tableoid | a | b
+----------+-----+-----
+ xy_1 | 100 | -10
+(1 row)
+
+reset enable_bitmapscan;
+reset enable_indexscan;
+deallocate ab_q1;
+deallocate ab_q2;
+deallocate ab_q3;
+deallocate ab_q4;
+deallocate ab_q5;
+deallocate ab_q6;
+-- UPDATE on a partition subtree has been seen to have problems.
+insert into ab values (1,2);
+explain (analyze, costs off, summary off, timing off)
+update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a = ab_a1.a;
+ QUERY PLAN
+-------------------------------------------------------------------------------------------
+ Update on ab_a1 (actual rows=0 loops=1)
+ Update on ab_a1_b1 ab_a1_1
+ Update on ab_a1_b2 ab_a1_2
+ Update on ab_a1_b3 ab_a1_3
+ -> Nested Loop (actual rows=1 loops=1)
+ -> Append (actual rows=1 loops=1)
+ -> Bitmap Heap Scan on ab_a1_b1 ab_a1_1 (actual rows=0 loops=1)
+ Recheck Cond: (a = 1)
+ -> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1)
+ Index Cond: (a = 1)
+ -> Bitmap Heap Scan on ab_a1_b2 ab_a1_2 (actual rows=1 loops=1)
+ Recheck Cond: (a = 1)
+ Heap Blocks: exact=1
+ -> Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1 loops=1)
+ Index Cond: (a = 1)
+ -> Bitmap Heap Scan on ab_a1_b3 ab_a1_3 (actual rows=0 loops=1)
+ Recheck Cond: (a = 1)
+ -> Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1 loops=1)
+ Index Cond: (a = 1)
+ -> Materialize (actual rows=1 loops=1)
+ -> Append (actual rows=1 loops=1)
+ -> Bitmap Heap Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1)
+ Recheck Cond: (a = 1)
+ -> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1)
+ Index Cond: (a = 1)
+ -> Bitmap Heap Scan on ab_a1_b2 ab_2 (actual rows=1 loops=1)
+ Recheck Cond: (a = 1)
+ Heap Blocks: exact=1
+ -> Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1 loops=1)
+ Index Cond: (a = 1)
+ -> Bitmap Heap Scan on ab_a1_b3 ab_3 (actual rows=0 loops=1)
+ Recheck Cond: (a = 1)
+ -> Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1 loops=1)
+ Index Cond: (a = 1)
+(34 rows)
+
+table ab;
+ a | b
+---+---
+ 1 | 3
+(1 row)
+
+-- Test UPDATE where source relation has run-time pruning enabled
+truncate ab;
+insert into ab values (1, 1), (1, 2), (1, 3), (2, 1);
+explain (analyze, costs off, summary off, timing off)
+update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1);
+ QUERY PLAN
+------------------------------------------------------------------------------
+ Update on ab_a1 (actual rows=0 loops=1)
+ Update on ab_a1_b1 ab_a1_1
+ Update on ab_a1_b2 ab_a1_2
+ Update on ab_a1_b3 ab_a1_3
+ InitPlan 1 (returns $0)
+ -> Result (actual rows=1 loops=1)
+ -> Nested Loop (actual rows=3 loops=1)
+ -> Append (actual rows=3 loops=1)
+ -> Seq Scan on ab_a1_b1 ab_a1_1 (actual rows=1 loops=1)
+ -> Seq Scan on ab_a1_b2 ab_a1_2 (actual rows=1 loops=1)
+ -> Seq Scan on ab_a1_b3 ab_a1_3 (actual rows=1 loops=1)
+ -> Materialize (actual rows=1 loops=3)
+ -> Append (actual rows=1 loops=1)
+ -> Seq Scan on ab_a2_b1 ab_a2_1 (actual rows=1 loops=1)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a2_b2 ab_a2_2 (never executed)
+ Filter: (b = $0)
+ -> Seq Scan on ab_a2_b3 ab_a2_3 (never executed)
+ Filter: (b = $0)
+(19 rows)
+
+select tableoid::regclass, * from ab;
+ tableoid | a | b
+----------+---+---
+ ab_a1_b3 | 1 | 3
+ ab_a1_b3 | 1 | 3
+ ab_a1_b3 | 1 | 3
+ ab_a2_b1 | 2 | 1
+(4 rows)
+
+drop table ab, lprt_a;
+-- Join
+create table tbl1(col1 int);
+insert into tbl1 values (501), (505);
+-- Basic table
+create table tprt (col1 int) partition by range (col1);
+create table tprt_1 partition of tprt for values from (1) to (501);
+create table tprt_2 partition of tprt for values from (501) to (1001);
+create table tprt_3 partition of tprt for values from (1001) to (2001);
+create table tprt_4 partition of tprt for values from (2001) to (3001);
+create table tprt_5 partition of tprt for values from (3001) to (4001);
+create table tprt_6 partition of tprt for values from (4001) to (5001);
+create index tprt1_idx on tprt_1 (col1);
+create index tprt2_idx on tprt_2 (col1);
+create index tprt3_idx on tprt_3 (col1);
+create index tprt4_idx on tprt_4 (col1);
+create index tprt5_idx on tprt_5 (col1);
+create index tprt6_idx on tprt_6 (col1);
+insert into tprt values (10), (20), (501), (502), (505), (1001), (4500);
+set enable_hashjoin = off;
+set enable_mergejoin = off;
+explain (analyze, costs off, summary off, timing off)
+select * from tbl1 join tprt on tbl1.col1 > tprt.col1;
+ QUERY PLAN
+--------------------------------------------------------------------------
+ Nested Loop (actual rows=6 loops=1)
+ -> Seq Scan on tbl1 (actual rows=2 loops=1)
+ -> Append (actual rows=3 loops=2)
+ -> Index Scan using tprt1_idx on tprt_1 (actual rows=2 loops=2)
+ Index Cond: (col1 < tbl1.col1)
+ -> Index Scan using tprt2_idx on tprt_2 (actual rows=2 loops=1)
+ Index Cond: (col1 < tbl1.col1)
+ -> Index Scan using tprt3_idx on tprt_3 (never executed)
+ Index Cond: (col1 < tbl1.col1)
+ -> Index Scan using tprt4_idx on tprt_4 (never executed)
+ Index Cond: (col1 < tbl1.col1)
+ -> Index Scan using tprt5_idx on tprt_5 (never executed)
+ Index Cond: (col1 < tbl1.col1)
+ -> Index Scan using tprt6_idx on tprt_6 (never executed)
+ Index Cond: (col1 < tbl1.col1)
+(15 rows)
+
+explain (analyze, costs off, summary off, timing off)
+select * from tbl1 join tprt on tbl1.col1 = tprt.col1;
+ QUERY PLAN
+--------------------------------------------------------------------------
+ Nested Loop (actual rows=2 loops=1)
+ -> Seq Scan on tbl1 (actual rows=2 loops=1)
+ -> Append (actual rows=1 loops=2)
+ -> Index Scan using tprt1_idx on tprt_1 (never executed)
+ Index Cond: (col1 = tbl1.col1)
+ -> Index Scan using tprt2_idx on tprt_2 (actual rows=1 loops=2)
+ Index Cond: (col1 = tbl1.col1)
+ -> Index Scan using tprt3_idx on tprt_3 (never executed)
+ Index Cond: (col1 = tbl1.col1)
+ -> Index Scan using tprt4_idx on tprt_4 (never executed)
+ Index Cond: (col1 = tbl1.col1)
+ -> Index Scan using tprt5_idx on tprt_5 (never executed)
+ Index Cond: (col1 = tbl1.col1)
+ -> Index Scan using tprt6_idx on tprt_6 (never executed)
+ Index Cond: (col1 = tbl1.col1)
+(15 rows)
+
+select tbl1.col1, tprt.col1 from tbl1
+inner join tprt on tbl1.col1 > tprt.col1
+order by tbl1.col1, tprt.col1;
+ col1 | col1
+------+------
+ 501 | 10
+ 501 | 20
+ 505 | 10
+ 505 | 20
+ 505 | 501
+ 505 | 502
+(6 rows)
+
+select tbl1.col1, tprt.col1 from tbl1
+inner join tprt on tbl1.col1 = tprt.col1
+order by tbl1.col1, tprt.col1;
+ col1 | col1
+------+------
+ 501 | 501
+ 505 | 505
+(2 rows)
+
+-- Multiple partitions
+insert into tbl1 values (1001), (1010), (1011);
+explain (analyze, costs off, summary off, timing off)
+select * from tbl1 inner join tprt on tbl1.col1 > tprt.col1;
+ QUERY PLAN
+--------------------------------------------------------------------------
+ Nested Loop (actual rows=23 loops=1)
+ -> Seq Scan on tbl1 (actual rows=5 loops=1)
+ -> Append (actual rows=5 loops=5)
+ -> Index Scan using tprt1_idx on tprt_1 (actual rows=2 loops=5)
+ Index Cond: (col1 < tbl1.col1)
+ -> Index Scan using tprt2_idx on tprt_2 (actual rows=3 loops=4)
+ Index Cond: (col1 < tbl1.col1)
+ -> Index Scan using tprt3_idx on tprt_3 (actual rows=1 loops=2)
+ Index Cond: (col1 < tbl1.col1)
+ -> Index Scan using tprt4_idx on tprt_4 (never executed)
+ Index Cond: (col1 < tbl1.col1)
+ -> Index Scan using tprt5_idx on tprt_5 (never executed)
+ Index Cond: (col1 < tbl1.col1)
+ -> Index Scan using tprt6_idx on tprt_6 (never executed)
+ Index Cond: (col1 < tbl1.col1)
+(15 rows)
+
+explain (analyze, costs off, summary off, timing off)
+select * from tbl1 inner join tprt on tbl1.col1 = tprt.col1;
+ QUERY PLAN
+--------------------------------------------------------------------------
+ Nested Loop (actual rows=3 loops=1)
+ -> Seq Scan on tbl1 (actual rows=5 loops=1)
+ -> Append (actual rows=1 loops=5)
+ -> Index Scan using tprt1_idx on tprt_1 (never executed)
+ Index Cond: (col1 = tbl1.col1)
+ -> Index Scan using tprt2_idx on tprt_2 (actual rows=1 loops=2)
+ Index Cond: (col1 = tbl1.col1)
+ -> Index Scan using tprt3_idx on tprt_3 (actual rows=0 loops=3)
+ Index Cond: (col1 = tbl1.col1)
+ -> Index Scan using tprt4_idx on tprt_4 (never executed)
+ Index Cond: (col1 = tbl1.col1)
+ -> Index Scan using tprt5_idx on tprt_5 (never executed)
+ Index Cond: (col1 = tbl1.col1)
+ -> Index Scan using tprt6_idx on tprt_6 (never executed)
+ Index Cond: (col1 = tbl1.col1)
+(15 rows)
+
+select tbl1.col1, tprt.col1 from tbl1
+inner join tprt on tbl1.col1 > tprt.col1
+order by tbl1.col1, tprt.col1;
+ col1 | col1
+------+------
+ 501 | 10
+ 501 | 20
+ 505 | 10
+ 505 | 20
+ 505 | 501
+ 505 | 502
+ 1001 | 10
+ 1001 | 20
+ 1001 | 501
+ 1001 | 502
+ 1001 | 505
+ 1010 | 10
+ 1010 | 20
+ 1010 | 501
+ 1010 | 502
+ 1010 | 505
+ 1010 | 1001
+ 1011 | 10
+ 1011 | 20
+ 1011 | 501
+ 1011 | 502
+ 1011 | 505
+ 1011 | 1001
+(23 rows)
+
+select tbl1.col1, tprt.col1 from tbl1
+inner join tprt on tbl1.col1 = tprt.col1
+order by tbl1.col1, tprt.col1;
+ col1 | col1
+------+------
+ 501 | 501
+ 505 | 505
+ 1001 | 1001
+(3 rows)
+
+-- Last partition
+delete from tbl1;
+insert into tbl1 values (4400);
+explain (analyze, costs off, summary off, timing off)
+select * from tbl1 join tprt on tbl1.col1 < tprt.col1;
+ QUERY PLAN
+--------------------------------------------------------------------------
+ Nested Loop (actual rows=1 loops=1)
+ -> Seq Scan on tbl1 (actual rows=1 loops=1)
+ -> Append (actual rows=1 loops=1)
+ -> Index Scan using tprt1_idx on tprt_1 (never executed)
+ Index Cond: (col1 > tbl1.col1)
+ -> Index Scan using tprt2_idx on tprt_2 (never executed)
+ Index Cond: (col1 > tbl1.col1)
+ -> Index Scan using tprt3_idx on tprt_3 (never executed)
+ Index Cond: (col1 > tbl1.col1)
+ -> Index Scan using tprt4_idx on tprt_4 (never executed)
+ Index Cond: (col1 > tbl1.col1)
+ -> Index Scan using tprt5_idx on tprt_5 (never executed)
+ Index Cond: (col1 > tbl1.col1)
+ -> Index Scan using tprt6_idx on tprt_6 (actual rows=1 loops=1)
+ Index Cond: (col1 > tbl1.col1)
+(15 rows)
+
+select tbl1.col1, tprt.col1 from tbl1
+inner join tprt on tbl1.col1 < tprt.col1
+order by tbl1.col1, tprt.col1;
+ col1 | col1
+------+------
+ 4400 | 4500
+(1 row)
+
+-- No matching partition
+delete from tbl1;
+insert into tbl1 values (10000);
+explain (analyze, costs off, summary off, timing off)
+select * from tbl1 join tprt on tbl1.col1 = tprt.col1;
+ QUERY PLAN
+-------------------------------------------------------------------
+ Nested Loop (actual rows=0 loops=1)
+ -> Seq Scan on tbl1 (actual rows=1 loops=1)
+ -> Append (actual rows=0 loops=1)
+ -> Index Scan using tprt1_idx on tprt_1 (never executed)
+ Index Cond: (col1 = tbl1.col1)
+ -> Index Scan using tprt2_idx on tprt_2 (never executed)
+ Index Cond: (col1 = tbl1.col1)
+ -> Index Scan using tprt3_idx on tprt_3 (never executed)
+ Index Cond: (col1 = tbl1.col1)
+ -> Index Scan using tprt4_idx on tprt_4 (never executed)
+ Index Cond: (col1 = tbl1.col1)
+ -> Index Scan using tprt5_idx on tprt_5 (never executed)
+ Index Cond: (col1 = tbl1.col1)
+ -> Index Scan using tprt6_idx on tprt_6 (never executed)
+ Index Cond: (col1 = tbl1.col1)
+(15 rows)
+
+select tbl1.col1, tprt.col1 from tbl1
+inner join tprt on tbl1.col1 = tprt.col1
+order by tbl1.col1, tprt.col1;
+ col1 | col1
+------+------
+(0 rows)
+
+drop table tbl1, tprt;
+-- Test with columns defined in varying orders between each level
+create table part_abc (a int not null, b int not null, c int not null) partition by list (a);
+create table part_bac (b int not null, a int not null, c int not null) partition by list (b);
+create table part_cab (c int not null, a int not null, b int not null) partition by list (c);
+create table part_abc_p1 (a int not null, b int not null, c int not null);
+alter table part_abc attach partition part_bac for values in(1);
+alter table part_bac attach partition part_cab for values in(2);
+alter table part_cab attach partition part_abc_p1 for values in(3);
+prepare part_abc_q1 (int, int, int) as
+select * from part_abc where a = $1 and b = $2 and c = $3;
+-- Single partition should be scanned.
+explain (analyze, costs off, summary off, timing off) execute part_abc_q1 (1, 2, 3);
+ QUERY PLAN
+----------------------------------------------------------
+ Seq Scan on part_abc_p1 part_abc (actual rows=0 loops=1)
+ Filter: ((a = $1) AND (b = $2) AND (c = $3))
+(2 rows)
+
+deallocate part_abc_q1;
+drop table part_abc;
+-- Ensure that an Append node properly handles a sub-partitioned table
+-- matching without any of its leaf partitions matching the clause.
+create table listp (a int, b int) partition by list (a);
+create table listp_1 partition of listp for values in(1) partition by list (b);
+create table listp_1_1 partition of listp_1 for values in(1);
+create table listp_2 partition of listp for values in(2) partition by list (b);
+create table listp_2_1 partition of listp_2 for values in(2);
+select * from listp where b = 1;
+ a | b
+---+---
+(0 rows)
+
+-- Ensure that an Append node properly can handle selection of all first level
+-- partitions before finally detecting the correct set of 2nd level partitions
+-- which match the given parameter.
+prepare q1 (int,int) as select * from listp where b in ($1,$2);
+explain (analyze, costs off, summary off, timing off) execute q1 (1,1);
+ QUERY PLAN
+-------------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 1
+ -> Seq Scan on listp_1_1 listp_1 (actual rows=0 loops=1)
+ Filter: (b = ANY (ARRAY[$1, $2]))
+(4 rows)
+
+explain (analyze, costs off, summary off, timing off) execute q1 (2,2);
+ QUERY PLAN
+-------------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 1
+ -> Seq Scan on listp_2_1 listp_1 (actual rows=0 loops=1)
+ Filter: (b = ANY (ARRAY[$1, $2]))
+(4 rows)
+
+-- Try with no matching partitions.
+explain (analyze, costs off, summary off, timing off) execute q1 (0,0);
+ QUERY PLAN
+--------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 2
+(2 rows)
+
+deallocate q1;
+-- Test more complex cases where a not-equal condition further eliminates partitions.
+prepare q1 (int,int,int,int) as select * from listp where b in($1,$2) and $3 <> b and $4 <> b;
+-- Both partitions allowed by IN clause, but one disallowed by <> clause
+explain (analyze, costs off, summary off, timing off) execute q1 (1,2,2,0);
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 1
+ -> Seq Scan on listp_1_1 listp_1 (actual rows=0 loops=1)
+ Filter: ((b = ANY (ARRAY[$1, $2])) AND ($3 <> b) AND ($4 <> b))
+(4 rows)
+
+-- Both partitions allowed by IN clause, then both excluded again by <> clauses.
+explain (analyze, costs off, summary off, timing off) execute q1 (1,2,2,1);
+ QUERY PLAN
+--------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 2
+(2 rows)
+
+-- Ensure Params that evaluate to NULL properly prune away all partitions
+explain (analyze, costs off, summary off, timing off)
+select * from listp where a = (select null::int);
+ QUERY PLAN
+------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ InitPlan 1 (returns $0)
+ -> Result (actual rows=1 loops=1)
+ -> Seq Scan on listp_1_1 listp_1 (never executed)
+ Filter: (a = $0)
+ -> Seq Scan on listp_2_1 listp_2 (never executed)
+ Filter: (a = $0)
+(7 rows)
+
+drop table listp;
+--
+-- check that stable query clauses are only used in run-time pruning
+--
+create table stable_qual_pruning (a timestamp) partition by range (a);
+create table stable_qual_pruning1 partition of stable_qual_pruning
+ for values from ('2000-01-01') to ('2000-02-01');
+create table stable_qual_pruning2 partition of stable_qual_pruning
+ for values from ('2000-02-01') to ('2000-03-01');
+create table stable_qual_pruning3 partition of stable_qual_pruning
+ for values from ('3000-02-01') to ('3000-03-01');
+-- comparison against a stable value requires run-time pruning
+explain (analyze, costs off, summary off, timing off)
+select * from stable_qual_pruning where a < localtimestamp;
+ QUERY PLAN
+--------------------------------------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 1
+ -> Seq Scan on stable_qual_pruning1 stable_qual_pruning_1 (actual rows=0 loops=1)
+ Filter: (a < LOCALTIMESTAMP)
+ -> Seq Scan on stable_qual_pruning2 stable_qual_pruning_2 (actual rows=0 loops=1)
+ Filter: (a < LOCALTIMESTAMP)
+(6 rows)
+
+-- timestamp < timestamptz comparison is only stable, not immutable
+explain (analyze, costs off, summary off, timing off)
+select * from stable_qual_pruning where a < '2000-02-01'::timestamptz;
+ QUERY PLAN
+--------------------------------------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 2
+ -> Seq Scan on stable_qual_pruning1 stable_qual_pruning_1 (actual rows=0 loops=1)
+ Filter: (a < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)
+(4 rows)
+
+-- check ScalarArrayOp cases
+explain (analyze, costs off, summary off, timing off)
+select * from stable_qual_pruning
+ where a = any(array['2010-02-01', '2020-01-01']::timestamp[]);
+ QUERY PLAN
+--------------------------------
+ Result (actual rows=0 loops=1)
+ One-Time Filter: false
+(2 rows)
+
+explain (analyze, costs off, summary off, timing off)
+select * from stable_qual_pruning
+ where a = any(array['2000-02-01', '2010-01-01']::timestamp[]);
+ QUERY PLAN
+----------------------------------------------------------------------------------------------------------------
+ Seq Scan on stable_qual_pruning2 stable_qual_pruning (actual rows=0 loops=1)
+ Filter: (a = ANY ('{"Tue Feb 01 00:00:00 2000","Fri Jan 01 00:00:00 2010"}'::timestamp without time zone[]))
+(2 rows)
+
+explain (analyze, costs off, summary off, timing off)
+select * from stable_qual_pruning
+ where a = any(array['2000-02-01', localtimestamp]::timestamp[]);
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 2
+ -> Seq Scan on stable_qual_pruning2 stable_qual_pruning_1 (actual rows=0 loops=1)
+ Filter: (a = ANY (ARRAY['Tue Feb 01 00:00:00 2000'::timestamp without time zone, LOCALTIMESTAMP]))
+(4 rows)
+
+explain (analyze, costs off, summary off, timing off)
+select * from stable_qual_pruning
+ where a = any(array['2010-02-01', '2020-01-01']::timestamptz[]);
+ QUERY PLAN
+--------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 3
+(2 rows)
+
+explain (analyze, costs off, summary off, timing off)
+select * from stable_qual_pruning
+ where a = any(array['2000-02-01', '2010-01-01']::timestamptz[]);
+ QUERY PLAN
+---------------------------------------------------------------------------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ Subplans Removed: 2
+ -> Seq Scan on stable_qual_pruning2 stable_qual_pruning_1 (actual rows=0 loops=1)
+ Filter: (a = ANY ('{"Tue Feb 01 00:00:00 2000 PST","Fri Jan 01 00:00:00 2010 PST"}'::timestamp with time zone[]))
+(4 rows)
+
+explain (analyze, costs off, summary off, timing off)
+select * from stable_qual_pruning
+ where a = any(null::timestamptz[]);
+ QUERY PLAN
+--------------------------------------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ -> Seq Scan on stable_qual_pruning1 stable_qual_pruning_1 (actual rows=0 loops=1)
+ Filter: (a = ANY (NULL::timestamp with time zone[]))
+ -> Seq Scan on stable_qual_pruning2 stable_qual_pruning_2 (actual rows=0 loops=1)
+ Filter: (a = ANY (NULL::timestamp with time zone[]))
+ -> Seq Scan on stable_qual_pruning3 stable_qual_pruning_3 (actual rows=0 loops=1)
+ Filter: (a = ANY (NULL::timestamp with time zone[]))
+(7 rows)
+
+drop table stable_qual_pruning;
+--
+-- Check that pruning with composite range partitioning works correctly when
+-- it must ignore clauses for trailing keys once it has seen a clause with
+-- non-inclusive operator for an earlier key
+--
+create table mc3p (a int, b int, c int) partition by range (a, abs(b), c);
+create table mc3p0 partition of mc3p
+ for values from (0, 0, 0) to (0, maxvalue, maxvalue);
+create table mc3p1 partition of mc3p
+ for values from (1, 1, 1) to (2, minvalue, minvalue);
+create table mc3p2 partition of mc3p
+ for values from (2, minvalue, minvalue) to (3, maxvalue, maxvalue);
+insert into mc3p values (0, 1, 1), (1, 1, 1), (2, 1, 1);
+explain (analyze, costs off, summary off, timing off)
+select * from mc3p where a < 3 and abs(b) = 1;
+ QUERY PLAN
+--------------------------------------------------------
+ Append (actual rows=3 loops=1)
+ -> Seq Scan on mc3p0 mc3p_1 (actual rows=1 loops=1)
+ Filter: ((a < 3) AND (abs(b) = 1))
+ -> Seq Scan on mc3p1 mc3p_2 (actual rows=1 loops=1)
+ Filter: ((a < 3) AND (abs(b) = 1))
+ -> Seq Scan on mc3p2 mc3p_3 (actual rows=1 loops=1)
+ Filter: ((a < 3) AND (abs(b) = 1))
+(7 rows)
+
+--
+-- Check that pruning with composite range partitioning works correctly when
+-- a combination of runtime parameters is specified, not all of whose values
+-- are available at the same time
+--
+prepare ps1 as
+ select * from mc3p where a = $1 and abs(b) < (select 3);
+explain (analyze, costs off, summary off, timing off)
+execute ps1(1);
+ QUERY PLAN
+--------------------------------------------------------
+ Append (actual rows=1 loops=1)
+ Subplans Removed: 2
+ InitPlan 1 (returns $0)
+ -> Result (actual rows=1 loops=1)
+ -> Seq Scan on mc3p1 mc3p_1 (actual rows=1 loops=1)
+ Filter: ((a = $1) AND (abs(b) < $0))
+(6 rows)
+
+deallocate ps1;
+prepare ps2 as
+ select * from mc3p where a <= $1 and abs(b) < (select 3);
+explain (analyze, costs off, summary off, timing off)
+execute ps2(1);
+ QUERY PLAN
+--------------------------------------------------------
+ Append (actual rows=2 loops=1)
+ Subplans Removed: 1
+ InitPlan 1 (returns $0)
+ -> Result (actual rows=1 loops=1)
+ -> Seq Scan on mc3p0 mc3p_1 (actual rows=1 loops=1)
+ Filter: ((a <= $1) AND (abs(b) < $0))
+ -> Seq Scan on mc3p1 mc3p_2 (actual rows=1 loops=1)
+ Filter: ((a <= $1) AND (abs(b) < $0))
+(8 rows)
+
+deallocate ps2;
+drop table mc3p;
+-- Ensure runtime pruning works with initplans params with boolean types
+create table boolvalues (value bool not null);
+insert into boolvalues values('t'),('f');
+create table boolp (a bool) partition by list (a);
+create table boolp_t partition of boolp for values in('t');
+create table boolp_f partition of boolp for values in('f');
+explain (analyze, costs off, summary off, timing off)
+select * from boolp where a = (select value from boolvalues where value);
+ QUERY PLAN
+-----------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ InitPlan 1 (returns $0)
+ -> Seq Scan on boolvalues (actual rows=1 loops=1)
+ Filter: value
+ Rows Removed by Filter: 1
+ -> Seq Scan on boolp_f boolp_1 (never executed)
+ Filter: (a = $0)
+ -> Seq Scan on boolp_t boolp_2 (actual rows=0 loops=1)
+ Filter: (a = $0)
+(9 rows)
+
+explain (analyze, costs off, summary off, timing off)
+select * from boolp where a = (select value from boolvalues where not value);
+ QUERY PLAN
+-----------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ InitPlan 1 (returns $0)
+ -> Seq Scan on boolvalues (actual rows=1 loops=1)
+ Filter: (NOT value)
+ Rows Removed by Filter: 1
+ -> Seq Scan on boolp_f boolp_1 (actual rows=0 loops=1)
+ Filter: (a = $0)
+ -> Seq Scan on boolp_t boolp_2 (never executed)
+ Filter: (a = $0)
+(9 rows)
+
+drop table boolp;
+--
+-- Test run-time pruning of MergeAppend subnodes
+--
+set enable_seqscan = off;
+set enable_sort = off;
+create table ma_test (a int, b int) partition by range (a);
+create table ma_test_p1 partition of ma_test for values from (0) to (10);
+create table ma_test_p2 partition of ma_test for values from (10) to (20);
+create table ma_test_p3 partition of ma_test for values from (20) to (30);
+insert into ma_test select x,x from generate_series(0,29) t(x);
+create index on ma_test (b);
+analyze ma_test;
+prepare mt_q1 (int) as select a from ma_test where a >= $1 and a % 10 = 5 order by b;
+explain (analyze, costs off, summary off, timing off) execute mt_q1(15);
+ QUERY PLAN
+-----------------------------------------------------------------------------------------
+ Merge Append (actual rows=2 loops=1)
+ Sort Key: ma_test.b
+ Subplans Removed: 1
+ -> Index Scan using ma_test_p2_b_idx on ma_test_p2 ma_test_1 (actual rows=1 loops=1)
+ Filter: ((a >= $1) AND ((a % 10) = 5))
+ Rows Removed by Filter: 9
+ -> Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_2 (actual rows=1 loops=1)
+ Filter: ((a >= $1) AND ((a % 10) = 5))
+ Rows Removed by Filter: 9
+(9 rows)
+
+execute mt_q1(15);
+ a
+----
+ 15
+ 25
+(2 rows)
+
+explain (analyze, costs off, summary off, timing off) execute mt_q1(25);
+ QUERY PLAN
+-----------------------------------------------------------------------------------------
+ Merge Append (actual rows=1 loops=1)
+ Sort Key: ma_test.b
+ Subplans Removed: 2
+ -> Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_1 (actual rows=1 loops=1)
+ Filter: ((a >= $1) AND ((a % 10) = 5))
+ Rows Removed by Filter: 9
+(6 rows)
+
+execute mt_q1(25);
+ a
+----
+ 25
+(1 row)
+
+-- Ensure MergeAppend behaves correctly when no subplans match
+explain (analyze, costs off, summary off, timing off) execute mt_q1(35);
+ QUERY PLAN
+--------------------------------------
+ Merge Append (actual rows=0 loops=1)
+ Sort Key: ma_test.b
+ Subplans Removed: 3
+(3 rows)
+
+execute mt_q1(35);
+ a
+---
+(0 rows)
+
+deallocate mt_q1;
+prepare mt_q2 (int) as select * from ma_test where a >= $1 order by b limit 1;
+-- Ensure output list looks sane when the MergeAppend has no subplans.
+explain (analyze, verbose, costs off, summary off, timing off) execute mt_q2 (35);
+ QUERY PLAN
+--------------------------------------------
+ Limit (actual rows=0 loops=1)
+ Output: ma_test.a, ma_test.b
+ -> Merge Append (actual rows=0 loops=1)
+ Sort Key: ma_test.b
+ Subplans Removed: 3
+(5 rows)
+
+deallocate mt_q2;
+-- ensure initplan params properly prune partitions
+explain (analyze, costs off, summary off, timing off) select * from ma_test where a >= (select min(b) from ma_test_p2) order by b;
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------
+ Merge Append (actual rows=20 loops=1)
+ Sort Key: ma_test.b
+ InitPlan 2 (returns $1)
+ -> Result (actual rows=1 loops=1)
+ InitPlan 1 (returns $0)
+ -> Limit (actual rows=1 loops=1)
+ -> Index Scan using ma_test_p2_b_idx on ma_test_p2 (actual rows=1 loops=1)
+ Index Cond: (b IS NOT NULL)
+ -> Index Scan using ma_test_p1_b_idx on ma_test_p1 ma_test_1 (never executed)
+ Filter: (a >= $1)
+ -> Index Scan using ma_test_p2_b_idx on ma_test_p2 ma_test_2 (actual rows=10 loops=1)
+ Filter: (a >= $1)
+ -> Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_3 (actual rows=10 loops=1)
+ Filter: (a >= $1)
+(14 rows)
+
+reset enable_seqscan;
+reset enable_sort;
+drop table ma_test;
+reset enable_indexonlyscan;
+--
+-- check that pruning works properly when the partition key is of a
+-- pseudotype
+--
+-- array type list partition key
+create table pp_arrpart (a int[]) partition by list (a);
+create table pp_arrpart1 partition of pp_arrpart for values in ('{1}');
+create table pp_arrpart2 partition of pp_arrpart for values in ('{2, 3}', '{4, 5}');
+explain (costs off) select * from pp_arrpart where a = '{1}';
+ QUERY PLAN
+------------------------------------
+ Seq Scan on pp_arrpart1 pp_arrpart
+ Filter: (a = '{1}'::integer[])
+(2 rows)
+
+explain (costs off) select * from pp_arrpart where a = '{1, 2}';
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+explain (costs off) select * from pp_arrpart where a in ('{4, 5}', '{1}');
+ QUERY PLAN
+----------------------------------------------------------------------
+ Append
+ -> Seq Scan on pp_arrpart1 pp_arrpart_1
+ Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[]))
+ -> Seq Scan on pp_arrpart2 pp_arrpart_2
+ Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[]))
+(5 rows)
+
+explain (costs off) update pp_arrpart set a = a where a = '{1}';
+ QUERY PLAN
+--------------------------------------------
+ Update on pp_arrpart
+ Update on pp_arrpart1 pp_arrpart_1
+ -> Seq Scan on pp_arrpart1 pp_arrpart_1
+ Filter: (a = '{1}'::integer[])
+(4 rows)
+
+explain (costs off) delete from pp_arrpart where a = '{1}';
+ QUERY PLAN
+--------------------------------------------
+ Delete on pp_arrpart
+ Delete on pp_arrpart1 pp_arrpart_1
+ -> Seq Scan on pp_arrpart1 pp_arrpart_1
+ Filter: (a = '{1}'::integer[])
+(4 rows)
+
+drop table pp_arrpart;
+-- array type hash partition key
+create table pph_arrpart (a int[]) partition by hash (a);
+create table pph_arrpart1 partition of pph_arrpart for values with (modulus 2, remainder 0);
+create table pph_arrpart2 partition of pph_arrpart for values with (modulus 2, remainder 1);
+insert into pph_arrpart values ('{1}'), ('{1, 2}'), ('{4, 5}');
+select tableoid::regclass, * from pph_arrpart order by 1;
+ tableoid | a
+--------------+-------
+ pph_arrpart1 | {1,2}
+ pph_arrpart1 | {4,5}
+ pph_arrpart2 | {1}
+(3 rows)
+
+explain (costs off) select * from pph_arrpart where a = '{1}';
+ QUERY PLAN
+--------------------------------------
+ Seq Scan on pph_arrpart2 pph_arrpart
+ Filter: (a = '{1}'::integer[])
+(2 rows)
+
+explain (costs off) select * from pph_arrpart where a = '{1, 2}';
+ QUERY PLAN
+--------------------------------------
+ Seq Scan on pph_arrpart1 pph_arrpart
+ Filter: (a = '{1,2}'::integer[])
+(2 rows)
+
+explain (costs off) select * from pph_arrpart where a in ('{4, 5}', '{1}');
+ QUERY PLAN
+----------------------------------------------------------------------
+ Append
+ -> Seq Scan on pph_arrpart1 pph_arrpart_1
+ Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[]))
+ -> Seq Scan on pph_arrpart2 pph_arrpart_2
+ Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[]))
+(5 rows)
+
+drop table pph_arrpart;
+-- enum type list partition key
+create type pp_colors as enum ('green', 'blue', 'black');
+create table pp_enumpart (a pp_colors) partition by list (a);
+create table pp_enumpart_green partition of pp_enumpart for values in ('green');
+create table pp_enumpart_blue partition of pp_enumpart for values in ('blue');
+explain (costs off) select * from pp_enumpart where a = 'blue';
+ QUERY PLAN
+------------------------------------------
+ Seq Scan on pp_enumpart_blue pp_enumpart
+ Filter: (a = 'blue'::pp_colors)
+(2 rows)
+
+explain (costs off) select * from pp_enumpart where a = 'black';
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+drop table pp_enumpart;
+drop type pp_colors;
+-- record type as partition key
+create type pp_rectype as (a int, b int);
+create table pp_recpart (a pp_rectype) partition by list (a);
+create table pp_recpart_11 partition of pp_recpart for values in ('(1,1)');
+create table pp_recpart_23 partition of pp_recpart for values in ('(2,3)');
+explain (costs off) select * from pp_recpart where a = '(1,1)'::pp_rectype;
+ QUERY PLAN
+--------------------------------------
+ Seq Scan on pp_recpart_11 pp_recpart
+ Filter: (a = '(1,1)'::pp_rectype)
+(2 rows)
+
+explain (costs off) select * from pp_recpart where a = '(1,2)'::pp_rectype;
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+drop table pp_recpart;
+drop type pp_rectype;
+-- range type partition key
+create table pp_intrangepart (a int4range) partition by list (a);
+create table pp_intrangepart12 partition of pp_intrangepart for values in ('[1,2]');
+create table pp_intrangepart2inf partition of pp_intrangepart for values in ('[2,)');
+explain (costs off) select * from pp_intrangepart where a = '[1,2]'::int4range;
+ QUERY PLAN
+-----------------------------------------------
+ Seq Scan on pp_intrangepart12 pp_intrangepart
+ Filter: (a = '[1,3)'::int4range)
+(2 rows)
+
+explain (costs off) select * from pp_intrangepart where a = '(1,2)'::int4range;
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+drop table pp_intrangepart;
+--
+-- Ensure the enable_partition_prune GUC properly disables partition pruning.
+--
+create table pp_lp (a int, value int) partition by list (a);
+create table pp_lp1 partition of pp_lp for values in(1);
+create table pp_lp2 partition of pp_lp for values in(2);
+explain (costs off) select * from pp_lp where a = 1;
+ QUERY PLAN
+--------------------------
+ Seq Scan on pp_lp1 pp_lp
+ Filter: (a = 1)
+(2 rows)
+
+explain (costs off) update pp_lp set value = 10 where a = 1;
+ QUERY PLAN
+----------------------------------
+ Update on pp_lp
+ Update on pp_lp1 pp_lp_1
+ -> Seq Scan on pp_lp1 pp_lp_1
+ Filter: (a = 1)
+(4 rows)
+
+explain (costs off) delete from pp_lp where a = 1;
+ QUERY PLAN
+----------------------------------
+ Delete on pp_lp
+ Delete on pp_lp1 pp_lp_1
+ -> Seq Scan on pp_lp1 pp_lp_1
+ Filter: (a = 1)
+(4 rows)
+
+set enable_partition_pruning = off;
+set constraint_exclusion = 'partition'; -- this should not affect the result.
+explain (costs off) select * from pp_lp where a = 1;
+ QUERY PLAN
+----------------------------------
+ Append
+ -> Seq Scan on pp_lp1 pp_lp_1
+ Filter: (a = 1)
+ -> Seq Scan on pp_lp2 pp_lp_2
+ Filter: (a = 1)
+(5 rows)
+
+explain (costs off) update pp_lp set value = 10 where a = 1;
+ QUERY PLAN
+----------------------------------------
+ Update on pp_lp
+ Update on pp_lp1 pp_lp_1
+ Update on pp_lp2 pp_lp_2
+ -> Append
+ -> Seq Scan on pp_lp1 pp_lp_1
+ Filter: (a = 1)
+ -> Seq Scan on pp_lp2 pp_lp_2
+ Filter: (a = 1)
+(8 rows)
+
+explain (costs off) delete from pp_lp where a = 1;
+ QUERY PLAN
+----------------------------------------
+ Delete on pp_lp
+ Delete on pp_lp1 pp_lp_1
+ Delete on pp_lp2 pp_lp_2
+ -> Append
+ -> Seq Scan on pp_lp1 pp_lp_1
+ Filter: (a = 1)
+ -> Seq Scan on pp_lp2 pp_lp_2
+ Filter: (a = 1)
+(8 rows)
+
+set constraint_exclusion = 'off'; -- this should not affect the result.
+explain (costs off) select * from pp_lp where a = 1;
+ QUERY PLAN
+----------------------------------
+ Append
+ -> Seq Scan on pp_lp1 pp_lp_1
+ Filter: (a = 1)
+ -> Seq Scan on pp_lp2 pp_lp_2
+ Filter: (a = 1)
+(5 rows)
+
+explain (costs off) update pp_lp set value = 10 where a = 1;
+ QUERY PLAN
+----------------------------------------
+ Update on pp_lp
+ Update on pp_lp1 pp_lp_1
+ Update on pp_lp2 pp_lp_2
+ -> Append
+ -> Seq Scan on pp_lp1 pp_lp_1
+ Filter: (a = 1)
+ -> Seq Scan on pp_lp2 pp_lp_2
+ Filter: (a = 1)
+(8 rows)
+
+explain (costs off) delete from pp_lp where a = 1;
+ QUERY PLAN
+----------------------------------------
+ Delete on pp_lp
+ Delete on pp_lp1 pp_lp_1
+ Delete on pp_lp2 pp_lp_2
+ -> Append
+ -> Seq Scan on pp_lp1 pp_lp_1
+ Filter: (a = 1)
+ -> Seq Scan on pp_lp2 pp_lp_2
+ Filter: (a = 1)
+(8 rows)
+
+drop table pp_lp;
+-- Ensure enable_partition_prune does not affect non-partitioned tables.
+create table inh_lp (a int, value int);
+create table inh_lp1 (a int, value int, check(a = 1)) inherits (inh_lp);
+NOTICE: merging column "a" with inherited definition
+NOTICE: merging column "value" with inherited definition
+create table inh_lp2 (a int, value int, check(a = 2)) inherits (inh_lp);
+NOTICE: merging column "a" with inherited definition
+NOTICE: merging column "value" with inherited definition
+set constraint_exclusion = 'partition';
+-- inh_lp2 should be removed in the following 3 cases.
+explain (costs off) select * from inh_lp where a = 1;
+ QUERY PLAN
+------------------------------------
+ Append
+ -> Seq Scan on inh_lp inh_lp_1
+ Filter: (a = 1)
+ -> Seq Scan on inh_lp1 inh_lp_2
+ Filter: (a = 1)
+(5 rows)
+
+explain (costs off) update inh_lp set value = 10 where a = 1;
+ QUERY PLAN
+------------------------------------------------
+ Update on inh_lp
+ Update on inh_lp inh_lp_1
+ Update on inh_lp1 inh_lp_2
+ -> Result
+ -> Append
+ -> Seq Scan on inh_lp inh_lp_1
+ Filter: (a = 1)
+ -> Seq Scan on inh_lp1 inh_lp_2
+ Filter: (a = 1)
+(9 rows)
+
+explain (costs off) delete from inh_lp where a = 1;
+ QUERY PLAN
+------------------------------------------
+ Delete on inh_lp
+ Delete on inh_lp inh_lp_1
+ Delete on inh_lp1 inh_lp_2
+ -> Append
+ -> Seq Scan on inh_lp inh_lp_1
+ Filter: (a = 1)
+ -> Seq Scan on inh_lp1 inh_lp_2
+ Filter: (a = 1)
+(8 rows)
+
+-- Ensure we don't exclude normal relations when we only expect to exclude
+-- inheritance children
+explain (costs off) update inh_lp1 set value = 10 where a = 2;
+ QUERY PLAN
+---------------------------
+ Update on inh_lp1
+ -> Seq Scan on inh_lp1
+ Filter: (a = 2)
+(3 rows)
+
+drop table inh_lp cascade;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table inh_lp1
+drop cascades to table inh_lp2
+reset enable_partition_pruning;
+reset constraint_exclusion;
+-- Check pruning for a partition tree containing only temporary relations
+create temp table pp_temp_parent (a int) partition by list (a);
+create temp table pp_temp_part_1 partition of pp_temp_parent for values in (1);
+create temp table pp_temp_part_def partition of pp_temp_parent default;
+explain (costs off) select * from pp_temp_parent where true;
+ QUERY PLAN
+-----------------------------------------------------
+ Append
+ -> Seq Scan on pp_temp_part_1 pp_temp_parent_1
+ -> Seq Scan on pp_temp_part_def pp_temp_parent_2
+(3 rows)
+
+explain (costs off) select * from pp_temp_parent where a = 2;
+ QUERY PLAN
+---------------------------------------------
+ Seq Scan on pp_temp_part_def pp_temp_parent
+ Filter: (a = 2)
+(2 rows)
+
+drop table pp_temp_parent;
+-- Stress run-time partition pruning a bit more, per bug reports
+create temp table p (a int, b int, c int) partition by list (a);
+create temp table p1 partition of p for values in (1);
+create temp table p2 partition of p for values in (2);
+create temp table q (a int, b int, c int) partition by list (a);
+create temp table q1 partition of q for values in (1) partition by list (b);
+create temp table q11 partition of q1 for values in (1) partition by list (c);
+create temp table q111 partition of q11 for values in (1);
+create temp table q2 partition of q for values in (2) partition by list (b);
+create temp table q21 partition of q2 for values in (1);
+create temp table q22 partition of q2 for values in (2);
+insert into q22 values (2, 2, 3);
+explain (costs off)
+select *
+from (
+ select * from p
+ union all
+ select * from q1
+ union all
+ select 1, 1, 1
+ ) s(a, b, c)
+where s.a = 1 and s.b = 1 and s.c = (select 1);
+ QUERY PLAN
+----------------------------------------------------
+ Append
+ InitPlan 1 (returns $0)
+ -> Result
+ -> Seq Scan on p1 p
+ Filter: ((a = 1) AND (b = 1) AND (c = $0))
+ -> Seq Scan on q111 q1
+ Filter: ((a = 1) AND (b = 1) AND (c = $0))
+ -> Result
+ One-Time Filter: (1 = $0)
+(9 rows)
+
+select *
+from (
+ select * from p
+ union all
+ select * from q1
+ union all
+ select 1, 1, 1
+ ) s(a, b, c)
+where s.a = 1 and s.b = 1 and s.c = (select 1);
+ a | b | c
+---+---+---
+ 1 | 1 | 1
+(1 row)
+
+prepare q (int, int) as
+select *
+from (
+ select * from p
+ union all
+ select * from q1
+ union all
+ select 1, 1, 1
+ ) s(a, b, c)
+where s.a = $1 and s.b = $2 and s.c = (select 1);
+explain (costs off) execute q (1, 1);
+ QUERY PLAN
+---------------------------------------------------------------
+ Append
+ Subplans Removed: 1
+ InitPlan 1 (returns $0)
+ -> Result
+ -> Seq Scan on p1 p
+ Filter: ((a = $1) AND (b = $2) AND (c = $0))
+ -> Seq Scan on q111 q1
+ Filter: ((a = $1) AND (b = $2) AND (c = $0))
+ -> Result
+ One-Time Filter: ((1 = $1) AND (1 = $2) AND (1 = $0))
+(10 rows)
+
+execute q (1, 1);
+ a | b | c
+---+---+---
+ 1 | 1 | 1
+(1 row)
+
+drop table p, q;
+-- Ensure run-time pruning works correctly when we match a partitioned table
+-- on the first level but find no matching partitions on the second level.
+create table listp (a int, b int) partition by list (a);
+create table listp1 partition of listp for values in(1);
+create table listp2 partition of listp for values in(2) partition by list(b);
+create table listp2_10 partition of listp2 for values in (10);
+explain (analyze, costs off, summary off, timing off)
+select * from listp where a = (select 2) and b <> 10;
+ QUERY PLAN
+--------------------------------------------------
+ Seq Scan on listp1 listp (actual rows=0 loops=1)
+ Filter: ((b <> 10) AND (a = $0))
+ InitPlan 1 (returns $0)
+ -> Result (never executed)
+(4 rows)
+
+--
+-- check that a partition directly accessed in a query is excluded with
+-- constraint_exclusion = on
+--
+-- turn off partition pruning, so that it doesn't interfere
+set enable_partition_pruning to off;
+-- setting constraint_exclusion to 'partition' disables exclusion
+set constraint_exclusion to 'partition';
+explain (costs off) select * from listp1 where a = 2;
+ QUERY PLAN
+--------------------
+ Seq Scan on listp1
+ Filter: (a = 2)
+(2 rows)
+
+explain (costs off) update listp1 set a = 1 where a = 2;
+ QUERY PLAN
+--------------------------
+ Update on listp1
+ -> Seq Scan on listp1
+ Filter: (a = 2)
+(3 rows)
+
+-- constraint exclusion enabled
+set constraint_exclusion to 'on';
+explain (costs off) select * from listp1 where a = 2;
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+explain (costs off) update listp1 set a = 1 where a = 2;
+ QUERY PLAN
+--------------------------------
+ Update on listp1
+ -> Result
+ One-Time Filter: false
+(3 rows)
+
+reset constraint_exclusion;
+reset enable_partition_pruning;
+drop table listp;
+-- Ensure run-time pruning works correctly for nested Append nodes
+set parallel_setup_cost to 0;
+set parallel_tuple_cost to 0;
+create table listp (a int) partition by list(a);
+create table listp_12 partition of listp for values in(1,2) partition by list(a);
+create table listp_12_1 partition of listp_12 for values in(1);
+create table listp_12_2 partition of listp_12 for values in(2);
+-- Force the 2nd subnode of the Append to be non-parallel. This results in
+-- a nested Append node because the mixed parallel / non-parallel paths cannot
+-- be pulled into the top-level Append.
+alter table listp_12_1 set (parallel_workers = 0);
+-- Ensure that listp_12_2 is not scanned. (The nested Append is not seen in
+-- the plan as it's pulled in setref.c due to having just a single subnode).
+select explain_parallel_append('select * from listp where a = (select 1);');
+ explain_parallel_append
+----------------------------------------------------------------------
+ Gather (actual rows=N loops=N)
+ Workers Planned: 2
+ Params Evaluated: $0
+ Workers Launched: N
+ InitPlan 1 (returns $0)
+ -> Result (actual rows=N loops=N)
+ -> Parallel Append (actual rows=N loops=N)
+ -> Seq Scan on listp_12_1 listp_1 (actual rows=N loops=N)
+ Filter: (a = $0)
+ -> Parallel Seq Scan on listp_12_2 listp_2 (never executed)
+ Filter: (a = $0)
+(11 rows)
+
+-- Like the above but throw some more complexity at the planner by adding
+-- a UNION ALL. We expect both sides of the union not to scan the
+-- non-required partitions.
+select explain_parallel_append(
+'select * from listp where a = (select 1)
+ union all
+select * from listp where a = (select 2);');
+ explain_parallel_append
+-----------------------------------------------------------------------------------
+ Append (actual rows=N loops=N)
+ -> Gather (actual rows=N loops=N)
+ Workers Planned: 2
+ Params Evaluated: $0
+ Workers Launched: N
+ InitPlan 1 (returns $0)
+ -> Result (actual rows=N loops=N)
+ -> Parallel Append (actual rows=N loops=N)
+ -> Seq Scan on listp_12_1 listp_1 (actual rows=N loops=N)
+ Filter: (a = $0)
+ -> Parallel Seq Scan on listp_12_2 listp_2 (never executed)
+ Filter: (a = $0)
+ -> Gather (actual rows=N loops=N)
+ Workers Planned: 2
+ Params Evaluated: $1
+ Workers Launched: N
+ InitPlan 2 (returns $1)
+ -> Result (actual rows=N loops=N)
+ -> Parallel Append (actual rows=N loops=N)
+ -> Seq Scan on listp_12_1 listp_4 (never executed)
+ Filter: (a = $1)
+ -> Parallel Seq Scan on listp_12_2 listp_5 (actual rows=N loops=N)
+ Filter: (a = $1)
+(23 rows)
+
+drop table listp;
+reset parallel_tuple_cost;
+reset parallel_setup_cost;
+-- Test case for run-time pruning with a nested Merge Append
+set enable_sort to 0;
+create table rangep (a int, b int) partition by range (a);
+create table rangep_0_to_100 partition of rangep for values from (0) to (100) partition by list (b);
+-- We need 3 sub-partitions. 1 to validate pruning worked and another two
+-- because a single remaining partition would be pulled up to the main Append.
+create table rangep_0_to_100_1 partition of rangep_0_to_100 for values in(1);
+create table rangep_0_to_100_2 partition of rangep_0_to_100 for values in(2);
+create table rangep_0_to_100_3 partition of rangep_0_to_100 for values in(3);
+create table rangep_100_to_200 partition of rangep for values from (100) to (200);
+create index on rangep (a);
+-- Ensure run-time pruning works on the nested Merge Append
+explain (analyze on, costs off, timing off, summary off)
+select * from rangep where b IN((select 1),(select 2)) order by a;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ InitPlan 1 (returns $0)
+ -> Result (actual rows=1 loops=1)
+ InitPlan 2 (returns $1)
+ -> Result (actual rows=1 loops=1)
+ -> Merge Append (actual rows=0 loops=1)
+ Sort Key: rangep_2.a
+ -> Index Scan using rangep_0_to_100_1_a_idx on rangep_0_to_100_1 rangep_2 (actual rows=0 loops=1)
+ Filter: (b = ANY (ARRAY[$0, $1]))
+ -> Index Scan using rangep_0_to_100_2_a_idx on rangep_0_to_100_2 rangep_3 (actual rows=0 loops=1)
+ Filter: (b = ANY (ARRAY[$0, $1]))
+ -> Index Scan using rangep_0_to_100_3_a_idx on rangep_0_to_100_3 rangep_4 (never executed)
+ Filter: (b = ANY (ARRAY[$0, $1]))
+ -> Index Scan using rangep_100_to_200_a_idx on rangep_100_to_200 rangep_5 (actual rows=0 loops=1)
+ Filter: (b = ANY (ARRAY[$0, $1]))
+(15 rows)
+
+reset enable_sort;
+drop table rangep;
+--
+-- Check that gen_prune_steps_from_opexps() works well for various cases of
+-- clauses for different partition keys
+--
+create table rp_prefix_test1 (a int, b varchar) partition by range(a, b);
+create table rp_prefix_test1_p1 partition of rp_prefix_test1 for values from (1, 'a') to (1, 'b');
+create table rp_prefix_test1_p2 partition of rp_prefix_test1 for values from (2, 'a') to (2, 'b');
+-- Don't call get_steps_using_prefix() with the last partition key b plus
+-- an empty prefix
+explain (costs off) select * from rp_prefix_test1 where a <= 1 and b = 'a';
+ QUERY PLAN
+--------------------------------------------------
+ Seq Scan on rp_prefix_test1_p1 rp_prefix_test1
+ Filter: ((a <= 1) AND ((b)::text = 'a'::text))
+(2 rows)
+
+create table rp_prefix_test2 (a int, b int, c int) partition by range(a, b, c);
+create table rp_prefix_test2_p1 partition of rp_prefix_test2 for values from (1, 1, 0) to (1, 1, 10);
+create table rp_prefix_test2_p2 partition of rp_prefix_test2 for values from (2, 2, 0) to (2, 2, 10);
+-- Don't call get_steps_using_prefix() with the last partition key c plus
+-- an invalid prefix (ie, b = 1)
+explain (costs off) select * from rp_prefix_test2 where a <= 1 and b = 1 and c >= 0;
+ QUERY PLAN
+------------------------------------------------
+ Seq Scan on rp_prefix_test2_p1 rp_prefix_test2
+ Filter: ((a <= 1) AND (c >= 0) AND (b = 1))
+(2 rows)
+
+create table rp_prefix_test3 (a int, b int, c int, d int) partition by range(a, b, c, d);
+create table rp_prefix_test3_p1 partition of rp_prefix_test3 for values from (1, 1, 1, 0) to (1, 1, 1, 10);
+create table rp_prefix_test3_p2 partition of rp_prefix_test3 for values from (2, 2, 2, 0) to (2, 2, 2, 10);
+-- Test that get_steps_using_prefix() handles a prefix that contains multiple
+-- clauses for the partition key b (ie, b >= 1 and b >= 2)
+explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b >= 2 and c >= 2 and d >= 0;
+ QUERY PLAN
+--------------------------------------------------------------------------
+ Seq Scan on rp_prefix_test3_p2 rp_prefix_test3
+ Filter: ((a >= 1) AND (b >= 1) AND (b >= 2) AND (c >= 2) AND (d >= 0))
+(2 rows)
+
+-- Test that get_steps_using_prefix() handles a prefix that contains multiple
+-- clauses for the partition key b (ie, b >= 1 and b = 2) (This also tests
+-- that the caller arranges clauses in that prefix in the required order)
+explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b = 2 and c = 2 and d >= 0;
+ QUERY PLAN
+------------------------------------------------------------------------
+ Seq Scan on rp_prefix_test3_p2 rp_prefix_test3
+ Filter: ((a >= 1) AND (b >= 1) AND (d >= 0) AND (b = 2) AND (c = 2))
+(2 rows)
+
+drop table rp_prefix_test1;
+drop table rp_prefix_test2;
+drop table rp_prefix_test3;
+--
+-- Test that get_steps_using_prefix() handles IS NULL clauses correctly
+--
+create table hp_prefix_test (a int, b int, c int, d int)
+ partition by hash (a part_test_int4_ops, b part_test_int4_ops, c part_test_int4_ops, d part_test_int4_ops);
+-- create 8 partitions
+select 'create table hp_prefix_test_p' || x::text || ' partition of hp_prefix_test for values with (modulus 8, remainder ' || x::text || ');'
+from generate_Series(0,7) x;
+ ?column?
+------------------------------------------------------------------------------------------------------
+ create table hp_prefix_test_p0 partition of hp_prefix_test for values with (modulus 8, remainder 0);
+ create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 8, remainder 1);
+ create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 8, remainder 2);
+ create table hp_prefix_test_p3 partition of hp_prefix_test for values with (modulus 8, remainder 3);
+ create table hp_prefix_test_p4 partition of hp_prefix_test for values with (modulus 8, remainder 4);
+ create table hp_prefix_test_p5 partition of hp_prefix_test for values with (modulus 8, remainder 5);
+ create table hp_prefix_test_p6 partition of hp_prefix_test for values with (modulus 8, remainder 6);
+ create table hp_prefix_test_p7 partition of hp_prefix_test for values with (modulus 8, remainder 7);
+(8 rows)
+
+\gexec
+create table hp_prefix_test_p0 partition of hp_prefix_test for values with (modulus 8, remainder 0);
+create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 8, remainder 1);
+create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 8, remainder 2);
+create table hp_prefix_test_p3 partition of hp_prefix_test for values with (modulus 8, remainder 3);
+create table hp_prefix_test_p4 partition of hp_prefix_test for values with (modulus 8, remainder 4);
+create table hp_prefix_test_p5 partition of hp_prefix_test for values with (modulus 8, remainder 5);
+create table hp_prefix_test_p6 partition of hp_prefix_test for values with (modulus 8, remainder 6);
+create table hp_prefix_test_p7 partition of hp_prefix_test for values with (modulus 8, remainder 7);
+-- insert 16 rows, one row for each test to perform.
+insert into hp_prefix_test
+select
+ case a when 0 then null else 1 end,
+ case b when 0 then null else 2 end,
+ case c when 0 then null else 3 end,
+ case d when 0 then null else 4 end
+from
+ generate_series(0,1) a,
+ generate_series(0,1) b,
+ generate_Series(0,1) c,
+ generate_Series(0,1) d;
+-- Ensure partition pruning works correctly for each combination of IS NULL
+-- and equality quals. This may seem a little excessive, but there have been
+-- a number of bugs in this area over the years. We make use of row only
+-- output to reduce the size of the expected results.
+\t on
+select
+ 'explain (costs off) select tableoid::regclass,* from hp_prefix_test where ' ||
+ string_agg(c.colname || case when g.s & (1 << c.colpos) = 0 then ' is null' else ' = ' || (colpos+1)::text end, ' and ' order by c.colpos)
+from (values('a',0),('b',1),('c',2),('d',3)) c(colname, colpos), generate_Series(0,15) g(s)
+group by g.s
+order by g.s;
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4
+ explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4
+
+\gexec
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null
+ Seq Scan on hp_prefix_test_p0 hp_prefix_test
+ Filter: ((a IS NULL) AND (b IS NULL) AND (c IS NULL) AND (d IS NULL))
+
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null
+ Seq Scan on hp_prefix_test_p1 hp_prefix_test
+ Filter: ((b IS NULL) AND (c IS NULL) AND (d IS NULL) AND (a = 1))
+
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null
+ Seq Scan on hp_prefix_test_p2 hp_prefix_test
+ Filter: ((a IS NULL) AND (c IS NULL) AND (d IS NULL) AND (b = 2))
+
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null
+ Seq Scan on hp_prefix_test_p4 hp_prefix_test
+ Filter: ((c IS NULL) AND (d IS NULL) AND (a = 1) AND (b = 2))
+
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null
+ Seq Scan on hp_prefix_test_p3 hp_prefix_test
+ Filter: ((a IS NULL) AND (b IS NULL) AND (d IS NULL) AND (c = 3))
+
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null
+ Seq Scan on hp_prefix_test_p7 hp_prefix_test
+ Filter: ((b IS NULL) AND (d IS NULL) AND (a = 1) AND (c = 3))
+
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null
+ Seq Scan on hp_prefix_test_p4 hp_prefix_test
+ Filter: ((a IS NULL) AND (d IS NULL) AND (b = 2) AND (c = 3))
+
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null
+ Seq Scan on hp_prefix_test_p5 hp_prefix_test
+ Filter: ((d IS NULL) AND (a = 1) AND (b = 2) AND (c = 3))
+
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4
+ Seq Scan on hp_prefix_test_p4 hp_prefix_test
+ Filter: ((a IS NULL) AND (b IS NULL) AND (c IS NULL) AND (d = 4))
+
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4
+ Seq Scan on hp_prefix_test_p6 hp_prefix_test
+ Filter: ((b IS NULL) AND (c IS NULL) AND (a = 1) AND (d = 4))
+
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4
+ Seq Scan on hp_prefix_test_p5 hp_prefix_test
+ Filter: ((a IS NULL) AND (c IS NULL) AND (b = 2) AND (d = 4))
+
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4
+ Seq Scan on hp_prefix_test_p6 hp_prefix_test
+ Filter: ((c IS NULL) AND (a = 1) AND (b = 2) AND (d = 4))
+
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4
+ Seq Scan on hp_prefix_test_p4 hp_prefix_test
+ Filter: ((a IS NULL) AND (b IS NULL) AND (c = 3) AND (d = 4))
+
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4
+ Seq Scan on hp_prefix_test_p5 hp_prefix_test
+ Filter: ((b IS NULL) AND (a = 1) AND (c = 3) AND (d = 4))
+
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4
+ Seq Scan on hp_prefix_test_p6 hp_prefix_test
+ Filter: ((a IS NULL) AND (b = 2) AND (c = 3) AND (d = 4))
+
+explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4
+ Seq Scan on hp_prefix_test_p4 hp_prefix_test
+ Filter: ((a = 1) AND (b = 2) AND (c = 3) AND (d = 4))
+
+-- And ensure we get exactly 1 row from each. Again, all 16 possible combinations.
+select
+ 'select tableoid::regclass,* from hp_prefix_test where ' ||
+ string_agg(c.colname || case when g.s & (1 << c.colpos) = 0 then ' is null' else ' = ' || (colpos+1)::text end, ' and ' order by c.colpos)
+from (values('a',0),('b',1),('c',2),('d',3)) c(colname, colpos), generate_Series(0,15) g(s)
+group by g.s
+order by g.s;
+ select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null
+ select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null
+ select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null
+ select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null
+ select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null
+ select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null
+ select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null
+ select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null
+ select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4
+ select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4
+ select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4
+ select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4
+ select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4
+ select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4
+ select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4
+ select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4
+
+\gexec
+select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null
+ hp_prefix_test_p0 | | | |
+
+select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null
+ hp_prefix_test_p1 | 1 | | |
+
+select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null
+ hp_prefix_test_p2 | | 2 | |
+
+select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null
+ hp_prefix_test_p4 | 1 | 2 | |
+
+select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null
+ hp_prefix_test_p3 | | | 3 |
+
+select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null
+ hp_prefix_test_p7 | 1 | | 3 |
+
+select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null
+ hp_prefix_test_p4 | | 2 | 3 |
+
+select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null
+ hp_prefix_test_p5 | 1 | 2 | 3 |
+
+select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4
+ hp_prefix_test_p4 | | | | 4
+
+select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4
+ hp_prefix_test_p6 | 1 | | | 4
+
+select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4
+ hp_prefix_test_p5 | | 2 | | 4
+
+select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4
+ hp_prefix_test_p6 | 1 | 2 | | 4
+
+select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4
+ hp_prefix_test_p4 | | | 3 | 4
+
+select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4
+ hp_prefix_test_p5 | 1 | | 3 | 4
+
+select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4
+ hp_prefix_test_p6 | | 2 | 3 | 4
+
+select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4
+ hp_prefix_test_p4 | 1 | 2 | 3 | 4
+
+\t off
+drop table hp_prefix_test;
+--
+-- Check that gen_partprune_steps() detects self-contradiction from clauses
+-- regardless of the order of the clauses (Here we use a custom operator to
+-- prevent the equivclass.c machinery from reordering the clauses)
+--
+create operator === (
+ leftarg = int4,
+ rightarg = int4,
+ procedure = int4eq,
+ commutator = ===,
+ hashes
+);
+create operator class part_test_int4_ops2
+for type int4
+using hash as
+operator 1 ===,
+function 2 part_hashint4_noop(int4, int8);
+create table hp_contradict_test (a int, b int) partition by hash (a part_test_int4_ops2, b part_test_int4_ops2);
+create table hp_contradict_test_p1 partition of hp_contradict_test for values with (modulus 2, remainder 0);
+create table hp_contradict_test_p2 partition of hp_contradict_test for values with (modulus 2, remainder 1);
+explain (costs off) select * from hp_contradict_test where a is null and a === 1 and b === 1;
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+explain (costs off) select * from hp_contradict_test where a === 1 and b === 1 and a is null;
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+drop table hp_contradict_test;
+drop operator class part_test_int4_ops2 using hash;
+drop operator ===(int4, int4);