From 3f16e328c1b334089a8521d2c7bb9a4f7ad4a50e Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Mon, 27 Jan 2020 20:09:49 -0800
Subject: [PATCH 3/3] Further extending partition join tests.

Adding more tests of the partition-wise join logic, this time
testing list partitioning rather than range partitioning.  Like
in the previous commit, the expected output for the new tests
is based on current behavior rather than what I would necessarily
like to see.  In particular, the EXPLAIN output for the new tests
shows more partitions being accessed than seem strictly necessary
to satisfy the queries being explained.

For example:

	EXPLAIN (COSTS OFF)
	SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
	                QUERY PLAN
	-------------------------------------------
	 Hash Join
	   Hash Cond: (t2.a = t1.a)
	   ->  Append
	         ->  Seq Scan on beta_d t2_1
	         ->  Seq Scan on beta_b t2_2
	         ->  Seq Scan on beta_a t2_3
	         ->  Seq Scan on beta_c t2_4
	         ->  Seq Scan on beta_default t2_5
	   ->  Hash
	         ->  Seq Scan on alpha_f t1
	               Filter: (a IS NULL)
	(11 rows)

It seems that if t1.a is null, and we join on t1.a = t2.a, then
the system should be able to work out that no rows will match, but
all partitions on t2 are scanned.

Similarly, given that t1 and t2 each only have one partition
for NULL values:

	EXPLAIN (COSTS OFF)
	SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NULL;
	                    QUERY PLAN
	---------------------------------------------------
	 Nested Loop
	   Join Filter: (NOT (t1.a IS DISTINCT FROM t2.a))
	   ->  Seq Scan on alpha_f t1
	         Filter: (a IS NULL)
	   ->  Append
	         ->  Seq Scan on beta_d t2_1
	         ->  Seq Scan on beta_b t2_2
	         ->  Seq Scan on beta_a t2_3
	         ->  Seq Scan on beta_c t2_4
	         ->  Seq Scan on beta_default t2_5
	(10 rows)

If t1.a is null, and t2.a is not distinct from t1.a, then t2.a must
be null, so scanning all partitions of t2 is unnecessary.
---
 src/test/regress/expected/partition_join.out | 631 +++++++++++++++++++
 src/test/regress/sql/partition_join.sql      | 143 +++++
 2 files changed, 774 insertions(+)

diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out
index f20a85689f..d7bbca98a1 100644
--- a/src/test/regress/expected/partition_join.out
+++ b/src/test/regress/expected/partition_join.out
@@ -4575,3 +4575,634 @@ SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a =
  ὀδυσσεύς | ὀδυσσεύς
 (1 row)
 
+-- Try again, this time with list partitioning
+DROP TABLE alpha CASCADE;
+DROP TABLE beta CASCADE;
+CREATE TABLE alpha (a TEXT) PARTITION BY LIST(a);
+CREATE TABLE alpha_a PARTITION OF alpha FOR VALUES IN ('Türkiye', 'TÜRKIYE');
+CREATE TABLE alpha_b PARTITION OF alpha FOR VALUES IN ('bıt', 'BIT');
+CREATE TABLE alpha_c PARTITION OF alpha FOR VALUES IN ('äbç', 'ÄBÇ');
+CREATE TABLE alpha_d PARTITION OF alpha FOR VALUES IN ('aaá', 'coté', 'Götz');
+CREATE TABLE alpha_e PARTITION OF alpha FOR VALUES IN ('ὀδυσσεύς', 'ὈΔΥΣΣΕΎΣ');
+CREATE TABLE alpha_f PARTITION OF alpha FOR VALUES IN ('を読み取り用', 'にオープンできませんでした', NULL);
+CREATE TABLE alpha_default PARTITION OF alpha DEFAULT;
+CREATE TABLE beta (a TEXT) PARTITION BY LIST(a);
+CREATE TABLE beta_a PARTITION OF beta FOR VALUES IN ('Türkiye', 'coté', 'ὈΔΥΣΣΕΎΣ');
+CREATE TABLE beta_b PARTITION OF beta FOR VALUES IN ('bıt', 'TÜRKIYE');
+CREATE TABLE beta_c PARTITION OF beta FOR VALUES IN ('äbç', 'を読み取り用', 'にオープンできませんでした');
+CREATE TABLE beta_d PARTITION OF beta FOR VALUES IN ('aaá', 'Götz', 'BIT', 'ὀδυσσεύς', 'ÄBÇ', NULL);
+CREATE TABLE beta_default PARTITION OF beta DEFAULT;
+INSERT INTO alpha (SELECT a FROM raw_data);
+INSERT INTO beta (SELECT a FROM raw_data);
+INSERT INTO alpha VALUES (null);
+INSERT INTO beta VALUES (null);
+INSERT INTO alpha VALUES ('grumble');
+INSERT INTO beta VALUES ('grumble');
+ANALYZE alpha;
+ANALYZE beta;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IN ('äbç', 'ὀδυσσεύς');
+                            QUERY PLAN                            
+------------------------------------------------------------------
+ Hash Join
+   Hash Cond: (t2.a = t1.a)
+   ->  Append
+         ->  Seq Scan on beta_d t2_1
+         ->  Seq Scan on beta_b t2_2
+         ->  Seq Scan on beta_a t2_3
+         ->  Seq Scan on beta_c t2_4
+         ->  Seq Scan on beta_default t2_5
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on alpha_c t1_1
+                     Filter: (a = ANY ('{äbç,ὀδυσσεύς}'::text[]))
+               ->  Seq Scan on alpha_e t1_2
+                     Filter: (a = ANY ('{äbç,ὀδυσσεύς}'::text[]))
+(14 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IN ('äbç', 'ὀδυσσεύς');
+    a     |    a     
+----------+----------
+ ὀδυσσεύς | ὀδυσσεύς
+ äbç      | äbç
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'äbç';
+            QUERY PLAN             
+-----------------------------------
+ Nested Loop
+   ->  Seq Scan on alpha_c t1
+         Filter: (a = 'äbç'::text)
+   ->  Seq Scan on beta_c t2
+         Filter: (a = 'äbç'::text)
+(5 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'äbç';
+  a  |  a  
+-----+-----
+ äbç | äbç
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'ὀδυσσεύς';
+               QUERY PLAN               
+----------------------------------------
+ Nested Loop
+   ->  Seq Scan on alpha_e t1
+         Filter: (a = 'ὀδυσσεύς'::text)
+   ->  Seq Scan on beta_d t2
+         Filter: (a = 'ὀδυσσεύς'::text)
+(5 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'ὀδυσσεύς';
+    a     |    a     
+----------+----------
+ ὀδυσσεύς | ὀδυσσεύς
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
+                QUERY PLAN                 
+-------------------------------------------
+ Hash Join
+   Hash Cond: (t2.a = t1.a)
+   ->  Append
+         ->  Seq Scan on beta_d t2_1
+         ->  Seq Scan on beta_b t2_2
+         ->  Seq Scan on beta_a t2_3
+         ->  Seq Scan on beta_c t2_4
+         ->  Seq Scan on beta_default t2_5
+   ->  Hash
+         ->  Seq Scan on alpha_f t1
+               Filter: (a IS NULL)
+(11 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
+ a | a 
+---+---
+(0 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NULL;
+                    QUERY PLAN                     
+---------------------------------------------------
+ Nested Loop
+   Join Filter: (NOT (t1.a IS DISTINCT FROM t2.a))
+   ->  Seq Scan on alpha_f t1
+         Filter: (a IS NULL)
+   ->  Append
+         ->  Seq Scan on beta_d t2_1
+         ->  Seq Scan on beta_b t2_2
+         ->  Seq Scan on beta_a t2_3
+         ->  Seq Scan on beta_c t2_4
+         ->  Seq Scan on beta_default t2_5
+(10 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NULL;
+ a | a 
+---+---
+   | 
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
+                QUERY PLAN                 
+-------------------------------------------
+ Hash Join
+   Hash Cond: (t2.a = t1.a)
+   ->  Append
+         ->  Seq Scan on beta_d t2_1
+         ->  Seq Scan on beta_b t2_2
+         ->  Seq Scan on beta_a t2_3
+         ->  Seq Scan on beta_c t2_4
+         ->  Seq Scan on beta_default t2_5
+   ->  Hash
+         ->  Seq Scan on alpha_f t1
+               Filter: (a IS NULL)
+(11 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
+ a | a 
+---+---
+(0 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NULL;
+                    QUERY PLAN                     
+---------------------------------------------------
+ Nested Loop
+   Join Filter: (NOT (t1.a IS DISTINCT FROM t2.a))
+   ->  Seq Scan on alpha_f t1
+         Filter: (a IS NULL)
+   ->  Append
+         ->  Seq Scan on beta_d t2_1
+         ->  Seq Scan on beta_b t2_2
+         ->  Seq Scan on beta_a t2_3
+         ->  Seq Scan on beta_c t2_4
+         ->  Seq Scan on beta_default t2_5
+(10 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NULL;
+ a | a 
+---+---
+   | 
+(1 row)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IN ('äbç', 'ὀδυσσεύς');
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Hash Semi Join
+   Hash Cond: (t1.a = t2.a)
+   ->  Append
+         ->  Seq Scan on alpha_c t1_1
+               Filter: (a = ANY ('{äbç,ὀδυσσεύς}'::text[]))
+         ->  Seq Scan on alpha_e t1_2
+               Filter: (a = ANY ('{äbç,ὀδυσσεύς}'::text[]))
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on beta_d t2_1
+               ->  Seq Scan on beta_b t2_2
+               ->  Seq Scan on beta_a t2_3
+               ->  Seq Scan on beta_c t2_4
+               ->  Seq Scan on beta_default t2_5
+(14 rows)
+
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IN ('äbç', 'ὀδυσσεύς');
+    a     
+----------
+ äbç
+ ὀδυσσεύς
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'äbç';
+            QUERY PLAN             
+-----------------------------------
+ Nested Loop Semi Join
+   ->  Seq Scan on alpha_c t1
+         Filter: (a = 'äbç'::text)
+   ->  Seq Scan on beta_c t2
+         Filter: (a = 'äbç'::text)
+(5 rows)
+
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'äbç';
+  a  
+-----
+ äbç
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'ὀδυσσεύς';
+               QUERY PLAN               
+----------------------------------------
+ Nested Loop Semi Join
+   ->  Seq Scan on alpha_e t1
+         Filter: (a = 'ὀδυσσεύς'::text)
+   ->  Seq Scan on beta_d t2
+         Filter: (a = 'ὀδυσσεύς'::text)
+(5 rows)
+
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'ὀδυσσεύς';
+    a     
+----------
+ ὀδυσσεύς
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a IS NOT DISTINCT FROM t2.a) AND t1.a IS NULL;
+                    QUERY PLAN                     
+---------------------------------------------------
+ Nested Loop Semi Join
+   Join Filter: (NOT (t1.a IS DISTINCT FROM t2.a))
+   ->  Seq Scan on alpha_f t1
+         Filter: (a IS NULL)
+   ->  Append
+         ->  Seq Scan on beta_d t2_1
+         ->  Seq Scan on beta_b t2_2
+         ->  Seq Scan on beta_a t2_3
+         ->  Seq Scan on beta_c t2_4
+         ->  Seq Scan on beta_default t2_5
+(10 rows)
+
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a IS NOT DISTINCT FROM t2.a) AND t1.a IS NULL;
+ a 
+---
+ 
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IS NULL;
+                QUERY PLAN                 
+-------------------------------------------
+ Nested Loop Semi Join
+   Join Filter: (t1.a = t2.a)
+   ->  Seq Scan on alpha_f t1
+         Filter: (a IS NULL)
+   ->  Append
+         ->  Seq Scan on beta_d t2_1
+         ->  Seq Scan on beta_b t2_2
+         ->  Seq Scan on beta_a t2_3
+         ->  Seq Scan on beta_c t2_4
+         ->  Seq Scan on beta_default t2_5
+(10 rows)
+
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IS NULL;
+ a 
+---
+(0 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IN ('äbç', 'ὀδυσσεύς');
+                            QUERY PLAN                            
+------------------------------------------------------------------
+ Hash Right Join
+   Hash Cond: (t2.a = t1.a)
+   ->  Append
+         ->  Seq Scan on beta_d t2_1
+         ->  Seq Scan on beta_b t2_2
+         ->  Seq Scan on beta_a t2_3
+         ->  Seq Scan on beta_c t2_4
+         ->  Seq Scan on beta_default t2_5
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on alpha_c t1_1
+                     Filter: (a = ANY ('{äbç,ὀδυσσεύς}'::text[]))
+               ->  Seq Scan on alpha_e t1_2
+                     Filter: (a = ANY ('{äbç,ὀδυσσεύς}'::text[]))
+(14 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IN ('äbç', 'ὀδυσσεύς');
+    a     |    a     
+----------+----------
+ ὀδυσσεύς | ὀδυσσεύς
+ äbç      | äbç
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'äbç';
+            QUERY PLAN             
+-----------------------------------
+ Nested Loop Left Join
+   Join Filter: (t1.a = t2.a)
+   ->  Seq Scan on alpha_c t1
+         Filter: (a = 'äbç'::text)
+   ->  Seq Scan on beta_c t2
+         Filter: (a = 'äbç'::text)
+(6 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'äbç';
+  a  |  a  
+-----+-----
+ äbç | äbç
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'ὀδυσσεύς';
+               QUERY PLAN               
+----------------------------------------
+ Nested Loop Left Join
+   Join Filter: (t1.a = t2.a)
+   ->  Seq Scan on alpha_e t1
+         Filter: (a = 'ὀδυσσεύς'::text)
+   ->  Seq Scan on beta_d t2
+         Filter: (a = 'ὀδυσσεύς'::text)
+(6 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'ὀδυσσεύς';
+    a     |    a     
+----------+----------
+ ὀδυσσεύς | ὀδυσσεύς
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NULL;
+                    QUERY PLAN                     
+---------------------------------------------------
+ Nested Loop Left Join
+   Join Filter: (NOT (t1.a IS DISTINCT FROM t2.a))
+   ->  Seq Scan on alpha_f t1
+         Filter: (a IS NULL)
+   ->  Append
+         ->  Seq Scan on beta_d t2_1
+         ->  Seq Scan on beta_b t2_2
+         ->  Seq Scan on beta_a t2_3
+         ->  Seq Scan on beta_c t2_4
+         ->  Seq Scan on beta_default t2_5
+(10 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NULL;
+ a | a 
+---+---
+   | 
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
+                QUERY PLAN                 
+-------------------------------------------
+ Hash Right Join
+   Hash Cond: (t2.a = t1.a)
+   ->  Append
+         ->  Seq Scan on beta_d t2_1
+         ->  Seq Scan on beta_b t2_2
+         ->  Seq Scan on beta_a t2_3
+         ->  Seq Scan on beta_c t2_4
+         ->  Seq Scan on beta_default t2_5
+   ->  Hash
+         ->  Seq Scan on alpha_f t1
+               Filter: (a IS NULL)
+(11 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
+ a | a 
+---+---
+   | 
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NOT DISTINCT FROM t2.a;
+                    QUERY PLAN                     
+---------------------------------------------------
+ Nested Loop Left Join
+   Join Filter: (NOT (t1.a IS DISTINCT FROM t2.a))
+   Filter: (NOT (t1.a IS DISTINCT FROM t2.a))
+   ->  Append
+         ->  Seq Scan on alpha_b t1_1
+         ->  Seq Scan on alpha_d t1_2
+         ->  Seq Scan on alpha_a t1_3
+         ->  Seq Scan on alpha_c t1_4
+         ->  Seq Scan on alpha_f t1_5
+         ->  Seq Scan on alpha_e t1_6
+         ->  Seq Scan on alpha_default t1_7
+   ->  Materialize
+         ->  Append
+               ->  Seq Scan on beta_d t2_1
+               ->  Seq Scan on beta_b t2_2
+               ->  Seq Scan on beta_a t2_3
+               ->  Seq Scan on beta_c t2_4
+               ->  Seq Scan on beta_default t2_5
+(18 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NOT DISTINCT FROM t2.a;
+             a              |             a              
+----------------------------+----------------------------
+ bıt                        | bıt
+ BIT                        | BIT
+ aaá                        | aaá
+ coté                       | coté
+ Götz                       | Götz
+ Türkiye                    | Türkiye
+ TÜRKIYE                    | TÜRKIYE
+ äbç                        | äbç
+ ÄBÇ                        | ÄBÇ
+ を読み取り用               | を読み取り用
+ にオープンできませんでした | にオープンできませんでした
+                            | 
+ ὀδυσσεύς                   | ὀδυσσεύς
+ ὈΔΥΣΣΕΎΣ                   | ὈΔΥΣΣΕΎΣ
+ grumble                    | grumble
+(15 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IN ('äbç', 'ὀδυσσεύς');
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Hash Anti Join
+   Hash Cond: (t1.a = t2.a)
+   ->  Append
+         ->  Seq Scan on alpha_c t1_1
+               Filter: (a = ANY ('{äbç,ὀδυσσεύς}'::text[]))
+         ->  Seq Scan on alpha_e t1_2
+               Filter: (a = ANY ('{äbç,ὀδυσσεύς}'::text[]))
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on beta_d t2_1
+               ->  Seq Scan on beta_b t2_2
+               ->  Seq Scan on beta_a t2_3
+               ->  Seq Scan on beta_c t2_4
+               ->  Seq Scan on beta_default t2_5
+(14 rows)
+
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IN ('äbç', 'ὀδυσσεύς');
+ a 
+---
+(0 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'äbç';
+            QUERY PLAN             
+-----------------------------------
+ Nested Loop Anti Join
+   Join Filter: (t1.a = t2.a)
+   ->  Seq Scan on alpha_c t1
+         Filter: (a = 'äbç'::text)
+   ->  Seq Scan on beta_c t2
+         Filter: (a = 'äbç'::text)
+(6 rows)
+
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'äbç';
+ a 
+---
+(0 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'ὀδυσσεύς';
+               QUERY PLAN               
+----------------------------------------
+ Nested Loop Anti Join
+   Join Filter: (t1.a = t2.a)
+   ->  Seq Scan on alpha_e t1
+         Filter: (a = 'ὀδυσσεύς'::text)
+   ->  Seq Scan on beta_d t2
+         Filter: (a = 'ὀδυσσεύς'::text)
+(6 rows)
+
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'ὀδυσσεύς';
+ a 
+---
+(0 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a IS NOT DISTINCT FROM t2.a) AND t1.a IS NULL;
+                    QUERY PLAN                     
+---------------------------------------------------
+ Nested Loop Anti Join
+   Join Filter: (NOT (t1.a IS DISTINCT FROM t2.a))
+   ->  Seq Scan on alpha_f t1
+         Filter: (a IS NULL)
+   ->  Append
+         ->  Seq Scan on beta_d t2_1
+         ->  Seq Scan on beta_b t2_2
+         ->  Seq Scan on beta_a t2_3
+         ->  Seq Scan on beta_c t2_4
+         ->  Seq Scan on beta_default t2_5
+(10 rows)
+
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a IS NOT DISTINCT FROM t2.a) AND t1.a IS NULL;
+ a 
+---
+(0 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IS NULL;
+                QUERY PLAN                 
+-------------------------------------------
+ Nested Loop Anti Join
+   Join Filter: (t1.a = t2.a)
+   ->  Seq Scan on alpha_f t1
+         Filter: (a IS NULL)
+   ->  Append
+         ->  Seq Scan on beta_d t2_1
+         ->  Seq Scan on beta_b t2_2
+         ->  Seq Scan on beta_a t2_3
+         ->  Seq Scan on beta_c t2_4
+         ->  Seq Scan on beta_default t2_5
+(10 rows)
+
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IS NULL;
+ a 
+---
+ 
+(1 row)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IN ('äbç', 'ὀδυσσεύς');
+                            QUERY PLAN                            
+------------------------------------------------------------------
+ Hash Right Join
+   Hash Cond: (t2.a = t1.a)
+   ->  Append
+         ->  Seq Scan on beta_d t2_1
+         ->  Seq Scan on beta_b t2_2
+         ->  Seq Scan on beta_a t2_3
+         ->  Seq Scan on beta_c t2_4
+         ->  Seq Scan on beta_default t2_5
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on alpha_c t1_1
+                     Filter: (a = ANY ('{äbç,ὀδυσσεύς}'::text[]))
+               ->  Seq Scan on alpha_e t1_2
+                     Filter: (a = ANY ('{äbç,ὀδυσσεύς}'::text[]))
+(14 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IN ('äbç', 'ὀδυσσεύς');
+    a     |    a     
+----------+----------
+ ὀδυσσεύς | ὀδυσσεύς
+ äbç      | äbç
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'äbç';
+            QUERY PLAN             
+-----------------------------------
+ Nested Loop Left Join
+   Join Filter: (t1.a = t2.a)
+   ->  Seq Scan on alpha_c t1
+         Filter: (a = 'äbç'::text)
+   ->  Seq Scan on beta_c t2
+         Filter: (a = 'äbç'::text)
+(6 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'äbç';
+  a  |  a  
+-----+-----
+ äbç | äbç
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'ὀδυσσεύς';
+               QUERY PLAN               
+----------------------------------------
+ Nested Loop Left Join
+   Join Filter: (t1.a = t2.a)
+   ->  Seq Scan on alpha_e t1
+         Filter: (a = 'ὀδυσσεύς'::text)
+   ->  Seq Scan on beta_d t2
+         Filter: (a = 'ὀδυσσεύς'::text)
+(6 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'ὀδυσσεύς';
+    a     |    a     
+----------+----------
+ ὀδυσσεύς | ὀδυσσεύς
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
+                   QUERY PLAN                    
+-------------------------------------------------
+ Hash Full Join
+   Hash Cond: (t1.a = t2.a)
+   Filter: (t1.a IS NULL)
+   ->  Append
+         ->  Seq Scan on alpha_b t1_1
+         ->  Seq Scan on alpha_d t1_2
+         ->  Seq Scan on alpha_a t1_3
+         ->  Seq Scan on alpha_c t1_4
+         ->  Seq Scan on alpha_f t1_5
+         ->  Seq Scan on alpha_e t1_6
+         ->  Seq Scan on alpha_default t1_7
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on beta_d t2_1
+               ->  Seq Scan on beta_b t2_2
+               ->  Seq Scan on beta_a t2_3
+               ->  Seq Scan on beta_c t2_4
+               ->  Seq Scan on beta_default t2_5
+(18 rows)
+
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
+ a | a 
+---+---
+   | 
+   | 
+(2 rows)
+
diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql
index 159d459831..9bc2188ca8 100644
--- a/src/test/regress/sql/partition_join.sql
+++ b/src/test/regress/sql/partition_join.sql
@@ -1064,3 +1064,146 @@ SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a =
 EXPLAIN (COSTS OFF)
 SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'ὀδυσσεύς';
 SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'ὀδυσσεύς';
+
+-- Try again, this time with list partitioning
+DROP TABLE alpha CASCADE;
+DROP TABLE beta CASCADE;
+
+CREATE TABLE alpha (a TEXT) PARTITION BY LIST(a);
+CREATE TABLE alpha_a PARTITION OF alpha FOR VALUES IN ('Türkiye', 'TÜRKIYE');
+CREATE TABLE alpha_b PARTITION OF alpha FOR VALUES IN ('bıt', 'BIT');
+CREATE TABLE alpha_c PARTITION OF alpha FOR VALUES IN ('äbç', 'ÄBÇ');
+CREATE TABLE alpha_d PARTITION OF alpha FOR VALUES IN ('aaá', 'coté', 'Götz');
+CREATE TABLE alpha_e PARTITION OF alpha FOR VALUES IN ('ὀδυσσεύς', 'ὈΔΥΣΣΕΎΣ');
+CREATE TABLE alpha_f PARTITION OF alpha FOR VALUES IN ('を読み取り用', 'にオープンできませんでした', NULL);
+CREATE TABLE alpha_default PARTITION OF alpha DEFAULT;
+
+CREATE TABLE beta (a TEXT) PARTITION BY LIST(a);
+CREATE TABLE beta_a PARTITION OF beta FOR VALUES IN ('Türkiye', 'coté', 'ὈΔΥΣΣΕΎΣ');
+CREATE TABLE beta_b PARTITION OF beta FOR VALUES IN ('bıt', 'TÜRKIYE');
+CREATE TABLE beta_c PARTITION OF beta FOR VALUES IN ('äbç', 'を読み取り用', 'にオープンできませんでした');
+CREATE TABLE beta_d PARTITION OF beta FOR VALUES IN ('aaá', 'Götz', 'BIT', 'ὀδυσσεύς', 'ÄBÇ', NULL);
+CREATE TABLE beta_default PARTITION OF beta DEFAULT;
+
+INSERT INTO alpha (SELECT a FROM raw_data);
+INSERT INTO beta (SELECT a FROM raw_data);
+INSERT INTO alpha VALUES (null);
+INSERT INTO beta VALUES (null);
+INSERT INTO alpha VALUES ('grumble');
+INSERT INTO beta VALUES ('grumble');
+
+ANALYZE alpha;
+ANALYZE beta;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IN ('äbç', 'ὀδυσσεύς');
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IN ('äbç', 'ὀδυσσεύς');
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'äbç';
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'äbç';
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'ὀδυσσεύς';
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'ὀδυσσεύς';
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NULL;
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NULL;
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NULL;
+SELECT t1.a, t2.a FROM alpha t1 INNER JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NULL;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IN ('äbç', 'ὀδυσσεύς');
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IN ('äbç', 'ὀδυσσεύς');
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'äbç';
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'äbç';
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'ὀδυσσεύς';
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'ὀδυσσεύς';
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a IS NOT DISTINCT FROM t2.a) AND t1.a IS NULL;
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a IS NOT DISTINCT FROM t2.a) AND t1.a IS NULL;
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IS NULL;
+SELECT t1.a FROM alpha t1 WHERE EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IS NULL;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IN ('äbç', 'ὀδυσσεύς');
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IN ('äbç', 'ὀδυσσεύς');
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'äbç';
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'äbç';
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'ὀδυσσεύς';
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'ὀδυσσεύς';
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NULL;
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NULL;
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NOT DISTINCT FROM t2.a;
+SELECT t1.a, t2.a FROM alpha t1 LEFT JOIN beta t2 ON (t1.a IS NOT DISTINCT FROM t2.a) WHERE t1.a IS NOT DISTINCT FROM t2.a;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IN ('äbç', 'ὀδυσσεύς');
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IN ('äbç', 'ὀδυσσεύς');
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'äbç';
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'äbç';
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'ὀδυσσεύς';
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a = 'ὀδυσσεύς';
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a IS NOT DISTINCT FROM t2.a) AND t1.a IS NULL;
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a IS NOT DISTINCT FROM t2.a) AND t1.a IS NULL;
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IS NULL;
+SELECT t1.a FROM alpha t1 WHERE NOT EXISTS (SELECT 1 FROM beta t2 WHERE t1.a = t2.a) AND t1.a IS NULL;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IN ('äbç', 'ὀδυσσεύς');
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IN ('äbç', 'ὀδυσσεύς');
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'äbç';
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'äbç';
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'ὀδυσσεύς';
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a = 'ὀδυσσεύς';
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
+SELECT t1.a, t2.a FROM alpha t1 FULL JOIN beta t2 ON (t1.a = t2.a) WHERE t1.a IS NULL;
-- 
2.21.1 (Apple Git-122.3)

