From ccc7c86b38f541bf9c819f13f32c548812e216e6 Mon Sep 17 00:00:00 2001
From: Lyu Pan <lyup@amazon.com>
Date: Mon, 19 Sep 2022 23:25:30 +0000
Subject: [PATCH 5/5] Add basic regress tests for semijoin filter.

Because of implementation limits (and bugs) in the prototype, only some basic sqls are tested.

1. Test that when force_mergejoin_semijoin_filter is ON, semijoin filter is used.
2. Test that semijoin filter works in two-table inner merge join.
3. Test that semijoin filter can be pushed throudh SORT node.
4. Test that semijoin filter works in a basic three-table-merge-join. However, due to implementation bugs, it doesn't work in all kinds of three table merge joins.
---
 .../expected/mergejoin_semijoinfilter.out     | 158 ++++++++++++++++++
 src/test/regress/parallel_schedule            |   1 +
 .../regress/sql/mergejoin_semijoinfilter.sql  |  62 +++++++
 3 files changed, 221 insertions(+)
 create mode 100644 src/test/regress/expected/mergejoin_semijoinfilter.out
 create mode 100644 src/test/regress/sql/mergejoin_semijoinfilter.sql

diff --git a/src/test/regress/expected/mergejoin_semijoinfilter.out b/src/test/regress/expected/mergejoin_semijoinfilter.out
new file mode 100644
index 0000000000..07c4acc253
--- /dev/null
+++ b/src/test/regress/expected/mergejoin_semijoinfilter.out
@@ -0,0 +1,158 @@
+SET enable_hashjoin = OFF;
+SET enable_nestloop = OFF;
+SET enable_mergejoin = ON;
+SET enable_mergejoin_semijoin_filter = ON;
+SET force_mergejoin_semijoin_filter = ON;
+CREATE TABLE t1 (
+  i integer,
+  j integer
+);
+CREATE TABLE t2 (
+  i integer,
+  k integer
+);
+CREATE TABLE t3 (
+  i integer,
+  m integer
+);
+INSERT INTO t1 (i, j)
+  SELECT
+    generate_series(1,100000) AS i,
+    generate_series(1,100000) AS j;
+INSERT INTO t2 (i, k)
+  SELECT
+    generate_series(1,100000) AS i,
+    generate_series(1,100000) AS k;
+INSERT INTO t3 (i, m)
+  SELECT
+    generate_series(1,100000) AS i,
+    generate_series(1,100000) AS m;
+-- Semijoin filter is not used when force_mergejoin_semijoin_filter is OFF.
+SET force_mergejoin_semijoin_filter = OFF;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT COUNT(*) FROM t1 JOIN t2 ON t1.i = t2.i;
+               QUERY PLAN                
+-----------------------------------------
+ Aggregate
+   Output: count(*)
+   ->  Merge Join
+         Merge Cond: (t1.i = t2.i)
+         ->  Sort
+               Output: t1.i
+               Sort Key: t1.i
+               ->  Seq Scan on public.t1
+                     Output: t1.i
+         ->  Sort
+               Output: t2.i
+               Sort Key: t2.i
+               ->  Seq Scan on public.t2
+                     Output: t2.i
+(14 rows)
+
+SET force_mergejoin_semijoin_filter = ON;
+-- One level of inner mergejoin: push semi-join filter to outer scan.
+EXPLAIN (VERBOSE, COSTS OFF) SELECT COUNT(*) FROM t1 JOIN t2 ON t1.i = t2.i;
+                       QUERY PLAN                        
+---------------------------------------------------------
+ Aggregate
+   Output: count(*)
+   ->  Merge Join
+         Merge Cond: (t1.i = t2.i)
+         SemiJoin Filter Created Based on: (t1.i = t2.i)
+         SemiJoin Estimated Filtering Rate: -0.0100
+         ->  Sort
+               Output: t1.i
+               Sort Key: t1.i
+               ->  Seq Scan on public.t1
+                     Output: t1.i
+         ->  Sort
+               Output: t2.i
+               Sort Key: t2.i
+               ->  Seq Scan on public.t2
+                     Output: t2.i
+(16 rows)
+
+SELECT COUNT(*) FROM t1 JOIN t2 ON t1.i = t2.i;
+ count  
+--------
+ 100000
+(1 row)
+
+-- Push semijoin filter through SORT node.
+EXPLAIN (VERBOSE, COSTS OFF) SELECT COUNT(*) FROM (SELECT DISTINCT t1.i FROM t1 ORDER BY t1.i) x JOIN t2 ON x.i = t2.i;
+                       QUERY PLAN                        
+---------------------------------------------------------
+ Aggregate
+   Output: count(*)
+   ->  Merge Join
+         Merge Cond: (t1.i = t2.i)
+         SemiJoin Filter Created Based on: (t1.i = t2.i)
+         SemiJoin Estimated Filtering Rate: -0.0100
+         ->  Sort
+               Output: t1.i
+               Sort Key: t1.i
+               ->  HashAggregate
+                     Output: t1.i
+                     Group Key: t1.i
+                     ->  Seq Scan on public.t1
+                           Output: t1.i, t1.j
+         ->  Sort
+               Output: t2.i
+               Sort Key: t2.i
+               ->  Seq Scan on public.t2
+                     Output: t2.i
+(19 rows)
+
+SELECT COUNT(*) FROM (SELECT DISTINCT t1.i FROM t1 ORDER BY t1.i) x JOIN t2 ON x.i = t2.i;
+ count  
+--------
+ 100000
+(1 row)
+
+-- Two levels of MergeJoin
+EXPLAIN (VERBOSE, COSTS OFF) SELECT COUNT(*) FROM t1 JOIN t2 ON t1.i = t2.i JOIN t3 ON t2.i = t3.i;
+                             QUERY PLAN                              
+---------------------------------------------------------------------
+ Aggregate
+   Output: count(*)
+   ->  Merge Join
+         Merge Cond: (t3.i = t1.i)
+         SemiJoin Filter Created Based on: (t3.i = t1.i)
+         SemiJoin Estimated Filtering Rate: -0.0100
+         ->  Sort
+               Output: t3.i
+               Sort Key: t3.i
+               ->  Seq Scan on public.t3
+                     Output: t3.i
+         ->  Materialize
+               Output: t1.i, t2.i
+               ->  Merge Join
+                     Output: t1.i, t2.i
+                     Merge Cond: (t1.i = t2.i)
+                     SemiJoin Filter Created Based on: (t1.i = t2.i)
+                     SemiJoin Estimated Filtering Rate: -0.0100
+                     ->  Sort
+                           Output: t1.i
+                           Sort Key: t1.i
+                           ->  Seq Scan on public.t1
+                                 Output: t1.i
+                     ->  Sort
+                           Output: t2.i
+                           Sort Key: t2.i
+                           ->  Seq Scan on public.t2
+                                 Output: t2.i
+(28 rows)
+
+SELECT COUNT(*) FROM t1 JOIN t2 ON t1.i = t2.i JOIN t3 ON t2.i = t3.i;
+ count  
+--------
+ 100000
+(1 row)
+
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
+RESET enable_mergejoin;
+RESET enable_memoize;
+RESET enable_mergejoin;
+RESET enable_mergejoin_semijoin_filter;
+RESET force_mergejoin_semijoin_filter;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 9f644a0c1b..6bde05b8cb 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -98,6 +98,7 @@ test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8
 test: select_parallel
 test: write_parallel
 test: vacuum_parallel
+test: mergejoin_semijoinfilter
 
 # no relation related tests can be put in this group
 test: publication subscription
diff --git a/src/test/regress/sql/mergejoin_semijoinfilter.sql b/src/test/regress/sql/mergejoin_semijoinfilter.sql
new file mode 100644
index 0000000000..6c6418f71a
--- /dev/null
+++ b/src/test/regress/sql/mergejoin_semijoinfilter.sql
@@ -0,0 +1,62 @@
+SET enable_hashjoin = OFF;
+SET enable_nestloop = OFF;
+SET enable_mergejoin = ON;
+SET enable_mergejoin_semijoin_filter = ON;
+SET force_mergejoin_semijoin_filter = ON;
+
+CREATE TABLE t1 (
+  i integer,
+  j integer
+);
+
+CREATE TABLE t2 (
+  i integer,
+  k integer
+);
+
+CREATE TABLE t3 (
+  i integer,
+  m integer
+);
+
+INSERT INTO t1 (i, j)
+  SELECT
+    generate_series(1,100000) AS i,
+    generate_series(1,100000) AS j;
+
+INSERT INTO t2 (i, k)
+  SELECT
+    generate_series(1,100000) AS i,
+    generate_series(1,100000) AS k;
+
+INSERT INTO t3 (i, m)
+  SELECT
+    generate_series(1,100000) AS i,
+    generate_series(1,100000) AS m;
+
+-- Semijoin filter is not used when force_mergejoin_semijoin_filter is OFF.
+SET force_mergejoin_semijoin_filter = OFF;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT COUNT(*) FROM t1 JOIN t2 ON t1.i = t2.i;
+SET force_mergejoin_semijoin_filter = ON;
+
+-- One level of inner mergejoin: push semi-join filter to outer scan.
+EXPLAIN (VERBOSE, COSTS OFF) SELECT COUNT(*) FROM t1 JOIN t2 ON t1.i = t2.i;
+SELECT COUNT(*) FROM t1 JOIN t2 ON t1.i = t2.i;
+
+-- Push semijoin filter through SORT node.
+EXPLAIN (VERBOSE, COSTS OFF) SELECT COUNT(*) FROM (SELECT DISTINCT t1.i FROM t1 ORDER BY t1.i) x JOIN t2 ON x.i = t2.i;
+SELECT COUNT(*) FROM (SELECT DISTINCT t1.i FROM t1 ORDER BY t1.i) x JOIN t2 ON x.i = t2.i;
+
+-- Two levels of MergeJoin
+EXPLAIN (VERBOSE, COSTS OFF) SELECT COUNT(*) FROM t1 JOIN t2 ON t1.i = t2.i JOIN t3 ON t2.i = t3.i;
+SELECT COUNT(*) FROM t1 JOIN t2 ON t1.i = t2.i JOIN t3 ON t2.i = t3.i;
+
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
+
+RESET enable_mergejoin;
+RESET enable_memoize;
+RESET enable_mergejoin;
+RESET enable_mergejoin_semijoin_filter;
+RESET force_mergejoin_semijoin_filter;
-- 
2.37.1

