# Test ORed ON clause predicates which may be split into unions or
# intersections. The rewrite is cost-based, so may not always show up in the
# query plan.
# Use tables both with and without a primary key, to test when PK columns are
# not included and also to allow null values.


exec-ddl
CREATE TABLE a(a1 INT, a2 INT, a3 INT, a4 INT, PRIMARY KEY(a1, a2, a3, a4))
----

exec-ddl
CREATE TABLE b(b1 INT, b2 INT, b3 INT, b4 INT,
               INDEX (b1, b2) STORING (b3, b4),
               INDEX (b2) STORING (b1, b3, b4),
               INDEX (b3) STORING (b1, b2, b4))
----

exec-ddl
CREATE TABLE c(c1 INT, c2 INT, c3 INT, c4 INT)
----

exec-ddl
CREATE TABLE d(d1 INT, d2 INT, d3 INT, d4 INT,
               INDEX d (d1) STORING (d2, d3, d4))
----

exec-ddl
CREATE TABLE e (
  e1 INT PRIMARY KEY,
  e2 INT NOT NULL,
  e3 INT NOT NULL,
  e4 STRING NOT NULL,
  e5 INT NULL,
  INDEX (e2 ASC, e3 ASC, e4 ASC),
  INDEX (e5 ASC),
  INDEX (e3 ASC, e5 ASC, e4 ASC),
  INDEX (e3 ASC) WHERE e4 IN ('a', 'b')
)
----

exec-ddl
CREATE TABLE f (
  f1 INT PRIMARY KEY,
  f2 INT NULL,
  f3 INT NULL,
  f4 INT NOT NULL,
  INDEX (f3 ASC),
  INDEX (f2 ASC) STORING (f3)
)
----

# --------------------------------------------------
# InnerJoin
# --------------------------------------------------

# The left AND right sides of the join already produce key columns
build-post-queries expect=SplitDisjunctionOfJoinTerms
SELECT t1.*, t2.* FROM a t1, a t2 WHERE t1.a1 = t2.a3 OR t1.a2 = t2.a4 OR t1.a1 = t2.a4
----
root
 └── project
      ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null
      ├── key: (1-4,7-10)
      └── select
           ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null t1.crdb_internal_mvcc_timestamp:5 t1.tableoid:6 t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null t2.crdb_internal_mvcc_timestamp:11 t2.tableoid:12
           ├── key: (1-4,7-10)
           ├── fd: (1-4)-->(5,6), (7-10)-->(11,12)
           ├── inner-join (cross)
           │    ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null t1.crdb_internal_mvcc_timestamp:5 t1.tableoid:6 t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null t2.crdb_internal_mvcc_timestamp:11 t2.tableoid:12
           │    ├── key: (1-4,7-10)
           │    ├── fd: (1-4)-->(5,6), (7-10)-->(11,12)
           │    ├── scan a [as=t1]
           │    │    ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null t1.crdb_internal_mvcc_timestamp:5 t1.tableoid:6
           │    │    ├── key: (1-4)
           │    │    └── fd: (1-4)-->(5,6)
           │    ├── scan a [as=t2]
           │    │    ├── columns: t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null t2.crdb_internal_mvcc_timestamp:11 t2.tableoid:12
           │    │    ├── key: (7-10)
           │    │    └── fd: (7-10)-->(11,12)
           │    └── filters (true)
           └── filters
                └── ((t1.a1:1 = t2.a3:9) OR (t1.a2:2 = t2.a4:10)) OR (t1.a1:1 = t2.a4:10) [outer=(1,2,9,10)]

# Join of tables with compound primary keys
opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a t1, a t2 WHERE (t1.a2 = t2.a2 OR t1.a3 = t2.a3) AND (t1.a1 = t2.a1 OR t1.a4 = t2.a4)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a1:7!null a2:8!null a3:9!null a4:10!null
 ├── key: (1-4,7-10)
 └── distinct-on
      ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null
      ├── grouping columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null
      ├── key: (1-4,7-10)
      └── union-all
           ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null
           ├── left columns: t1.a1:13 t1.a2:14 t1.a3:15 t1.a4:16 t2.a1:19 t2.a2:20 t2.a3:21 t2.a4:22
           ├── right columns: t1.a1:25 t1.a2:26 t1.a3:27 t1.a4:28 t2.a1:31 t2.a2:32 t2.a3:33 t2.a4:34
           ├── inner-join (hash)
           │    ├── columns: t1.a1:13!null t1.a2:14!null t1.a3:15!null t1.a4:16!null t2.a1:19!null t2.a2:20!null t2.a3:21!null t2.a4:22!null
           │    ├── key: (13,15,16,19-22)
           │    ├── fd: (14)==(20), (20)==(14)
           │    ├── scan a [as=t1]
           │    │    ├── columns: t1.a1:13!null t1.a2:14!null t1.a3:15!null t1.a4:16!null
           │    │    └── key: (13-16)
           │    ├── scan a [as=t2]
           │    │    ├── columns: t2.a1:19!null t2.a2:20!null t2.a3:21!null t2.a4:22!null
           │    │    └── key: (19-22)
           │    └── filters
           │         ├── t1.a2:14 = t2.a2:20 [outer=(14,20), constraints=(/14: (/NULL - ]; /20: (/NULL - ]), fd=(14)==(20), (20)==(14)]
           │         └── (t1.a1:13 = t2.a1:19) OR (t1.a4:16 = t2.a4:22) [outer=(13,16,19,22)]
           └── inner-join (hash)
                ├── columns: t1.a1:25!null t1.a2:26!null t1.a3:27!null t1.a4:28!null t2.a1:31!null t2.a2:32!null t2.a3:33!null t2.a4:34!null
                ├── key: (25,26,28,31-34)
                ├── fd: (27)==(33), (33)==(27)
                ├── scan a [as=t1]
                │    ├── columns: t1.a1:25!null t1.a2:26!null t1.a3:27!null t1.a4:28!null
                │    └── key: (25-28)
                ├── scan a [as=t2]
                │    ├── columns: t2.a1:31!null t2.a2:32!null t2.a3:33!null t2.a4:34!null
                │    └── key: (31-34)
                └── filters
                     ├── t1.a3:27 = t2.a3:33 [outer=(27,33), constraints=(/27: (/NULL - ]; /33: (/NULL - ]), fd=(27)==(33), (33)==(27)]
                     └── (t1.a1:25 = t2.a1:31) OR (t1.a4:28 = t2.a4:34) [outer=(25,28,31,34)]

# Join of tables with compound primary keys
opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a, B WHERE (a2 = b2 OR a3 = b3) AND (a1 = b1 OR a4 = b4)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7 b2:8 b3:9 b4:10
 └── distinct-on
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7 b2:8 b3:9 b4:10 rowid:11!null
      ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null rowid:11!null
      ├── key: (1-4,11)
      ├── fd: (1-4,11)-->(7-10)
      ├── union-all
      │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7 b2:8 b3:9 b4:10 rowid:11!null
      │    ├── left columns: a1:14 a2:15 a3:16 a4:17 b1:20 b2:21 b3:22 b4:23 rowid:24
      │    ├── right columns: a1:27 a2:28 a3:29 a4:30 b1:33 b2:34 b3:35 b4:36 rowid:37
      │    ├── inner-join (hash)
      │    │    ├── columns: a1:14!null a2:15!null a3:16!null a4:17!null b1:20 b2:21!null b3:22 b4:23 rowid:24!null
      │    │    ├── key: (14,16,17,24)
      │    │    ├── fd: (24)-->(20-23), (15)==(21), (21)==(15)
      │    │    ├── scan a
      │    │    │    ├── columns: a1:14!null a2:15!null a3:16!null a4:17!null
      │    │    │    └── key: (14-17)
      │    │    ├── scan b
      │    │    │    ├── columns: b1:20 b2:21 b3:22 b4:23 rowid:24!null
      │    │    │    ├── key: (24)
      │    │    │    └── fd: (24)-->(20-23)
      │    │    └── filters
      │    │         ├── a2:15 = b2:21 [outer=(15,21), constraints=(/15: (/NULL - ]; /21: (/NULL - ]), fd=(15)==(21), (21)==(15)]
      │    │         └── (a1:14 = b1:20) OR (a4:17 = b4:23) [outer=(14,17,20,23)]
      │    └── inner-join (hash)
      │         ├── columns: a1:27!null a2:28!null a3:29!null a4:30!null b1:33 b2:34 b3:35!null b4:36 rowid:37!null
      │         ├── key: (27,28,30,37)
      │         ├── fd: (37)-->(33-36), (29)==(35), (35)==(29)
      │         ├── scan a
      │         │    ├── columns: a1:27!null a2:28!null a3:29!null a4:30!null
      │         │    └── key: (27-30)
      │         ├── scan b
      │         │    ├── columns: b1:33 b2:34 b3:35 b4:36 rowid:37!null
      │         │    ├── key: (37)
      │         │    └── fd: (37)-->(33-36)
      │         └── filters
      │              ├── a3:29 = b3:35 [outer=(29,35), constraints=(/29: (/NULL - ]; /35: (/NULL - ]), fd=(29)==(35), (35)==(29)]
      │              └── (a1:27 = b1:33) OR (a4:30 = b4:36) [outer=(27,30,33,36)]
      └── aggregations
           ├── const-agg [as=b1:7, outer=(7)]
           │    └── b1:7
           ├── const-agg [as=b2:8, outer=(8)]
           │    └── b2:8
           ├── const-agg [as=b3:9, outer=(9)]
           │    └── b3:9
           └── const-agg [as=b4:10, outer=(10)]
                └── b4:10

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a t1, a t2 WHERE (t1.a2 = t2.a2 OR t1.a3 = t2.a3) AND (t1.a1 = t2.a1 OR t1.a4 = t2.a4)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a1:7!null a2:8!null a3:9!null a4:10!null
 ├── key: (1-4,7-10)
 └── distinct-on
      ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null
      ├── grouping columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null
      ├── key: (1-4,7-10)
      └── union-all
           ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null
           ├── left columns: t1.a1:13 t1.a2:14 t1.a3:15 t1.a4:16 t2.a1:19 t2.a2:20 t2.a3:21 t2.a4:22
           ├── right columns: t1.a1:25 t1.a2:26 t1.a3:27 t1.a4:28 t2.a1:31 t2.a2:32 t2.a3:33 t2.a4:34
           ├── inner-join (hash)
           │    ├── columns: t1.a1:13!null t1.a2:14!null t1.a3:15!null t1.a4:16!null t2.a1:19!null t2.a2:20!null t2.a3:21!null t2.a4:22!null
           │    ├── key: (13,15,16,19-22)
           │    ├── fd: (14)==(20), (20)==(14)
           │    ├── scan a [as=t1]
           │    │    ├── columns: t1.a1:13!null t1.a2:14!null t1.a3:15!null t1.a4:16!null
           │    │    └── key: (13-16)
           │    ├── scan a [as=t2]
           │    │    ├── columns: t2.a1:19!null t2.a2:20!null t2.a3:21!null t2.a4:22!null
           │    │    └── key: (19-22)
           │    └── filters
           │         ├── t1.a2:14 = t2.a2:20 [outer=(14,20), constraints=(/14: (/NULL - ]; /20: (/NULL - ]), fd=(14)==(20), (20)==(14)]
           │         └── (t1.a1:13 = t2.a1:19) OR (t1.a4:16 = t2.a4:22) [outer=(13,16,19,22)]
           └── inner-join (hash)
                ├── columns: t1.a1:25!null t1.a2:26!null t1.a3:27!null t1.a4:28!null t2.a1:31!null t2.a2:32!null t2.a3:33!null t2.a4:34!null
                ├── key: (25,26,28,31-34)
                ├── fd: (27)==(33), (33)==(27)
                ├── scan a [as=t1]
                │    ├── columns: t1.a1:25!null t1.a2:26!null t1.a3:27!null t1.a4:28!null
                │    └── key: (25-28)
                ├── scan a [as=t2]
                │    ├── columns: t2.a1:31!null t2.a2:32!null t2.a3:33!null t2.a4:34!null
                │    └── key: (31-34)
                └── filters
                     ├── t1.a3:27 = t2.a3:33 [outer=(27,33), constraints=(/27: (/NULL - ]; /33: (/NULL - ]), fd=(27)==(33), (33)==(27)]
                     └── (t1.a1:25 = t2.a1:31) OR (t1.a4:28 = t2.a4:34) [outer=(25,28,31,34)]

opt expect=SplitDisjunctionOfJoinTerms
SELECT a1,a2,a3 FROM a,b WHERE (a1=b1 AND a2=b2 AND (a1=1 OR b1=1)) OR (a3=b3 AND a4=b4)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null
 └── project
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7 b2:8 b3:9 b4:10
      └── distinct-on
           ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7 b2:8 b3:9 b4:10 rowid:11!null
           ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null rowid:11!null
           ├── key: (1-4,11)
           ├── fd: (1-4,11)-->(7-10)
           ├── union-all
           │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7 b2:8 b3:9 b4:10 rowid:11!null
           │    ├── left columns: a1:14 a2:15 a3:16 a4:17 b1:20 b2:21 b3:22 b4:23 rowid:24
           │    ├── right columns: a1:27 a2:28 a3:29 a4:30 b1:33 b2:34 b3:35 b4:36 rowid:37
           │    ├── inner-join (merge)
           │    │    ├── columns: a1:14!null a2:15!null a3:16!null a4:17!null b1:20!null b2:21!null b3:22 b4:23 rowid:24!null
           │    │    ├── left ordering: +15,+14
           │    │    ├── right ordering: +21,+20
           │    │    ├── key: (16,17,24)
           │    │    ├── fd: ()-->(14,20), (24)-->(21-23), (14)==(20), (20)==(14), (15)==(21), (21)==(15)
           │    │    ├── scan a
           │    │    │    ├── columns: a1:14!null a2:15!null a3:16!null a4:17!null
           │    │    │    ├── constraint: /14/15/16/17: [/1 - /1]
           │    │    │    ├── key: (15-17)
           │    │    │    ├── fd: ()-->(14)
           │    │    │    └── ordering: +15 opt(14) [actual: +15]
           │    │    ├── scan b@b_b1_b2_idx
           │    │    │    ├── columns: b1:20!null b2:21 b3:22 b4:23 rowid:24!null
           │    │    │    ├── constraint: /20/21/24: [/1 - /1]
           │    │    │    ├── key: (24)
           │    │    │    ├── fd: ()-->(20), (24)-->(21-23)
           │    │    │    └── ordering: +21 opt(20) [actual: +21]
           │    │    └── filters (true)
           │    └── inner-join (hash)
           │         ├── columns: a1:27!null a2:28!null a3:29!null a4:30!null b1:33 b2:34 b3:35!null b4:36!null rowid:37!null
           │         ├── key: (27,28,37)
           │         ├── fd: (37)-->(33-36), (29)==(35), (35)==(29), (30)==(36), (36)==(30)
           │         ├── scan a
           │         │    ├── columns: a1:27!null a2:28!null a3:29!null a4:30!null
           │         │    └── key: (27-30)
           │         ├── scan b
           │         │    ├── columns: b1:33 b2:34 b3:35 b4:36 rowid:37!null
           │         │    ├── key: (37)
           │         │    └── fd: (37)-->(33-36)
           │         └── filters
           │              ├── a3:29 = b3:35 [outer=(29,35), constraints=(/29: (/NULL - ]; /35: (/NULL - ]), fd=(29)==(35), (35)==(29)]
           │              └── a4:30 = b4:36 [outer=(30,36), constraints=(/30: (/NULL - ]; /36: (/NULL - ]), fd=(30)==(36), (36)==(30)]
           └── aggregations
                ├── const-agg [as=b1:7, outer=(7)]
                │    └── b1:7
                ├── const-agg [as=b2:8, outer=(8)]
                │    └── b2:8
                ├── const-agg [as=b3:9, outer=(9)]
                │    └── b3:9
                └── const-agg [as=b4:10, outer=(10)]
                     └── b4:10

opt expect=SplitDisjunctionOfJoinTerms
SELECT a1,a2,a3 FROM a,b WHERE a1=1 AND (a2=b2 OR a3=b3)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null
 ├── fd: ()-->(1)
 └── project
      ├── columns: a1:1!null a2:2!null a3:3!null b2:8 b3:9
      ├── fd: ()-->(1)
      └── distinct-on
           ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b2:8 b3:9 rowid:11!null
           ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null rowid:11!null
           ├── key: (1-4,11)
           ├── fd: (1-4,11)-->(8,9)
           ├── union-all
           │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b2:8 b3:9 rowid:11!null
           │    ├── left columns: a1:14 a2:15 a3:16 a4:17 b2:21 b3:22 rowid:24
           │    ├── right columns: a1:27 a2:28 a3:29 a4:30 b2:34 b3:35 rowid:37
           │    ├── inner-join (lookup b@b_b2_idx)
           │    │    ├── columns: a1:14!null a2:15!null a3:16!null a4:17!null b2:21!null b3:22 rowid:24!null
           │    │    ├── key columns: [15] = [21]
           │    │    ├── key: (16,17,24)
           │    │    ├── fd: ()-->(14), (24)-->(21,22), (15)==(21), (21)==(15)
           │    │    ├── scan a
           │    │    │    ├── columns: a1:14!null a2:15!null a3:16!null a4:17!null
           │    │    │    ├── constraint: /14/15/16/17: [/1 - /1]
           │    │    │    ├── key: (15-17)
           │    │    │    └── fd: ()-->(14)
           │    │    └── filters (true)
           │    └── inner-join (lookup b@b_b3_idx)
           │         ├── columns: a1:27!null a2:28!null a3:29!null a4:30!null b2:34 b3:35!null rowid:37!null
           │         ├── key columns: [29] = [35]
           │         ├── key: (28,30,37)
           │         ├── fd: ()-->(27), (37)-->(34,35), (29)==(35), (35)==(29)
           │         ├── scan a
           │         │    ├── columns: a1:27!null a2:28!null a3:29!null a4:30!null
           │         │    ├── constraint: /27/28/29/30: [/1 - /1]
           │         │    ├── key: (28-30)
           │         │    └── fd: ()-->(27)
           │         └── filters (true)
           └── aggregations
                ├── const-agg [as=b2:8, outer=(8)]
                │    └── b2:8
                └── const-agg [as=b3:9, outer=(9)]
                     └── b3:9

# More than one disjunction in the filter
build-post-queries expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a, c WHERE (a1 = c1 OR a2 = c2 OR a3 = c3 OR a4 = c4)
----
root
 └── project
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null c1:7 c2:8 c3:9 c4:10
      └── select
           ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a.crdb_internal_mvcc_timestamp:5 a.tableoid:6 c1:7 c2:8 c3:9 c4:10 rowid:11!null c.crdb_internal_mvcc_timestamp:12 c.tableoid:13
           ├── key: (1-4,11)
           ├── fd: (1-4)-->(5,6), (11)-->(7-10,12,13)
           ├── inner-join (cross)
           │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a.crdb_internal_mvcc_timestamp:5 a.tableoid:6 c1:7 c2:8 c3:9 c4:10 rowid:11!null c.crdb_internal_mvcc_timestamp:12 c.tableoid:13
           │    ├── key: (1-4,11)
           │    ├── fd: (1-4)-->(5,6), (11)-->(7-10,12,13)
           │    ├── scan a
           │    │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a.crdb_internal_mvcc_timestamp:5 a.tableoid:6
           │    │    ├── key: (1-4)
           │    │    └── fd: (1-4)-->(5,6)
           │    ├── scan c
           │    │    ├── columns: c1:7 c2:8 c3:9 c4:10 rowid:11!null c.crdb_internal_mvcc_timestamp:12 c.tableoid:13
           │    │    ├── key: (11)
           │    │    └── fd: (11)-->(7-10,12,13)
           │    └── filters (true)
           └── filters
                └── (((a1:1 = c1:7) OR (a2:2 = c2:8)) OR (a3:3 = c3:9)) OR (a4:4 = c4:10) [outer=(1-4,7-10)]

build-post-queries expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a, c WHERE (a1 = c2 OR a2 = c1 OR a3 = c4 OR a3 = c4)
----
root
 └── project
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null c1:7 c2:8 c3:9 c4:10
      └── select
           ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a.crdb_internal_mvcc_timestamp:5 a.tableoid:6 c1:7 c2:8 c3:9 c4:10 rowid:11!null c.crdb_internal_mvcc_timestamp:12 c.tableoid:13
           ├── key: (1-4,11)
           ├── fd: (1-4)-->(5,6), (11)-->(7-10,12,13)
           ├── inner-join (cross)
           │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a.crdb_internal_mvcc_timestamp:5 a.tableoid:6 c1:7 c2:8 c3:9 c4:10 rowid:11!null c.crdb_internal_mvcc_timestamp:12 c.tableoid:13
           │    ├── key: (1-4,11)
           │    ├── fd: (1-4)-->(5,6), (11)-->(7-10,12,13)
           │    ├── scan a
           │    │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a.crdb_internal_mvcc_timestamp:5 a.tableoid:6
           │    │    ├── key: (1-4)
           │    │    └── fd: (1-4)-->(5,6)
           │    ├── scan c
           │    │    ├── columns: c1:7 c2:8 c3:9 c4:10 rowid:11!null c.crdb_internal_mvcc_timestamp:12 c.tableoid:13
           │    │    ├── key: (11)
           │    │    └── fd: (11)-->(7-10,12,13)
           │    └── filters (true)
           └── filters
                └── (((a1:1 = c2:8) OR (a2:2 = c1:7)) OR (a3:3 = c4:10)) OR (a3:3 = c4:10) [outer=(1-3,7,8,10)]

# Equality filters that do not reference a column on each side of the join
opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM b, d WHERE (b1 = b2 OR b3 = d3)
----
project
 ├── columns: b1:1 b2:2 b3:3 b4:4 d1:8 d2:9 d3:10 d4:11
 └── distinct-on
      ├── columns: b1:1 b2:2 b3:3 b4:4 b.rowid:5!null d1:8 d2:9 d3:10 d4:11 d.rowid:12!null
      ├── grouping columns: b.rowid:5!null d.rowid:12!null
      ├── key: (5,12)
      ├── fd: (5,12)-->(1-4,8-11)
      ├── union-all
      │    ├── columns: b1:1 b2:2 b3:3 b4:4 b.rowid:5!null d1:8 d2:9 d3:10 d4:11 d.rowid:12!null
      │    ├── left columns: b1:15 b2:16 b3:17 b4:18 b.rowid:19 d1:22 d2:23 d3:24 d4:25 d.rowid:26
      │    ├── right columns: b1:29 b2:30 b3:31 b4:32 b.rowid:33 d1:36 d2:37 d3:38 d4:39 d.rowid:40
      │    ├── inner-join (cross)
      │    │    ├── columns: b1:15!null b2:16!null b3:17 b4:18 b.rowid:19!null d1:22 d2:23 d3:24 d4:25 d.rowid:26!null
      │    │    ├── key: (19,26)
      │    │    ├── fd: (19)-->(15-18), (26)-->(22-25), (15)==(16), (16)==(15)
      │    │    ├── scan d
      │    │    │    ├── columns: d1:22 d2:23 d3:24 d4:25 d.rowid:26!null
      │    │    │    ├── key: (26)
      │    │    │    └── fd: (26)-->(22-25)
      │    │    ├── select
      │    │    │    ├── columns: b1:15!null b2:16!null b3:17 b4:18 b.rowid:19!null
      │    │    │    ├── key: (19)
      │    │    │    ├── fd: (19)-->(15-18), (15)==(16), (16)==(15)
      │    │    │    ├── scan b@b_b1_b2_idx
      │    │    │    │    ├── columns: b1:15!null b2:16 b3:17 b4:18 b.rowid:19!null
      │    │    │    │    ├── constraint: /15/16/19: (/NULL - ]
      │    │    │    │    ├── key: (19)
      │    │    │    │    └── fd: (19)-->(15-18)
      │    │    │    └── filters
      │    │    │         └── b1:15 = b2:16 [outer=(15,16), constraints=(/15: (/NULL - ]; /16: (/NULL - ]), fd=(15)==(16), (16)==(15)]
      │    │    └── filters (true)
      │    └── inner-join (hash)
      │         ├── columns: b1:29 b2:30 b3:31!null b4:32 b.rowid:33!null d1:36 d2:37 d3:38!null d4:39 d.rowid:40!null
      │         ├── key: (33,40)
      │         ├── fd: (33)-->(29-32), (40)-->(36-39), (31)==(38), (38)==(31)
      │         ├── scan b
      │         │    ├── columns: b1:29 b2:30 b3:31 b4:32 b.rowid:33!null
      │         │    ├── key: (33)
      │         │    └── fd: (33)-->(29-32)
      │         ├── scan d
      │         │    ├── columns: d1:36 d2:37 d3:38 d4:39 d.rowid:40!null
      │         │    ├── key: (40)
      │         │    └── fd: (40)-->(36-39)
      │         └── filters
      │              └── b3:31 = d3:38 [outer=(31,38), constraints=(/31: (/NULL - ]; /38: (/NULL - ]), fd=(31)==(38), (38)==(31)]
      └── aggregations
           ├── const-agg [as=b1:1, outer=(1)]
           │    └── b1:1
           ├── const-agg [as=b2:2, outer=(2)]
           │    └── b2:2
           ├── const-agg [as=b3:3, outer=(3)]
           │    └── b3:3
           ├── const-agg [as=b4:4, outer=(4)]
           │    └── b4:4
           ├── const-agg [as=d1:8, outer=(8)]
           │    └── d1:8
           ├── const-agg [as=d2:9, outer=(9)]
           │    └── d2:9
           ├── const-agg [as=d3:10, outer=(10)]
           │    └── d3:10
           └── const-agg [as=d4:11, outer=(11)]
                └── d4:11

build-post-queries expect=SplitDisjunctionOfJoinTerms
SELECT * FROM b, d WHERE (b1 = b2 OR b3 = d3 OR b4 = d4 OR d1 = d2)
----
root
 └── project
      ├── columns: b1:1 b2:2 b3:3 b4:4 d1:8 d2:9 d3:10 d4:11
      └── select
           ├── columns: b1:1 b2:2 b3:3 b4:4 b.rowid:5!null b.crdb_internal_mvcc_timestamp:6 b.tableoid:7 d1:8 d2:9 d3:10 d4:11 d.rowid:12!null d.crdb_internal_mvcc_timestamp:13 d.tableoid:14
           ├── key: (5,12)
           ├── fd: (5)-->(1-4,6,7), (12)-->(8-11,13,14)
           ├── inner-join (cross)
           │    ├── columns: b1:1 b2:2 b3:3 b4:4 b.rowid:5!null b.crdb_internal_mvcc_timestamp:6 b.tableoid:7 d1:8 d2:9 d3:10 d4:11 d.rowid:12!null d.crdb_internal_mvcc_timestamp:13 d.tableoid:14
           │    ├── key: (5,12)
           │    ├── fd: (5)-->(1-4,6,7), (12)-->(8-11,13,14)
           │    ├── scan b
           │    │    ├── columns: b1:1 b2:2 b3:3 b4:4 b.rowid:5!null b.crdb_internal_mvcc_timestamp:6 b.tableoid:7
           │    │    ├── key: (5)
           │    │    └── fd: (5)-->(1-4,6,7)
           │    ├── scan d
           │    │    ├── columns: d1:8 d2:9 d3:10 d4:11 d.rowid:12!null d.crdb_internal_mvcc_timestamp:13 d.tableoid:14
           │    │    ├── key: (12)
           │    │    └── fd: (12)-->(8-11,13,14)
           │    └── filters (true)
           └── filters
                └── (((b1:1 = b2:2) OR (b3:3 = d3:10)) OR (b4:4 = d4:11)) OR (d1:8 = d2:9) [outer=(1-4,8-11)]

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM b, d WHERE (b1 = 0 OR b1 = d1)   AND
                         (b1 = 0 OR b2 = 5)    AND
                         (b2 = d1 OR b1 = d1)  AND
                         (b2 = d1 OR b2 = 5)
----
project
 ├── columns: b1:1!null b2:2!null b3:3 b4:4 d1:8!null d2:9 d3:10 d4:11
 └── distinct-on
      ├── columns: b1:1!null b2:2!null b3:3 b4:4 b.rowid:5!null d1:8!null d2:9 d3:10 d4:11 d.rowid:12!null
      ├── grouping columns: b.rowid:5!null d.rowid:12!null
      ├── key: (5,12)
      ├── fd: (5,12)-->(1-4,8-11)
      ├── union-all
      │    ├── columns: b1:1!null b2:2!null b3:3 b4:4 b.rowid:5!null d1:8!null d2:9 d3:10 d4:11 d.rowid:12!null
      │    ├── left columns: b1:29 b2:30 b3:31 b4:32 b.rowid:33 d1:36 d2:37 d3:38 d4:39 d.rowid:40
      │    ├── right columns: b1:43 b2:44 b3:45 b4:46 b.rowid:47 d1:50 d2:51 d3:52 d4:53 d.rowid:54
      │    ├── project
      │    │    ├── columns: b1:29!null b2:30!null b3:31 b4:32 b.rowid:33!null d1:36!null d2:37 d3:38 d4:39 d.rowid:40!null
      │    │    ├── key: (33,40)
      │    │    ├── fd: ()-->(29), (33)-->(30-32), (40)-->(36-39)
      │    │    └── distinct-on
      │    │         ├── columns: b1:29!null b2:30!null b3:31 b4:32 b.rowid:33!null d1:36!null d2:37 d3:38 d4:39 d.rowid:40!null
      │    │         ├── grouping columns: b.rowid:33!null d.rowid:40!null
      │    │         ├── key: (33,40)
      │    │         ├── fd: (33,40)-->(29-32,36-39)
      │    │         ├── union-all
      │    │         │    ├── columns: b1:29!null b2:30!null b3:31 b4:32 b.rowid:33!null d1:36!null d2:37 d3:38 d4:39 d.rowid:40!null
      │    │         │    ├── left columns: b1:57 b2:58 b3:59 b4:60 b.rowid:61 d1:64 d2:65 d3:66 d4:67 d.rowid:68
      │    │         │    ├── right columns: b1:71 b2:72 b3:73 b4:74 b.rowid:75 d1:78 d2:79 d3:80 d4:81 d.rowid:82
      │    │         │    ├── inner-join (lookup d@d)
      │    │         │    │    ├── columns: b1:57!null b2:58!null b3:59 b4:60 b.rowid:61!null d1:64!null d2:65 d3:66 d4:67 d.rowid:68!null
      │    │         │    │    ├── key columns: [58] = [64]
      │    │         │    │    ├── key: (61,68)
      │    │         │    │    ├── fd: ()-->(57), (61)-->(58-60), (68)-->(64-67), (58)==(64), (64)==(58)
      │    │         │    │    ├── scan b@b_b1_b2_idx
      │    │         │    │    │    ├── columns: b1:57!null b2:58!null b3:59 b4:60 b.rowid:61!null
      │    │         │    │    │    ├── constraint: /57/58/61: (/0/NULL - /0]
      │    │         │    │    │    ├── key: (61)
      │    │         │    │    │    └── fd: ()-->(57), (61)-->(58-60)
      │    │         │    │    └── filters
      │    │         │    │         └── ((d1:64 IS DISTINCT FROM CAST(NULL AS INT8)) OR CAST(NULL AS BOOL)) OR (d1:64 = 5) [outer=(64), constraints=(/64: (/NULL - ]; tight)]
      │    │         │    └── inner-join (lookup d@d)
      │    │         │         ├── columns: b1:71!null b2:72!null b3:73 b4:74 b.rowid:75!null d1:78!null d2:79 d3:80 d4:81 d.rowid:82!null
      │    │         │         ├── key columns: [71] = [78]
      │    │         │         ├── key: (75,82)
      │    │         │         ├── fd: ()-->(71,78), (75)-->(72-74), (82)-->(79-81), (71)==(78), (78)==(71)
      │    │         │         ├── scan b@b_b1_b2_idx
      │    │         │         │    ├── columns: b1:71!null b2:72!null b3:73 b4:74 b.rowid:75!null
      │    │         │         │    ├── constraint: /71/72/75
      │    │         │         │    │    ├── [/0/0 - /0/0]
      │    │         │         │    │    └── [/0/5 - /0/5]
      │    │         │         │    ├── key: (75)
      │    │         │         │    └── fd: ()-->(71), (75)-->(72-74)
      │    │         │         └── filters (true)
      │    │         └── aggregations
      │    │              ├── const-agg [as=b1:29, outer=(29)]
      │    │              │    └── b1:29
      │    │              ├── const-agg [as=b2:30, outer=(30)]
      │    │              │    └── b2:30
      │    │              ├── const-agg [as=b3:31, outer=(31)]
      │    │              │    └── b3:31
      │    │              ├── const-agg [as=b4:32, outer=(32)]
      │    │              │    └── b4:32
      │    │              ├── const-agg [as=d1:36, outer=(36)]
      │    │              │    └── d1:36
      │    │              ├── const-agg [as=d2:37, outer=(37)]
      │    │              │    └── d2:37
      │    │              ├── const-agg [as=d3:38, outer=(38)]
      │    │              │    └── d3:38
      │    │              └── const-agg [as=d4:39, outer=(39)]
      │    │                   └── d4:39
      │    └── inner-join (lookup d@d)
      │         ├── columns: b1:43!null b2:44!null b3:45 b4:46 b.rowid:47!null d1:50!null d2:51 d3:52 d4:53 d.rowid:54!null
      │         ├── key columns: [43] = [50]
      │         ├── key: (47,54)
      │         ├── fd: (47)-->(43-46), (54)-->(50-53), (43)==(50), (50)==(43)
      │         ├── distinct-on
      │         │    ├── columns: b1:43!null b2:44!null b3:45 b4:46 b.rowid:47!null
      │         │    ├── grouping columns: b.rowid:47!null
      │         │    ├── key: (47)
      │         │    ├── fd: (47)-->(43-46)
      │         │    ├── union-all
      │         │    │    ├── columns: b1:43!null b2:44!null b3:45 b4:46 b.rowid:47!null
      │         │    │    ├── left columns: b1:115 b2:116 b3:117 b4:118 b.rowid:119
      │         │    │    ├── right columns: b1:122 b2:123 b3:124 b4:125 b.rowid:126
      │         │    │    ├── scan b@b_b1_b2_idx
      │         │    │    │    ├── columns: b1:115!null b2:116!null b3:117 b4:118 b.rowid:119!null
      │         │    │    │    ├── constraint: /115/116/119
      │         │    │    │    │    ├── [/0/0 - /0/0]
      │         │    │    │    │    └── [/0/5 - /0/5]
      │         │    │    │    ├── key: (119)
      │         │    │    │    └── fd: ()-->(115), (119)-->(116-118)
      │         │    │    └── select
      │         │    │         ├── columns: b1:122!null b2:123!null b3:124 b4:125 b.rowid:126!null
      │         │    │         ├── key: (126)
      │         │    │         ├── fd: ()-->(123), (126)-->(122,124,125)
      │         │    │         ├── scan b@b_b2_idx
      │         │    │         │    ├── columns: b1:122 b2:123!null b3:124 b4:125 b.rowid:126!null
      │         │    │         │    ├── constraint: /123/126: [/5 - /5]
      │         │    │         │    ├── key: (126)
      │         │    │         │    └── fd: ()-->(123), (126)-->(122,124,125)
      │         │    │         └── filters
      │         │    │              └── (b1:122 = 5) OR ((b1:122 IS DISTINCT FROM CAST(NULL AS INT8)) OR CAST(NULL AS BOOL)) [outer=(122), constraints=(/122: (/NULL - ]; tight)]
      │         │    └── aggregations
      │         │         ├── const-agg [as=b1:43, outer=(43)]
      │         │         │    └── b1:43
      │         │         ├── const-agg [as=b2:44, outer=(44)]
      │         │         │    └── b2:44
      │         │         ├── const-agg [as=b3:45, outer=(45)]
      │         │         │    └── b3:45
      │         │         └── const-agg [as=b4:46, outer=(46)]
      │         │              └── b4:46
      │         └── filters (true)
      └── aggregations
           ├── const-agg [as=b1:1, outer=(1)]
           │    └── b1:1
           ├── const-agg [as=b2:2, outer=(2)]
           │    └── b2:2
           ├── const-agg [as=b3:3, outer=(3)]
           │    └── b3:3
           ├── const-agg [as=b4:4, outer=(4)]
           │    └── b4:4
           ├── const-agg [as=d1:8, outer=(8)]
           │    └── d1:8
           ├── const-agg [as=d2:9, outer=(9)]
           │    └── d2:9
           ├── const-agg [as=d3:10, outer=(10)]
           │    └── d3:10
           └── const-agg [as=d4:11, outer=(11)]
                └── d4:11

# ON filters that are a disjunction of equality filters AND And expressions
opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM c, d WHERE (c1 = d1 AND c2 = d2) OR c3 = d3
----
project
 ├── columns: c1:1 c2:2 c3:3 c4:4 d1:8 d2:9 d3:10 d4:11
 └── distinct-on
      ├── columns: c1:1 c2:2 c3:3 c4:4 c.rowid:5!null d1:8 d2:9 d3:10 d4:11 d.rowid:12!null
      ├── grouping columns: c.rowid:5!null d.rowid:12!null
      ├── key: (5,12)
      ├── fd: (5,12)-->(1-4,8-11)
      ├── union-all
      │    ├── columns: c1:1 c2:2 c3:3 c4:4 c.rowid:5!null d1:8 d2:9 d3:10 d4:11 d.rowid:12!null
      │    ├── left columns: c1:15 c2:16 c3:17 c4:18 c.rowid:19 d1:22 d2:23 d3:24 d4:25 d.rowid:26
      │    ├── right columns: c1:29 c2:30 c3:31 c4:32 c.rowid:33 d1:36 d2:37 d3:38 d4:39 d.rowid:40
      │    ├── inner-join (hash)
      │    │    ├── columns: c1:15!null c2:16!null c3:17 c4:18 c.rowid:19!null d1:22!null d2:23!null d3:24 d4:25 d.rowid:26!null
      │    │    ├── key: (19,26)
      │    │    ├── fd: (19)-->(15-18), (26)-->(22-25), (15)==(22), (22)==(15), (16)==(23), (23)==(16)
      │    │    ├── scan c
      │    │    │    ├── columns: c1:15 c2:16 c3:17 c4:18 c.rowid:19!null
      │    │    │    ├── key: (19)
      │    │    │    └── fd: (19)-->(15-18)
      │    │    ├── scan d
      │    │    │    ├── columns: d1:22 d2:23 d3:24 d4:25 d.rowid:26!null
      │    │    │    ├── key: (26)
      │    │    │    └── fd: (26)-->(22-25)
      │    │    └── filters
      │    │         ├── c1:15 = d1:22 [outer=(15,22), constraints=(/15: (/NULL - ]; /22: (/NULL - ]), fd=(15)==(22), (22)==(15)]
      │    │         └── c2:16 = d2:23 [outer=(16,23), constraints=(/16: (/NULL - ]; /23: (/NULL - ]), fd=(16)==(23), (23)==(16)]
      │    └── inner-join (hash)
      │         ├── columns: c1:29 c2:30 c3:31!null c4:32 c.rowid:33!null d1:36 d2:37 d3:38!null d4:39 d.rowid:40!null
      │         ├── key: (33,40)
      │         ├── fd: (33)-->(29-32), (40)-->(36-39), (31)==(38), (38)==(31)
      │         ├── scan c
      │         │    ├── columns: c1:29 c2:30 c3:31 c4:32 c.rowid:33!null
      │         │    ├── key: (33)
      │         │    └── fd: (33)-->(29-32)
      │         ├── scan d
      │         │    ├── columns: d1:36 d2:37 d3:38 d4:39 d.rowid:40!null
      │         │    ├── key: (40)
      │         │    └── fd: (40)-->(36-39)
      │         └── filters
      │              └── c3:31 = d3:38 [outer=(31,38), constraints=(/31: (/NULL - ]; /38: (/NULL - ]), fd=(31)==(38), (38)==(31)]
      └── aggregations
           ├── const-agg [as=c1:1, outer=(1)]
           │    └── c1:1
           ├── const-agg [as=c2:2, outer=(2)]
           │    └── c2:2
           ├── const-agg [as=c3:3, outer=(3)]
           │    └── c3:3
           ├── const-agg [as=c4:4, outer=(4)]
           │    └── c4:4
           ├── const-agg [as=d1:8, outer=(8)]
           │    └── d1:8
           ├── const-agg [as=d2:9, outer=(9)]
           │    └── d2:9
           ├── const-agg [as=d3:10, outer=(10)]
           │    └── d3:10
           └── const-agg [as=d4:11, outer=(11)]
                └── d4:11

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a, d WHERE (a1 = d2 AND a2 = d1) OR (a3 = d4 AND a4 = d3)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null d1:7 d2:8 d3:9 d4:10
 └── distinct-on
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null d1:7 d2:8 d3:9 d4:10 rowid:11!null
      ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null rowid:11!null
      ├── key: (1-4,11)
      ├── fd: (1-4,11)-->(7-10)
      ├── union-all
      │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null d1:7 d2:8 d3:9 d4:10 rowid:11!null
      │    ├── left columns: a1:14 a2:15 a3:16 a4:17 d1:20 d2:21 d3:22 d4:23 rowid:24
      │    ├── right columns: a1:27 a2:28 a3:29 a4:30 d1:33 d2:34 d3:35 d4:36 rowid:37
      │    ├── inner-join (hash)
      │    │    ├── columns: a1:14!null a2:15!null a3:16!null a4:17!null d1:20!null d2:21!null d3:22 d4:23 rowid:24!null
      │    │    ├── key: (16,17,24)
      │    │    ├── fd: (24)-->(20-23), (14)==(21), (21)==(14), (15)==(20), (20)==(15)
      │    │    ├── scan a
      │    │    │    ├── columns: a1:14!null a2:15!null a3:16!null a4:17!null
      │    │    │    └── key: (14-17)
      │    │    ├── scan d
      │    │    │    ├── columns: d1:20 d2:21 d3:22 d4:23 rowid:24!null
      │    │    │    ├── key: (24)
      │    │    │    └── fd: (24)-->(20-23)
      │    │    └── filters
      │    │         ├── a1:14 = d2:21 [outer=(14,21), constraints=(/14: (/NULL - ]; /21: (/NULL - ]), fd=(14)==(21), (21)==(14)]
      │    │         └── a2:15 = d1:20 [outer=(15,20), constraints=(/15: (/NULL - ]; /20: (/NULL - ]), fd=(15)==(20), (20)==(15)]
      │    └── inner-join (hash)
      │         ├── columns: a1:27!null a2:28!null a3:29!null a4:30!null d1:33 d2:34 d3:35!null d4:36!null rowid:37!null
      │         ├── key: (27,28,37)
      │         ├── fd: (37)-->(33-36), (29)==(36), (36)==(29), (30)==(35), (35)==(30)
      │         ├── scan a
      │         │    ├── columns: a1:27!null a2:28!null a3:29!null a4:30!null
      │         │    └── key: (27-30)
      │         ├── scan d
      │         │    ├── columns: d1:33 d2:34 d3:35 d4:36 rowid:37!null
      │         │    ├── key: (37)
      │         │    └── fd: (37)-->(33-36)
      │         └── filters
      │              ├── a3:29 = d4:36 [outer=(29,36), constraints=(/29: (/NULL - ]; /36: (/NULL - ]), fd=(29)==(36), (36)==(29)]
      │              └── a4:30 = d3:35 [outer=(30,35), constraints=(/30: (/NULL - ]; /35: (/NULL - ]), fd=(30)==(35), (35)==(30)]
      └── aggregations
           ├── const-agg [as=d1:7, outer=(7)]
           │    └── d1:7
           ├── const-agg [as=d2:8, outer=(8)]
           │    └── d2:8
           ├── const-agg [as=d3:9, outer=(9)]
           │    └── d3:9
           └── const-agg [as=d4:10, outer=(10)]
                └── d4:10

# Same test as above, but with the order of variables in equality conditions
# swapped.
opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a, d WHERE (d2 = a1 AND d1 = a2) OR (d4 = a3 AND d3 = a4)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null d1:7 d2:8 d3:9 d4:10
 └── distinct-on
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null d1:7 d2:8 d3:9 d4:10 rowid:11!null
      ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null rowid:11!null
      ├── key: (1-4,11)
      ├── fd: (1-4,11)-->(7-10)
      ├── union-all
      │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null d1:7 d2:8 d3:9 d4:10 rowid:11!null
      │    ├── left columns: a1:14 a2:15 a3:16 a4:17 d1:20 d2:21 d3:22 d4:23 rowid:24
      │    ├── right columns: a1:27 a2:28 a3:29 a4:30 d1:33 d2:34 d3:35 d4:36 rowid:37
      │    ├── inner-join (hash)
      │    │    ├── columns: a1:14!null a2:15!null a3:16!null a4:17!null d1:20!null d2:21!null d3:22 d4:23 rowid:24!null
      │    │    ├── key: (16,17,24)
      │    │    ├── fd: (24)-->(20-23), (14)==(21), (21)==(14), (15)==(20), (20)==(15)
      │    │    ├── scan a
      │    │    │    ├── columns: a1:14!null a2:15!null a3:16!null a4:17!null
      │    │    │    └── key: (14-17)
      │    │    ├── scan d
      │    │    │    ├── columns: d1:20 d2:21 d3:22 d4:23 rowid:24!null
      │    │    │    ├── key: (24)
      │    │    │    └── fd: (24)-->(20-23)
      │    │    └── filters
      │    │         ├── d2:21 = a1:14 [outer=(14,21), constraints=(/14: (/NULL - ]; /21: (/NULL - ]), fd=(14)==(21), (21)==(14)]
      │    │         └── d1:20 = a2:15 [outer=(15,20), constraints=(/15: (/NULL - ]; /20: (/NULL - ]), fd=(15)==(20), (20)==(15)]
      │    └── inner-join (hash)
      │         ├── columns: a1:27!null a2:28!null a3:29!null a4:30!null d1:33 d2:34 d3:35!null d4:36!null rowid:37!null
      │         ├── key: (27,28,37)
      │         ├── fd: (37)-->(33-36), (29)==(36), (36)==(29), (30)==(35), (35)==(30)
      │         ├── scan a
      │         │    ├── columns: a1:27!null a2:28!null a3:29!null a4:30!null
      │         │    └── key: (27-30)
      │         ├── scan d
      │         │    ├── columns: d1:33 d2:34 d3:35 d4:36 rowid:37!null
      │         │    ├── key: (37)
      │         │    └── fd: (37)-->(33-36)
      │         └── filters
      │              ├── d4:36 = a3:29 [outer=(29,36), constraints=(/29: (/NULL - ]; /36: (/NULL - ]), fd=(29)==(36), (36)==(29)]
      │              └── d3:35 = a4:30 [outer=(30,35), constraints=(/30: (/NULL - ]; /35: (/NULL - ]), fd=(30)==(35), (35)==(30)]
      └── aggregations
           ├── const-agg [as=d1:7, outer=(7)]
           │    └── d1:7
           ├── const-agg [as=d2:8, outer=(8)]
           │    └── d2:8
           ├── const-agg [as=d3:9, outer=(9)]
           │    └── d3:9
           └── const-agg [as=d4:10, outer=(10)]
                └── d4:10

# We should be able to split the disjunction even though the predicates are
# equalities but not equijoin expressions.
opt expect=SplitDisjunctionOfJoinTerms
SELECT e.*
FROM
  e
  INNER JOIN f ON f.f3 = e.e2
WHERE
  e.e5 = 30
  OR (e.e3 = 10 AND f.f2 = 20)
----
project
 ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5
 ├── fd: (1)-->(2-5)
 └── project
      ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5 f2:9 f3:10!null
      ├── fd: (1)-->(2-5), (2)==(10), (10)==(2)
      └── distinct-on
           ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5 f1:8!null f2:9 f3:10!null
           ├── grouping columns: e1:1!null f1:8!null
           ├── key: (1,8)
           ├── fd: (1,8)-->(2-5,9,10), (2)==(10), (10)==(2)
           ├── union-all
           │    ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5 f1:8!null f2:9 f3:10!null
           │    ├── left columns: e1:14 e2:15 e3:16 e4:17 e5:18 f1:21 f2:22 f3:23
           │    ├── right columns: e1:27 e2:28 e3:29 e4:30 e5:31 f1:34 f2:35 f3:36
           │    ├── fd: (2)==(10), (10)==(2)
           │    ├── inner-join (lookup f)
           │    │    ├── columns: e1:14!null e2:15!null e3:16!null e4:17!null e5:18!null f1:21!null f2:22 f3:23!null
           │    │    ├── key columns: [21] = [21]
           │    │    ├── lookup columns are key
           │    │    ├── key: (14,21)
           │    │    ├── fd: ()-->(18), (14)-->(15-17), (21)-->(22,23), (15)==(23), (23)==(15)
           │    │    ├── inner-join (lookup f@f_f3_idx)
           │    │    │    ├── columns: e1:14!null e2:15!null e3:16!null e4:17!null e5:18!null f1:21!null f3:23!null
           │    │    │    ├── key columns: [15] = [23]
           │    │    │    ├── key: (14,21)
           │    │    │    ├── fd: ()-->(18), (14)-->(15-17), (21)-->(23), (15)==(23), (23)==(15)
           │    │    │    ├── index-join e
           │    │    │    │    ├── columns: e1:14!null e2:15!null e3:16!null e4:17!null e5:18!null
           │    │    │    │    ├── key: (14)
           │    │    │    │    ├── fd: ()-->(18), (14)-->(15-17)
           │    │    │    │    └── scan e@e_e5_idx
           │    │    │    │         ├── columns: e1:14!null e5:18!null
           │    │    │    │         ├── constraint: /18/14: [/30 - /30]
           │    │    │    │         ├── key: (14)
           │    │    │    │         └── fd: ()-->(18)
           │    │    │    └── filters (true)
           │    │    └── filters (true)
           │    └── inner-join (hash)
           │         ├── columns: e1:27!null e2:28!null e3:29!null e4:30!null e5:31 f1:34!null f2:35!null f3:36!null
           │         ├── key: (27,34)
           │         ├── fd: ()-->(29,35), (27)-->(28,30,31), (34)-->(36), (28)==(36), (36)==(28)
           │         ├── index-join e
           │         │    ├── columns: e1:27!null e2:28!null e3:29!null e4:30!null e5:31
           │         │    ├── key: (27)
           │         │    ├── fd: ()-->(29), (27)-->(28,30,31)
           │         │    └── scan e@e_e3_e5_e4_idx
           │         │         ├── columns: e1:27!null e3:29!null e4:30!null e5:31
           │         │         ├── constraint: /29/31/30/27: [/10 - /10]
           │         │         ├── key: (27)
           │         │         └── fd: ()-->(29), (27)-->(30,31)
           │         ├── scan f@f_f2_idx
           │         │    ├── columns: f1:34!null f2:35!null f3:36
           │         │    ├── constraint: /35/34: [/20 - /20]
           │         │    ├── key: (34)
           │         │    └── fd: ()-->(35), (34)-->(36)
           │         └── filters
           │              └── f3:36 = e2:28 [outer=(28,36), constraints=(/28: (/NULL - ]; /36: (/NULL - ]), fd=(28)==(36), (36)==(28)]
           └── aggregations
                ├── const-agg [as=e2:2, outer=(2)]
                │    └── e2:2
                ├── const-agg [as=e3:3, outer=(3)]
                │    └── e3:3
                ├── const-agg [as=e4:4, outer=(4)]
                │    └── e4:4
                ├── const-agg [as=e5:5, outer=(5)]
                │    └── e5:5
                ├── const-agg [as=f2:9, outer=(9)]
                │    └── f2:9
                └── const-agg [as=f3:10, outer=(10)]
                     └── f3:10

# We should be able to split the disjunction even though the predicates are
# not equalities.
opt expect=SplitDisjunctionOfJoinTerms
SELECT e.*
FROM
  e
  INNER JOIN f ON f.f3 = e.e2
WHERE
  (e.e5 > 30 AND e.e5 < 40)
  OR (e.e4 IN ('a', 'b') AND f.f2 < 20 AND f.f2 > 10)
----
project
 ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5
 ├── fd: (1)-->(2-5)
 └── project
      ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5 f2:9 f3:10!null
      ├── fd: (1)-->(2-5), (2)==(10), (10)==(2)
      └── distinct-on
           ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5 f1:8!null f2:9 f3:10!null
           ├── grouping columns: e1:1!null f1:8!null
           ├── key: (1,8)
           ├── fd: (1,8)-->(2-5,9,10), (2)==(10), (10)==(2)
           ├── union-all
           │    ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5 f1:8!null f2:9 f3:10!null
           │    ├── left columns: e1:14 e2:15 e3:16 e4:17 e5:18 f1:21 f2:22 f3:23
           │    ├── right columns: e1:27 e2:28 e3:29 e4:30 e5:31 f1:34 f2:35 f3:36
           │    ├── fd: (2)==(10), (10)==(2)
           │    ├── inner-join (hash)
           │    │    ├── columns: e1:14!null e2:15!null e3:16!null e4:17!null e5:18!null f1:21!null f2:22 f3:23!null
           │    │    ├── key: (14,21)
           │    │    ├── fd: (14)-->(15-18), (21)-->(22,23), (15)==(23), (23)==(15)
           │    │    ├── scan f@f_f2_idx
           │    │    │    ├── columns: f1:21!null f2:22 f3:23
           │    │    │    ├── key: (21)
           │    │    │    └── fd: (21)-->(22,23)
           │    │    ├── index-join e
           │    │    │    ├── columns: e1:14!null e2:15!null e3:16!null e4:17!null e5:18!null
           │    │    │    ├── key: (14)
           │    │    │    ├── fd: (14)-->(15-18)
           │    │    │    └── scan e@e_e5_idx
           │    │    │         ├── columns: e1:14!null e5:18!null
           │    │    │         ├── constraint: /18/14: [/31 - /39]
           │    │    │         ├── key: (14)
           │    │    │         └── fd: (14)-->(18)
           │    │    └── filters
           │    │         └── f3:23 = e2:15 [outer=(15,23), constraints=(/15: (/NULL - ]; /23: (/NULL - ]), fd=(15)==(23), (23)==(15)]
           │    └── inner-join (hash)
           │         ├── columns: e1:27!null e2:28!null e3:29!null e4:30!null e5:31 f1:34!null f2:35!null f3:36!null
           │         ├── key: (27,34)
           │         ├── fd: (27)-->(28-31), (34)-->(35,36), (28)==(36), (36)==(28)
           │         ├── scan f@f_f2_idx
           │         │    ├── columns: f1:34!null f2:35!null f3:36
           │         │    ├── constraint: /35/34: [/11 - /19]
           │         │    ├── key: (34)
           │         │    └── fd: (34)-->(35,36)
           │         ├── index-join e
           │         │    ├── columns: e1:27!null e2:28!null e3:29!null e4:30!null e5:31
           │         │    ├── key: (27)
           │         │    ├── fd: (27)-->(28-31)
           │         │    └── scan e@e_e3_idx,partial
           │         │         ├── columns: e1:27!null e3:29!null
           │         │         ├── key: (27)
           │         │         └── fd: (27)-->(29)
           │         └── filters
           │              └── f3:36 = e2:28 [outer=(28,36), constraints=(/28: (/NULL - ]; /36: (/NULL - ]), fd=(28)==(36), (36)==(28)]
           └── aggregations
                ├── const-agg [as=e2:2, outer=(2)]
                │    └── e2:2
                ├── const-agg [as=e3:3, outer=(3)]
                │    └── e3:3
                ├── const-agg [as=e4:4, outer=(4)]
                │    └── e4:4
                ├── const-agg [as=e5:5, outer=(5)]
                │    └── e5:5
                ├── const-agg [as=f2:9, outer=(9)]
                │    └── f2:9
                └── const-agg [as=f3:10, outer=(10)]
                     └── f3:10

#########################
# Uncorrelated semijoin #
#########################

# Join of tables with compound primary keys
opt expect-not=SplitDisjunctionOfJoinTerms
SELECT * FROM a WHERE EXISTS (SELECT 1 FROM d WHERE d1 = 4 OR d2 = 50)
----
select
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── key: (1-4)
 ├── scan a
 │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 │    └── key: (1-4)
 └── filters
      └── coalesce [subquery]
           ├── subquery
           │    └── project
           │         ├── columns: column16:16!null
           │         ├── cardinality: [0 - 1]
           │         ├── key: ()
           │         ├── fd: ()-->(16)
           │         ├── limit
           │         │    ├── columns: d1:7 d2:8
           │         │    ├── cardinality: [0 - 1]
           │         │    ├── key: ()
           │         │    ├── fd: ()-->(7,8)
           │         │    ├── select
           │         │    │    ├── columns: d1:7 d2:8
           │         │    │    ├── limit hint: 1.00
           │         │    │    ├── scan d
           │         │    │    │    ├── columns: d1:7 d2:8
           │         │    │    │    └── limit hint: 50.25
           │         │    │    └── filters
           │         │    │         └── (d1:7 = 4) OR (d2:8 = 50) [outer=(7,8)]
           │         │    └── 1
           │         └── projections
           │              └── true [as=column16:16]
           └── false

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a WHERE EXISTS (SELECT 1 FROM c, d WHERE d1 = 4 OR c2 = 50 HAVING sum(d4) > 40)
----
select
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── immutable
 ├── key: (1-4)
 ├── scan a
 │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 │    └── key: (1-4)
 └── filters
      └── coalesce [immutable, subquery]
           ├── subquery
           │    └── project
           │         ├── columns: column24:24!null
           │         ├── cardinality: [0 - 1]
           │         ├── immutable
           │         ├── key: ()
           │         ├── fd: ()-->(24)
           │         ├── select
           │         │    ├── columns: sum:21!null
           │         │    ├── cardinality: [0 - 1]
           │         │    ├── immutable
           │         │    ├── key: ()
           │         │    ├── fd: ()-->(21)
           │         │    ├── scalar-group-by
           │         │    │    ├── columns: sum:21
           │         │    │    ├── cardinality: [1 - 1]
           │         │    │    ├── key: ()
           │         │    │    ├── fd: ()-->(21)
           │         │    │    ├── distinct-on
           │         │    │    │    ├── columns: c2:8 c.rowid:11!null d1:14 d4:17 d.rowid:18!null
           │         │    │    │    ├── grouping columns: c.rowid:11!null d.rowid:18!null
           │         │    │    │    ├── key: (11,18)
           │         │    │    │    ├── fd: (11,18)-->(8,14,17)
           │         │    │    │    ├── union-all
           │         │    │    │    │    ├── columns: c2:8 c.rowid:11!null d1:14 d4:17 d.rowid:18!null
           │         │    │    │    │    ├── left columns: c2:26 c.rowid:29 d1:32 d4:35 d.rowid:36
           │         │    │    │    │    ├── right columns: c2:40 c.rowid:43 d1:46 d4:49 d.rowid:50
           │         │    │    │    │    ├── inner-join (cross)
           │         │    │    │    │    │    ├── columns: c2:26 c.rowid:29!null d1:32!null d4:35 d.rowid:36!null
           │         │    │    │    │    │    ├── key: (29,36)
           │         │    │    │    │    │    ├── fd: ()-->(32), (29)-->(26), (36)-->(35)
           │         │    │    │    │    │    ├── scan c
           │         │    │    │    │    │    │    ├── columns: c2:26 c.rowid:29!null
           │         │    │    │    │    │    │    ├── key: (29)
           │         │    │    │    │    │    │    └── fd: (29)-->(26)
           │         │    │    │    │    │    ├── scan d@d
           │         │    │    │    │    │    │    ├── columns: d1:32!null d4:35 d.rowid:36!null
           │         │    │    │    │    │    │    ├── constraint: /32/36: [/4 - /4]
           │         │    │    │    │    │    │    ├── key: (36)
           │         │    │    │    │    │    │    └── fd: ()-->(32), (36)-->(35)
           │         │    │    │    │    │    └── filters (true)
           │         │    │    │    │    └── inner-join (cross)
           │         │    │    │    │         ├── columns: c2:40!null c.rowid:43!null d1:46 d4:49 d.rowid:50!null
           │         │    │    │    │         ├── key: (43,50)
           │         │    │    │    │         ├── fd: ()-->(40), (50)-->(46,49)
           │         │    │    │    │         ├── scan d
           │         │    │    │    │         │    ├── columns: d1:46 d4:49 d.rowid:50!null
           │         │    │    │    │         │    ├── key: (50)
           │         │    │    │    │         │    └── fd: (50)-->(46,49)
           │         │    │    │    │         ├── select
           │         │    │    │    │         │    ├── columns: c2:40!null c.rowid:43!null
           │         │    │    │    │         │    ├── key: (43)
           │         │    │    │    │         │    ├── fd: ()-->(40)
           │         │    │    │    │         │    ├── scan c
           │         │    │    │    │         │    │    ├── columns: c2:40 c.rowid:43!null
           │         │    │    │    │         │    │    ├── key: (43)
           │         │    │    │    │         │    │    └── fd: (43)-->(40)
           │         │    │    │    │         │    └── filters
           │         │    │    │    │         │         └── c2:40 = 50 [outer=(40), constraints=(/40: [/50 - /50]; tight), fd=()-->(40)]
           │         │    │    │    │         └── filters (true)
           │         │    │    │    └── aggregations
           │         │    │    │         ├── const-agg [as=c2:8, outer=(8)]
           │         │    │    │         │    └── c2:8
           │         │    │    │         ├── const-agg [as=d1:14, outer=(14)]
           │         │    │    │         │    └── d1:14
           │         │    │    │         └── const-agg [as=d4:17, outer=(17)]
           │         │    │    │              └── d4:17
           │         │    │    └── aggregations
           │         │    │         └── sum [as=sum:21, outer=(17)]
           │         │    │              └── d4:17
           │         │    └── filters
           │         │         └── sum:21 > 40 [outer=(21), immutable, constraints=(/21: (/40 - ]; tight)]
           │         └── projections
           │              └── true [as=column24:24]
           └── false

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a WHERE EXISTS (SELECT 1 FROM c, d WHERE c1 = d2 or c2 = d1)
----
select
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── key: (1-4)
 ├── scan a
 │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 │    └── key: (1-4)
 └── filters
      └── coalesce [subquery]
           ├── subquery
           │    └── project
           │         ├── columns: column23:23!null
           │         ├── cardinality: [0 - 1]
           │         ├── key: ()
           │         ├── fd: ()-->(23)
           │         ├── limit
           │         │    ├── columns: c1:7 c2:8 d1:14 d2:15
           │         │    ├── cardinality: [0 - 1]
           │         │    ├── key: ()
           │         │    ├── fd: ()-->(7,8,14,15)
           │         │    ├── project
           │         │    │    ├── columns: c1:7 c2:8 d1:14 d2:15
           │         │    │    ├── limit hint: 1.00
           │         │    │    └── distinct-on
           │         │    │         ├── columns: c1:7 c2:8 c.rowid:11!null d1:14 d2:15 d.rowid:18!null
           │         │    │         ├── grouping columns: c.rowid:11!null d.rowid:18!null
           │         │    │         ├── key: (11,18)
           │         │    │         ├── fd: (11,18)-->(7,8,14,15)
           │         │    │         ├── limit hint: 1.00
           │         │    │         ├── union-all
           │         │    │         │    ├── columns: c1:7 c2:8 c.rowid:11!null d1:14 d2:15 d.rowid:18!null
           │         │    │         │    ├── left columns: c1:24 c2:25 c.rowid:28 d1:31 d2:32 d.rowid:35
           │         │    │         │    ├── right columns: c1:38 c2:39 c.rowid:42 d1:45 d2:46 d.rowid:49
           │         │    │         │    ├── limit hint: 1.20
           │         │    │         │    ├── inner-join (hash)
           │         │    │         │    │    ├── columns: c1:24!null c2:25 c.rowid:28!null d1:31 d2:32!null d.rowid:35!null
           │         │    │         │    │    ├── key: (28,35)
           │         │    │         │    │    ├── fd: (28)-->(24,25), (35)-->(31,32), (24)==(32), (32)==(24)
           │         │    │         │    │    ├── limit hint: 1.20
           │         │    │         │    │    ├── scan c
           │         │    │         │    │    │    ├── columns: c1:24 c2:25 c.rowid:28!null
           │         │    │         │    │    │    ├── key: (28)
           │         │    │         │    │    │    └── fd: (28)-->(24,25)
           │         │    │         │    │    ├── scan d
           │         │    │         │    │    │    ├── columns: d1:31 d2:32 d.rowid:35!null
           │         │    │         │    │    │    ├── key: (35)
           │         │    │         │    │    │    └── fd: (35)-->(31,32)
           │         │    │         │    │    └── filters
           │         │    │         │    │         └── c1:24 = d2:32 [outer=(24,32), constraints=(/24: (/NULL - ]; /32: (/NULL - ]), fd=(24)==(32), (32)==(24)]
           │         │    │         │    └── inner-join (hash)
           │         │    │         │         ├── columns: c1:38 c2:39!null c.rowid:42!null d1:45!null d2:46 d.rowid:49!null
           │         │    │         │         ├── key: (42,49)
           │         │    │         │         ├── fd: (42)-->(38,39), (49)-->(45,46), (39)==(45), (45)==(39)
           │         │    │         │         ├── limit hint: 1.20
           │         │    │         │         ├── scan c
           │         │    │         │         │    ├── columns: c1:38 c2:39 c.rowid:42!null
           │         │    │         │         │    ├── key: (42)
           │         │    │         │         │    └── fd: (42)-->(38,39)
           │         │    │         │         ├── scan d
           │         │    │         │         │    ├── columns: d1:45 d2:46 d.rowid:49!null
           │         │    │         │         │    ├── key: (49)
           │         │    │         │         │    └── fd: (49)-->(45,46)
           │         │    │         │         └── filters
           │         │    │         │              └── c2:39 = d1:45 [outer=(39,45), constraints=(/39: (/NULL - ]; /45: (/NULL - ]), fd=(39)==(45), (45)==(39)]
           │         │    │         └── aggregations
           │         │    │              ├── const-agg [as=c1:7, outer=(7)]
           │         │    │              │    └── c1:7
           │         │    │              ├── const-agg [as=c2:8, outer=(8)]
           │         │    │              │    └── c2:8
           │         │    │              ├── const-agg [as=d1:14, outer=(14)]
           │         │    │              │    └── d1:14
           │         │    │              └── const-agg [as=d2:15, outer=(15)]
           │         │    │                   └── d2:15
           │         │    └── 1
           │         └── projections
           │              └── true [as=column23:23]
           └── false

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a WHERE (a1, a2) IN (SELECT c1, d1 FROM c, d WHERE c3 = d3 or c3 = d4)
----
semi-join (hash)
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── key: (1-4)
 ├── scan a
 │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 │    └── key: (1-4)
 ├── project
 │    ├── columns: c1:7 c3:9!null d1:14 d3:16 d4:17
 │    └── distinct-on
 │         ├── columns: c1:7 c3:9!null c.rowid:11!null d1:14 d3:16 d4:17 d.rowid:18!null
 │         ├── grouping columns: c.rowid:11!null d.rowid:18!null
 │         ├── key: (11,18)
 │         ├── fd: (11,18)-->(7,9,14,16,17)
 │         ├── union-all
 │         │    ├── columns: c1:7 c3:9!null c.rowid:11!null d1:14 d3:16 d4:17 d.rowid:18!null
 │         │    ├── left columns: c1:23 c3:25 c.rowid:27 d1:30 d3:32 d4:33 d.rowid:34
 │         │    ├── right columns: c1:37 c3:39 c.rowid:41 d1:44 d3:46 d4:47 d.rowid:48
 │         │    ├── inner-join (hash)
 │         │    │    ├── columns: c1:23 c3:25!null c.rowid:27!null d1:30 d3:32!null d4:33 d.rowid:34!null
 │         │    │    ├── key: (27,34)
 │         │    │    ├── fd: (27)-->(23,25), (34)-->(30,32,33), (25)==(32), (32)==(25)
 │         │    │    ├── scan c
 │         │    │    │    ├── columns: c1:23 c3:25 c.rowid:27!null
 │         │    │    │    ├── key: (27)
 │         │    │    │    └── fd: (27)-->(23,25)
 │         │    │    ├── scan d
 │         │    │    │    ├── columns: d1:30 d3:32 d4:33 d.rowid:34!null
 │         │    │    │    ├── key: (34)
 │         │    │    │    └── fd: (34)-->(30,32,33)
 │         │    │    └── filters
 │         │    │         └── c3:25 = d3:32 [outer=(25,32), constraints=(/25: (/NULL - ]; /32: (/NULL - ]), fd=(25)==(32), (32)==(25)]
 │         │    └── inner-join (hash)
 │         │         ├── columns: c1:37 c3:39!null c.rowid:41!null d1:44 d3:46 d4:47!null d.rowid:48!null
 │         │         ├── key: (41,48)
 │         │         ├── fd: (41)-->(37,39), (48)-->(44,46,47), (39)==(47), (47)==(39)
 │         │         ├── scan c
 │         │         │    ├── columns: c1:37 c3:39 c.rowid:41!null
 │         │         │    ├── key: (41)
 │         │         │    └── fd: (41)-->(37,39)
 │         │         ├── scan d
 │         │         │    ├── columns: d1:44 d3:46 d4:47 d.rowid:48!null
 │         │         │    ├── key: (48)
 │         │         │    └── fd: (48)-->(44,46,47)
 │         │         └── filters
 │         │              └── c3:39 = d4:47 [outer=(39,47), constraints=(/39: (/NULL - ]; /47: (/NULL - ]), fd=(39)==(47), (47)==(39)]
 │         └── aggregations
 │              ├── const-agg [as=c1:7, outer=(7)]
 │              │    └── c1:7
 │              ├── const-agg [as=c3:9, outer=(9)]
 │              │    └── c3:9
 │              ├── const-agg [as=d1:14, outer=(14)]
 │              │    └── d1:14
 │              ├── const-agg [as=d3:16, outer=(16)]
 │              │    └── d3:16
 │              └── const-agg [as=d4:17, outer=(17)]
 │                   └── d4:17
 └── filters
      ├── c1:7 = a1:1 [outer=(1,7), constraints=(/1: (/NULL - ]; /7: (/NULL - ]), fd=(1)==(7), (7)==(1)]
      └── d1:14 = a2:2 [outer=(2,14), constraints=(/2: (/NULL - ]; /14: (/NULL - ]), fd=(2)==(14), (14)==(2)]

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a WHERE (a1, a2) IN (SELECT c1, d1 FROM c, d WHERE c3 = d3 or c2 = d2 EXCEPT ALL
                                   SELECT c1, d1 FROM c, d WHERE c3 = d3 or c2 = d2)
----
semi-join (hash)
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── key: (1-4)
 ├── scan a
 │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 │    └── key: (1-4)
 ├── except-all
 │    ├── columns: c1:7 d1:14
 │    ├── left columns: c1:7 d1:14
 │    ├── right columns: c1:21 d1:28
 │    ├── project
 │    │    ├── columns: c1:7 d1:14
 │    │    └── project
 │    │         ├── columns: c1:7 c2:8 c3:9 d1:14 d2:15 d3:16
 │    │         └── distinct-on
 │    │              ├── columns: c1:7 c2:8 c3:9 c.rowid:11!null d1:14 d2:15 d3:16 d.rowid:18!null
 │    │              ├── grouping columns: c.rowid:11!null d.rowid:18!null
 │    │              ├── key: (11,18)
 │    │              ├── fd: (11,18)-->(7-9,14-16)
 │    │              ├── union-all
 │    │              │    ├── columns: c1:7 c2:8 c3:9 c.rowid:11!null d1:14 d2:15 d3:16 d.rowid:18!null
 │    │              │    ├── left columns: c1:37 c2:38 c3:39 c.rowid:41 d1:44 d2:45 d3:46 d.rowid:48
 │    │              │    ├── right columns: c1:51 c2:52 c3:53 c.rowid:55 d1:58 d2:59 d3:60 d.rowid:62
 │    │              │    ├── inner-join (hash)
 │    │              │    │    ├── columns: c1:37 c2:38 c3:39!null c.rowid:41!null d1:44 d2:45 d3:46!null d.rowid:48!null
 │    │              │    │    ├── key: (41,48)
 │    │              │    │    ├── fd: (41)-->(37-39), (48)-->(44-46), (39)==(46), (46)==(39)
 │    │              │    │    ├── scan c
 │    │              │    │    │    ├── columns: c1:37 c2:38 c3:39 c.rowid:41!null
 │    │              │    │    │    ├── key: (41)
 │    │              │    │    │    └── fd: (41)-->(37-39)
 │    │              │    │    ├── scan d
 │    │              │    │    │    ├── columns: d1:44 d2:45 d3:46 d.rowid:48!null
 │    │              │    │    │    ├── key: (48)
 │    │              │    │    │    └── fd: (48)-->(44-46)
 │    │              │    │    └── filters
 │    │              │    │         └── c3:39 = d3:46 [outer=(39,46), constraints=(/39: (/NULL - ]; /46: (/NULL - ]), fd=(39)==(46), (46)==(39)]
 │    │              │    └── inner-join (hash)
 │    │              │         ├── columns: c1:51 c2:52!null c3:53 c.rowid:55!null d1:58 d2:59!null d3:60 d.rowid:62!null
 │    │              │         ├── key: (55,62)
 │    │              │         ├── fd: (55)-->(51-53), (62)-->(58-60), (52)==(59), (59)==(52)
 │    │              │         ├── scan c
 │    │              │         │    ├── columns: c1:51 c2:52 c3:53 c.rowid:55!null
 │    │              │         │    ├── key: (55)
 │    │              │         │    └── fd: (55)-->(51-53)
 │    │              │         ├── scan d
 │    │              │         │    ├── columns: d1:58 d2:59 d3:60 d.rowid:62!null
 │    │              │         │    ├── key: (62)
 │    │              │         │    └── fd: (62)-->(58-60)
 │    │              │         └── filters
 │    │              │              └── c2:52 = d2:59 [outer=(52,59), constraints=(/52: (/NULL - ]; /59: (/NULL - ]), fd=(52)==(59), (59)==(52)]
 │    │              └── aggregations
 │    │                   ├── const-agg [as=c1:7, outer=(7)]
 │    │                   │    └── c1:7
 │    │                   ├── const-agg [as=c2:8, outer=(8)]
 │    │                   │    └── c2:8
 │    │                   ├── const-agg [as=c3:9, outer=(9)]
 │    │                   │    └── c3:9
 │    │                   ├── const-agg [as=d1:14, outer=(14)]
 │    │                   │    └── d1:14
 │    │                   ├── const-agg [as=d2:15, outer=(15)]
 │    │                   │    └── d2:15
 │    │                   └── const-agg [as=d3:16, outer=(16)]
 │    │                        └── d3:16
 │    └── project
 │         ├── columns: c1:21 d1:28
 │         └── project
 │              ├── columns: c1:21 c2:22 c3:23 d1:28 d2:29 d3:30
 │              └── distinct-on
 │                   ├── columns: c1:21 c2:22 c3:23 c.rowid:25!null d1:28 d2:29 d3:30 d.rowid:32!null
 │                   ├── grouping columns: c.rowid:25!null d.rowid:32!null
 │                   ├── key: (25,32)
 │                   ├── fd: (25,32)-->(21-23,28-30)
 │                   ├── union-all
 │                   │    ├── columns: c1:21 c2:22 c3:23 c.rowid:25!null d1:28 d2:29 d3:30 d.rowid:32!null
 │                   │    ├── left columns: c1:93 c2:94 c3:95 c.rowid:97 d1:100 d2:101 d3:102 d.rowid:104
 │                   │    ├── right columns: c1:107 c2:108 c3:109 c.rowid:111 d1:114 d2:115 d3:116 d.rowid:118
 │                   │    ├── inner-join (hash)
 │                   │    │    ├── columns: c1:93 c2:94 c3:95!null c.rowid:97!null d1:100 d2:101 d3:102!null d.rowid:104!null
 │                   │    │    ├── key: (97,104)
 │                   │    │    ├── fd: (97)-->(93-95), (104)-->(100-102), (95)==(102), (102)==(95)
 │                   │    │    ├── scan c
 │                   │    │    │    ├── columns: c1:93 c2:94 c3:95 c.rowid:97!null
 │                   │    │    │    ├── key: (97)
 │                   │    │    │    └── fd: (97)-->(93-95)
 │                   │    │    ├── scan d
 │                   │    │    │    ├── columns: d1:100 d2:101 d3:102 d.rowid:104!null
 │                   │    │    │    ├── key: (104)
 │                   │    │    │    └── fd: (104)-->(100-102)
 │                   │    │    └── filters
 │                   │    │         └── c3:95 = d3:102 [outer=(95,102), constraints=(/95: (/NULL - ]; /102: (/NULL - ]), fd=(95)==(102), (102)==(95)]
 │                   │    └── inner-join (hash)
 │                   │         ├── columns: c1:107 c2:108!null c3:109 c.rowid:111!null d1:114 d2:115!null d3:116 d.rowid:118!null
 │                   │         ├── key: (111,118)
 │                   │         ├── fd: (111)-->(107-109), (118)-->(114-116), (108)==(115), (115)==(108)
 │                   │         ├── scan c
 │                   │         │    ├── columns: c1:107 c2:108 c3:109 c.rowid:111!null
 │                   │         │    ├── key: (111)
 │                   │         │    └── fd: (111)-->(107-109)
 │                   │         ├── scan d
 │                   │         │    ├── columns: d1:114 d2:115 d3:116 d.rowid:118!null
 │                   │         │    ├── key: (118)
 │                   │         │    └── fd: (118)-->(114-116)
 │                   │         └── filters
 │                   │              └── c2:108 = d2:115 [outer=(108,115), constraints=(/108: (/NULL - ]; /115: (/NULL - ]), fd=(108)==(115), (115)==(108)]
 │                   └── aggregations
 │                        ├── const-agg [as=c1:21, outer=(21)]
 │                        │    └── c1:21
 │                        ├── const-agg [as=c2:22, outer=(22)]
 │                        │    └── c2:22
 │                        ├── const-agg [as=c3:23, outer=(23)]
 │                        │    └── c3:23
 │                        ├── const-agg [as=d1:28, outer=(28)]
 │                        │    └── d1:28
 │                        ├── const-agg [as=d2:29, outer=(29)]
 │                        │    └── d2:29
 │                        └── const-agg [as=d3:30, outer=(30)]
 │                             └── d3:30
 └── filters
      ├── c1:7 = a1:1 [outer=(1,7), constraints=(/1: (/NULL - ]; /7: (/NULL - ]), fd=(1)==(7), (7)==(1)]
      └── d1:14 = a2:2 [outer=(2,14), constraints=(/2: (/NULL - ]; /14: (/NULL - ]), fd=(2)==(14), (14)==(2)]

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a WHERE (a1, a2) IN (SELECT c1, d1 FROM c, d WHERE c1 IS NULL OR c1 = d1)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── key: (1-4)
 └── inner-join (hash)
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null c1:7!null d1:14!null
      ├── multiplicity: left-rows(zero-or-more), right-rows(zero-or-one)
      ├── key: (3,4,7,14)
      ├── fd: (1)==(7), (7)==(1), (2)==(14), (14)==(2)
      ├── distinct-on
      │    ├── columns: c1:7 d1:14
      │    ├── grouping columns: c1:7 d1:14
      │    ├── key: (7,14)
      │    └── distinct-on
      │         ├── columns: c1:7 c.rowid:11!null d1:14 d.rowid:18!null
      │         ├── grouping columns: c.rowid:11!null d.rowid:18!null
      │         ├── key: (11,18)
      │         ├── fd: (11,18)-->(7,14)
      │         ├── union-all
      │         │    ├── columns: c1:7 c.rowid:11!null d1:14 d.rowid:18!null
      │         │    ├── left columns: c1:35 c.rowid:39 d1:42 d.rowid:46
      │         │    ├── right columns: c1:49 c.rowid:53 d1:56 d.rowid:60
      │         │    ├── inner-join (cross)
      │         │    │    ├── columns: c1:35 c.rowid:39!null d1:42 d.rowid:46!null
      │         │    │    ├── key: (39,46)
      │         │    │    ├── fd: ()-->(35), (46)-->(42)
      │         │    │    ├── scan d
      │         │    │    │    ├── columns: d1:42 d.rowid:46!null
      │         │    │    │    ├── key: (46)
      │         │    │    │    └── fd: (46)-->(42)
      │         │    │    ├── select
      │         │    │    │    ├── columns: c1:35 c.rowid:39!null
      │         │    │    │    ├── key: (39)
      │         │    │    │    ├── fd: ()-->(35)
      │         │    │    │    ├── scan c
      │         │    │    │    │    ├── columns: c1:35 c.rowid:39!null
      │         │    │    │    │    ├── key: (39)
      │         │    │    │    │    └── fd: (39)-->(35)
      │         │    │    │    └── filters
      │         │    │    │         └── c1:35 IS NULL [outer=(35), constraints=(/35: [/NULL - /NULL]; tight), fd=()-->(35)]
      │         │    │    └── filters (true)
      │         │    └── inner-join (hash)
      │         │         ├── columns: c1:49!null c.rowid:53!null d1:56!null d.rowid:60!null
      │         │         ├── key: (53,60)
      │         │         ├── fd: (53)-->(49), (60)-->(56), (49)==(56), (56)==(49)
      │         │         ├── scan c
      │         │         │    ├── columns: c1:49 c.rowid:53!null
      │         │         │    ├── key: (53)
      │         │         │    └── fd: (53)-->(49)
      │         │         ├── scan d
      │         │         │    ├── columns: d1:56 d.rowid:60!null
      │         │         │    ├── key: (60)
      │         │         │    └── fd: (60)-->(56)
      │         │         └── filters
      │         │              └── c1:49 = d1:56 [outer=(49,56), constraints=(/49: (/NULL - ]; /56: (/NULL - ]), fd=(49)==(56), (56)==(49)]
      │         └── aggregations
      │              ├── const-agg [as=c1:7, outer=(7)]
      │              │    └── c1:7
      │              └── const-agg [as=d1:14, outer=(14)]
      │                   └── d1:14
      ├── select
      │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      │    ├── key: (1-4)
      │    ├── scan a
      │    │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      │    │    └── key: (1-4)
      │    └── filters
      │         └── (a1:1 IS NULL) OR (a1:1 = a2:2) [outer=(1,2)]
      └── filters
           ├── c1:7 = a1:1 [outer=(1,7), constraints=(/1: (/NULL - ]; /7: (/NULL - ]), fd=(1)==(7), (7)==(1)]
           └── d1:14 = a2:2 [outer=(2,14), constraints=(/2: (/NULL - ]; /14: (/NULL - ]), fd=(2)==(14), (14)==(2)]

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM b WHERE b1 NOT IN (SELECT c1 FROM c, d WHERE c1 IS NULL OR c1 = d1)
----
anti-join (cross)
 ├── columns: b1:1 b2:2 b3:3 b4:4
 ├── scan b
 │    └── columns: b1:1 b2:2 b3:3 b4:4
 ├── project
 │    ├── columns: c1:8 d1:15
 │    └── distinct-on
 │         ├── columns: c1:8 c.rowid:12!null d1:15 d.rowid:19!null
 │         ├── grouping columns: c.rowid:12!null d.rowid:19!null
 │         ├── key: (12,19)
 │         ├── fd: (12,19)-->(8,15)
 │         ├── union-all
 │         │    ├── columns: c1:8 c.rowid:12!null d1:15 d.rowid:19!null
 │         │    ├── left columns: c1:23 c.rowid:27 d1:30 d.rowid:34
 │         │    ├── right columns: c1:37 c.rowid:41 d1:44 d.rowid:48
 │         │    ├── inner-join (cross)
 │         │    │    ├── columns: c1:23 c.rowid:27!null d1:30 d.rowid:34!null
 │         │    │    ├── key: (27,34)
 │         │    │    ├── fd: ()-->(23), (34)-->(30)
 │         │    │    ├── scan d
 │         │    │    │    ├── columns: d1:30 d.rowid:34!null
 │         │    │    │    ├── key: (34)
 │         │    │    │    └── fd: (34)-->(30)
 │         │    │    ├── select
 │         │    │    │    ├── columns: c1:23 c.rowid:27!null
 │         │    │    │    ├── key: (27)
 │         │    │    │    ├── fd: ()-->(23)
 │         │    │    │    ├── scan c
 │         │    │    │    │    ├── columns: c1:23 c.rowid:27!null
 │         │    │    │    │    ├── key: (27)
 │         │    │    │    │    └── fd: (27)-->(23)
 │         │    │    │    └── filters
 │         │    │    │         └── c1:23 IS NULL [outer=(23), constraints=(/23: [/NULL - /NULL]; tight), fd=()-->(23)]
 │         │    │    └── filters (true)
 │         │    └── inner-join (hash)
 │         │         ├── columns: c1:37!null c.rowid:41!null d1:44!null d.rowid:48!null
 │         │         ├── key: (41,48)
 │         │         ├── fd: (41)-->(37), (48)-->(44), (37)==(44), (44)==(37)
 │         │         ├── scan c
 │         │         │    ├── columns: c1:37 c.rowid:41!null
 │         │         │    ├── key: (41)
 │         │         │    └── fd: (41)-->(37)
 │         │         ├── scan d
 │         │         │    ├── columns: d1:44 d.rowid:48!null
 │         │         │    ├── key: (48)
 │         │         │    └── fd: (48)-->(44)
 │         │         └── filters
 │         │              └── c1:37 = d1:44 [outer=(37,44), constraints=(/37: (/NULL - ]; /44: (/NULL - ]), fd=(37)==(44), (44)==(37)]
 │         └── aggregations
 │              ├── const-agg [as=c1:8, outer=(8)]
 │              │    └── c1:8
 │              └── const-agg [as=d1:15, outer=(15)]
 │                   └── d1:15
 └── filters
      └── (b1:1 = c1:8) IS NOT false [outer=(1,8)]

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM b WHERE (b1, b2) NOT IN (SELECT c1, c2 FROM c, d WHERE c1 IS NULL OR c1 = d1)
----
anti-join (cross)
 ├── columns: b1:1 b2:2 b3:3 b4:4
 ├── immutable
 ├── scan b
 │    └── columns: b1:1 b2:2 b3:3 b4:4
 ├── project
 │    ├── columns: column22:22
 │    ├── project
 │    │    ├── columns: c1:8 c2:9 d1:15
 │    │    └── distinct-on
 │    │         ├── columns: c1:8 c2:9 c.rowid:12!null d1:15 d.rowid:19!null
 │    │         ├── grouping columns: c.rowid:12!null d.rowid:19!null
 │    │         ├── key: (12,19)
 │    │         ├── fd: (12,19)-->(8,9,15)
 │    │         ├── union-all
 │    │         │    ├── columns: c1:8 c2:9 c.rowid:12!null d1:15 d.rowid:19!null
 │    │         │    ├── left columns: c1:24 c2:25 c.rowid:28 d1:31 d.rowid:35
 │    │         │    ├── right columns: c1:38 c2:39 c.rowid:42 d1:45 d.rowid:49
 │    │         │    ├── inner-join (cross)
 │    │         │    │    ├── columns: c1:24 c2:25 c.rowid:28!null d1:31 d.rowid:35!null
 │    │         │    │    ├── key: (28,35)
 │    │         │    │    ├── fd: ()-->(24), (28)-->(25), (35)-->(31)
 │    │         │    │    ├── scan d
 │    │         │    │    │    ├── columns: d1:31 d.rowid:35!null
 │    │         │    │    │    ├── key: (35)
 │    │         │    │    │    └── fd: (35)-->(31)
 │    │         │    │    ├── select
 │    │         │    │    │    ├── columns: c1:24 c2:25 c.rowid:28!null
 │    │         │    │    │    ├── key: (28)
 │    │         │    │    │    ├── fd: ()-->(24), (28)-->(25)
 │    │         │    │    │    ├── scan c
 │    │         │    │    │    │    ├── columns: c1:24 c2:25 c.rowid:28!null
 │    │         │    │    │    │    ├── key: (28)
 │    │         │    │    │    │    └── fd: (28)-->(24,25)
 │    │         │    │    │    └── filters
 │    │         │    │    │         └── c1:24 IS NULL [outer=(24), constraints=(/24: [/NULL - /NULL]; tight), fd=()-->(24)]
 │    │         │    │    └── filters (true)
 │    │         │    └── inner-join (hash)
 │    │         │         ├── columns: c1:38!null c2:39 c.rowid:42!null d1:45!null d.rowid:49!null
 │    │         │         ├── key: (42,49)
 │    │         │         ├── fd: (42)-->(38,39), (49)-->(45), (38)==(45), (45)==(38)
 │    │         │         ├── scan c
 │    │         │         │    ├── columns: c1:38 c2:39 c.rowid:42!null
 │    │         │         │    ├── key: (42)
 │    │         │         │    └── fd: (42)-->(38,39)
 │    │         │         ├── scan d
 │    │         │         │    ├── columns: d1:45 d.rowid:49!null
 │    │         │         │    ├── key: (49)
 │    │         │         │    └── fd: (49)-->(45)
 │    │         │         └── filters
 │    │         │              └── c1:38 = d1:45 [outer=(38,45), constraints=(/38: (/NULL - ]; /45: (/NULL - ]), fd=(38)==(45), (45)==(38)]
 │    │         └── aggregations
 │    │              ├── const-agg [as=c1:8, outer=(8)]
 │    │              │    └── c1:8
 │    │              ├── const-agg [as=c2:9, outer=(9)]
 │    │              │    └── c2:9
 │    │              └── const-agg [as=d1:15, outer=(15)]
 │    │                   └── d1:15
 │    └── projections
 │         └── (c1:8, c2:9) [as=column22:22, outer=(8,9)]
 └── filters
      └── (column22:22 = (b1:1, b2:2)) IS NOT false [outer=(1,2,22), immutable]

#########################
# Uncorrelated antijoin #
#########################

opt expect-not=SplitDisjunctionOfJoinTerms
SELECT * FROM a WHERE NOT EXISTS (SELECT 1 FROM b WHERE b1 = 4 OR b2 = 50)
----
select
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── key: (1-4)
 ├── scan a
 │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 │    └── key: (1-4)
 └── filters
      └── not [subquery]
           └── coalesce
                ├── subquery
                │    └── project
                │         ├── columns: column16:16!null
                │         ├── cardinality: [0 - 1]
                │         ├── key: ()
                │         ├── fd: ()-->(16)
                │         ├── limit
                │         │    ├── columns: b1:7 b2:8
                │         │    ├── cardinality: [0 - 1]
                │         │    ├── key: ()
                │         │    ├── fd: ()-->(7,8)
                │         │    ├── project
                │         │    │    ├── columns: b1:7 b2:8
                │         │    │    ├── limit hint: 1.00
                │         │    │    └── distinct-on
                │         │    │         ├── columns: b1:7 b2:8 rowid:11!null
                │         │    │         ├── grouping columns: rowid:11!null
                │         │    │         ├── key: (11)
                │         │    │         ├── fd: (11)-->(7,8)
                │         │    │         ├── limit hint: 1.00
                │         │    │         ├── union-all
                │         │    │         │    ├── columns: b1:7 b2:8 rowid:11!null
                │         │    │         │    ├── left columns: b1:17 b2:18 rowid:21
                │         │    │         │    ├── right columns: b1:24 b2:25 rowid:28
                │         │    │         │    ├── limit hint: 1.21
                │         │    │         │    ├── scan b@b_b1_b2_idx
                │         │    │         │    │    ├── columns: b1:17!null b2:18 rowid:21!null
                │         │    │         │    │    ├── constraint: /17/18/21: [/4 - /4]
                │         │    │         │    │    ├── key: (21)
                │         │    │         │    │    ├── fd: ()-->(17), (21)-->(18)
                │         │    │         │    │    └── limit hint: 1.21
                │         │    │         │    └── scan b@b_b2_idx
                │         │    │         │         ├── columns: b1:24 b2:25!null rowid:28!null
                │         │    │         │         ├── constraint: /25/28: [/50 - /50]
                │         │    │         │         ├── key: (28)
                │         │    │         │         ├── fd: ()-->(25), (28)-->(24)
                │         │    │         │         └── limit hint: 1.21
                │         │    │         └── aggregations
                │         │    │              ├── const-agg [as=b1:7, outer=(7)]
                │         │    │              │    └── b1:7
                │         │    │              └── const-agg [as=b2:8, outer=(8)]
                │         │    │                   └── b2:8
                │         │    └── 1
                │         └── projections
                │              └── true [as=column16:16]
                └── false

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a WHERE NOT EXISTS (SELECT 1 FROM c, d WHERE d1 = 4 OR c2 = 50 or d2+c3 > 5)
----
select
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── immutable
 ├── key: (1-4)
 ├── scan a
 │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 │    └── key: (1-4)
 └── filters
      └── not [immutable, subquery]
           └── coalesce
                ├── subquery
                │    └── project
                │         ├── columns: column23:23!null
                │         ├── cardinality: [0 - 1]
                │         ├── immutable
                │         ├── key: ()
                │         ├── fd: ()-->(23)
                │         ├── limit
                │         │    ├── columns: c2:8 c3:9 d1:14 d2:15
                │         │    ├── cardinality: [0 - 1]
                │         │    ├── immutable
                │         │    ├── key: ()
                │         │    ├── fd: ()-->(8,9,14,15)
                │         │    ├── inner-join (cross)
                │         │    │    ├── columns: c2:8 c3:9 d1:14 d2:15
                │         │    │    ├── immutable
                │         │    │    ├── limit hint: 1.00
                │         │    │    ├── scan c
                │         │    │    │    └── columns: c2:8 c3:9
                │         │    │    ├── scan d
                │         │    │    │    └── columns: d1:14 d2:15
                │         │    │    └── filters
                │         │    │         └── ((d1:14 = 4) OR (c2:8 = 50)) OR ((d2:15 + c3:9) > 5) [outer=(8,9,14,15), immutable]
                │         │    └── 1
                │         └── projections
                │              └── true [as=column23:23]
                └── false

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a WHERE NOT EXISTS (SELECT 1 FROM c, d WHERE c3 = d4 or c4 = d3)
----
select
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── key: (1-4)
 ├── scan a
 │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 │    └── key: (1-4)
 └── filters
      └── not [subquery]
           └── coalesce
                ├── subquery
                │    └── project
                │         ├── columns: column23:23!null
                │         ├── cardinality: [0 - 1]
                │         ├── key: ()
                │         ├── fd: ()-->(23)
                │         ├── limit
                │         │    ├── columns: c3:9 c4:10 d3:16 d4:17
                │         │    ├── cardinality: [0 - 1]
                │         │    ├── key: ()
                │         │    ├── fd: ()-->(9,10,16,17)
                │         │    ├── project
                │         │    │    ├── columns: c3:9 c4:10 d3:16 d4:17
                │         │    │    ├── limit hint: 1.00
                │         │    │    └── distinct-on
                │         │    │         ├── columns: c3:9 c4:10 c.rowid:11!null d3:16 d4:17 d.rowid:18!null
                │         │    │         ├── grouping columns: c.rowid:11!null d.rowid:18!null
                │         │    │         ├── key: (11,18)
                │         │    │         ├── fd: (11,18)-->(9,10,16,17)
                │         │    │         ├── limit hint: 1.00
                │         │    │         ├── union-all
                │         │    │         │    ├── columns: c3:9 c4:10 c.rowid:11!null d3:16 d4:17 d.rowid:18!null
                │         │    │         │    ├── left columns: c3:26 c4:27 c.rowid:28 d3:33 d4:34 d.rowid:35
                │         │    │         │    ├── right columns: c3:40 c4:41 c.rowid:42 d3:47 d4:48 d.rowid:49
                │         │    │         │    ├── limit hint: 1.20
                │         │    │         │    ├── inner-join (hash)
                │         │    │         │    │    ├── columns: c3:26!null c4:27 c.rowid:28!null d3:33 d4:34!null d.rowid:35!null
                │         │    │         │    │    ├── key: (28,35)
                │         │    │         │    │    ├── fd: (28)-->(26,27), (35)-->(33,34), (26)==(34), (34)==(26)
                │         │    │         │    │    ├── limit hint: 1.20
                │         │    │         │    │    ├── scan c
                │         │    │         │    │    │    ├── columns: c3:26 c4:27 c.rowid:28!null
                │         │    │         │    │    │    ├── key: (28)
                │         │    │         │    │    │    └── fd: (28)-->(26,27)
                │         │    │         │    │    ├── scan d
                │         │    │         │    │    │    ├── columns: d3:33 d4:34 d.rowid:35!null
                │         │    │         │    │    │    ├── key: (35)
                │         │    │         │    │    │    └── fd: (35)-->(33,34)
                │         │    │         │    │    └── filters
                │         │    │         │    │         └── c3:26 = d4:34 [outer=(26,34), constraints=(/26: (/NULL - ]; /34: (/NULL - ]), fd=(26)==(34), (34)==(26)]
                │         │    │         │    └── inner-join (hash)
                │         │    │         │         ├── columns: c3:40 c4:41!null c.rowid:42!null d3:47!null d4:48 d.rowid:49!null
                │         │    │         │         ├── key: (42,49)
                │         │    │         │         ├── fd: (42)-->(40,41), (49)-->(47,48), (41)==(47), (47)==(41)
                │         │    │         │         ├── limit hint: 1.20
                │         │    │         │         ├── scan c
                │         │    │         │         │    ├── columns: c3:40 c4:41 c.rowid:42!null
                │         │    │         │         │    ├── key: (42)
                │         │    │         │         │    └── fd: (42)-->(40,41)
                │         │    │         │         ├── scan d
                │         │    │         │         │    ├── columns: d3:47 d4:48 d.rowid:49!null
                │         │    │         │         │    ├── key: (49)
                │         │    │         │         │    └── fd: (49)-->(47,48)
                │         │    │         │         └── filters
                │         │    │         │              └── c4:41 = d3:47 [outer=(41,47), constraints=(/41: (/NULL - ]; /47: (/NULL - ]), fd=(41)==(47), (47)==(41)]
                │         │    │         └── aggregations
                │         │    │              ├── const-agg [as=c3:9, outer=(9)]
                │         │    │              │    └── c3:9
                │         │    │              ├── const-agg [as=c4:10, outer=(10)]
                │         │    │              │    └── c4:10
                │         │    │              ├── const-agg [as=d3:16, outer=(16)]
                │         │    │              │    └── d3:16
                │         │    │              └── const-agg [as=d4:17, outer=(17)]
                │         │    │                   └── d4:17
                │         │    └── 1
                │         └── projections
                │              └── true [as=column23:23]
                └── false

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a WHERE (a1, a2) NOT IN (SELECT c1, d1 FROM c, d WHERE c3 = d3 or c3 = d4)
----
anti-join (cross)
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── immutable
 ├── key: (1-4)
 ├── scan a
 │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 │    └── key: (1-4)
 ├── project
 │    ├── columns: column21:21
 │    ├── project
 │    │    ├── columns: c1:7 c3:9!null d1:14 d3:16 d4:17
 │    │    └── distinct-on
 │    │         ├── columns: c1:7 c3:9!null c.rowid:11!null d1:14 d3:16 d4:17 d.rowid:18!null
 │    │         ├── grouping columns: c.rowid:11!null d.rowid:18!null
 │    │         ├── key: (11,18)
 │    │         ├── fd: (11,18)-->(7,9,14,16,17)
 │    │         ├── union-all
 │    │         │    ├── columns: c1:7 c3:9!null c.rowid:11!null d1:14 d3:16 d4:17 d.rowid:18!null
 │    │         │    ├── left columns: c1:23 c3:25 c.rowid:27 d1:30 d3:32 d4:33 d.rowid:34
 │    │         │    ├── right columns: c1:37 c3:39 c.rowid:41 d1:44 d3:46 d4:47 d.rowid:48
 │    │         │    ├── inner-join (hash)
 │    │         │    │    ├── columns: c1:23 c3:25!null c.rowid:27!null d1:30 d3:32!null d4:33 d.rowid:34!null
 │    │         │    │    ├── key: (27,34)
 │    │         │    │    ├── fd: (27)-->(23,25), (34)-->(30,32,33), (25)==(32), (32)==(25)
 │    │         │    │    ├── scan c
 │    │         │    │    │    ├── columns: c1:23 c3:25 c.rowid:27!null
 │    │         │    │    │    ├── key: (27)
 │    │         │    │    │    └── fd: (27)-->(23,25)
 │    │         │    │    ├── scan d
 │    │         │    │    │    ├── columns: d1:30 d3:32 d4:33 d.rowid:34!null
 │    │         │    │    │    ├── key: (34)
 │    │         │    │    │    └── fd: (34)-->(30,32,33)
 │    │         │    │    └── filters
 │    │         │    │         └── c3:25 = d3:32 [outer=(25,32), constraints=(/25: (/NULL - ]; /32: (/NULL - ]), fd=(25)==(32), (32)==(25)]
 │    │         │    └── inner-join (hash)
 │    │         │         ├── columns: c1:37 c3:39!null c.rowid:41!null d1:44 d3:46 d4:47!null d.rowid:48!null
 │    │         │         ├── key: (41,48)
 │    │         │         ├── fd: (41)-->(37,39), (48)-->(44,46,47), (39)==(47), (47)==(39)
 │    │         │         ├── scan c
 │    │         │         │    ├── columns: c1:37 c3:39 c.rowid:41!null
 │    │         │         │    ├── key: (41)
 │    │         │         │    └── fd: (41)-->(37,39)
 │    │         │         ├── scan d
 │    │         │         │    ├── columns: d1:44 d3:46 d4:47 d.rowid:48!null
 │    │         │         │    ├── key: (48)
 │    │         │         │    └── fd: (48)-->(44,46,47)
 │    │         │         └── filters
 │    │         │              └── c3:39 = d4:47 [outer=(39,47), constraints=(/39: (/NULL - ]; /47: (/NULL - ]), fd=(39)==(47), (47)==(39)]
 │    │         └── aggregations
 │    │              ├── const-agg [as=c1:7, outer=(7)]
 │    │              │    └── c1:7
 │    │              ├── const-agg [as=c3:9, outer=(9)]
 │    │              │    └── c3:9
 │    │              ├── const-agg [as=d1:14, outer=(14)]
 │    │              │    └── d1:14
 │    │              ├── const-agg [as=d3:16, outer=(16)]
 │    │              │    └── d3:16
 │    │              └── const-agg [as=d4:17, outer=(17)]
 │    │                   └── d4:17
 │    └── projections
 │         └── (c1:7, d1:14) [as=column21:21, outer=(7,14)]
 └── filters
      └── (column21:21 = (a1:1, a2:2)) IS NOT false [outer=(1,2,21), immutable]

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a WHERE (a1, a2) NOT IN (SELECT c1, d1 FROM c, d WHERE c3 = d3 or c2 = d2 EXCEPT ALL
                                       SELECT c1, d1 FROM c, d WHERE c3 = d3 or c2 = d2)
----
anti-join (cross)
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── immutable
 ├── key: (1-4)
 ├── scan a
 │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 │    └── key: (1-4)
 ├── project
 │    ├── columns: column35:35
 │    ├── except-all
 │    │    ├── columns: c1:7 d1:14
 │    │    ├── left columns: c1:7 d1:14
 │    │    ├── right columns: c1:21 d1:28
 │    │    ├── project
 │    │    │    ├── columns: c1:7 d1:14
 │    │    │    └── project
 │    │    │         ├── columns: c1:7 c2:8 c3:9 d1:14 d2:15 d3:16
 │    │    │         └── distinct-on
 │    │    │              ├── columns: c1:7 c2:8 c3:9 c.rowid:11!null d1:14 d2:15 d3:16 d.rowid:18!null
 │    │    │              ├── grouping columns: c.rowid:11!null d.rowid:18!null
 │    │    │              ├── key: (11,18)
 │    │    │              ├── fd: (11,18)-->(7-9,14-16)
 │    │    │              ├── union-all
 │    │    │              │    ├── columns: c1:7 c2:8 c3:9 c.rowid:11!null d1:14 d2:15 d3:16 d.rowid:18!null
 │    │    │              │    ├── left columns: c1:37 c2:38 c3:39 c.rowid:41 d1:44 d2:45 d3:46 d.rowid:48
 │    │    │              │    ├── right columns: c1:51 c2:52 c3:53 c.rowid:55 d1:58 d2:59 d3:60 d.rowid:62
 │    │    │              │    ├── inner-join (hash)
 │    │    │              │    │    ├── columns: c1:37 c2:38 c3:39!null c.rowid:41!null d1:44 d2:45 d3:46!null d.rowid:48!null
 │    │    │              │    │    ├── key: (41,48)
 │    │    │              │    │    ├── fd: (41)-->(37-39), (48)-->(44-46), (39)==(46), (46)==(39)
 │    │    │              │    │    ├── scan c
 │    │    │              │    │    │    ├── columns: c1:37 c2:38 c3:39 c.rowid:41!null
 │    │    │              │    │    │    ├── key: (41)
 │    │    │              │    │    │    └── fd: (41)-->(37-39)
 │    │    │              │    │    ├── scan d
 │    │    │              │    │    │    ├── columns: d1:44 d2:45 d3:46 d.rowid:48!null
 │    │    │              │    │    │    ├── key: (48)
 │    │    │              │    │    │    └── fd: (48)-->(44-46)
 │    │    │              │    │    └── filters
 │    │    │              │    │         └── c3:39 = d3:46 [outer=(39,46), constraints=(/39: (/NULL - ]; /46: (/NULL - ]), fd=(39)==(46), (46)==(39)]
 │    │    │              │    └── inner-join (hash)
 │    │    │              │         ├── columns: c1:51 c2:52!null c3:53 c.rowid:55!null d1:58 d2:59!null d3:60 d.rowid:62!null
 │    │    │              │         ├── key: (55,62)
 │    │    │              │         ├── fd: (55)-->(51-53), (62)-->(58-60), (52)==(59), (59)==(52)
 │    │    │              │         ├── scan c
 │    │    │              │         │    ├── columns: c1:51 c2:52 c3:53 c.rowid:55!null
 │    │    │              │         │    ├── key: (55)
 │    │    │              │         │    └── fd: (55)-->(51-53)
 │    │    │              │         ├── scan d
 │    │    │              │         │    ├── columns: d1:58 d2:59 d3:60 d.rowid:62!null
 │    │    │              │         │    ├── key: (62)
 │    │    │              │         │    └── fd: (62)-->(58-60)
 │    │    │              │         └── filters
 │    │    │              │              └── c2:52 = d2:59 [outer=(52,59), constraints=(/52: (/NULL - ]; /59: (/NULL - ]), fd=(52)==(59), (59)==(52)]
 │    │    │              └── aggregations
 │    │    │                   ├── const-agg [as=c1:7, outer=(7)]
 │    │    │                   │    └── c1:7
 │    │    │                   ├── const-agg [as=c2:8, outer=(8)]
 │    │    │                   │    └── c2:8
 │    │    │                   ├── const-agg [as=c3:9, outer=(9)]
 │    │    │                   │    └── c3:9
 │    │    │                   ├── const-agg [as=d1:14, outer=(14)]
 │    │    │                   │    └── d1:14
 │    │    │                   ├── const-agg [as=d2:15, outer=(15)]
 │    │    │                   │    └── d2:15
 │    │    │                   └── const-agg [as=d3:16, outer=(16)]
 │    │    │                        └── d3:16
 │    │    └── project
 │    │         ├── columns: c1:21 d1:28
 │    │         └── project
 │    │              ├── columns: c1:21 c2:22 c3:23 d1:28 d2:29 d3:30
 │    │              └── distinct-on
 │    │                   ├── columns: c1:21 c2:22 c3:23 c.rowid:25!null d1:28 d2:29 d3:30 d.rowid:32!null
 │    │                   ├── grouping columns: c.rowid:25!null d.rowid:32!null
 │    │                   ├── key: (25,32)
 │    │                   ├── fd: (25,32)-->(21-23,28-30)
 │    │                   ├── union-all
 │    │                   │    ├── columns: c1:21 c2:22 c3:23 c.rowid:25!null d1:28 d2:29 d3:30 d.rowid:32!null
 │    │                   │    ├── left columns: c1:93 c2:94 c3:95 c.rowid:97 d1:100 d2:101 d3:102 d.rowid:104
 │    │                   │    ├── right columns: c1:107 c2:108 c3:109 c.rowid:111 d1:114 d2:115 d3:116 d.rowid:118
 │    │                   │    ├── inner-join (hash)
 │    │                   │    │    ├── columns: c1:93 c2:94 c3:95!null c.rowid:97!null d1:100 d2:101 d3:102!null d.rowid:104!null
 │    │                   │    │    ├── key: (97,104)
 │    │                   │    │    ├── fd: (97)-->(93-95), (104)-->(100-102), (95)==(102), (102)==(95)
 │    │                   │    │    ├── scan c
 │    │                   │    │    │    ├── columns: c1:93 c2:94 c3:95 c.rowid:97!null
 │    │                   │    │    │    ├── key: (97)
 │    │                   │    │    │    └── fd: (97)-->(93-95)
 │    │                   │    │    ├── scan d
 │    │                   │    │    │    ├── columns: d1:100 d2:101 d3:102 d.rowid:104!null
 │    │                   │    │    │    ├── key: (104)
 │    │                   │    │    │    └── fd: (104)-->(100-102)
 │    │                   │    │    └── filters
 │    │                   │    │         └── c3:95 = d3:102 [outer=(95,102), constraints=(/95: (/NULL - ]; /102: (/NULL - ]), fd=(95)==(102), (102)==(95)]
 │    │                   │    └── inner-join (hash)
 │    │                   │         ├── columns: c1:107 c2:108!null c3:109 c.rowid:111!null d1:114 d2:115!null d3:116 d.rowid:118!null
 │    │                   │         ├── key: (111,118)
 │    │                   │         ├── fd: (111)-->(107-109), (118)-->(114-116), (108)==(115), (115)==(108)
 │    │                   │         ├── scan c
 │    │                   │         │    ├── columns: c1:107 c2:108 c3:109 c.rowid:111!null
 │    │                   │         │    ├── key: (111)
 │    │                   │         │    └── fd: (111)-->(107-109)
 │    │                   │         ├── scan d
 │    │                   │         │    ├── columns: d1:114 d2:115 d3:116 d.rowid:118!null
 │    │                   │         │    ├── key: (118)
 │    │                   │         │    └── fd: (118)-->(114-116)
 │    │                   │         └── filters
 │    │                   │              └── c2:108 = d2:115 [outer=(108,115), constraints=(/108: (/NULL - ]; /115: (/NULL - ]), fd=(108)==(115), (115)==(108)]
 │    │                   └── aggregations
 │    │                        ├── const-agg [as=c1:21, outer=(21)]
 │    │                        │    └── c1:21
 │    │                        ├── const-agg [as=c2:22, outer=(22)]
 │    │                        │    └── c2:22
 │    │                        ├── const-agg [as=c3:23, outer=(23)]
 │    │                        │    └── c3:23
 │    │                        ├── const-agg [as=d1:28, outer=(28)]
 │    │                        │    └── d1:28
 │    │                        ├── const-agg [as=d2:29, outer=(29)]
 │    │                        │    └── d2:29
 │    │                        └── const-agg [as=d3:30, outer=(30)]
 │    │                             └── d3:30
 │    └── projections
 │         └── (c1:7, d1:14) [as=column35:35, outer=(7,14)]
 └── filters
      └── (column35:35 = (a1:1, a2:2)) IS NOT false [outer=(1,2,35), immutable]

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a WHERE (a1, a2) NOT IN (SELECT c1, d1 FROM c, d WHERE c1 IS NULL OR c1 = d1)
----
anti-join (cross)
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── immutable
 ├── key: (1-4)
 ├── scan a
 │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 │    └── key: (1-4)
 ├── project
 │    ├── columns: column21:21
 │    ├── project
 │    │    ├── columns: c1:7 d1:14
 │    │    └── distinct-on
 │    │         ├── columns: c1:7 c.rowid:11!null d1:14 d.rowid:18!null
 │    │         ├── grouping columns: c.rowid:11!null d.rowid:18!null
 │    │         ├── key: (11,18)
 │    │         ├── fd: (11,18)-->(7,14)
 │    │         ├── union-all
 │    │         │    ├── columns: c1:7 c.rowid:11!null d1:14 d.rowid:18!null
 │    │         │    ├── left columns: c1:23 c.rowid:27 d1:30 d.rowid:34
 │    │         │    ├── right columns: c1:37 c.rowid:41 d1:44 d.rowid:48
 │    │         │    ├── inner-join (cross)
 │    │         │    │    ├── columns: c1:23 c.rowid:27!null d1:30 d.rowid:34!null
 │    │         │    │    ├── key: (27,34)
 │    │         │    │    ├── fd: ()-->(23), (34)-->(30)
 │    │         │    │    ├── scan d
 │    │         │    │    │    ├── columns: d1:30 d.rowid:34!null
 │    │         │    │    │    ├── key: (34)
 │    │         │    │    │    └── fd: (34)-->(30)
 │    │         │    │    ├── select
 │    │         │    │    │    ├── columns: c1:23 c.rowid:27!null
 │    │         │    │    │    ├── key: (27)
 │    │         │    │    │    ├── fd: ()-->(23)
 │    │         │    │    │    ├── scan c
 │    │         │    │    │    │    ├── columns: c1:23 c.rowid:27!null
 │    │         │    │    │    │    ├── key: (27)
 │    │         │    │    │    │    └── fd: (27)-->(23)
 │    │         │    │    │    └── filters
 │    │         │    │    │         └── c1:23 IS NULL [outer=(23), constraints=(/23: [/NULL - /NULL]; tight), fd=()-->(23)]
 │    │         │    │    └── filters (true)
 │    │         │    └── inner-join (hash)
 │    │         │         ├── columns: c1:37!null c.rowid:41!null d1:44!null d.rowid:48!null
 │    │         │         ├── key: (41,48)
 │    │         │         ├── fd: (41)-->(37), (48)-->(44), (37)==(44), (44)==(37)
 │    │         │         ├── scan c
 │    │         │         │    ├── columns: c1:37 c.rowid:41!null
 │    │         │         │    ├── key: (41)
 │    │         │         │    └── fd: (41)-->(37)
 │    │         │         ├── scan d
 │    │         │         │    ├── columns: d1:44 d.rowid:48!null
 │    │         │         │    ├── key: (48)
 │    │         │         │    └── fd: (48)-->(44)
 │    │         │         └── filters
 │    │         │              └── c1:37 = d1:44 [outer=(37,44), constraints=(/37: (/NULL - ]; /44: (/NULL - ]), fd=(37)==(44), (44)==(37)]
 │    │         └── aggregations
 │    │              ├── const-agg [as=c1:7, outer=(7)]
 │    │              │    └── c1:7
 │    │              └── const-agg [as=d1:14, outer=(14)]
 │    │                   └── d1:14
 │    └── projections
 │         └── (c1:7, d1:14) [as=column21:21, outer=(7,14)]
 └── filters
      └── (column21:21 = (a1:1, a2:2)) IS NOT false [outer=(1,2,21), immutable]

#######################
# Correlated semijoin #
#######################

opt expect=SplitDisjunctionOfJoinTerms
SELECT a1,a2,a3 FROM a WHERE EXISTS (SELECT * FROM b WHERE a2 = b2 OR a3 = b3)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null
 └── distinct-on
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null
      ├── key: (1-4)
      └── union-all
           ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
           ├── left columns: a1:15 a2:16 a3:17 a4:18
           ├── right columns: a1:28 a2:29 a3:30 a4:31
           ├── project
           │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
           │    ├── key: (15-18)
           │    └── inner-join (hash)
           │         ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null b2:22!null
           │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
           │         ├── key: (15,17,18,22)
           │         ├── fd: (16)==(22), (22)==(16)
           │         ├── scan a
           │         │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
           │         │    └── key: (15-18)
           │         ├── distinct-on
           │         │    ├── columns: b2:22
           │         │    ├── grouping columns: b2:22
           │         │    ├── internal-ordering: +22
           │         │    ├── key: (22)
           │         │    └── scan b@b_b2_idx
           │         │         ├── columns: b2:22
           │         │         └── ordering: +22
           │         └── filters
           │              └── a2:16 = b2:22 [outer=(16,22), constraints=(/16: (/NULL - ]; /22: (/NULL - ]), fd=(16)==(22), (22)==(16)]
           └── project
                ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                ├── key: (28-31)
                └── inner-join (hash)
                     ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null b3:36!null
                     ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
                     ├── key: (28,29,31,36)
                     ├── fd: (30)==(36), (36)==(30)
                     ├── scan a
                     │    ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                     │    └── key: (28-31)
                     ├── distinct-on
                     │    ├── columns: b3:36
                     │    ├── grouping columns: b3:36
                     │    ├── internal-ordering: +36
                     │    ├── key: (36)
                     │    └── scan b@b_b3_idx
                     │         ├── columns: b3:36
                     │         └── ordering: +36
                     └── filters
                          └── a3:30 = b3:36 [outer=(30,36), constraints=(/30: (/NULL - ]; /36: (/NULL - ]), fd=(30)==(36), (36)==(30)]

opt expect=SplitDisjunctionOfJoinTerms
SELECT a1,a2,a3 FROM a WHERE EXISTS (SELECT * FROM b WHERE a1 = b1 OR a1 = b2)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null
 └── distinct-on
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null
      ├── key: (1-4)
      └── union-all
           ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
           ├── left columns: a1:15 a2:16 a3:17 a4:18
           ├── right columns: a1:28 a2:29 a3:30 a4:31
           ├── project
           │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
           │    ├── key: (15-18)
           │    └── inner-join (merge)
           │         ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null b1:21!null
           │         ├── left ordering: +15
           │         ├── right ordering: +21
           │         ├── key: (16-18,21)
           │         ├── fd: (15)==(21), (21)==(15)
           │         ├── scan a
           │         │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
           │         │    ├── key: (15-18)
           │         │    └── ordering: +15
           │         ├── distinct-on
           │         │    ├── columns: b1:21
           │         │    ├── grouping columns: b1:21
           │         │    ├── key: (21)
           │         │    ├── ordering: +21
           │         │    └── scan b@b_b1_b2_idx
           │         │         ├── columns: b1:21
           │         │         └── ordering: +21
           │         └── filters (true)
           └── project
                ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                ├── key: (28-31)
                └── inner-join (merge)
                     ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null b2:35!null
                     ├── left ordering: +28
                     ├── right ordering: +35
                     ├── key: (29-31,35)
                     ├── fd: (28)==(35), (35)==(28)
                     ├── scan a
                     │    ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                     │    ├── key: (28-31)
                     │    └── ordering: +28
                     ├── distinct-on
                     │    ├── columns: b2:35
                     │    ├── grouping columns: b2:35
                     │    ├── key: (35)
                     │    ├── ordering: +35
                     │    └── scan b@b_b2_idx
                     │         ├── columns: b2:35
                     │         └── ordering: +35
                     └── filters (true)

# The left side of the join already produces key columns
opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a WHERE EXISTS (SELECT * FROM b WHERE a1 = b1 OR a2 = b2 OR a3 = b3 OR a4 = b4)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── key: (1-4)
 └── distinct-on
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null
      ├── key: (1-4)
      └── union-all
           ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
           ├── left columns: a1:15 a2:16 a3:17 a4:18
           ├── right columns: a1:28 a2:29 a3:30 a4:31
           ├── project
           │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
           │    ├── key: (15-18)
           │    └── inner-join (merge)
           │         ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null b1:21!null
           │         ├── left ordering: +15
           │         ├── right ordering: +21
           │         ├── key: (16-18,21)
           │         ├── fd: (15)==(21), (21)==(15)
           │         ├── scan a
           │         │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
           │         │    ├── key: (15-18)
           │         │    └── ordering: +15
           │         ├── distinct-on
           │         │    ├── columns: b1:21
           │         │    ├── grouping columns: b1:21
           │         │    ├── key: (21)
           │         │    ├── ordering: +21
           │         │    └── scan b@b_b1_b2_idx
           │         │         ├── columns: b1:21
           │         │         └── ordering: +21
           │         └── filters (true)
           └── project
                ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                ├── key: (28-31)
                └── distinct-on
                     ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                     ├── grouping columns: a1:28!null a2:29!null a3:30!null a4:31!null
                     ├── key: (28-31)
                     └── union-all
                          ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                          ├── left columns: a1:275 a2:276 a3:277 a4:278
                          ├── right columns: a1:288 a2:289 a3:290 a4:291
                          ├── project
                          │    ├── columns: a1:275!null a2:276!null a3:277!null a4:278!null
                          │    ├── key: (275-278)
                          │    └── inner-join (hash)
                          │         ├── columns: a1:275!null a2:276!null a3:277!null a4:278!null b4:284!null
                          │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
                          │         ├── key: (275-277,284)
                          │         ├── fd: (278)==(284), (284)==(278)
                          │         ├── scan a
                          │         │    ├── columns: a1:275!null a2:276!null a3:277!null a4:278!null
                          │         │    └── key: (275-278)
                          │         ├── distinct-on
                          │         │    ├── columns: b4:284
                          │         │    ├── grouping columns: b4:284
                          │         │    ├── key: (284)
                          │         │    └── scan b
                          │         │         └── columns: b4:284
                          │         └── filters
                          │              └── a4:278 = b4:284 [outer=(278,284), constraints=(/278: (/NULL - ]; /284: (/NULL - ]), fd=(278)==(284), (284)==(278)]
                          └── project
                               ├── columns: a1:288!null a2:289!null a3:290!null a4:291!null
                               ├── key: (288-291)
                               └── distinct-on
                                    ├── columns: a1:288!null a2:289!null a3:290!null a4:291!null
                                    ├── grouping columns: a1:288!null a2:289!null a3:290!null a4:291!null
                                    ├── key: (288-291)
                                    └── union-all
                                         ├── columns: a1:288!null a2:289!null a3:290!null a4:291!null
                                         ├── left columns: a1:431 a2:432 a3:433 a4:434
                                         ├── right columns: a1:444 a2:445 a3:446 a4:447
                                         ├── project
                                         │    ├── columns: a1:431!null a2:432!null a3:433!null a4:434!null
                                         │    ├── key: (431-434)
                                         │    └── inner-join (hash)
                                         │         ├── columns: a1:431!null a2:432!null a3:433!null a4:434!null b2:438!null
                                         │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
                                         │         ├── key: (431,433,434,438)
                                         │         ├── fd: (432)==(438), (438)==(432)
                                         │         ├── scan a
                                         │         │    ├── columns: a1:431!null a2:432!null a3:433!null a4:434!null
                                         │         │    └── key: (431-434)
                                         │         ├── distinct-on
                                         │         │    ├── columns: b2:438
                                         │         │    ├── grouping columns: b2:438
                                         │         │    ├── internal-ordering: +438
                                         │         │    ├── key: (438)
                                         │         │    └── scan b@b_b2_idx
                                         │         │         ├── columns: b2:438
                                         │         │         └── ordering: +438
                                         │         └── filters
                                         │              └── a2:432 = b2:438 [outer=(432,438), constraints=(/432: (/NULL - ]; /438: (/NULL - ]), fd=(432)==(438), (438)==(432)]
                                         └── project
                                              ├── columns: a1:444!null a2:445!null a3:446!null a4:447!null
                                              ├── key: (444-447)
                                              └── inner-join (hash)
                                                   ├── columns: a1:444!null a2:445!null a3:446!null a4:447!null b3:452!null
                                                   ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
                                                   ├── key: (444,445,447,452)
                                                   ├── fd: (446)==(452), (452)==(446)
                                                   ├── scan a
                                                   │    ├── columns: a1:444!null a2:445!null a3:446!null a4:447!null
                                                   │    └── key: (444-447)
                                                   ├── distinct-on
                                                   │    ├── columns: b3:452
                                                   │    ├── grouping columns: b3:452
                                                   │    ├── internal-ordering: +452
                                                   │    ├── key: (452)
                                                   │    └── scan b@b_b3_idx
                                                   │         ├── columns: b3:452
                                                   │         └── ordering: +452
                                                   └── filters
                                                        └── a3:446 = b3:452 [outer=(446,452), constraints=(/446: (/NULL - ]; /452: (/NULL - ]), fd=(446)==(452), (452)==(446)]

# More than one disjunction in the filter
opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a WHERE EXISTS (SELECT * FROM c WHERE a1 = c1 OR a2 = c2 OR a3 = c3 OR a4 = c4)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── key: (1-4)
 └── distinct-on
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null
      ├── key: (1-4)
      └── union-all
           ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
           ├── left columns: a1:15 a2:16 a3:17 a4:18
           ├── right columns: a1:28 a2:29 a3:30 a4:31
           ├── project
           │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
           │    ├── key: (15-18)
           │    └── inner-join (hash)
           │         ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null c1:21!null
           │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
           │         ├── key: (16-18,21)
           │         ├── fd: (15)==(21), (21)==(15)
           │         ├── scan a
           │         │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
           │         │    └── key: (15-18)
           │         ├── distinct-on
           │         │    ├── columns: c1:21
           │         │    ├── grouping columns: c1:21
           │         │    ├── key: (21)
           │         │    └── scan c
           │         │         └── columns: c1:21
           │         └── filters
           │              └── a1:15 = c1:21 [outer=(15,21), constraints=(/15: (/NULL - ]; /21: (/NULL - ]), fd=(15)==(21), (21)==(15)]
           └── project
                ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                ├── key: (28-31)
                └── distinct-on
                     ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                     ├── grouping columns: a1:28!null a2:29!null a3:30!null a4:31!null
                     ├── key: (28-31)
                     └── union-all
                          ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                          ├── left columns: a1:275 a2:276 a3:277 a4:278
                          ├── right columns: a1:288 a2:289 a3:290 a4:291
                          ├── project
                          │    ├── columns: a1:275!null a2:276!null a3:277!null a4:278!null
                          │    ├── key: (275-278)
                          │    └── inner-join (hash)
                          │         ├── columns: a1:275!null a2:276!null a3:277!null a4:278!null c4:284!null
                          │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
                          │         ├── key: (275-277,284)
                          │         ├── fd: (278)==(284), (284)==(278)
                          │         ├── scan a
                          │         │    ├── columns: a1:275!null a2:276!null a3:277!null a4:278!null
                          │         │    └── key: (275-278)
                          │         ├── distinct-on
                          │         │    ├── columns: c4:284
                          │         │    ├── grouping columns: c4:284
                          │         │    ├── key: (284)
                          │         │    └── scan c
                          │         │         └── columns: c4:284
                          │         └── filters
                          │              └── a4:278 = c4:284 [outer=(278,284), constraints=(/278: (/NULL - ]; /284: (/NULL - ]), fd=(278)==(284), (284)==(278)]
                          └── project
                               ├── columns: a1:288!null a2:289!null a3:290!null a4:291!null
                               ├── key: (288-291)
                               └── distinct-on
                                    ├── columns: a1:288!null a2:289!null a3:290!null a4:291!null
                                    ├── grouping columns: a1:288!null a2:289!null a3:290!null a4:291!null
                                    ├── key: (288-291)
                                    └── union-all
                                         ├── columns: a1:288!null a2:289!null a3:290!null a4:291!null
                                         ├── left columns: a1:431 a2:432 a3:433 a4:434
                                         ├── right columns: a1:444 a2:445 a3:446 a4:447
                                         ├── project
                                         │    ├── columns: a1:431!null a2:432!null a3:433!null a4:434!null
                                         │    ├── key: (431-434)
                                         │    └── inner-join (hash)
                                         │         ├── columns: a1:431!null a2:432!null a3:433!null a4:434!null c2:438!null
                                         │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
                                         │         ├── key: (431,433,434,438)
                                         │         ├── fd: (432)==(438), (438)==(432)
                                         │         ├── scan a
                                         │         │    ├── columns: a1:431!null a2:432!null a3:433!null a4:434!null
                                         │         │    └── key: (431-434)
                                         │         ├── distinct-on
                                         │         │    ├── columns: c2:438
                                         │         │    ├── grouping columns: c2:438
                                         │         │    ├── key: (438)
                                         │         │    └── scan c
                                         │         │         └── columns: c2:438
                                         │         └── filters
                                         │              └── a2:432 = c2:438 [outer=(432,438), constraints=(/432: (/NULL - ]; /438: (/NULL - ]), fd=(432)==(438), (438)==(432)]
                                         └── project
                                              ├── columns: a1:444!null a2:445!null a3:446!null a4:447!null
                                              ├── key: (444-447)
                                              └── inner-join (hash)
                                                   ├── columns: a1:444!null a2:445!null a3:446!null a4:447!null c3:452!null
                                                   ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
                                                   ├── key: (444,445,447,452)
                                                   ├── fd: (446)==(452), (452)==(446)
                                                   ├── scan a
                                                   │    ├── columns: a1:444!null a2:445!null a3:446!null a4:447!null
                                                   │    └── key: (444-447)
                                                   ├── distinct-on
                                                   │    ├── columns: c3:452
                                                   │    ├── grouping columns: c3:452
                                                   │    ├── key: (452)
                                                   │    └── scan c
                                                   │         └── columns: c3:452
                                                   └── filters
                                                        └── a3:446 = c3:452 [outer=(446,452), constraints=(/446: (/NULL - ]; /452: (/NULL - ]), fd=(446)==(452), (452)==(446)]

# More than one disjunction in the filter
opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a WHERE EXISTS (SELECT * FROM c WHERE a1 = c2 OR a2 = c1 OR a3 = c4 OR a3 = c4)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── key: (1-4)
 └── distinct-on
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null
      ├── key: (1-4)
      └── union-all
           ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
           ├── left columns: a1:15 a2:16 a3:17 a4:18
           ├── right columns: a1:28 a2:29 a3:30 a4:31
           ├── project
           │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
           │    ├── key: (15-18)
           │    └── inner-join (hash)
           │         ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null c2:22!null
           │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
           │         ├── key: (16-18,22)
           │         ├── fd: (15)==(22), (22)==(15)
           │         ├── scan a
           │         │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
           │         │    └── key: (15-18)
           │         ├── distinct-on
           │         │    ├── columns: c2:22
           │         │    ├── grouping columns: c2:22
           │         │    ├── key: (22)
           │         │    └── scan c
           │         │         └── columns: c2:22
           │         └── filters
           │              └── a1:15 = c2:22 [outer=(15,22), constraints=(/15: (/NULL - ]; /22: (/NULL - ]), fd=(15)==(22), (22)==(15)]
           └── project
                ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                ├── key: (28-31)
                └── distinct-on
                     ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                     ├── grouping columns: a1:28!null a2:29!null a3:30!null a4:31!null
                     ├── key: (28-31)
                     └── union-all
                          ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                          ├── left columns: a1:275 a2:276 a3:277 a4:278
                          ├── right columns: a1:288 a2:289 a3:290 a4:291
                          ├── project
                          │    ├── columns: a1:275!null a2:276!null a3:277!null a4:278!null
                          │    ├── key: (275-278)
                          │    └── inner-join (hash)
                          │         ├── columns: a1:275!null a2:276!null a3:277!null a4:278!null c4:284!null
                          │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
                          │         ├── key: (275,276,278,284)
                          │         ├── fd: (277)==(284), (284)==(277)
                          │         ├── scan a
                          │         │    ├── columns: a1:275!null a2:276!null a3:277!null a4:278!null
                          │         │    └── key: (275-278)
                          │         ├── distinct-on
                          │         │    ├── columns: c4:284
                          │         │    ├── grouping columns: c4:284
                          │         │    ├── key: (284)
                          │         │    └── scan c
                          │         │         └── columns: c4:284
                          │         └── filters
                          │              └── a3:277 = c4:284 [outer=(277,284), constraints=(/277: (/NULL - ]; /284: (/NULL - ]), fd=(277)==(284), (284)==(277)]
                          └── project
                               ├── columns: a1:288!null a2:289!null a3:290!null a4:291!null
                               ├── key: (288-291)
                               └── distinct-on
                                    ├── columns: a1:288!null a2:289!null a3:290!null a4:291!null
                                    ├── grouping columns: a1:288!null a2:289!null a3:290!null a4:291!null
                                    ├── key: (288-291)
                                    └── union-all
                                         ├── columns: a1:288!null a2:289!null a3:290!null a4:291!null
                                         ├── left columns: a1:431 a2:432 a3:433 a4:434
                                         ├── right columns: a1:444 a2:445 a3:446 a4:447
                                         ├── project
                                         │    ├── columns: a1:431!null a2:432!null a3:433!null a4:434!null
                                         │    ├── key: (431-434)
                                         │    └── inner-join (hash)
                                         │         ├── columns: a1:431!null a2:432!null a3:433!null a4:434!null c1:437!null
                                         │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
                                         │         ├── key: (431,433,434,437)
                                         │         ├── fd: (432)==(437), (437)==(432)
                                         │         ├── scan a
                                         │         │    ├── columns: a1:431!null a2:432!null a3:433!null a4:434!null
                                         │         │    └── key: (431-434)
                                         │         ├── distinct-on
                                         │         │    ├── columns: c1:437
                                         │         │    ├── grouping columns: c1:437
                                         │         │    ├── key: (437)
                                         │         │    └── scan c
                                         │         │         └── columns: c1:437
                                         │         └── filters
                                         │              └── a2:432 = c1:437 [outer=(432,437), constraints=(/432: (/NULL - ]; /437: (/NULL - ]), fd=(432)==(437), (437)==(432)]
                                         └── project
                                              ├── columns: a1:444!null a2:445!null a3:446!null a4:447!null
                                              ├── key: (444-447)
                                              └── inner-join (hash)
                                                   ├── columns: a1:444!null a2:445!null a3:446!null a4:447!null c4:453!null
                                                   ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
                                                   ├── key: (444,445,447,453)
                                                   ├── fd: (446)==(453), (453)==(446)
                                                   ├── scan a
                                                   │    ├── columns: a1:444!null a2:445!null a3:446!null a4:447!null
                                                   │    └── key: (444-447)
                                                   ├── distinct-on
                                                   │    ├── columns: c4:453
                                                   │    ├── grouping columns: c4:453
                                                   │    ├── key: (453)
                                                   │    └── scan c
                                                   │         └── columns: c4:453
                                                   └── filters
                                                        └── a3:446 = c4:453 [outer=(446,453), constraints=(/446: (/NULL - ]; /453: (/NULL - ]), fd=(446)==(453), (453)==(446)]

# IN subquery
opt expect=SplitDisjunctionOfJoinTerms
SELECT a1+a3-a2 FROM a WHERE a1 IN (SELECT b1 FROM b WHERE EXISTS (SELECT 1 FROM c WHERE c2 IS NULL OR c2=b2 OR c2=b3))
----
project
 ├── columns: "?column?":24!null
 ├── immutable
 ├── project
 │    ├── columns: a1:1!null a2:2!null a3:3!null
 │    └── inner-join (hash)
 │         ├── columns: a1:1!null a2:2!null a3:3!null b1:7!null
 │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │         ├── fd: (1)==(7), (7)==(1)
 │         ├── scan a
 │         │    └── columns: a1:1!null a2:2!null a3:3!null
 │         ├── distinct-on
 │         │    ├── columns: b1:7
 │         │    ├── grouping columns: b1:7
 │         │    ├── key: (7)
 │         │    └── project
 │         │         ├── columns: b1:7 b2:8 b3:9
 │         │         └── distinct-on
 │         │              ├── columns: b1:7 b2:8 b3:9 b.rowid:11!null
 │         │              ├── grouping columns: b.rowid:11!null
 │         │              ├── key: (11)
 │         │              ├── fd: (11)-->(7-9)
 │         │              ├── union-all
 │         │              │    ├── columns: b1:7 b2:8 b3:9 b.rowid:11!null
 │         │              │    ├── left columns: b1:25 b2:26 b3:27 b.rowid:29
 │         │              │    ├── right columns: b1:39 b2:40 b3:41 b.rowid:43
 │         │              │    ├── project
 │         │              │    │    ├── columns: b1:25 b2:26 b3:27 b.rowid:29!null
 │         │              │    │    ├── key: (29)
 │         │              │    │    ├── fd: (29)-->(25-27)
 │         │              │    │    └── project
 │         │              │    │         ├── columns: b1:25 b2:26 b3:27 b.rowid:29!null
 │         │              │    │         ├── key: (29)
 │         │              │    │         ├── fd: (29)-->(25-27)
 │         │              │    │         └── inner-join (cross)
 │         │              │    │              ├── columns: b1:25 b2:26 b3:27 b.rowid:29!null c2:33
 │         │              │    │              ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │         │              │    │              ├── key: (29)
 │         │              │    │              ├── fd: ()-->(33), (29)-->(25-27)
 │         │              │    │              ├── scan b
 │         │              │    │              │    ├── columns: b1:25 b2:26 b3:27 b.rowid:29!null
 │         │              │    │              │    ├── key: (29)
 │         │              │    │              │    └── fd: (29)-->(25-27)
 │         │              │    │              ├── limit
 │         │              │    │              │    ├── columns: c2:33
 │         │              │    │              │    ├── cardinality: [0 - 1]
 │         │              │    │              │    ├── key: ()
 │         │              │    │              │    ├── fd: ()-->(33)
 │         │              │    │              │    ├── select
 │         │              │    │              │    │    ├── columns: c2:33
 │         │              │    │              │    │    ├── fd: ()-->(33)
 │         │              │    │              │    │    ├── limit hint: 1.00
 │         │              │    │              │    │    ├── scan c
 │         │              │    │              │    │    │    ├── columns: c2:33
 │         │              │    │              │    │    │    └── limit hint: 100.00
 │         │              │    │              │    │    └── filters
 │         │              │    │              │    │         └── c2:33 IS NULL [outer=(33), constraints=(/33: [/NULL - /NULL]; tight), fd=()-->(33)]
 │         │              │    │              │    └── 1
 │         │              │    │              └── filters (true)
 │         │              │    └── project
 │         │              │         ├── columns: b1:39 b2:40 b3:41 b.rowid:43!null
 │         │              │         ├── key: (43)
 │         │              │         ├── fd: (43)-->(39-41)
 │         │              │         └── distinct-on
 │         │              │              ├── columns: b1:39 b2:40 b3:41 b.rowid:43!null
 │         │              │              ├── grouping columns: b.rowid:43!null
 │         │              │              ├── key: (43)
 │         │              │              ├── fd: (43)-->(39-41)
 │         │              │              ├── union-all
 │         │              │              │    ├── columns: b1:39 b2:40 b3:41 b.rowid:43!null
 │         │              │              │    ├── left columns: b1:193 b2:194 b3:195 b.rowid:197
 │         │              │              │    ├── right columns: b1:207 b2:208 b3:209 b.rowid:211
 │         │              │              │    ├── project
 │         │              │              │    │    ├── columns: b1:193 b2:194 b3:195 b.rowid:197!null
 │         │              │              │    │    ├── key: (197)
 │         │              │              │    │    ├── fd: (197)-->(193-195)
 │         │              │              │    │    └── inner-join (hash)
 │         │              │              │    │         ├── columns: b1:193 b2:194 b3:195!null b.rowid:197!null c2:201!null
 │         │              │              │    │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │         │              │              │    │         ├── key: (197)
 │         │              │              │    │         ├── fd: (197)-->(193-195), (195)==(201), (201)==(195)
 │         │              │              │    │         ├── scan b
 │         │              │              │    │         │    ├── columns: b1:193 b2:194 b3:195 b.rowid:197!null
 │         │              │              │    │         │    ├── key: (197)
 │         │              │              │    │         │    └── fd: (197)-->(193-195)
 │         │              │              │    │         ├── distinct-on
 │         │              │              │    │         │    ├── columns: c2:201
 │         │              │              │    │         │    ├── grouping columns: c2:201
 │         │              │              │    │         │    ├── key: (201)
 │         │              │              │    │         │    └── scan c
 │         │              │              │    │         │         └── columns: c2:201
 │         │              │              │    │         └── filters
 │         │              │              │    │              └── c2:201 = b3:195 [outer=(195,201), constraints=(/195: (/NULL - ]; /201: (/NULL - ]), fd=(195)==(201), (201)==(195)]
 │         │              │              │    └── project
 │         │              │              │         ├── columns: b1:207 b2:208 b3:209 b.rowid:211!null
 │         │              │              │         ├── key: (211)
 │         │              │              │         ├── fd: (211)-->(207-209)
 │         │              │              │         └── inner-join (hash)
 │         │              │              │              ├── columns: b1:207 b2:208!null b3:209 b.rowid:211!null c2:215!null
 │         │              │              │              ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │         │              │              │              ├── key: (211)
 │         │              │              │              ├── fd: (211)-->(207-209), (208)==(215), (215)==(208)
 │         │              │              │              ├── scan b
 │         │              │              │              │    ├── columns: b1:207 b2:208 b3:209 b.rowid:211!null
 │         │              │              │              │    ├── key: (211)
 │         │              │              │              │    └── fd: (211)-->(207-209)
 │         │              │              │              ├── distinct-on
 │         │              │              │              │    ├── columns: c2:215
 │         │              │              │              │    ├── grouping columns: c2:215
 │         │              │              │              │    ├── key: (215)
 │         │              │              │              │    └── scan c
 │         │              │              │              │         └── columns: c2:215
 │         │              │              │              └── filters
 │         │              │              │                   └── c2:215 = b2:208 [outer=(208,215), constraints=(/208: (/NULL - ]; /215: (/NULL - ]), fd=(208)==(215), (215)==(208)]
 │         │              │              └── aggregations
 │         │              │                   ├── const-agg [as=b1:39, outer=(39)]
 │         │              │                   │    └── b1:39
 │         │              │                   ├── const-agg [as=b2:40, outer=(40)]
 │         │              │                   │    └── b2:40
 │         │              │                   └── const-agg [as=b3:41, outer=(41)]
 │         │              │                        └── b3:41
 │         │              └── aggregations
 │         │                   ├── const-agg [as=b1:7, outer=(7)]
 │         │                   │    └── b1:7
 │         │                   ├── const-agg [as=b2:8, outer=(8)]
 │         │                   │    └── b2:8
 │         │                   └── const-agg [as=b3:9, outer=(9)]
 │         │                        └── b3:9
 │         └── filters
 │              └── a1:1 = b1:7 [outer=(1,7), constraints=(/1: (/NULL - ]; /7: (/NULL - ]), fd=(1)==(7), (7)==(1)]
 └── projections
      └── (a1:1 + a3:3) - a2:2 [as="?column?":24, outer=(1-3), immutable]

# IN subquery, 2 columns
opt expect=SplitDisjunctionOfJoinTerms
SELECT a1+a3-a2 FROM a WHERE (a1,a2) IN (SELECT b1,b2 FROM b WHERE
                                            EXISTS (SELECT 1 FROM c WHERE c2 IS NULL OR c2=b2 OR c2=b3))
----
project
 ├── columns: "?column?":25!null
 ├── immutable
 ├── semi-join (hash)
 │    ├── columns: a1:1!null a2:2!null a3:3!null
 │    ├── scan a
 │    │    └── columns: a1:1!null a2:2!null a3:3!null
 │    ├── project
 │    │    ├── columns: b1:7 b2:8 b3:9
 │    │    └── distinct-on
 │    │         ├── columns: b1:7 b2:8 b3:9 b.rowid:11!null
 │    │         ├── grouping columns: b.rowid:11!null
 │    │         ├── key: (11)
 │    │         ├── fd: (11)-->(7-9)
 │    │         ├── union-all
 │    │         │    ├── columns: b1:7 b2:8 b3:9 b.rowid:11!null
 │    │         │    ├── left columns: b1:26 b2:27 b3:28 b.rowid:30
 │    │         │    ├── right columns: b1:40 b2:41 b3:42 b.rowid:44
 │    │         │    ├── project
 │    │         │    │    ├── columns: b1:26 b2:27 b3:28 b.rowid:30!null
 │    │         │    │    ├── key: (30)
 │    │         │    │    ├── fd: (30)-->(26-28)
 │    │         │    │    └── project
 │    │         │    │         ├── columns: b1:26 b2:27 b3:28 b.rowid:30!null
 │    │         │    │         ├── key: (30)
 │    │         │    │         ├── fd: (30)-->(26-28)
 │    │         │    │         └── inner-join (cross)
 │    │         │    │              ├── columns: b1:26 b2:27 b3:28 b.rowid:30!null c2:34
 │    │         │    │              ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │    │         │    │              ├── key: (30)
 │    │         │    │              ├── fd: ()-->(34), (30)-->(26-28)
 │    │         │    │              ├── scan b
 │    │         │    │              │    ├── columns: b1:26 b2:27 b3:28 b.rowid:30!null
 │    │         │    │              │    ├── key: (30)
 │    │         │    │              │    └── fd: (30)-->(26-28)
 │    │         │    │              ├── limit
 │    │         │    │              │    ├── columns: c2:34
 │    │         │    │              │    ├── cardinality: [0 - 1]
 │    │         │    │              │    ├── key: ()
 │    │         │    │              │    ├── fd: ()-->(34)
 │    │         │    │              │    ├── select
 │    │         │    │              │    │    ├── columns: c2:34
 │    │         │    │              │    │    ├── fd: ()-->(34)
 │    │         │    │              │    │    ├── limit hint: 1.00
 │    │         │    │              │    │    ├── scan c
 │    │         │    │              │    │    │    ├── columns: c2:34
 │    │         │    │              │    │    │    └── limit hint: 100.00
 │    │         │    │              │    │    └── filters
 │    │         │    │              │    │         └── c2:34 IS NULL [outer=(34), constraints=(/34: [/NULL - /NULL]; tight), fd=()-->(34)]
 │    │         │    │              │    └── 1
 │    │         │    │              └── filters (true)
 │    │         │    └── project
 │    │         │         ├── columns: b1:40 b2:41 b3:42 b.rowid:44!null
 │    │         │         ├── key: (44)
 │    │         │         ├── fd: (44)-->(40-42)
 │    │         │         └── distinct-on
 │    │         │              ├── columns: b1:40 b2:41 b3:42 b.rowid:44!null
 │    │         │              ├── grouping columns: b.rowid:44!null
 │    │         │              ├── key: (44)
 │    │         │              ├── fd: (44)-->(40-42)
 │    │         │              ├── union-all
 │    │         │              │    ├── columns: b1:40 b2:41 b3:42 b.rowid:44!null
 │    │         │              │    ├── left columns: b1:194 b2:195 b3:196 b.rowid:198
 │    │         │              │    ├── right columns: b1:208 b2:209 b3:210 b.rowid:212
 │    │         │              │    ├── project
 │    │         │              │    │    ├── columns: b1:194 b2:195 b3:196 b.rowid:198!null
 │    │         │              │    │    ├── key: (198)
 │    │         │              │    │    ├── fd: (198)-->(194-196)
 │    │         │              │    │    └── inner-join (hash)
 │    │         │              │    │         ├── columns: b1:194 b2:195 b3:196!null b.rowid:198!null c2:202!null
 │    │         │              │    │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │    │         │              │    │         ├── key: (198)
 │    │         │              │    │         ├── fd: (198)-->(194-196), (196)==(202), (202)==(196)
 │    │         │              │    │         ├── scan b
 │    │         │              │    │         │    ├── columns: b1:194 b2:195 b3:196 b.rowid:198!null
 │    │         │              │    │         │    ├── key: (198)
 │    │         │              │    │         │    └── fd: (198)-->(194-196)
 │    │         │              │    │         ├── distinct-on
 │    │         │              │    │         │    ├── columns: c2:202
 │    │         │              │    │         │    ├── grouping columns: c2:202
 │    │         │              │    │         │    ├── key: (202)
 │    │         │              │    │         │    └── scan c
 │    │         │              │    │         │         └── columns: c2:202
 │    │         │              │    │         └── filters
 │    │         │              │    │              └── c2:202 = b3:196 [outer=(196,202), constraints=(/196: (/NULL - ]; /202: (/NULL - ]), fd=(196)==(202), (202)==(196)]
 │    │         │              │    └── project
 │    │         │              │         ├── columns: b1:208 b2:209 b3:210 b.rowid:212!null
 │    │         │              │         ├── key: (212)
 │    │         │              │         ├── fd: (212)-->(208-210)
 │    │         │              │         └── inner-join (hash)
 │    │         │              │              ├── columns: b1:208 b2:209!null b3:210 b.rowid:212!null c2:216!null
 │    │         │              │              ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │    │         │              │              ├── key: (212)
 │    │         │              │              ├── fd: (212)-->(208-210), (209)==(216), (216)==(209)
 │    │         │              │              ├── scan b
 │    │         │              │              │    ├── columns: b1:208 b2:209 b3:210 b.rowid:212!null
 │    │         │              │              │    ├── key: (212)
 │    │         │              │              │    └── fd: (212)-->(208-210)
 │    │         │              │              ├── distinct-on
 │    │         │              │              │    ├── columns: c2:216
 │    │         │              │              │    ├── grouping columns: c2:216
 │    │         │              │              │    ├── key: (216)
 │    │         │              │              │    └── scan c
 │    │         │              │              │         └── columns: c2:216
 │    │         │              │              └── filters
 │    │         │              │                   └── c2:216 = b2:209 [outer=(209,216), constraints=(/209: (/NULL - ]; /216: (/NULL - ]), fd=(209)==(216), (216)==(209)]
 │    │         │              └── aggregations
 │    │         │                   ├── const-agg [as=b1:40, outer=(40)]
 │    │         │                   │    └── b1:40
 │    │         │                   ├── const-agg [as=b2:41, outer=(41)]
 │    │         │                   │    └── b2:41
 │    │         │                   └── const-agg [as=b3:42, outer=(42)]
 │    │         │                        └── b3:42
 │    │         └── aggregations
 │    │              ├── const-agg [as=b1:7, outer=(7)]
 │    │              │    └── b1:7
 │    │              ├── const-agg [as=b2:8, outer=(8)]
 │    │              │    └── b2:8
 │    │              └── const-agg [as=b3:9, outer=(9)]
 │    │                   └── b3:9
 │    └── filters
 │         ├── b1:7 = a1:1 [outer=(1,7), constraints=(/1: (/NULL - ]; /7: (/NULL - ]), fd=(1)==(7), (7)==(1)]
 │         └── b2:8 = a2:2 [outer=(2,8), constraints=(/2: (/NULL - ]; /8: (/NULL - ]), fd=(2)==(8), (8)==(2)]
 └── projections
      └── (a1:1 + a3:3) - a2:2 [as="?column?":25, outer=(1-3), immutable]

# ANDed disjuncts
opt expect=SplitDisjunctionOfJoinTerms
SELECT a1,a2,a3,c.* FROM a,c
       WHERE a2 = c2 AND EXISTS (SELECT * FROM b WHERE (a1 = b1 OR a1 = a2) AND (a1 = c1 OR c1 = c2))
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null c1:7!null c2:8!null c3:9 c4:10
 ├── fd: (2)==(8), (8)==(2)
 └── group-by (hash)
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null c1:7!null c2:8!null c3:9 c4:10 c.rowid:11!null
      ├── grouping columns: a1:1!null a3:3!null a4:4!null c.rowid:11!null
      ├── key: (1,3,4,11)
      ├── fd: (11)-->(7-10), (1,3,4,11)-->(2,7-10), (2)==(8), (8)==(2)
      ├── inner-join (cross)
      │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null c1:7!null c2:8!null c3:9 c4:10 c.rowid:11!null b1:14
      │    ├── fd: (11)-->(7-10), (2)==(8), (8)==(2)
      │    ├── scan b
      │    │    └── columns: b1:14
      │    ├── inner-join (hash)
      │    │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null c1:7!null c2:8!null c3:9 c4:10 c.rowid:11!null
      │    │    ├── key: (1,3,4,11)
      │    │    ├── fd: (11)-->(7-10), (2)==(8), (8)==(2)
      │    │    ├── scan a
      │    │    │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      │    │    │    └── key: (1-4)
      │    │    ├── scan c
      │    │    │    ├── columns: c1:7 c2:8 c3:9 c4:10 c.rowid:11!null
      │    │    │    ├── key: (11)
      │    │    │    └── fd: (11)-->(7-10)
      │    │    └── filters
      │    │         ├── (a1:1 = c1:7) OR (c1:7 = c2:8) [outer=(1,7,8), constraints=(/7: (/NULL - ])]
      │    │         └── a2:2 = c2:8 [outer=(2,8), constraints=(/2: (/NULL - ]; /8: (/NULL - ]), fd=(2)==(8), (8)==(2)]
      │    └── filters
      │         └── (a1:1 = b1:14) OR (a1:1 = a2:2) [outer=(1,2,14), constraints=(/1: (/NULL - ])]
      └── aggregations
           ├── const-agg [as=c1:7, outer=(7)]
           │    └── c1:7
           ├── const-agg [as=c2:8, outer=(8)]
           │    └── c2:8
           ├── const-agg [as=c3:9, outer=(9)]
           │    └── c3:9
           ├── const-agg [as=c4:10, outer=(10)]
           │    └── c4:10
           └── const-agg [as=a2:2, outer=(2)]
                └── a2:2

opt expect=SplitDisjunctionOfJoinTerms
SELECT a1,a2,a3,c.* FROM a,c
       WHERE a2 = c2 AND EXISTS (SELECT * FROM b WHERE (a1 = b1 OR a1 = b2) AND (c1 = b1 OR c1 = b2))
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null c1:7!null c2:8!null c3:9 c4:10
 ├── fd: (2)==(8), (8)==(2)
 └── group-by (hash)
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null c1:7!null c2:8!null c3:9 c4:10 c.rowid:11!null
      ├── grouping columns: a1:1!null a3:3!null a4:4!null c.rowid:11!null
      ├── key: (1,3,4,11)
      ├── fd: (11)-->(7-10), (1,3,4,11)-->(2,7-10), (2)==(8), (8)==(2)
      ├── inner-join (hash)
      │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null c1:7!null c2:8!null c3:9 c4:10 c.rowid:11!null b1:14 b2:15
      │    ├── fd: (11)-->(7-10), (2)==(8), (8)==(2)
      │    ├── project
      │    │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:14 b2:15
      │    │    └── distinct-on
      │    │         ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:14 b2:15 b.rowid:18!null
      │    │         ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null b.rowid:18!null
      │    │         ├── key: (1-4,18)
      │    │         ├── fd: (1-4,18)-->(14,15)
      │    │         ├── union-all
      │    │         │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:14 b2:15 b.rowid:18!null
      │    │         │    ├── left columns: a1:81 a2:82 a3:83 a4:84 b1:87 b2:88 b.rowid:91
      │    │         │    ├── right columns: a1:94 a2:95 a3:96 a4:97 b1:100 b2:101 b.rowid:104
      │    │         │    ├── inner-join (merge)
      │    │         │    │    ├── columns: a1:81!null a2:82!null a3:83!null a4:84!null b1:87!null b2:88 b.rowid:91!null
      │    │         │    │    ├── left ordering: +81
      │    │         │    │    ├── right ordering: +87
      │    │         │    │    ├── key: (82-84,91)
      │    │         │    │    ├── fd: (91)-->(87,88), (81)==(87), (87)==(81)
      │    │         │    │    ├── scan a
      │    │         │    │    │    ├── columns: a1:81!null a2:82!null a3:83!null a4:84!null
      │    │         │    │    │    ├── key: (81-84)
      │    │         │    │    │    └── ordering: +81
      │    │         │    │    ├── scan b@b_b1_b2_idx
      │    │         │    │    │    ├── columns: b1:87 b2:88 b.rowid:91!null
      │    │         │    │    │    ├── key: (91)
      │    │         │    │    │    ├── fd: (91)-->(87,88)
      │    │         │    │    │    └── ordering: +87
      │    │         │    │    └── filters (true)
      │    │         │    └── inner-join (merge)
      │    │         │         ├── columns: a1:94!null a2:95!null a3:96!null a4:97!null b1:100 b2:101!null b.rowid:104!null
      │    │         │         ├── left ordering: +94
      │    │         │         ├── right ordering: +101
      │    │         │         ├── key: (95-97,104)
      │    │         │         ├── fd: (104)-->(100,101), (94)==(101), (101)==(94)
      │    │         │         ├── scan a
      │    │         │         │    ├── columns: a1:94!null a2:95!null a3:96!null a4:97!null
      │    │         │         │    ├── key: (94-97)
      │    │         │         │    └── ordering: +94
      │    │         │         ├── scan b@b_b2_idx
      │    │         │         │    ├── columns: b1:100 b2:101 b.rowid:104!null
      │    │         │         │    ├── key: (104)
      │    │         │         │    ├── fd: (104)-->(100,101)
      │    │         │         │    └── ordering: +101
      │    │         │         └── filters (true)
      │    │         └── aggregations
      │    │              ├── const-agg [as=b1:14, outer=(14)]
      │    │              │    └── b1:14
      │    │              └── const-agg [as=b2:15, outer=(15)]
      │    │                   └── b2:15
      │    ├── scan c
      │    │    ├── columns: c1:7 c2:8 c3:9 c4:10 c.rowid:11!null
      │    │    ├── key: (11)
      │    │    └── fd: (11)-->(7-10)
      │    └── filters
      │         ├── a2:2 = c2:8 [outer=(2,8), constraints=(/2: (/NULL - ]; /8: (/NULL - ]), fd=(2)==(8), (8)==(2)]
      │         └── (c1:7 = b1:14) OR (c1:7 = b2:15) [outer=(7,14,15), constraints=(/7: (/NULL - ])]
      └── aggregations
           ├── const-agg [as=c1:7, outer=(7)]
           │    └── c1:7
           ├── const-agg [as=c2:8, outer=(8)]
           │    └── c2:8
           ├── const-agg [as=c3:9, outer=(9)]
           │    └── c3:9
           ├── const-agg [as=c4:10, outer=(10)]
           │    └── c4:10
           └── const-agg [as=a2:2, outer=(2)]
                └── a2:2

opt expect=SplitDisjunctionOfJoinTerms
SELECT a1,a2,a3,c.* FROM a,c
       WHERE a2 = c2 AND EXISTS (SELECT * FROM b WHERE (a1 = b1 OR a1 = b3) AND (a1 = c1 OR a1 = c3))
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null c1:7 c2:8!null c3:9 c4:10
 ├── fd: (2)==(8), (8)==(2)
 └── group-by (hash)
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null c1:7 c2:8!null c3:9 c4:10 c.rowid:11!null
      ├── grouping columns: a1:1!null a3:3!null a4:4!null c.rowid:11!null
      ├── key: (1,3,4,11)
      ├── fd: (11)-->(7-10), (1,3,4,11)-->(2,7-10), (2)==(8), (8)==(2)
      ├── inner-join (cross)
      │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null c1:7 c2:8!null c3:9 c4:10 c.rowid:11!null b1:14 b3:16
      │    ├── fd: (11)-->(7-10), (2)==(8), (8)==(2)
      │    ├── scan b
      │    │    └── columns: b1:14 b3:16
      │    ├── inner-join (hash)
      │    │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null c1:7 c2:8!null c3:9 c4:10 c.rowid:11!null
      │    │    ├── key: (1,3,4,11)
      │    │    ├── fd: (11)-->(7-10), (2)==(8), (8)==(2)
      │    │    ├── scan a
      │    │    │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      │    │    │    └── key: (1-4)
      │    │    ├── scan c
      │    │    │    ├── columns: c1:7 c2:8 c3:9 c4:10 c.rowid:11!null
      │    │    │    ├── key: (11)
      │    │    │    └── fd: (11)-->(7-10)
      │    │    └── filters
      │    │         ├── (a1:1 = c1:7) OR (a1:1 = c3:9) [outer=(1,7,9), constraints=(/1: (/NULL - ])]
      │    │         └── a2:2 = c2:8 [outer=(2,8), constraints=(/2: (/NULL - ]; /8: (/NULL - ]), fd=(2)==(8), (8)==(2)]
      │    └── filters
      │         └── (a1:1 = b1:14) OR (a1:1 = b3:16) [outer=(1,14,16), constraints=(/1: (/NULL - ])]
      └── aggregations
           ├── const-agg [as=c1:7, outer=(7)]
           │    └── c1:7
           ├── const-agg [as=c2:8, outer=(8)]
           │    └── c2:8
           ├── const-agg [as=c3:9, outer=(9)]
           │    └── c3:9
           ├── const-agg [as=c4:10, outer=(10)]
           │    └── c4:10
           └── const-agg [as=a2:2, outer=(2)]
                └── a2:2

# Nested EXISTS
opt expect=SplitDisjunctionOfJoinTerms
SELECT a2,a4 FROM a WHERE EXISTS(SELECT * FROM b WHERE (a1=b1 OR a1=b2) AND EXISTS(SELECT 1 FROM c WHERE b1=c1))
----
project
 ├── columns: a2:2!null a4:4!null
 └── project
      ├── columns: a1:1!null a2:2!null a4:4!null
      └── distinct-on
           ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
           ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null
           ├── key: (1-4)
           └── inner-join (hash)
                ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7!null b2:8 c1:14!null
                ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
                ├── fd: (7)==(14), (14)==(7)
                ├── project
                │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7 b2:8
                │    └── distinct-on
                │         ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7 b2:8 b.rowid:11!null
                │         ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null b.rowid:11!null
                │         ├── key: (1-4,11)
                │         ├── fd: (1-4,11)-->(7,8)
                │         ├── union-all
                │         │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7 b2:8 b.rowid:11!null
                │         │    ├── left columns: a1:24 a2:25 a3:26 a4:27 b1:30 b2:31 b.rowid:34
                │         │    ├── right columns: a1:37 a2:38 a3:39 a4:40 b1:43 b2:44 b.rowid:47
                │         │    ├── inner-join (merge)
                │         │    │    ├── columns: a1:24!null a2:25!null a3:26!null a4:27!null b1:30!null b2:31 b.rowid:34!null
                │         │    │    ├── left ordering: +24
                │         │    │    ├── right ordering: +30
                │         │    │    ├── key: (25-27,34)
                │         │    │    ├── fd: (34)-->(30,31), (24)==(30), (30)==(24)
                │         │    │    ├── scan a
                │         │    │    │    ├── columns: a1:24!null a2:25!null a3:26!null a4:27!null
                │         │    │    │    ├── key: (24-27)
                │         │    │    │    └── ordering: +24
                │         │    │    ├── scan b@b_b1_b2_idx
                │         │    │    │    ├── columns: b1:30 b2:31 b.rowid:34!null
                │         │    │    │    ├── key: (34)
                │         │    │    │    ├── fd: (34)-->(30,31)
                │         │    │    │    └── ordering: +30
                │         │    │    └── filters (true)
                │         │    └── inner-join (merge)
                │         │         ├── columns: a1:37!null a2:38!null a3:39!null a4:40!null b1:43 b2:44!null b.rowid:47!null
                │         │         ├── left ordering: +37
                │         │         ├── right ordering: +44
                │         │         ├── key: (38-40,47)
                │         │         ├── fd: (47)-->(43,44), (37)==(44), (44)==(37)
                │         │         ├── scan a
                │         │         │    ├── columns: a1:37!null a2:38!null a3:39!null a4:40!null
                │         │         │    ├── key: (37-40)
                │         │         │    └── ordering: +37
                │         │         ├── scan b@b_b2_idx
                │         │         │    ├── columns: b1:43 b2:44 b.rowid:47!null
                │         │         │    ├── key: (47)
                │         │         │    ├── fd: (47)-->(43,44)
                │         │         │    └── ordering: +44
                │         │         └── filters (true)
                │         └── aggregations
                │              ├── const-agg [as=b1:7, outer=(7)]
                │              │    └── b1:7
                │              └── const-agg [as=b2:8, outer=(8)]
                │                   └── b2:8
                ├── distinct-on
                │    ├── columns: c1:14
                │    ├── grouping columns: c1:14
                │    ├── key: (14)
                │    └── scan c
                │         └── columns: c1:14
                └── filters
                     └── b1:7 = c1:14 [outer=(7,14), constraints=(/7: (/NULL - ]; /14: (/NULL - ]), fd=(7)==(14), (14)==(7)]

# Two EXISTS at same nesting level; only one disjuction pair can be optimized
opt expect=SplitDisjunctionOfJoinTerms
SELECT a2,a4 FROM a WHERE EXISTS(SELECT * FROM b WHERE a1=b1 OR a1=b2) AND
                          EXISTS(SELECT * FROM c WHERE a1=c1 OR a1=c2)
----
project
 ├── columns: a2:2!null a4:4!null
 └── semi-join (cross)
      ├── columns: a1:1!null a2:2!null a4:4!null
      ├── project
      │    ├── columns: a1:1!null a2:2!null a4:4!null
      │    └── distinct-on
      │         ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      │         ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null
      │         ├── key: (1-4)
      │         └── union-all
      │              ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      │              ├── left columns: a1:102 a2:103 a3:104 a4:105
      │              ├── right columns: a1:115 a2:116 a3:117 a4:118
      │              ├── project
      │              │    ├── columns: a1:102!null a2:103!null a3:104!null a4:105!null
      │              │    ├── key: (102-105)
      │              │    └── inner-join (merge)
      │              │         ├── columns: a1:102!null a2:103!null a3:104!null a4:105!null b1:108!null
      │              │         ├── left ordering: +102
      │              │         ├── right ordering: +108
      │              │         ├── key: (103-105,108)
      │              │         ├── fd: (102)==(108), (108)==(102)
      │              │         ├── scan a
      │              │         │    ├── columns: a1:102!null a2:103!null a3:104!null a4:105!null
      │              │         │    ├── key: (102-105)
      │              │         │    └── ordering: +102
      │              │         ├── distinct-on
      │              │         │    ├── columns: b1:108
      │              │         │    ├── grouping columns: b1:108
      │              │         │    ├── key: (108)
      │              │         │    ├── ordering: +108
      │              │         │    └── scan b@b_b1_b2_idx
      │              │         │         ├── columns: b1:108
      │              │         │         └── ordering: +108
      │              │         └── filters (true)
      │              └── project
      │                   ├── columns: a1:115!null a2:116!null a3:117!null a4:118!null
      │                   ├── key: (115-118)
      │                   └── inner-join (merge)
      │                        ├── columns: a1:115!null a2:116!null a3:117!null a4:118!null b2:122!null
      │                        ├── left ordering: +115
      │                        ├── right ordering: +122
      │                        ├── key: (116-118,122)
      │                        ├── fd: (115)==(122), (122)==(115)
      │                        ├── scan a
      │                        │    ├── columns: a1:115!null a2:116!null a3:117!null a4:118!null
      │                        │    ├── key: (115-118)
      │                        │    └── ordering: +115
      │                        ├── distinct-on
      │                        │    ├── columns: b2:122
      │                        │    ├── grouping columns: b2:122
      │                        │    ├── key: (122)
      │                        │    ├── ordering: +122
      │                        │    └── scan b@b_b2_idx
      │                        │         ├── columns: b2:122
      │                        │         └── ordering: +122
      │                        └── filters (true)
      ├── scan c
      │    └── columns: c1:14 c2:15
      └── filters
           └── (a1:1 = c1:14) OR (a1:1 = c2:15) [outer=(1,14,15), constraints=(/1: (/NULL - ])]

# Two EXISTS at same nesting level; only one disjuction chain can be optimized
opt expect=SplitDisjunctionOfJoinTerms
SELECT a2,a4 FROM a WHERE EXISTS(SELECT * FROM b WHERE a1=b1 OR a1=b2 OR a1=b3 OR a1=b4) AND
                          EXISTS(SELECT * FROM c WHERE a1=c1 OR a1=c2 OR a1=c3 OR a1=c4)
----
project
 ├── columns: a2:2!null a4:4!null
 └── semi-join (cross)
      ├── columns: a1:1!null a2:2!null a4:4!null
      ├── project
      │    ├── columns: a1:1!null a2:2!null a4:4!null
      │    └── distinct-on
      │         ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      │         ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null
      │         ├── key: (1-4)
      │         └── union-all
      │              ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      │              ├── left columns: a1:518 a2:519 a3:520 a4:521
      │              ├── right columns: a1:531 a2:532 a3:533 a4:534
      │              ├── project
      │              │    ├── columns: a1:518!null a2:519!null a3:520!null a4:521!null
      │              │    ├── key: (518-521)
      │              │    └── inner-join (merge)
      │              │         ├── columns: a1:518!null a2:519!null a3:520!null a4:521!null b1:524!null
      │              │         ├── left ordering: +518
      │              │         ├── right ordering: +524
      │              │         ├── key: (519-521,524)
      │              │         ├── fd: (518)==(524), (524)==(518)
      │              │         ├── scan a
      │              │         │    ├── columns: a1:518!null a2:519!null a3:520!null a4:521!null
      │              │         │    ├── key: (518-521)
      │              │         │    └── ordering: +518
      │              │         ├── distinct-on
      │              │         │    ├── columns: b1:524
      │              │         │    ├── grouping columns: b1:524
      │              │         │    ├── key: (524)
      │              │         │    ├── ordering: +524
      │              │         │    └── scan b@b_b1_b2_idx
      │              │         │         ├── columns: b1:524
      │              │         │         └── ordering: +524
      │              │         └── filters (true)
      │              └── project
      │                   ├── columns: a1:531!null a2:532!null a3:533!null a4:534!null
      │                   ├── key: (531-534)
      │                   └── distinct-on
      │                        ├── columns: a1:531!null a2:532!null a3:533!null a4:534!null
      │                        ├── grouping columns: a1:531!null a2:532!null a3:533!null a4:534!null
      │                        ├── key: (531-534)
      │                        └── union-all
      │                             ├── columns: a1:531!null a2:532!null a3:533!null a4:534!null
      │                             ├── left columns: a1:622 a2:623 a3:624 a4:625
      │                             ├── right columns: a1:635 a2:636 a3:637 a4:638
      │                             ├── project
      │                             │    ├── columns: a1:622!null a2:623!null a3:624!null a4:625!null
      │                             │    ├── key: (622-625)
      │                             │    └── inner-join (hash)
      │                             │         ├── columns: a1:622!null a2:623!null a3:624!null a4:625!null b4:631!null
      │                             │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
      │                             │         ├── key: (623-625,631)
      │                             │         ├── fd: (622)==(631), (631)==(622)
      │                             │         ├── scan a
      │                             │         │    ├── columns: a1:622!null a2:623!null a3:624!null a4:625!null
      │                             │         │    └── key: (622-625)
      │                             │         ├── distinct-on
      │                             │         │    ├── columns: b4:631
      │                             │         │    ├── grouping columns: b4:631
      │                             │         │    ├── key: (631)
      │                             │         │    └── scan b
      │                             │         │         └── columns: b4:631
      │                             │         └── filters
      │                             │              └── a1:622 = b4:631 [outer=(622,631), constraints=(/622: (/NULL - ]; /631: (/NULL - ]), fd=(622)==(631), (631)==(622)]
      │                             └── project
      │                                  ├── columns: a1:635!null a2:636!null a3:637!null a4:638!null
      │                                  ├── key: (635-638)
      │                                  └── distinct-on
      │                                       ├── columns: a1:635!null a2:636!null a3:637!null a4:638!null
      │                                       ├── grouping columns: a1:635!null a2:636!null a3:637!null a4:638!null
      │                                       ├── key: (635-638)
      │                                       └── union-all
      │                                            ├── columns: a1:635!null a2:636!null a3:637!null a4:638!null
      │                                            ├── left columns: a1:700 a2:701 a3:702 a4:703
      │                                            ├── right columns: a1:713 a2:714 a3:715 a4:716
      │                                            ├── project
      │                                            │    ├── columns: a1:700!null a2:701!null a3:702!null a4:703!null
      │                                            │    ├── key: (700-703)
      │                                            │    └── inner-join (merge)
      │                                            │         ├── columns: a1:700!null a2:701!null a3:702!null a4:703!null b2:707!null
      │                                            │         ├── left ordering: +700
      │                                            │         ├── right ordering: +707
      │                                            │         ├── key: (701-703,707)
      │                                            │         ├── fd: (700)==(707), (707)==(700)
      │                                            │         ├── scan a
      │                                            │         │    ├── columns: a1:700!null a2:701!null a3:702!null a4:703!null
      │                                            │         │    ├── key: (700-703)
      │                                            │         │    └── ordering: +700
      │                                            │         ├── distinct-on
      │                                            │         │    ├── columns: b2:707
      │                                            │         │    ├── grouping columns: b2:707
      │                                            │         │    ├── key: (707)
      │                                            │         │    ├── ordering: +707
      │                                            │         │    └── scan b@b_b2_idx
      │                                            │         │         ├── columns: b2:707
      │                                            │         │         └── ordering: +707
      │                                            │         └── filters (true)
      │                                            └── project
      │                                                 ├── columns: a1:713!null a2:714!null a3:715!null a4:716!null
      │                                                 ├── key: (713-716)
      │                                                 └── inner-join (merge)
      │                                                      ├── columns: a1:713!null a2:714!null a3:715!null a4:716!null b3:721!null
      │                                                      ├── left ordering: +713
      │                                                      ├── right ordering: +721
      │                                                      ├── key: (714-716,721)
      │                                                      ├── fd: (713)==(721), (721)==(713)
      │                                                      ├── scan a
      │                                                      │    ├── columns: a1:713!null a2:714!null a3:715!null a4:716!null
      │                                                      │    ├── key: (713-716)
      │                                                      │    └── ordering: +713
      │                                                      ├── distinct-on
      │                                                      │    ├── columns: b3:721
      │                                                      │    ├── grouping columns: b3:721
      │                                                      │    ├── key: (721)
      │                                                      │    ├── ordering: +721
      │                                                      │    └── scan b@b_b3_idx
      │                                                      │         ├── columns: b3:721
      │                                                      │         └── ordering: +721
      │                                                      └── filters (true)
      ├── scan c
      │    └── columns: c1:14 c2:15 c3:16 c4:17
      └── filters
           └── (((a1:1 = c1:14) OR (a1:1 = c2:15)) OR (a1:1 = c3:16)) OR (a1:1 = c4:17) [outer=(1,14-17), constraints=(/1: (/NULL - ])]

# Outer Select is Join
opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a JOIN (SELECT * FROM b WHERE b1 > 0 AND EXISTS (SELECT 1 FROM c WHERE c1=b1))
                       AS foo on a1=foo.b1 OR a2=foo.b2
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7!null b2:8 b3:9 b4:10
 └── inner-join (hash)
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7!null b2:8 b3:9 b4:10 c1:14!null
      ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
      ├── fd: (7)==(14), (14)==(7)
      ├── project
      │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7!null b2:8 b3:9 b4:10
      │    └── distinct-on
      │         ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7!null b2:8 b3:9 b4:10 b.rowid:11!null
      │         ├── grouping columns: a1:1!null a2:2!null a3:3!null a4:4!null b.rowid:11!null
      │         ├── key: (1-4,11)
      │         ├── fd: (1-4,11)-->(7-10)
      │         ├── union-all
      │         │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7!null b2:8 b3:9 b4:10 b.rowid:11!null
      │         │    ├── left columns: a1:23 a2:24 a3:25 a4:26 b1:29 b2:30 b3:31 b4:32 b.rowid:33
      │         │    ├── right columns: a1:36 a2:37 a3:38 a4:39 b1:42 b2:43 b3:44 b4:45 b.rowid:46
      │         │    ├── inner-join (merge)
      │         │    │    ├── columns: a1:23!null a2:24!null a3:25!null a4:26!null b1:29!null b2:30 b3:31 b4:32 b.rowid:33!null
      │         │    │    ├── left ordering: +23
      │         │    │    ├── right ordering: +29
      │         │    │    ├── key: (24-26,33)
      │         │    │    ├── fd: (33)-->(29-32), (23)==(29), (29)==(23)
      │         │    │    ├── scan a
      │         │    │    │    ├── columns: a1:23!null a2:24!null a3:25!null a4:26!null
      │         │    │    │    ├── key: (23-26)
      │         │    │    │    └── ordering: +23
      │         │    │    ├── scan b@b_b1_b2_idx
      │         │    │    │    ├── columns: b1:29!null b2:30 b3:31 b4:32 b.rowid:33!null
      │         │    │    │    ├── constraint: /29/30/33: [/1 - ]
      │         │    │    │    ├── key: (33)
      │         │    │    │    ├── fd: (33)-->(29-32)
      │         │    │    │    └── ordering: +29
      │         │    │    └── filters (true)
      │         │    └── inner-join (hash)
      │         │         ├── columns: a1:36!null a2:37!null a3:38!null a4:39!null b1:42!null b2:43!null b3:44 b4:45 b.rowid:46!null
      │         │         ├── key: (36,38,39,46)
      │         │         ├── fd: (46)-->(42-45), (37)==(43), (43)==(37)
      │         │         ├── scan a
      │         │         │    ├── columns: a1:36!null a2:37!null a3:38!null a4:39!null
      │         │         │    └── key: (36-39)
      │         │         ├── scan b@b_b1_b2_idx
      │         │         │    ├── columns: b1:42!null b2:43 b3:44 b4:45 b.rowid:46!null
      │         │         │    ├── constraint: /42/43/46: [/1 - ]
      │         │         │    ├── key: (46)
      │         │         │    └── fd: (46)-->(42-45)
      │         │         └── filters
      │         │              └── a2:37 = b2:43 [outer=(37,43), constraints=(/37: (/NULL - ]; /43: (/NULL - ]), fd=(37)==(43), (43)==(37)]
      │         └── aggregations
      │              ├── const-agg [as=b1:7, outer=(7)]
      │              │    └── b1:7
      │              ├── const-agg [as=b2:8, outer=(8)]
      │              │    └── b2:8
      │              ├── const-agg [as=b3:9, outer=(9)]
      │              │    └── b3:9
      │              └── const-agg [as=b4:10, outer=(10)]
      │                   └── b4:10
      ├── distinct-on
      │    ├── columns: c1:14!null
      │    ├── grouping columns: c1:14!null
      │    ├── key: (14)
      │    └── select
      │         ├── columns: c1:14!null
      │         ├── scan c
      │         │    └── columns: c1:14
      │         └── filters
      │              └── c1:14 > 0 [outer=(14), constraints=(/14: [/1 - ]; tight)]
      └── filters
           └── c1:14 = b1:7 [outer=(7,14), constraints=(/7: (/NULL - ]; /14: (/NULL - ]), fd=(7)==(14), (14)==(7)]

opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a JOIN (SELECT * FROM b WHERE b1 > 0 AND EXISTS (SELECT 1 FROM c WHERE c1=b1 or c2=b2))
                       AS foo on a1=foo.b1
----
inner-join (hash)
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7!null b2:8 b3:9 b4:10
 ├── fd: (1)==(7), (7)==(1)
 ├── scan a
 │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 │    └── key: (1-4)
 ├── project
 │    ├── columns: b1:7!null b2:8 b3:9 b4:10
 │    └── distinct-on
 │         ├── columns: b1:7!null b2:8 b3:9 b4:10 b.rowid:11!null
 │         ├── grouping columns: b.rowid:11!null
 │         ├── key: (11)
 │         ├── fd: (11)-->(7-10)
 │         ├── union-all
 │         │    ├── columns: b1:7!null b2:8 b3:9 b4:10 b.rowid:11!null
 │         │    ├── left columns: b1:23 b2:24 b3:25 b4:26 b.rowid:27
 │         │    ├── right columns: b1:37 b2:38 b3:39 b4:40 b.rowid:41
 │         │    ├── project
 │         │    │    ├── columns: b1:23!null b2:24 b3:25 b4:26 b.rowid:27!null
 │         │    │    ├── key: (27)
 │         │    │    ├── fd: (27)-->(23-26)
 │         │    │    └── inner-join (hash)
 │         │    │         ├── columns: b1:23!null b2:24 b3:25 b4:26 b.rowid:27!null c1:30!null
 │         │    │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │         │    │         ├── key: (27)
 │         │    │         ├── fd: (27)-->(23-26), (23)==(30), (30)==(23)
 │         │    │         ├── scan b@b_b1_b2_idx
 │         │    │         │    ├── columns: b1:23!null b2:24 b3:25 b4:26 b.rowid:27!null
 │         │    │         │    ├── constraint: /23/24/27: [/1 - ]
 │         │    │         │    ├── key: (27)
 │         │    │         │    └── fd: (27)-->(23-26)
 │         │    │         ├── distinct-on
 │         │    │         │    ├── columns: c1:30
 │         │    │         │    ├── grouping columns: c1:30
 │         │    │         │    ├── key: (30)
 │         │    │         │    └── scan c
 │         │    │         │         └── columns: c1:30
 │         │    │         └── filters
 │         │    │              └── c1:30 = b1:23 [outer=(23,30), constraints=(/23: (/NULL - ]; /30: (/NULL - ]), fd=(23)==(30), (30)==(23)]
 │         │    └── project
 │         │         ├── columns: b1:37!null b2:38 b3:39 b4:40 b.rowid:41!null
 │         │         ├── key: (41)
 │         │         ├── fd: (41)-->(37-40)
 │         │         └── inner-join (hash)
 │         │              ├── columns: b1:37!null b2:38!null b3:39 b4:40 b.rowid:41!null c2:45!null
 │         │              ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │         │              ├── key: (41)
 │         │              ├── fd: (41)-->(37-40), (38)==(45), (45)==(38)
 │         │              ├── scan b@b_b1_b2_idx
 │         │              │    ├── columns: b1:37!null b2:38 b3:39 b4:40 b.rowid:41!null
 │         │              │    ├── constraint: /37/38/41: [/1 - ]
 │         │              │    ├── key: (41)
 │         │              │    └── fd: (41)-->(37-40)
 │         │              ├── distinct-on
 │         │              │    ├── columns: c2:45
 │         │              │    ├── grouping columns: c2:45
 │         │              │    ├── key: (45)
 │         │              │    └── scan c
 │         │              │         └── columns: c2:45
 │         │              └── filters
 │         │                   └── c2:45 = b2:38 [outer=(38,45), constraints=(/38: (/NULL - ]; /45: (/NULL - ]), fd=(38)==(45), (45)==(38)]
 │         └── aggregations
 │              ├── const-agg [as=b1:7, outer=(7)]
 │              │    └── b1:7
 │              ├── const-agg [as=b2:8, outer=(8)]
 │              │    └── b2:8
 │              ├── const-agg [as=b3:9, outer=(9)]
 │              │    └── b3:9
 │              └── const-agg [as=b4:10, outer=(10)]
 │                   └── b4:10
 └── filters
      └── a1:1 = b1:7 [outer=(1,7), constraints=(/1: (/NULL - ]; /7: (/NULL - ]), fd=(1)==(7), (7)==(1)]

# We should be able to split the disjunction even though the predicates are
# equalities but not equijoin expressions.
opt expect=SplitDisjunctionOfJoinTerms
SELECT
  e.*
FROM
  e
WHERE EXISTS (
  SELECT *
  FROM f
  WHERE (e5 = 30 OR (e3 = 10 AND f2 = 20)) AND f3 = e2
);
----
project
 ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 └── distinct-on
      ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5
      ├── grouping columns: e1:1!null
      ├── key: (1)
      ├── fd: (1)-->(2-5)
      ├── union-all
      │    ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5
      │    ├── left columns: e1:15 e2:16 e3:17 e4:18 e5:19
      │    ├── right columns: e1:28 e2:29 e3:30 e4:31 e5:32
      │    ├── semi-join (lookup f@f_f3_idx)
      │    │    ├── columns: e1:15!null e2:16!null e3:17!null e4:18!null e5:19!null
      │    │    ├── key columns: [16] = [24]
      │    │    ├── key: (15)
      │    │    ├── fd: ()-->(19), (15)-->(16-18)
      │    │    ├── index-join e
      │    │    │    ├── columns: e1:15!null e2:16!null e3:17!null e4:18!null e5:19!null
      │    │    │    ├── key: (15)
      │    │    │    ├── fd: ()-->(19), (15)-->(16-18)
      │    │    │    └── scan e@e_e5_idx
      │    │    │         ├── columns: e1:15!null e5:19!null
      │    │    │         ├── constraint: /19/15: [/30 - /30]
      │    │    │         ├── key: (15)
      │    │    │         └── fd: ()-->(19)
      │    │    └── filters (true)
      │    └── semi-join (hash)
      │         ├── columns: e1:28!null e2:29!null e3:30!null e4:31!null e5:32
      │         ├── key: (28)
      │         ├── fd: ()-->(30), (28)-->(29,31,32)
      │         ├── index-join e
      │         │    ├── columns: e1:28!null e2:29!null e3:30!null e4:31!null e5:32
      │         │    ├── key: (28)
      │         │    ├── fd: ()-->(30), (28)-->(29,31,32)
      │         │    └── scan e@e_e3_e5_e4_idx
      │         │         ├── columns: e1:28!null e3:30!null e4:31!null e5:32
      │         │         ├── constraint: /30/32/31/28: [/10 - /10]
      │         │         ├── key: (28)
      │         │         └── fd: ()-->(30), (28)-->(31,32)
      │         ├── scan f@f_f2_idx
      │         │    ├── columns: f2:36!null f3:37
      │         │    ├── constraint: /36/35: [/20 - /20]
      │         │    └── fd: ()-->(36)
      │         └── filters
      │              └── f3:37 = e2:29 [outer=(29,37), constraints=(/29: (/NULL - ]; /37: (/NULL - ]), fd=(29)==(37), (37)==(29)]
      └── aggregations
           ├── const-agg [as=e2:2, outer=(2)]
           │    └── e2:2
           ├── const-agg [as=e3:3, outer=(3)]
           │    └── e3:3
           ├── const-agg [as=e4:4, outer=(4)]
           │    └── e4:4
           └── const-agg [as=e5:5, outer=(5)]
                └── e5:5

# We should be able to split the disjunction even though the predicates are
# not equalities.
opt expect=SplitDisjunctionOfJoinTerms
SELECT
  e.*
FROM
  e
WHERE EXISTS (
  SELECT *
  FROM f
  WHERE (
    (e.e5 > 30 AND e.e5 < 40)
    OR (e.e4 IN ('a', 'b') AND f.f2 < 20 AND f.f2 > 10)
  ) AND f3 = e2
)
----
project
 ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 └── distinct-on
      ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5
      ├── grouping columns: e1:1!null
      ├── key: (1)
      ├── fd: (1)-->(2-5)
      ├── union-all
      │    ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5
      │    ├── left columns: e1:15 e2:16 e3:17 e4:18 e5:19
      │    ├── right columns: e1:28 e2:29 e3:30 e4:31 e5:32
      │    ├── project
      │    │    ├── columns: e1:15!null e2:16!null e3:17!null e4:18!null e5:19!null
      │    │    ├── key: (15)
      │    │    ├── fd: (15)-->(16-19)
      │    │    └── inner-join (hash)
      │    │         ├── columns: e1:15!null e2:16!null e3:17!null e4:18!null e5:19!null f3:24!null
      │    │         ├── multiplicity: left-rows(zero-or-more), right-rows(zero-or-one)
      │    │         ├── key: (15)
      │    │         ├── fd: (15)-->(16-19), (16)==(24), (24)==(16)
      │    │         ├── distinct-on
      │    │         │    ├── columns: f3:24
      │    │         │    ├── grouping columns: f3:24
      │    │         │    ├── internal-ordering: +24
      │    │         │    ├── key: (24)
      │    │         │    └── scan f@f_f3_idx
      │    │         │         ├── columns: f3:24
      │    │         │         └── ordering: +24
      │    │         ├── index-join e
      │    │         │    ├── columns: e1:15!null e2:16!null e3:17!null e4:18!null e5:19!null
      │    │         │    ├── key: (15)
      │    │         │    ├── fd: (15)-->(16-19)
      │    │         │    └── scan e@e_e5_idx
      │    │         │         ├── columns: e1:15!null e5:19!null
      │    │         │         ├── constraint: /19/15: [/31 - /39]
      │    │         │         ├── key: (15)
      │    │         │         └── fd: (15)-->(19)
      │    │         └── filters
      │    │              └── f3:24 = e2:16 [outer=(16,24), constraints=(/16: (/NULL - ]; /24: (/NULL - ]), fd=(16)==(24), (24)==(16)]
      │    └── semi-join (hash)
      │         ├── columns: e1:28!null e2:29!null e3:30!null e4:31!null e5:32
      │         ├── key: (28)
      │         ├── fd: (28)-->(29-32)
      │         ├── index-join e
      │         │    ├── columns: e1:28!null e2:29!null e3:30!null e4:31!null e5:32
      │         │    ├── key: (28)
      │         │    ├── fd: (28)-->(29-32)
      │         │    └── scan e@e_e3_idx,partial
      │         │         ├── columns: e1:28!null e3:30!null
      │         │         ├── key: (28)
      │         │         └── fd: (28)-->(30)
      │         ├── scan f@f_f2_idx
      │         │    ├── columns: f2:36!null f3:37
      │         │    └── constraint: /36/35: [/11 - /19]
      │         └── filters
      │              └── f3:37 = e2:29 [outer=(29,37), constraints=(/29: (/NULL - ]; /37: (/NULL - ]), fd=(29)==(37), (37)==(29)]
      └── aggregations
           ├── const-agg [as=e2:2, outer=(2)]
           │    └── e2:2
           ├── const-agg [as=e3:3, outer=(3)]
           │    └── e3:3
           ├── const-agg [as=e4:4, outer=(4)]
           │    └── e4:4
           └── const-agg [as=e5:5, outer=(5)]
                └── e5:5


#######################
# Correlated antijoin #
#######################

opt expect=SplitDisjunctionOfAntiJoinTerms
SELECT a1,a2,a3 FROM a WHERE NOT EXISTS (SELECT * FROM b WHERE a2 = b2 OR a3 = b3)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null
 └── intersect-all
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      ├── left columns: a1:15 a2:16 a3:17 a4:18
      ├── right columns: a1:28 a2:29 a3:30 a4:31
      ├── key: (1-4)
      ├── anti-join (hash)
      │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
      │    ├── key: (15-18)
      │    ├── scan a
      │    │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
      │    │    └── key: (15-18)
      │    ├── scan b
      │    │    └── columns: b2:22
      │    └── filters
      │         └── a2:16 = b2:22 [outer=(16,22), constraints=(/16: (/NULL - ]; /22: (/NULL - ]), fd=(16)==(22), (22)==(16)]
      └── anti-join (hash)
           ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
           ├── key: (28-31)
           ├── scan a
           │    ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
           │    └── key: (28-31)
           ├── scan b
           │    └── columns: b3:36
           └── filters
                └── a3:30 = b3:36 [outer=(30,36), constraints=(/30: (/NULL - ]; /36: (/NULL - ]), fd=(30)==(36), (36)==(30)]

opt expect=SplitDisjunctionOfAntiJoinTerms
SELECT a1,a2,a3 FROM a WHERE NOT EXISTS (SELECT * FROM b WHERE a1 = b1 OR a1 = b2)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null
 └── intersect-all
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      ├── left columns: a1:15 a2:16 a3:17 a4:18
      ├── right columns: a1:28 a2:29 a3:30 a4:31
      ├── key: (1-4)
      ├── anti-join (merge)
      │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
      │    ├── left ordering: +15
      │    ├── right ordering: +21
      │    ├── key: (15-18)
      │    ├── scan a
      │    │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
      │    │    ├── key: (15-18)
      │    │    └── ordering: +15
      │    ├── scan b@b_b1_b2_idx
      │    │    ├── columns: b1:21
      │    │    └── ordering: +21
      │    └── filters (true)
      └── anti-join (merge)
           ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
           ├── left ordering: +28
           ├── right ordering: +35
           ├── key: (28-31)
           ├── scan a
           │    ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
           │    ├── key: (28-31)
           │    └── ordering: +28
           ├── scan b@b_b2_idx
           │    ├── columns: b2:35
           │    └── ordering: +35
           └── filters (true)

# The left side of the join already produces key columns
opt expect=SplitDisjunctionOfAntiJoinTerms
SELECT * FROM a WHERE NOT EXISTS (SELECT * FROM b WHERE a1 = b1 OR a2 = b2 OR a3 = b3 OR a4 = b4)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── key: (1-4)
 └── intersect-all
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      ├── left columns: a1:15 a2:16 a3:17 a4:18
      ├── right columns: a1:28 a2:29 a3:30 a4:31
      ├── key: (1-4)
      ├── anti-join (merge)
      │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
      │    ├── left ordering: +15
      │    ├── right ordering: +21
      │    ├── key: (15-18)
      │    ├── scan a
      │    │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
      │    │    ├── key: (15-18)
      │    │    └── ordering: +15
      │    ├── scan b@b_b1_b2_idx
      │    │    ├── columns: b1:21
      │    │    └── ordering: +21
      │    └── filters (true)
      └── project
           ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
           ├── key: (28-31)
           └── intersect-all
                ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                ├── left columns: a1:41 a2:42 a3:43 a4:44
                ├── right columns: a1:54 a2:55 a3:56 a4:57
                ├── key: (28-31)
                ├── anti-join (hash)
                │    ├── columns: a1:41!null a2:42!null a3:43!null a4:44!null
                │    ├── key: (41-44)
                │    ├── scan a
                │    │    ├── columns: a1:41!null a2:42!null a3:43!null a4:44!null
                │    │    └── key: (41-44)
                │    ├── scan b
                │    │    └── columns: b4:50
                │    └── filters
                │         └── a4:44 = b4:50 [outer=(44,50), constraints=(/44: (/NULL - ]; /50: (/NULL - ]), fd=(44)==(50), (50)==(44)]
                └── project
                     ├── columns: a1:54!null a2:55!null a3:56!null a4:57!null
                     ├── key: (54-57)
                     └── intersect-all
                          ├── columns: a1:54!null a2:55!null a3:56!null a4:57!null
                          ├── left columns: a1:67 a2:68 a3:69 a4:70
                          ├── right columns: a1:80 a2:81 a3:82 a4:83
                          ├── key: (54-57)
                          ├── anti-join (hash)
                          │    ├── columns: a1:67!null a2:68!null a3:69!null a4:70!null
                          │    ├── key: (67-70)
                          │    ├── scan a
                          │    │    ├── columns: a1:67!null a2:68!null a3:69!null a4:70!null
                          │    │    └── key: (67-70)
                          │    ├── scan b
                          │    │    └── columns: b2:74
                          │    └── filters
                          │         └── a2:68 = b2:74 [outer=(68,74), constraints=(/68: (/NULL - ]; /74: (/NULL - ]), fd=(68)==(74), (74)==(68)]
                          └── anti-join (hash)
                               ├── columns: a1:80!null a2:81!null a3:82!null a4:83!null
                               ├── key: (80-83)
                               ├── scan a
                               │    ├── columns: a1:80!null a2:81!null a3:82!null a4:83!null
                               │    └── key: (80-83)
                               ├── scan b
                               │    └── columns: b3:88
                               └── filters
                                    └── a3:82 = b3:88 [outer=(82,88), constraints=(/82: (/NULL - ]; /88: (/NULL - ]), fd=(82)==(88), (88)==(82)]

# More than one disjunction in the filter
opt expect=SplitDisjunctionOfAntiJoinTerms
SELECT * FROM a WHERE NOT EXISTS (SELECT * FROM c WHERE a1 = c1 OR a2 = c2 OR a3 = c3 OR a4 = c4)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── key: (1-4)
 └── intersect-all
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      ├── left columns: a1:15 a2:16 a3:17 a4:18
      ├── right columns: a1:28 a2:29 a3:30 a4:31
      ├── key: (1-4)
      ├── anti-join (hash)
      │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
      │    ├── key: (15-18)
      │    ├── scan a
      │    │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
      │    │    └── key: (15-18)
      │    ├── scan c
      │    │    └── columns: c1:21
      │    └── filters
      │         └── a1:15 = c1:21 [outer=(15,21), constraints=(/15: (/NULL - ]; /21: (/NULL - ]), fd=(15)==(21), (21)==(15)]
      └── project
           ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
           ├── key: (28-31)
           └── intersect-all
                ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                ├── left columns: a1:41 a2:42 a3:43 a4:44
                ├── right columns: a1:54 a2:55 a3:56 a4:57
                ├── key: (28-31)
                ├── anti-join (hash)
                │    ├── columns: a1:41!null a2:42!null a3:43!null a4:44!null
                │    ├── key: (41-44)
                │    ├── scan a
                │    │    ├── columns: a1:41!null a2:42!null a3:43!null a4:44!null
                │    │    └── key: (41-44)
                │    ├── scan c
                │    │    └── columns: c4:50
                │    └── filters
                │         └── a4:44 = c4:50 [outer=(44,50), constraints=(/44: (/NULL - ]; /50: (/NULL - ]), fd=(44)==(50), (50)==(44)]
                └── project
                     ├── columns: a1:54!null a2:55!null a3:56!null a4:57!null
                     ├── key: (54-57)
                     └── intersect-all
                          ├── columns: a1:54!null a2:55!null a3:56!null a4:57!null
                          ├── left columns: a1:67 a2:68 a3:69 a4:70
                          ├── right columns: a1:80 a2:81 a3:82 a4:83
                          ├── key: (54-57)
                          ├── anti-join (hash)
                          │    ├── columns: a1:67!null a2:68!null a3:69!null a4:70!null
                          │    ├── key: (67-70)
                          │    ├── scan a
                          │    │    ├── columns: a1:67!null a2:68!null a3:69!null a4:70!null
                          │    │    └── key: (67-70)
                          │    ├── scan c
                          │    │    └── columns: c2:74
                          │    └── filters
                          │         └── a2:68 = c2:74 [outer=(68,74), constraints=(/68: (/NULL - ]; /74: (/NULL - ]), fd=(68)==(74), (74)==(68)]
                          └── anti-join (hash)
                               ├── columns: a1:80!null a2:81!null a3:82!null a4:83!null
                               ├── key: (80-83)
                               ├── scan a
                               │    ├── columns: a1:80!null a2:81!null a3:82!null a4:83!null
                               │    └── key: (80-83)
                               ├── scan c
                               │    └── columns: c3:88
                               └── filters
                                    └── a3:82 = c3:88 [outer=(82,88), constraints=(/82: (/NULL - ]; /88: (/NULL - ]), fd=(82)==(88), (88)==(82)]

opt expect=SplitDisjunctionOfAntiJoinTerms
SELECT * FROM a WHERE NOT EXISTS (SELECT * FROM c WHERE a1 = c2 OR a2 = c1 OR a3 = c4 OR a3 = c4)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── key: (1-4)
 └── intersect-all
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      ├── left columns: a1:15 a2:16 a3:17 a4:18
      ├── right columns: a1:28 a2:29 a3:30 a4:31
      ├── key: (1-4)
      ├── anti-join (hash)
      │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
      │    ├── key: (15-18)
      │    ├── scan a
      │    │    ├── columns: a1:15!null a2:16!null a3:17!null a4:18!null
      │    │    └── key: (15-18)
      │    ├── scan c
      │    │    └── columns: c2:22
      │    └── filters
      │         └── a1:15 = c2:22 [outer=(15,22), constraints=(/15: (/NULL - ]; /22: (/NULL - ]), fd=(15)==(22), (22)==(15)]
      └── project
           ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
           ├── key: (28-31)
           └── intersect-all
                ├── columns: a1:28!null a2:29!null a3:30!null a4:31!null
                ├── left columns: a1:41 a2:42 a3:43 a4:44
                ├── right columns: a1:54 a2:55 a3:56 a4:57
                ├── key: (28-31)
                ├── anti-join (hash)
                │    ├── columns: a1:41!null a2:42!null a3:43!null a4:44!null
                │    ├── key: (41-44)
                │    ├── scan a
                │    │    ├── columns: a1:41!null a2:42!null a3:43!null a4:44!null
                │    │    └── key: (41-44)
                │    ├── scan c
                │    │    └── columns: c4:50
                │    └── filters
                │         └── a3:43 = c4:50 [outer=(43,50), constraints=(/43: (/NULL - ]; /50: (/NULL - ]), fd=(43)==(50), (50)==(43)]
                └── project
                     ├── columns: a1:54!null a2:55!null a3:56!null a4:57!null
                     ├── key: (54-57)
                     └── intersect-all
                          ├── columns: a1:54!null a2:55!null a3:56!null a4:57!null
                          ├── left columns: a1:67 a2:68 a3:69 a4:70
                          ├── right columns: a1:80 a2:81 a3:82 a4:83
                          ├── key: (54-57)
                          ├── anti-join (hash)
                          │    ├── columns: a1:67!null a2:68!null a3:69!null a4:70!null
                          │    ├── key: (67-70)
                          │    ├── scan a
                          │    │    ├── columns: a1:67!null a2:68!null a3:69!null a4:70!null
                          │    │    └── key: (67-70)
                          │    ├── scan c
                          │    │    └── columns: c1:73
                          │    └── filters
                          │         └── a2:68 = c1:73 [outer=(68,73), constraints=(/68: (/NULL - ]; /73: (/NULL - ]), fd=(68)==(73), (73)==(68)]
                          └── anti-join (hash)
                               ├── columns: a1:80!null a2:81!null a3:82!null a4:83!null
                               ├── key: (80-83)
                               ├── scan a
                               │    ├── columns: a1:80!null a2:81!null a3:82!null a4:83!null
                               │    └── key: (80-83)
                               ├── scan c
                               │    └── columns: c4:89
                               └── filters
                                    └── a3:82 = c4:89 [outer=(82,89), constraints=(/82: (/NULL - ]; /89: (/NULL - ]), fd=(82)==(89), (89)==(82)]

# Nested NOT EXISTS
# Not currently supported. Maybe needs better subquery decorrelation?
opt expect-not=SplitDisjunctionOfAntiJoinTerms
SELECT a2,a4 FROM a WHERE NOT EXISTS(SELECT * FROM b WHERE (a1=b1 OR a1=b2) AND NOT EXISTS(SELECT 1 FROM c WHERE b1=c1))
----
project
 ├── columns: a2:2!null a4:4!null
 └── anti-join (cross)
      ├── columns: a1:1!null a2:2!null a4:4!null
      ├── scan a
      │    └── columns: a1:1!null a2:2!null a4:4!null
      ├── anti-join (hash)
      │    ├── columns: b1:7 b2:8
      │    ├── scan b
      │    │    └── columns: b1:7 b2:8
      │    ├── scan c
      │    │    └── columns: c1:14
      │    └── filters
      │         └── b1:7 = c1:14 [outer=(7,14), constraints=(/7: (/NULL - ]; /14: (/NULL - ]), fd=(7)==(14), (14)==(7)]
      └── filters
           └── (a1:1 = b1:7) OR (a1:1 = b2:8) [outer=(1,7,8), constraints=(/1: (/NULL - ])]

# Two NOT EXISTS at same nesting level; only one disjuction pair can be optimized
opt expect=SplitDisjunctionOfAntiJoinTerms
SELECT a2,a4 FROM a WHERE NOT EXISTS(SELECT * FROM b WHERE a1=b1 OR a1=b2) AND
                          NOT EXISTS(SELECT * FROM c WHERE a1=c1 OR a1=c2)
----
project
 ├── columns: a2:2!null a4:4!null
 └── anti-join (cross)
      ├── columns: a1:1!null a2:2!null a4:4!null
      ├── project
      │    ├── columns: a1:1!null a2:2!null a4:4!null
      │    └── intersect-all
      │         ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
      │         ├── left columns: a1:49 a2:50 a3:51 a4:52
      │         ├── right columns: a1:62 a2:63 a3:64 a4:65
      │         ├── key: (1-4)
      │         ├── anti-join (merge)
      │         │    ├── columns: a1:49!null a2:50!null a3:51!null a4:52!null
      │         │    ├── left ordering: +49
      │         │    ├── right ordering: +55
      │         │    ├── key: (49-52)
      │         │    ├── scan a
      │         │    │    ├── columns: a1:49!null a2:50!null a3:51!null a4:52!null
      │         │    │    ├── key: (49-52)
      │         │    │    └── ordering: +49
      │         │    ├── scan b@b_b1_b2_idx
      │         │    │    ├── columns: b1:55
      │         │    │    └── ordering: +55
      │         │    └── filters (true)
      │         └── anti-join (merge)
      │              ├── columns: a1:62!null a2:63!null a3:64!null a4:65!null
      │              ├── left ordering: +62
      │              ├── right ordering: +69
      │              ├── key: (62-65)
      │              ├── scan a
      │              │    ├── columns: a1:62!null a2:63!null a3:64!null a4:65!null
      │              │    ├── key: (62-65)
      │              │    └── ordering: +62
      │              ├── scan b@b_b2_idx
      │              │    ├── columns: b2:69
      │              │    └── ordering: +69
      │              └── filters (true)
      ├── scan c
      │    └── columns: c1:14 c2:15
      └── filters
           └── (a1:1 = c1:14) OR (a1:1 = c2:15) [outer=(1,14,15), constraints=(/1: (/NULL - ])]

# Outer Select is Join
build-post-queries expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a JOIN (SELECT * FROM b WHERE b1 > 0 AND NOT EXISTS (SELECT 1 FROM c WHERE c1=b1))
                       AS foo on a1=foo.b1 OR a2=foo.b2
----
root
 └── project
      ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7!null b2:8 b3:9 b4:10
      └── inner-join (cross)
           ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a.crdb_internal_mvcc_timestamp:5 a.tableoid:6 b1:7!null b2:8 b3:9 b4:10
           ├── fd: (1-4)-->(5,6)
           ├── scan a
           │    ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a.crdb_internal_mvcc_timestamp:5 a.tableoid:6
           │    ├── key: (1-4)
           │    └── fd: (1-4)-->(5,6)
           ├── project
           │    ├── columns: b1:7!null b2:8 b3:9 b4:10
           │    └── select
           │         ├── columns: b1:7!null b2:8 b3:9 b4:10 b.rowid:11!null b.crdb_internal_mvcc_timestamp:12 b.tableoid:13
           │         ├── key: (11)
           │         ├── fd: (11)-->(7-10,12,13)
           │         ├── scan b
           │         │    ├── columns: b1:7 b2:8 b3:9 b4:10 b.rowid:11!null b.crdb_internal_mvcc_timestamp:12 b.tableoid:13
           │         │    ├── key: (11)
           │         │    └── fd: (11)-->(7-10,12,13)
           │         └── filters
           │              └── and [outer=(7), correlated-subquery, constraints=(/7: [/1 - ])]
           │                   ├── b1:7 > 0
           │                   └── not
           │                        └── exists
           │                             └── project
           │                                  ├── columns: "?column?":21!null
           │                                  ├── outer: (7)
           │                                  ├── fd: ()-->(21)
           │                                  ├── select
           │                                  │    ├── columns: c1:14!null c2:15 c3:16 c4:17 c.rowid:18!null c.crdb_internal_mvcc_timestamp:19 c.tableoid:20
           │                                  │    ├── outer: (7)
           │                                  │    ├── key: (18)
           │                                  │    ├── fd: ()-->(14), (18)-->(15-17,19,20)
           │                                  │    ├── scan c
           │                                  │    │    ├── columns: c1:14 c2:15 c3:16 c4:17 c.rowid:18!null c.crdb_internal_mvcc_timestamp:19 c.tableoid:20
           │                                  │    │    ├── key: (18)
           │                                  │    │    └── fd: (18)-->(14-17,19,20)
           │                                  │    └── filters
           │                                  │         └── c1:14 = b1:7 [outer=(7,14), constraints=(/7: (/NULL - ]; /14: (/NULL - ]), fd=(7)==(14), (14)==(7)]
           │                                  └── projections
           │                                       └── 1 [as="?column?":21]
           └── filters
                └── (a1:1 = b1:7) OR (a2:2 = b2:8) [outer=(1,2,7,8)]

opt expect=SplitDisjunctionOfAntiJoinTerms
SELECT * FROM a JOIN (SELECT * FROM b WHERE b1 > 0 AND NOT EXISTS (SELECT 1 FROM c WHERE c1=b1 or c2=b2))
                       AS foo on a1=foo.b1
----
inner-join (lookup a)
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null b1:7!null b2:8 b3:9 b4:10
 ├── key columns: [7] = [1]
 ├── fd: (1)==(7), (7)==(1)
 ├── project
 │    ├── columns: b1:7!null b2:8 b3:9 b4:10
 │    └── intersect-all
 │         ├── columns: b1:7!null b2:8 b3:9 b4:10 b.rowid:11!null
 │         ├── left columns: b1:23 b2:24 b3:25 b4:26 b.rowid:27
 │         ├── right columns: b1:37 b2:38 b3:39 b4:40 b.rowid:41
 │         ├── key: (11)
 │         ├── fd: (11)-->(7-10)
 │         ├── anti-join (hash)
 │         │    ├── columns: b1:23!null b2:24 b3:25 b4:26 b.rowid:27!null
 │         │    ├── key: (27)
 │         │    ├── fd: (27)-->(23-26)
 │         │    ├── scan b@b_b1_b2_idx
 │         │    │    ├── columns: b1:23!null b2:24 b3:25 b4:26 b.rowid:27!null
 │         │    │    ├── constraint: /23/24/27: [/1 - ]
 │         │    │    ├── key: (27)
 │         │    │    └── fd: (27)-->(23-26)
 │         │    ├── scan c
 │         │    │    └── columns: c1:30
 │         │    └── filters
 │         │         └── c1:30 = b1:23 [outer=(23,30), constraints=(/23: (/NULL - ]; /30: (/NULL - ]), fd=(23)==(30), (30)==(23)]
 │         └── anti-join (hash)
 │              ├── columns: b1:37!null b2:38 b3:39 b4:40 b.rowid:41!null
 │              ├── key: (41)
 │              ├── fd: (41)-->(37-40)
 │              ├── scan b@b_b1_b2_idx
 │              │    ├── columns: b1:37!null b2:38 b3:39 b4:40 b.rowid:41!null
 │              │    ├── constraint: /37/38/41: [/1 - ]
 │              │    ├── key: (41)
 │              │    └── fd: (41)-->(37-40)
 │              ├── scan c
 │              │    └── columns: c2:45
 │              └── filters
 │                   └── c2:45 = b2:38 [outer=(38,45), constraints=(/38: (/NULL - ]; /45: (/NULL - ]), fd=(38)==(45), (45)==(38)]
 └── filters (true)

# We should be able to split the disjunction even though the predicates are
# equalities but not equijoin expressions.
# TODO(rytaft): investigate whether bad stats are causing us to select the
# wrong plan.
opt expect=SplitDisjunctionOfAntiJoinTerms
SELECT
  e.*
FROM
  e
WHERE NOT EXISTS (
  SELECT *
  FROM f
  WHERE ((e5 = 30 AND f2 = 40) OR (e3 = 10 AND f2 = 20)) AND f3 = e2
);
----
anti-join (hash)
 ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan e
 │    ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5
 │    ├── partial index predicates
 │    │    └── e_e3_idx: filters
 │    │         └── e4:4 IN ('a', 'b') [outer=(4), constraints=(/4: [/'a' - /'a'] [/'b' - /'b']; tight)]
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 ├── scan f@f_f2_idx
 │    └── columns: f2:9 f3:10
 └── filters
      ├── ((e5:5 = 30) AND (f2:9 = 40)) OR ((e3:3 = 10) AND (f2:9 = 20)) [outer=(3,5,9), constraints=(/9: [/20 - /20] [/40 - /40])]
      └── f3:10 = e2:2 [outer=(2,10), constraints=(/2: (/NULL - ]; /10: (/NULL - ]), fd=(2)==(10), (10)==(2)]

# We should be able to split the disjunction even though the predicates are
# not equalities.
# TODO(rytaft): investigate whether bad stats are causing us to select the
# wrong plan.
opt expect=SplitDisjunctionOfAntiJoinTerms
SELECT
  e.*
FROM
  e
WHERE NOT EXISTS (
  SELECT *
  FROM f
  WHERE (
    (e.e5 > 30 AND e.e5 < 40)
    OR (e.e4 IN ('a', 'b') AND f.f2 < 20 AND f.f2 > 10)
  ) AND f3 = e2
)
----
anti-join (hash)
 ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan e
 │    ├── columns: e1:1!null e2:2!null e3:3!null e4:4!null e5:5
 │    ├── partial index predicates
 │    │    └── e_e3_idx: filters
 │    │         └── e4:4 IN ('a', 'b') [outer=(4), constraints=(/4: [/'a' - /'a'] [/'b' - /'b']; tight)]
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 ├── scan f@f_f2_idx
 │    └── columns: f2:9 f3:10
 └── filters
      ├── ((e5:5 > 30) AND (e5:5 < 40)) OR (((e4:4 IN ('a', 'b')) AND (f2:9 < 20)) AND (f2:9 > 10)) [outer=(4,5,9)]
      └── f3:10 = e2:2 [outer=(2,10), constraints=(/2: (/NULL - ]; /10: (/NULL - ]), fd=(2)==(10), (10)==(2)]

# NOT IN subquery
opt expect=SplitDisjunctionOfJoinTerms
SELECT d3,d1,d2 FROM d WHERE d1 NOT IN (SELECT b1 FROM b WHERE EXISTS (SELECT 1 FROM c WHERE c2=b2 OR c2=b3))
----
anti-join (cross)
 ├── columns: d3:3 d1:1 d2:2
 ├── scan d
 │    └── columns: d1:1 d2:2 d3:3
 ├── project
 │    ├── columns: b1:8 b2:9 b3:10
 │    └── distinct-on
 │         ├── columns: b1:8 b2:9 b3:10 b.rowid:12!null
 │         ├── grouping columns: b.rowid:12!null
 │         ├── key: (12)
 │         ├── fd: (12)-->(8-10)
 │         ├── union-all
 │         │    ├── columns: b1:8 b2:9 b3:10 b.rowid:12!null
 │         │    ├── left columns: b1:25 b2:26 b3:27 b.rowid:29
 │         │    ├── right columns: b1:39 b2:40 b3:41 b.rowid:43
 │         │    ├── project
 │         │    │    ├── columns: b1:25 b2:26 b3:27 b.rowid:29!null
 │         │    │    ├── key: (29)
 │         │    │    ├── fd: (29)-->(25-27)
 │         │    │    └── inner-join (hash)
 │         │    │         ├── columns: b1:25 b2:26!null b3:27 b.rowid:29!null c2:33!null
 │         │    │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │         │    │         ├── key: (29)
 │         │    │         ├── fd: (29)-->(25-27), (26)==(33), (33)==(26)
 │         │    │         ├── scan b
 │         │    │         │    ├── columns: b1:25 b2:26 b3:27 b.rowid:29!null
 │         │    │         │    ├── key: (29)
 │         │    │         │    └── fd: (29)-->(25-27)
 │         │    │         ├── distinct-on
 │         │    │         │    ├── columns: c2:33
 │         │    │         │    ├── grouping columns: c2:33
 │         │    │         │    ├── key: (33)
 │         │    │         │    └── scan c
 │         │    │         │         └── columns: c2:33
 │         │    │         └── filters
 │         │    │              └── c2:33 = b2:26 [outer=(26,33), constraints=(/26: (/NULL - ]; /33: (/NULL - ]), fd=(26)==(33), (33)==(26)]
 │         │    └── project
 │         │         ├── columns: b1:39 b2:40 b3:41 b.rowid:43!null
 │         │         ├── key: (43)
 │         │         ├── fd: (43)-->(39-41)
 │         │         └── inner-join (hash)
 │         │              ├── columns: b1:39 b2:40 b3:41!null b.rowid:43!null c2:47!null
 │         │              ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │         │              ├── key: (43)
 │         │              ├── fd: (43)-->(39-41), (41)==(47), (47)==(41)
 │         │              ├── scan b
 │         │              │    ├── columns: b1:39 b2:40 b3:41 b.rowid:43!null
 │         │              │    ├── key: (43)
 │         │              │    └── fd: (43)-->(39-41)
 │         │              ├── distinct-on
 │         │              │    ├── columns: c2:47
 │         │              │    ├── grouping columns: c2:47
 │         │              │    ├── key: (47)
 │         │              │    └── scan c
 │         │              │         └── columns: c2:47
 │         │              └── filters
 │         │                   └── c2:47 = b3:41 [outer=(41,47), constraints=(/41: (/NULL - ]; /47: (/NULL - ]), fd=(41)==(47), (47)==(41)]
 │         └── aggregations
 │              ├── const-agg [as=b1:8, outer=(8)]
 │              │    └── b1:8
 │              ├── const-agg [as=b2:9, outer=(9)]
 │              │    └── b2:9
 │              └── const-agg [as=b3:10, outer=(10)]
 │                   └── b3:10
 └── filters
      └── (d1:1 = b1:8) IS NOT false [outer=(1,8)]

# NOT IN subquery, 2 columns
opt expect=SplitDisjunctionOfJoinTerms
SELECT d3,d1,d2 FROM d WHERE (d1,d3) NOT IN (SELECT b1,b2 FROM b WHERE EXISTS (SELECT 1 FROM c WHERE c2=b2 OR c2=b3))
----
anti-join (cross)
 ├── columns: d3:3 d1:1 d2:2
 ├── immutable
 ├── scan d
 │    └── columns: d1:1 d2:2 d3:3
 ├── project
 │    ├── columns: column24:24
 │    ├── project
 │    │    ├── columns: b1:8 b2:9 b3:10
 │    │    └── distinct-on
 │    │         ├── columns: b1:8 b2:9 b3:10 b.rowid:12!null
 │    │         ├── grouping columns: b.rowid:12!null
 │    │         ├── key: (12)
 │    │         ├── fd: (12)-->(8-10)
 │    │         ├── union-all
 │    │         │    ├── columns: b1:8 b2:9 b3:10 b.rowid:12!null
 │    │         │    ├── left columns: b1:26 b2:27 b3:28 b.rowid:30
 │    │         │    ├── right columns: b1:40 b2:41 b3:42 b.rowid:44
 │    │         │    ├── project
 │    │         │    │    ├── columns: b1:26 b2:27 b3:28 b.rowid:30!null
 │    │         │    │    ├── key: (30)
 │    │         │    │    ├── fd: (30)-->(26-28)
 │    │         │    │    └── inner-join (hash)
 │    │         │    │         ├── columns: b1:26 b2:27!null b3:28 b.rowid:30!null c2:34!null
 │    │         │    │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │    │         │    │         ├── key: (30)
 │    │         │    │         ├── fd: (30)-->(26-28), (27)==(34), (34)==(27)
 │    │         │    │         ├── scan b
 │    │         │    │         │    ├── columns: b1:26 b2:27 b3:28 b.rowid:30!null
 │    │         │    │         │    ├── key: (30)
 │    │         │    │         │    └── fd: (30)-->(26-28)
 │    │         │    │         ├── distinct-on
 │    │         │    │         │    ├── columns: c2:34
 │    │         │    │         │    ├── grouping columns: c2:34
 │    │         │    │         │    ├── key: (34)
 │    │         │    │         │    └── scan c
 │    │         │    │         │         └── columns: c2:34
 │    │         │    │         └── filters
 │    │         │    │              └── c2:34 = b2:27 [outer=(27,34), constraints=(/27: (/NULL - ]; /34: (/NULL - ]), fd=(27)==(34), (34)==(27)]
 │    │         │    └── project
 │    │         │         ├── columns: b1:40 b2:41 b3:42 b.rowid:44!null
 │    │         │         ├── key: (44)
 │    │         │         ├── fd: (44)-->(40-42)
 │    │         │         └── inner-join (hash)
 │    │         │              ├── columns: b1:40 b2:41 b3:42!null b.rowid:44!null c2:48!null
 │    │         │              ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │    │         │              ├── key: (44)
 │    │         │              ├── fd: (44)-->(40-42), (42)==(48), (48)==(42)
 │    │         │              ├── scan b
 │    │         │              │    ├── columns: b1:40 b2:41 b3:42 b.rowid:44!null
 │    │         │              │    ├── key: (44)
 │    │         │              │    └── fd: (44)-->(40-42)
 │    │         │              ├── distinct-on
 │    │         │              │    ├── columns: c2:48
 │    │         │              │    ├── grouping columns: c2:48
 │    │         │              │    ├── key: (48)
 │    │         │              │    └── scan c
 │    │         │              │         └── columns: c2:48
 │    │         │              └── filters
 │    │         │                   └── c2:48 = b3:42 [outer=(42,48), constraints=(/42: (/NULL - ]; /48: (/NULL - ]), fd=(42)==(48), (48)==(42)]
 │    │         └── aggregations
 │    │              ├── const-agg [as=b1:8, outer=(8)]
 │    │              │    └── b1:8
 │    │              ├── const-agg [as=b2:9, outer=(9)]
 │    │              │    └── b2:9
 │    │              └── const-agg [as=b3:10, outer=(10)]
 │    │                   └── b3:10
 │    └── projections
 │         └── (b1:8, b2:9) [as=column24:24, outer=(8,9)]
 └── filters
      └── (column24:24 = (d1:1, d3:3)) IS NOT false [outer=(1,3,24), immutable]

# The conjuncts under the top-level disjunct contain no join terms directly,
# but are nested in another OR. Join terms should still be detected.
opt expect=SplitDisjunctionOfJoinTerms
SELECT * FROM a t1, a t2 WHERE (((t1.a1 = t2.a1 OR t1.a1 = t2.a2) AND (t1.a2 = t2.a2 OR t1.a2 = t2.a2)) OR
                                ((t1.a1+1 = t2.a1 OR t1.a1+4 = t2.a2) AND (t1.a2+8 = t2.a2 OR t1.a2+9 = t2.a2)))
----
inner-join (cross)
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a1:7!null a2:8!null a3:9!null a4:10!null
 ├── immutable
 ├── key: (1-4,7-10)
 ├── scan a [as=t1]
 │    ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null
 │    └── key: (1-4)
 ├── scan a [as=t2]
 │    ├── columns: t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null
 │    └── key: (7-10)
 └── filters
      └── (((t1.a1:1 = t2.a1:7) OR (t1.a1:1 = t2.a2:8)) AND (t1.a2:2 = t2.a2:8)) OR (((t2.a1:7 = (t1.a1:1 + 1)) OR (t2.a2:8 = (t1.a1:1 + 4))) AND ((t2.a2:8 = (t1.a2:2 + 8)) OR (t2.a2:8 = (t1.a2:2 + 9)))) [outer=(1,2,7,8), immutable, constraints=(/8: (/NULL - ])]

# We can split the disjunction here even if the setting
# optimizer_use_improved_split_disjunction_for_joins is false, since we
# don't require bare variables on either side of the join equalities.
opt expect=SplitDisjunctionOfJoinTerms set=optimizer_use_improved_split_disjunction_for_joins=false
SELECT * FROM a t1, a t2 WHERE (t1.a2 = t2.a2+1 OR t1.a3 = t2.a3+1) AND (t1.a1 = t2.a1+1 OR t1.a4 = t2.a4+1)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a1:7!null a2:8!null a3:9!null a4:10!null
 ├── immutable
 ├── key: (1-4,7-10)
 └── distinct-on
      ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null
      ├── grouping columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null
      ├── immutable
      ├── key: (1-4,7-10)
      └── union-all
           ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null
           ├── left columns: t1.a1:13 t1.a2:14 t1.a3:15 t1.a4:16 t2.a1:19 t2.a2:20 t2.a3:21 t2.a4:22
           ├── right columns: t1.a1:25 t1.a2:26 t1.a3:27 t1.a4:28 t2.a1:31 t2.a2:32 t2.a3:33 t2.a4:34
           ├── immutable
           ├── project
           │    ├── columns: t1.a1:13!null t1.a2:14!null t1.a3:15!null t1.a4:16!null t2.a1:19!null t2.a2:20!null t2.a3:21!null t2.a4:22!null
           │    ├── immutable
           │    ├── key: (13,15,16,19-22)
           │    ├── fd: (20)-->(14)
           │    └── inner-join (hash)
           │         ├── columns: t1.a1:13!null t1.a2:14!null t1.a3:15!null t1.a4:16!null t2.a1:19!null t2.a2:20!null t2.a3:21!null t2.a4:22!null column37:37!null
           │         ├── immutable
           │         ├── key: (13,15,16,19-22)
           │         ├── fd: (20)-->(37), (14)==(37), (37)==(14)
           │         ├── scan a [as=t1]
           │         │    ├── columns: t1.a1:13!null t1.a2:14!null t1.a3:15!null t1.a4:16!null
           │         │    └── key: (13-16)
           │         ├── project
           │         │    ├── columns: column37:37!null t2.a1:19!null t2.a2:20!null t2.a3:21!null t2.a4:22!null
           │         │    ├── immutable
           │         │    ├── key: (19-22)
           │         │    ├── fd: (20)-->(37)
           │         │    ├── scan a [as=t2]
           │         │    │    ├── columns: t2.a1:19!null t2.a2:20!null t2.a3:21!null t2.a4:22!null
           │         │    │    └── key: (19-22)
           │         │    └── projections
           │         │         └── t2.a2:20 + 1 [as=column37:37, outer=(20), immutable]
           │         └── filters
           │              ├── (t1.a1:13 = (t2.a1:19 + 1)) OR (t1.a4:16 = (t2.a4:22 + 1)) [outer=(13,16,19,22), immutable]
           │              └── t1.a2:14 = column37:37 [outer=(14,37), constraints=(/14: (/NULL - ]; /37: (/NULL - ]), fd=(14)==(37), (37)==(14)]
           └── project
                ├── columns: t1.a1:25!null t1.a2:26!null t1.a3:27!null t1.a4:28!null t2.a1:31!null t2.a2:32!null t2.a3:33!null t2.a4:34!null
                ├── immutable
                ├── key: (25,26,28,31-34)
                ├── fd: (33)-->(27)
                └── inner-join (hash)
                     ├── columns: t1.a1:25!null t1.a2:26!null t1.a3:27!null t1.a4:28!null t2.a1:31!null t2.a2:32!null t2.a3:33!null t2.a4:34!null column38:38!null
                     ├── immutable
                     ├── key: (25,26,28,31-34)
                     ├── fd: (33)-->(38), (27)==(38), (38)==(27)
                     ├── scan a [as=t1]
                     │    ├── columns: t1.a1:25!null t1.a2:26!null t1.a3:27!null t1.a4:28!null
                     │    └── key: (25-28)
                     ├── project
                     │    ├── columns: column38:38!null t2.a1:31!null t2.a2:32!null t2.a3:33!null t2.a4:34!null
                     │    ├── immutable
                     │    ├── key: (31-34)
                     │    ├── fd: (33)-->(38)
                     │    ├── scan a [as=t2]
                     │    │    ├── columns: t2.a1:31!null t2.a2:32!null t2.a3:33!null t2.a4:34!null
                     │    │    └── key: (31-34)
                     │    └── projections
                     │         └── t2.a3:33 + 1 [as=column38:38, outer=(33), immutable]
                     └── filters
                          ├── (t1.a1:25 = (t2.a1:31 + 1)) OR (t1.a4:28 = (t2.a4:34 + 1)) [outer=(25,28,31,34), immutable]
                          └── t1.a3:27 = column38:38 [outer=(27,38), constraints=(/27: (/NULL - ]; /38: (/NULL - ]), fd=(27)==(38), (38)==(27)]

# Negative Tests.
# No interesting terms. Must be at least one equality predicate referencing
# columns from both tables if optimizer_use_improved_split_disjunction_for_joins
# is false.
opt expect-not=SplitDisjunctionOfJoinTerms set=optimizer_use_improved_split_disjunction_for_joins=false
SELECT * FROM a t1, a t2 WHERE (t1.a2 > t2.a2 AND t1.a3 > t2.a3 OR t2.a2 = 2)
----
inner-join (cross)
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a1:7!null a2:8!null a3:9!null a4:10!null
 ├── key: (1-4,7-10)
 ├── scan a [as=t1]
 │    ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null
 │    └── key: (1-4)
 ├── scan a [as=t2]
 │    ├── columns: t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null
 │    └── key: (7-10)
 └── filters
      └── ((t1.a2:2 > t2.a2:8) AND (t1.a3:3 > t2.a3:9)) OR (t2.a2:8 = 2) [outer=(2,3,8,9), constraints=(/8: (/NULL - ])]

# Outer join not supported with this optimization
opt expect-not=SplitDisjunctionOfJoinTerms
SELECT * FROM a t1 LEFT OUTER JOIN a t2 ON (t1.a2 = t2.a2 OR t1.a3 = t2.a3)
----
left-join (cross)
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a1:7 a2:8 a3:9 a4:10
 ├── key: (1-4,7-10)
 ├── scan a [as=t1]
 │    ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null
 │    └── key: (1-4)
 ├── scan a [as=t2]
 │    ├── columns: t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null
 │    └── key: (7-10)
 └── filters
      └── (t1.a2:2 = t2.a2:8) OR (t1.a3:3 = t2.a3:9) [outer=(2,3,8,9)]

# Outer join not supported with this optimization
opt expect-not=SplitDisjunctionOfJoinTerms
SELECT * FROM a t1 FULL OUTER JOIN a t2 ON (t1.a2 = t2.a2 OR t1.a3 = t2.a3)
----
full-join (cross)
 ├── columns: a1:1 a2:2 a3:3 a4:4 a1:7 a2:8 a3:9 a4:10
 ├── key: (1-4,7-10)
 ├── scan a [as=t1]
 │    ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null
 │    └── key: (1-4)
 ├── scan a [as=t2]
 │    ├── columns: t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null
 │    └── key: (7-10)
 └── filters
      └── (t1.a2:2 = t2.a2:8) OR (t1.a3:3 = t2.a3:9) [outer=(2,3,8,9)]

# Complex join condition where we can't decorrelate, so the join terms aren't
# between 2 scans.
opt expect-not=SplitDisjunctionOfJoinTerms
SELECT * FROM a t1 WHERE a2 IN (SELECT a2 from a t2 WHERE t1.a3 = t2.a3 OR t1.a1 = t2.a1)
                   OR    a2 IN (SELECT a2 from a t3 WHERE t3.a3 = t3.a3 OR t3.a1 = t3.a1)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── key: (1-4)
 └── select
      ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null case:21
      ├── key: (1-4)
      ├── fd: (1-4)-->(21)
      ├── project
      │    ├── columns: case:21 t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null
      │    ├── key: (1-4)
      │    ├── fd: (1-4)-->(21)
      │    ├── group-by (hash)
      │    │    ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null bool_or:20
      │    │    ├── grouping columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null
      │    │    ├── key: (1-4)
      │    │    ├── fd: (1-4)-->(20)
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null t2.a1:7 t2.a2:8 t2.a3:9 notnull:19
      │    │    │    ├── fd: (8)-->(19)
      │    │    │    ├── scan a [as=t1]
      │    │    │    │    ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null
      │    │    │    │    └── key: (1-4)
      │    │    │    ├── project
      │    │    │    │    ├── columns: notnull:19!null t2.a1:7!null t2.a2:8!null t2.a3:9!null
      │    │    │    │    ├── fd: (8)-->(19)
      │    │    │    │    ├── scan a [as=t2]
      │    │    │    │    │    └── columns: t2.a1:7!null t2.a2:8!null t2.a3:9!null
      │    │    │    │    └── projections
      │    │    │    │         └── t2.a2:8 IS NOT NULL [as=notnull:19, outer=(8)]
      │    │    │    └── filters
      │    │    │         ├── (t1.a3:3 = t2.a3:9) OR (t1.a1:1 = t2.a1:7) [outer=(1,3,7,9)]
      │    │    │         └── t1.a2:2 = t2.a2:8 [outer=(2,8), constraints=(/2: (/NULL - ]; /8: (/NULL - ]), fd=(2)==(8), (8)==(2)]
      │    │    └── aggregations
      │    │         └── bool-or [as=bool_or:20, outer=(19)]
      │    │              └── notnull:19
      │    └── projections
      │         └── CASE WHEN bool_or:20 AND (t1.a2:2 IS NOT NULL) THEN true WHEN bool_or:20 IS NULL THEN false ELSE CAST(NULL AS BOOL) END [as=case:21, outer=(2,20)]
      └── filters
           └── or [outer=(2,21), correlated-subquery]
                ├── case:21
                └── any: eq
                     ├── project
                     │    ├── columns: t3.a2:14!null
                     │    └── select
                     │         ├── columns: t3.a1:13!null t3.a2:14!null t3.a3:15!null
                     │         ├── scan a [as=t3]
                     │         │    └── columns: t3.a1:13!null t3.a2:14!null t3.a3:15!null
                     │         └── filters
                     │              └── ((t3.a3:15 IS DISTINCT FROM CAST(NULL AS INT8)) OR CAST(NULL AS BOOL)) OR ((t3.a1:13 IS DISTINCT FROM CAST(NULL AS INT8)) OR CAST(NULL AS BOOL)) [outer=(13,15)]
                     └── t1.a2:2

# Complex join condition where we can't decorrelate, so the join terms aren't
# between 2 scans.
opt expect-not=SplitDisjunctionOfJoinTerms
SELECT * FROM a t1 WHERE a2 NOT IN (SELECT a2 from a t2 WHERE t1.a3 = t2.a3 OR t1.a1 = t2.a1)
                   OR    a2 NOT IN (SELECT a2 from a t3 WHERE t3.a3 = t3.a3 OR t3.a1 = t3.a1)
----
project
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null
 ├── key: (1-4)
 └── select
      ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null case:21
      ├── key: (1-4)
      ├── fd: (1-4)-->(21)
      ├── project
      │    ├── columns: case:21 t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null
      │    ├── key: (1-4)
      │    ├── fd: (1-4)-->(21)
      │    ├── group-by (hash)
      │    │    ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null bool_or:20
      │    │    ├── grouping columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null
      │    │    ├── key: (1-4)
      │    │    ├── fd: (1-4)-->(20)
      │    │    ├── left-join (hash)
      │    │    │    ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null t2.a1:7 t2.a2:8 t2.a3:9 notnull:19
      │    │    │    ├── fd: (8)-->(19)
      │    │    │    ├── scan a [as=t1]
      │    │    │    │    ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null
      │    │    │    │    └── key: (1-4)
      │    │    │    ├── project
      │    │    │    │    ├── columns: notnull:19!null t2.a1:7!null t2.a2:8!null t2.a3:9!null
      │    │    │    │    ├── fd: (8)-->(19)
      │    │    │    │    ├── scan a [as=t2]
      │    │    │    │    │    └── columns: t2.a1:7!null t2.a2:8!null t2.a3:9!null
      │    │    │    │    └── projections
      │    │    │    │         └── t2.a2:8 IS NOT NULL [as=notnull:19, outer=(8)]
      │    │    │    └── filters
      │    │    │         ├── (t1.a3:3 = t2.a3:9) OR (t1.a1:1 = t2.a1:7) [outer=(1,3,7,9)]
      │    │    │         └── t1.a2:2 = t2.a2:8 [outer=(2,8), constraints=(/2: (/NULL - ]; /8: (/NULL - ]), fd=(2)==(8), (8)==(2)]
      │    │    └── aggregations
      │    │         └── bool-or [as=bool_or:20, outer=(19)]
      │    │              └── notnull:19
      │    └── projections
      │         └── CASE WHEN bool_or:20 AND (t1.a2:2 IS NOT NULL) THEN true WHEN bool_or:20 IS NULL THEN false ELSE CAST(NULL AS BOOL) END [as=case:21, outer=(2,20)]
      └── filters
           └── or [outer=(2,21), correlated-subquery]
                ├── NOT case:21
                └── not
                     └── any: eq
                          ├── project
                          │    ├── columns: t3.a2:14!null
                          │    └── select
                          │         ├── columns: t3.a1:13!null t3.a2:14!null t3.a3:15!null
                          │         ├── scan a [as=t3]
                          │         │    └── columns: t3.a1:13!null t3.a2:14!null t3.a3:15!null
                          │         └── filters
                          │              └── ((t3.a3:15 IS DISTINCT FROM CAST(NULL AS INT8)) OR CAST(NULL AS BOOL)) OR ((t3.a1:13 IS DISTINCT FROM CAST(NULL AS INT8)) OR CAST(NULL AS BOOL)) [outer=(13,15)]
                          └── t1.a2:2

# The conjuncts under the top-level disjunct contain no equijoin terms, and
# neither do the nested simple predicates. The rule does not fire in this
# case if optimizer_use_improved_split_disjunction_for_joins is false.
opt expect-not=SplitDisjunctionOfJoinTerms set=optimizer_use_improved_split_disjunction_for_joins=false
SELECT * FROM a t1, a t2 WHERE (((t1.a1 > t2.a1 OR t1.a1 <= t2.a2) AND (t1.a2 > t2.a2+1 OR t1.a1 <= t2.a2-1)) OR
                                ((t1.a1 > t2.a1+5 OR t1.a1 <= t2.a2+5) AND (t1.a2 > t2.a2+7 OR t1.a1 <= t2.a2-9)))
----
inner-join (cross)
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a1:7!null a2:8!null a3:9!null a4:10!null
 ├── immutable
 ├── key: (1-4,7-10)
 ├── scan a [as=t1]
 │    ├── columns: t1.a1:1!null t1.a2:2!null t1.a3:3!null t1.a4:4!null
 │    └── key: (1-4)
 ├── scan a [as=t2]
 │    ├── columns: t2.a1:7!null t2.a2:8!null t2.a3:9!null t2.a4:10!null
 │    └── key: (7-10)
 └── filters
      └── (((t1.a1:1 > t2.a1:7) OR (t1.a1:1 <= t2.a2:8)) AND ((t1.a2:2 > (t2.a2:8 + 1)) OR (t1.a1:1 <= (t2.a2:8 - 1)))) OR (((t1.a1:1 > (t2.a1:7 + 5)) OR (t1.a1:1 <= (t2.a2:8 + 5))) AND ((t1.a2:2 > (t2.a2:8 + 7)) OR (t1.a1:1 <= (t2.a2:8 - 9)))) [outer=(1,2,7,8), immutable, constraints=(/1: (/NULL - ])]

# Can't consume both OR terms with a join to a single Scan relation. Both sides
# of each interesting predicate must be applied on a simple Scan or Select.
opt expect-not=SplitDisjunctionOfJoinTerms
SELECT * FROM a t2 INNER JOIN a t1 ON TRUE LEFT OUTER JOIN a t3 ON (t1.a1 = t3.a1 OR t2.a2 = t3.a2)
----
left-join (cross)
 ├── columns: a1:1!null a2:2!null a3:3!null a4:4!null a1:7!null a2:8!null a3:9!null a4:10!null a1:13 a2:14 a3:15 a4:16
 ├── key: (1-4,7-10,13-16)
 ├── inner-join (cross)
 │    ├── columns: t2.a1:1!null t2.a2:2!null t2.a3:3!null t2.a4:4!null t1.a1:7!null t1.a2:8!null t1.a3:9!null t1.a4:10!null
 │    ├── key: (1-4,7-10)
 │    ├── scan a [as=t2]
 │    │    ├── columns: t2.a1:1!null t2.a2:2!null t2.a3:3!null t2.a4:4!null
 │    │    └── key: (1-4)
 │    ├── scan a [as=t1]
 │    │    ├── columns: t1.a1:7!null t1.a2:8!null t1.a3:9!null t1.a4:10!null
 │    │    └── key: (7-10)
 │    └── filters (true)
 ├── scan a [as=t3]
 │    ├── columns: t3.a1:13!null t3.a2:14!null t3.a3:15!null t3.a4:16!null
 │    └── key: (13-16)
 └── filters
      └── (t1.a1:7 = t3.a1:13) OR (t2.a2:2 = t3.a2:14) [outer=(2,7,13,14)]
