exec-ddl
CREATE TABLE a (k INT PRIMARY KEY, i INT, f FLOAT, s STRING, j JSON)
----
TABLE a
 ├── k int not null
 ├── i int
 ├── f float
 ├── s string
 ├── j jsonb
 └── INDEX primary
      └── k int not null

exec-ddl
CREATE TABLE xy (x INT PRIMARY KEY, y INT)
----
TABLE xy
 ├── x int not null
 ├── y int
 └── INDEX primary
      └── x int not null

exec-ddl
CREATE TABLE uv (u INT PRIMARY KEY, v INT)
----
TABLE uv
 ├── u int not null
 ├── v int
 └── INDEX primary
      └── u int not null

exec-ddl
CREATE TABLE cd (c INT PRIMARY KEY, d INT NOT NULL)
----
TABLE cd
 ├── c int not null
 ├── d int not null
 └── INDEX primary
      └── c int not null

# --------------------------------------------------
# DecorrelateJoin
# --------------------------------------------------
opt expect=DecorrelateJoin
SELECT * FROM a WHERE EXISTS(SELECT * FROM xy WHERE x=k)
----
semi-join (merge)
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── left ordering: +1
 ├── right ordering: +6
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    └── ordering: +1
 ├── scan xy
 │    ├── columns: x:6(int!null) y:7(int)
 │    ├── key: (6)
 │    ├── fd: (6)-->(7)
 │    └── ordering: +6
 └── filters (true)

opt expect=DecorrelateJoin
SELECT * FROM a WHERE NOT EXISTS(SELECT * FROM xy WHERE x=k)
----
anti-join (merge)
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── left ordering: +1
 ├── right ordering: +6
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    └── ordering: +1
 ├── scan xy
 │    ├── columns: x:6(int!null) y:7(int)
 │    ├── key: (6)
 │    ├── fd: (6)-->(7)
 │    └── ordering: +6
 └── filters (true)

# Decorrelate UPDATE statement.
opt expect=DecorrelateJoin
UPDATE xy SET (x, y)=(SELECT * FROM uv WHERE u=x)
----
update xy
 ├── columns: <none>
 ├── fetch columns: x:3(int) y:4(int)
 ├── update-mapping:
 │    ├──  u:5 => x:1
 │    └──  v:6 => y:2
 ├── cardinality: [0 - 0]
 ├── side-effects, mutations
 └── left-join (merge)
      ├── columns: x:3(int!null) y:4(int) u:5(int) v:6(int)
      ├── left ordering: +3
      ├── right ordering: +5
      ├── key: (3,5)
      ├── fd: (3)-->(4), (5)-->(6)
      ├── scan xy
      │    ├── columns: x:3(int!null) y:4(int)
      │    ├── key: (3)
      │    ├── fd: (3)-->(4)
      │    └── ordering: +3
      ├── scan uv
      │    ├── columns: u:5(int!null) v:6(int)
      │    ├── key: (5)
      │    ├── fd: (5)-->(6)
      │    └── ordering: +5
      └── filters (true)

# Decorrelate INSERT..ON CONFLICT statement.
opt expect=DecorrelateJoin
INSERT INTO xy VALUES (1,2), (3,4)
ON CONFLICT (x) DO UPDATE SET (x, y)=(SELECT * FROM uv WHERE u=excluded.x)
RETURNING *
----
upsert xy
 ├── columns: x:1(int!null) y:2(int)
 ├── canary column: 5
 ├── fetch columns: x:5(int) y:6(int)
 ├── insert-mapping:
 │    ├──  column1:3 => x:1
 │    └──  column2:4 => y:2
 ├── update-mapping:
 │    ├──  upsert_x:9 => x:1
 │    └──  upsert_y:10 => y:2
 ├── return-mapping:
 │    ├──  upsert_x:9 => x:1
 │    └──  upsert_y:10 => y:2
 ├── cardinality: [2 - ]
 ├── side-effects, mutations
 └── project
      ├── columns: upsert_x:9(int) upsert_y:10(int) column1:3(int) column2:4(int) x:5(int) y:6(int)
      ├── cardinality: [2 - ]
      ├── fd: (5)-->(6)
      ├── left-join (lookup uv)
      │    ├── columns: column1:3(int) column2:4(int) x:5(int) y:6(int) u:7(int) v:8(int)
      │    ├── key columns: [3] = [7]
      │    ├── cardinality: [2 - ]
      │    ├── fd: (5)-->(6), (7)-->(8)
      │    ├── left-join (lookup xy)
      │    │    ├── columns: column1:3(int) column2:4(int) x:5(int) y:6(int)
      │    │    ├── key columns: [3] = [5]
      │    │    ├── cardinality: [2 - ]
      │    │    ├── fd: (5)-->(6)
      │    │    ├── values
      │    │    │    ├── columns: column1:3(int) column2:4(int)
      │    │    │    ├── cardinality: [2 - 2]
      │    │    │    ├── (1, 2) [type=tuple{int, int}]
      │    │    │    └── (3, 4) [type=tuple{int, int}]
      │    │    └── filters (true)
      │    └── filters (true)
      └── projections
           ├── CASE WHEN x IS NULL THEN column1 ELSE u END [type=int, outer=(3,5,7)]
           └── CASE WHEN x IS NULL THEN column2 ELSE v END [type=int, outer=(4,5,8)]

# Decorrelate DELETE statement.
opt expect=DecorrelateJoin
DELETE FROM xy WHERE EXISTS(SELECT * FROM uv WHERE u=x)
----
delete xy
 ├── columns: <none>
 ├── fetch columns: x:3(int)
 ├── cardinality: [0 - 0]
 ├── side-effects, mutations
 └── semi-join (merge)
      ├── columns: x:3(int!null)
      ├── left ordering: +3
      ├── right ordering: +5
      ├── key: (3)
      ├── scan xy
      │    ├── columns: x:3(int!null)
      │    ├── key: (3)
      │    └── ordering: +3
      ├── scan uv
      │    ├── columns: u:5(int!null) v:6(int)
      │    ├── key: (5)
      │    ├── fd: (5)-->(6)
      │    └── ordering: +5
      └── filters (true)

# --------------------------------------------------
# DecorrelateProjectSet
# --------------------------------------------------

opt expect=DecorrelateProjectSet
SELECT generate_series(0, 5) FROM xy
----
inner-join
 ├── columns: generate_series:3(int)
 ├── side-effects
 ├── scan xy
 ├── project-set
 │    ├── columns: generate_series:3(int)
 │    ├── side-effects
 │    ├── values
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── tuple [type=tuple]
 │    └── zip
 │         └── function: generate_series [type=int, side-effects]
 │              ├── const: 0 [type=int]
 │              └── const: 5 [type=int]
 └── filters (true)

opt expect=DecorrelateProjectSet
SELECT * FROM a WHERE i IN (SELECT generate_series(k, i) FROM xy)
----
semi-join-apply
 ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb)
 ├── side-effects
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 ├── inner-join
 │    ├── columns: generate_series:8(int)
 │    ├── outer: (1,2)
 │    ├── side-effects
 │    ├── scan xy
 │    ├── project-set
 │    │    ├── columns: generate_series:8(int)
 │    │    ├── outer: (1,2)
 │    │    ├── side-effects
 │    │    ├── values
 │    │    │    ├── cardinality: [1 - 1]
 │    │    │    ├── key: ()
 │    │    │    └── tuple [type=tuple]
 │    │    └── zip
 │    │         └── function: generate_series [type=int, outer=(1,2), side-effects]
 │    │              ├── variable: k [type=int]
 │    │              └── variable: i [type=int]
 │    └── filters (true)
 └── filters
      └── i = generate_series [type=bool, outer=(2,8), constraints=(/2: (/NULL - ]; /8: (/NULL - ]), fd=(2)==(8), (8)==(2)]

opt expect=DecorrelateProjectSet
SELECT generate_series(0, (SELECT generate_series(1,0) FROM xy)) FROM uv
----
inner-join
 ├── columns: generate_series:6(int)
 ├── side-effects
 ├── scan uv
 ├── project-set
 │    ├── columns: generate_series:6(int)
 │    ├── side-effects
 │    ├── values
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── tuple [type=tuple]
 │    └── zip
 │         └── function: generate_series [type=int, side-effects, subquery]
 │              ├── const: 0 [type=int]
 │              └── subquery [type=int]
 │                   └── max1-row
 │                        ├── columns: generate_series:5(int)
 │                        ├── cardinality: [0 - 1]
 │                        ├── side-effects
 │                        ├── key: ()
 │                        ├── fd: ()-->(5)
 │                        └── inner-join
 │                             ├── columns: generate_series:5(int)
 │                             ├── side-effects
 │                             ├── scan xy
 │                             ├── project-set
 │                             │    ├── columns: generate_series:5(int)
 │                             │    ├── side-effects
 │                             │    ├── values
 │                             │    │    ├── cardinality: [1 - 1]
 │                             │    │    ├── key: ()
 │                             │    │    └── tuple [type=tuple]
 │                             │    └── zip
 │                             │         └── function: generate_series [type=int, side-effects]
 │                             │              ├── const: 1 [type=int]
 │                             │              └── const: 0 [type=int]
 │                             └── filters (true)
 └── filters (true)

# --------------------------------------------------
# TryDecorrelateSelect
# --------------------------------------------------
opt expect=TryDecorrelateSelect
SELECT * FROM a WHERE EXISTS(SELECT * FROM (VALUES (k), (i)) WHERE column1=k)
----
semi-join-apply
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 ├── values
 │    ├── columns: column1:6(int)
 │    ├── outer: (1,2)
 │    ├── cardinality: [2 - 2]
 │    ├── (k,) [type=tuple{int}]
 │    └── (i,) [type=tuple{int}]
 └── filters
      └── column1 = k [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]

opt expect=TryDecorrelateSelect
SELECT * FROM a WHERE NOT EXISTS(SELECT * FROM (VALUES (k), (i)) WHERE column1=k)
----
anti-join-apply
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 ├── values
 │    ├── columns: column1:6(int)
 │    ├── outer: (1,2)
 │    ├── cardinality: [2 - 2]
 │    ├── (k,) [type=tuple{int}]
 │    └── (i,) [type=tuple{int}]
 └── filters
      └── column1 = k [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]

# Attempt to decorrelate query by pulling up outer select. But since limit query
# cannot be decorrelated, push the outer select back down again (and make sure
# potential rule cycle is detected and handled).
opt expect=TryDecorrelateSelect
SELECT * FROM a WHERE EXISTS(SELECT * FROM (SELECT * FROM xy WHERE y=k LIMIT 1) WHERE y=10)
----
semi-join-apply
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 ├── limit
 │    ├── columns: x:6(int!null) y:7(int!null)
 │    ├── outer: (1)
 │    ├── cardinality: [0 - 1]
 │    ├── key: ()
 │    ├── fd: ()-->(6,7)
 │    ├── select
 │    │    ├── columns: x:6(int!null) y:7(int!null)
 │    │    ├── outer: (1)
 │    │    ├── key: (6)
 │    │    ├── fd: ()-->(7)
 │    │    ├── scan xy
 │    │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    │    ├── key: (6)
 │    │    │    └── fd: (6)-->(7)
 │    │    └── filters
 │    │         └── y = k [type=bool, outer=(1,7), constraints=(/1: (/NULL - ]; /7: (/NULL - ]), fd=(1)==(7), (7)==(1)]
 │    └── const: 1 [type=int]
 └── filters
      └── y = 10 [type=bool, outer=(7), constraints=(/7: [/10 - /10]; tight), fd=()-->(7)]

# Same as previous, but using anti-join.
opt expect=TryDecorrelateSelect
SELECT * FROM a WHERE NOT EXISTS(SELECT * FROM (SELECT * FROM xy WHERE y=k LIMIT 1) WHERE y=10)
----
anti-join-apply
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 ├── limit
 │    ├── columns: x:6(int!null) y:7(int!null)
 │    ├── outer: (1)
 │    ├── cardinality: [0 - 1]
 │    ├── key: ()
 │    ├── fd: ()-->(6,7)
 │    ├── select
 │    │    ├── columns: x:6(int!null) y:7(int!null)
 │    │    ├── outer: (1)
 │    │    ├── key: (6)
 │    │    ├── fd: ()-->(7)
 │    │    ├── scan xy
 │    │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    │    ├── key: (6)
 │    │    │    └── fd: (6)-->(7)
 │    │    └── filters
 │    │         └── y = k [type=bool, outer=(1,7), constraints=(/1: (/NULL - ]; /7: (/NULL - ]), fd=(1)==(7), (7)==(1)]
 │    └── const: 1 [type=int]
 └── filters
      └── y = 10 [type=bool, outer=(7), constraints=(/7: [/10 - /10]; tight), fd=()-->(7)]

# Decorrelate Select with reference to outer column and no limit.
opt expect=TryDecorrelateSelect
SELECT * FROM a WHERE (SELECT x FROM xy WHERE x=i) > 100
----
project
 ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 └── inner-join
      ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb) x:6(int!null)
      ├── key: (1)
      ├── fd: (1)-->(2-5), (2)==(6), (6)==(2)
      ├── scan xy
      │    ├── columns: x:6(int!null)
      │    ├── constraint: /6: [/101 - ]
      │    └── key: (6)
      ├── select
      │    ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-5)
      │    ├── scan a
      │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    │    ├── key: (1)
      │    │    └── fd: (1)-->(2-5)
      │    └── filters
      │         └── i > 100 [type=bool, outer=(2), constraints=(/2: [/101 - ]; tight)]
      └── filters
           └── x = i [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]

# Decorrelate Select with LeftJoinApply.
opt expect=TryDecorrelateSelect
SELECT * FROM a WHERE (SELECT x FROM (SELECT * FROM xy LIMIT 1) WHERE k=x) > 100
----
project
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── cardinality: [0 - 1]
 ├── key: ()
 ├── fd: ()-->(1-5)
 └── inner-join (lookup a)
      ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null)
      ├── key columns: [6] = [1]
      ├── cardinality: [0 - 1]
      ├── key: ()
      ├── fd: ()-->(1-6)
      ├── select
      │    ├── columns: x:6(int!null)
      │    ├── cardinality: [0 - 1]
      │    ├── key: ()
      │    ├── fd: ()-->(6)
      │    ├── scan xy
      │    │    ├── columns: x:6(int!null)
      │    │    ├── limit: 1
      │    │    ├── key: ()
      │    │    └── fd: ()-->(6)
      │    └── filters
      │         └── x > 100 [type=bool, outer=(6), constraints=(/6: [/101 - ]; tight)]
      └── filters
           └── k > 100 [type=bool, outer=(1), constraints=(/1: [/101 - ]; tight)]

# Decorrelate with non-apply operator because of multi-level nesting.
opt expect=TryDecorrelateSelect
SELECT *
FROM a
WHERE EXISTS(SELECT * FROM xy WHERE x=k AND EXISTS(SELECT * FROM uv WHERE u=10 AND s='foo'))
----
semi-join-apply
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 ├── semi-join
 │    ├── columns: x:6(int!null) y:7(int)
 │    ├── outer: (4)
 │    ├── key: (6)
 │    ├── fd: (6)-->(7)
 │    ├── scan xy
 │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    ├── key: (6)
 │    │    └── fd: (6)-->(7)
 │    ├── scan uv
 │    │    ├── columns: u:8(int!null) v:9(int)
 │    │    ├── constraint: /8: [/10 - /10]
 │    │    ├── cardinality: [0 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(8,9)
 │    └── filters
 │         └── s = 'foo' [type=bool, outer=(4), constraints=(/4: [/'foo' - /'foo']; tight), fd=()-->(4)]
 └── filters
      └── x = k [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]

# --------------------------------------------------
# TryDecorrelateProject +
# TryDecorrelateProjectSelect +
# TryDecorrelateScalarGroupBy
#
# Start with some shared test cases that exercise multiple
# decorrelation rules.
# --------------------------------------------------

# Left join caused by correlated ANY clause.
opt expect=(TryDecorrelateProject,TryDecorrelateProjectSelect,TryDecorrelateScalarGroupBy)
SELECT 5=ANY(SELECT y FROM xy WHERE x=k) AS r FROM a
----
project
 ├── columns: r:8(bool)
 ├── group-by
 │    ├── columns: k:1(int!null) bool_or:10(bool)
 │    ├── grouping columns: k:1(int!null)
 │    ├── internal-ordering: +1
 │    ├── key: (1)
 │    ├── fd: (1)-->(10)
 │    ├── left-join (merge)
 │    │    ├── columns: k:1(int!null) x:6(int) notnull:9(bool)
 │    │    ├── left ordering: +1
 │    │    ├── right ordering: +6
 │    │    ├── key: (1,6)
 │    │    ├── fd: (6)-->(9)
 │    │    ├── ordering: +1
 │    │    ├── scan a
 │    │    │    ├── columns: k:1(int!null)
 │    │    │    ├── key: (1)
 │    │    │    └── ordering: +1
 │    │    ├── project
 │    │    │    ├── columns: notnull:9(bool) x:6(int!null)
 │    │    │    ├── key: (6)
 │    │    │    ├── fd: (6)-->(9)
 │    │    │    ├── ordering: +6
 │    │    │    ├── select
 │    │    │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    │    │    ├── key: (6)
 │    │    │    │    ├── fd: (6)-->(7)
 │    │    │    │    ├── ordering: +6
 │    │    │    │    ├── scan xy
 │    │    │    │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    │    │    │    ├── key: (6)
 │    │    │    │    │    ├── fd: (6)-->(7)
 │    │    │    │    │    └── ordering: +6
 │    │    │    │    └── filters
 │    │    │    │         └── (y = 5) IS NOT false [type=bool, outer=(7)]
 │    │    │    └── projections
 │    │    │         └── y IS NOT NULL [type=bool, outer=(7)]
 │    │    └── filters (true)
 │    └── aggregations
 │         └── bool-or [type=bool, outer=(9)]
 │              └── variable: notnull [type=bool]
 └── projections
      └── CASE WHEN bool_or THEN true WHEN bool_or IS NULL THEN false END [type=bool, outer=(10)]

# Left join caused by zero or one cardinality subquery.
opt expect=TryDecorrelateProjectSelect
SELECT * FROM a WHERE (SELECT y+1 AS r FROM (SELECT * FROM xy LIMIT 1) WHERE x=k) > 10
----
project
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── cardinality: [0 - 1]
 ├── key: ()
 ├── fd: ()-->(1-5)
 └── inner-join (lookup a)
      ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) y:7(int!null)
      ├── key columns: [6] = [1]
      ├── cardinality: [0 - 1]
      ├── key: ()
      ├── fd: ()-->(1-7)
      ├── select
      │    ├── columns: x:6(int!null) y:7(int!null)
      │    ├── cardinality: [0 - 1]
      │    ├── key: ()
      │    ├── fd: ()-->(6,7)
      │    ├── scan xy
      │    │    ├── columns: x:6(int!null) y:7(int)
      │    │    ├── limit: 1
      │    │    ├── key: ()
      │    │    └── fd: ()-->(6,7)
      │    └── filters
      │         └── y > 9 [type=bool, outer=(7), constraints=(/7: [/10 - ]; tight)]
      └── filters (true)

# Any clause with constant.
opt expect=(TryDecorrelateProject,TryDecorrelateProjectSelect,TryDecorrelateScalarGroupBy)
SELECT 5=ANY(SELECT y FROM xy WHERE x=k) AS r FROM a
----
project
 ├── columns: r:8(bool)
 ├── group-by
 │    ├── columns: k:1(int!null) bool_or:10(bool)
 │    ├── grouping columns: k:1(int!null)
 │    ├── internal-ordering: +1
 │    ├── key: (1)
 │    ├── fd: (1)-->(10)
 │    ├── left-join (merge)
 │    │    ├── columns: k:1(int!null) x:6(int) notnull:9(bool)
 │    │    ├── left ordering: +1
 │    │    ├── right ordering: +6
 │    │    ├── key: (1,6)
 │    │    ├── fd: (6)-->(9)
 │    │    ├── ordering: +1
 │    │    ├── scan a
 │    │    │    ├── columns: k:1(int!null)
 │    │    │    ├── key: (1)
 │    │    │    └── ordering: +1
 │    │    ├── project
 │    │    │    ├── columns: notnull:9(bool) x:6(int!null)
 │    │    │    ├── key: (6)
 │    │    │    ├── fd: (6)-->(9)
 │    │    │    ├── ordering: +6
 │    │    │    ├── select
 │    │    │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    │    │    ├── key: (6)
 │    │    │    │    ├── fd: (6)-->(7)
 │    │    │    │    ├── ordering: +6
 │    │    │    │    ├── scan xy
 │    │    │    │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    │    │    │    ├── key: (6)
 │    │    │    │    │    ├── fd: (6)-->(7)
 │    │    │    │    │    └── ordering: +6
 │    │    │    │    └── filters
 │    │    │    │         └── (y = 5) IS NOT false [type=bool, outer=(7)]
 │    │    │    └── projections
 │    │    │         └── y IS NOT NULL [type=bool, outer=(7)]
 │    │    └── filters (true)
 │    └── aggregations
 │         └── bool-or [type=bool, outer=(9)]
 │              └── variable: notnull [type=bool]
 └── projections
      └── CASE WHEN bool_or THEN true WHEN bool_or IS NULL THEN false END [type=bool, outer=(10)]

# Any clause with variable.
opt expect=(TryDecorrelateProject,TryDecorrelateProjectSelect,TryDecorrelateScalarGroupBy)
SELECT i=ANY(SELECT y FROM xy WHERE x=k) AS r FROM a
----
project
 ├── columns: r:8(bool)
 ├── group-by
 │    ├── columns: k:1(int!null) i:2(int) bool_or:10(bool)
 │    ├── grouping columns: k:1(int!null)
 │    ├── internal-ordering: +1
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,10)
 │    ├── left-join (merge)
 │    │    ├── columns: k:1(int!null) i:2(int) x:6(int) y:7(int) notnull:9(bool)
 │    │    ├── left ordering: +1
 │    │    ├── right ordering: +6
 │    │    ├── key: (1,6)
 │    │    ├── fd: (1)-->(2), (6)-->(7), (7)~~>(9), (1,6)-->(9)
 │    │    ├── ordering: +1
 │    │    ├── scan a
 │    │    │    ├── columns: k:1(int!null) i:2(int)
 │    │    │    ├── key: (1)
 │    │    │    ├── fd: (1)-->(2)
 │    │    │    └── ordering: +1
 │    │    ├── project
 │    │    │    ├── columns: notnull:9(bool) x:6(int!null) y:7(int)
 │    │    │    ├── key: (6)
 │    │    │    ├── fd: (6)-->(7), (7)-->(9)
 │    │    │    ├── ordering: +6
 │    │    │    ├── scan xy
 │    │    │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    │    │    ├── key: (6)
 │    │    │    │    ├── fd: (6)-->(7)
 │    │    │    │    └── ordering: +6
 │    │    │    └── projections
 │    │    │         └── y IS NOT NULL [type=bool, outer=(7)]
 │    │    └── filters
 │    │         └── (i = y) IS NOT false [type=bool, outer=(2,7)]
 │    └── aggregations
 │         ├── bool-or [type=bool, outer=(9)]
 │         │    └── variable: notnull [type=bool]
 │         └── const-agg [type=int, outer=(2)]
 │              └── variable: i [type=int]
 └── projections
      └── CASE WHEN bool_or AND (i IS NOT NULL) THEN true WHEN bool_or IS NULL THEN false END [type=bool, outer=(2,10)]

# Any clause with more complex expression that must be cached.
opt expect=(TryDecorrelateProject,TryDecorrelateProjectSelect,TryDecorrelateScalarGroupBy)
SELECT i*i/5=ANY(SELECT y FROM xy WHERE x=k) AS r FROM a
----
project
 ├── columns: r:8(bool)
 ├── side-effects
 ├── group-by
 │    ├── columns: k:1(int!null) scalar:9(decimal) bool_or:11(bool)
 │    ├── grouping columns: k:1(int!null)
 │    ├── internal-ordering: +1
 │    ├── side-effects
 │    ├── key: (1)
 │    ├── fd: (1)-->(9,11)
 │    ├── left-join (merge)
 │    │    ├── columns: k:1(int!null) x:6(int) y:7(int) scalar:9(decimal) notnull:10(bool)
 │    │    ├── left ordering: +1
 │    │    ├── right ordering: +6
 │    │    ├── side-effects
 │    │    ├── key: (1,6)
 │    │    ├── fd: (1)-->(9), (6)-->(7), (7)~~>(10), (1,6)-->(10)
 │    │    ├── ordering: +1
 │    │    ├── project
 │    │    │    ├── columns: scalar:9(decimal) k:1(int!null)
 │    │    │    ├── side-effects
 │    │    │    ├── key: (1)
 │    │    │    ├── fd: (1)-->(9)
 │    │    │    ├── ordering: +1
 │    │    │    ├── scan a
 │    │    │    │    ├── columns: k:1(int!null) i:2(int)
 │    │    │    │    ├── key: (1)
 │    │    │    │    ├── fd: (1)-->(2)
 │    │    │    │    └── ordering: +1
 │    │    │    └── projections
 │    │    │         └── (i * i) / 5 [type=decimal, outer=(2), side-effects]
 │    │    ├── project
 │    │    │    ├── columns: notnull:10(bool) x:6(int!null) y:7(int)
 │    │    │    ├── key: (6)
 │    │    │    ├── fd: (6)-->(7), (7)-->(10)
 │    │    │    ├── ordering: +6
 │    │    │    ├── scan xy
 │    │    │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    │    │    ├── key: (6)
 │    │    │    │    ├── fd: (6)-->(7)
 │    │    │    │    └── ordering: +6
 │    │    │    └── projections
 │    │    │         └── y IS NOT NULL [type=bool, outer=(7)]
 │    │    └── filters
 │    │         └── (scalar = y) IS NOT false [type=bool, outer=(7,9)]
 │    └── aggregations
 │         ├── bool-or [type=bool, outer=(10)]
 │         │    └── variable: notnull [type=bool]
 │         └── const-agg [type=decimal, outer=(9)]
 │              └── variable: scalar [type=decimal]
 └── projections
      └── CASE WHEN bool_or AND (scalar IS NOT NULL) THEN true WHEN bool_or IS NULL THEN false END [type=bool, outer=(9,11)]

# --------------------------------------------------
# TryDecorrelateProject
# --------------------------------------------------
opt expect=TryDecorrelateProject
SELECT k FROM a
WHERE EXISTS
(
    SELECT * FROM xy INNER JOIN (SELECT u, u/1.1 AS div FROM uv WHERE i=5) ON x=div
)
----
distinct-on
 ├── columns: k:1(int!null)
 ├── grouping columns: k:1(int!null)
 ├── side-effects
 ├── key: (1)
 └── select
      ├── columns: k:1(int!null) x:6(int!null) div:10(decimal!null)
      ├── side-effects
      ├── fd: (6)==(10), (10)==(6)
      ├── project
      │    ├── columns: div:10(decimal) k:1(int!null) x:6(int!null)
      │    ├── side-effects
      │    ├── inner-join
      │    │    ├── columns: k:1(int!null) i:2(int!null) x:6(int!null) u:8(int!null)
      │    │    ├── key: (1,6,8)
      │    │    ├── fd: ()-->(2)
      │    │    ├── inner-join
      │    │    │    ├── columns: k:1(int!null) i:2(int!null) u:8(int!null)
      │    │    │    ├── key: (1,8)
      │    │    │    ├── fd: ()-->(2)
      │    │    │    ├── scan uv
      │    │    │    │    ├── columns: u:8(int!null)
      │    │    │    │    └── key: (8)
      │    │    │    ├── select
      │    │    │    │    ├── columns: k:1(int!null) i:2(int!null)
      │    │    │    │    ├── key: (1)
      │    │    │    │    ├── fd: ()-->(2)
      │    │    │    │    ├── scan a
      │    │    │    │    │    ├── columns: k:1(int!null) i:2(int)
      │    │    │    │    │    ├── key: (1)
      │    │    │    │    │    └── fd: (1)-->(2)
      │    │    │    │    └── filters
      │    │    │    │         └── i = 5 [type=bool, outer=(2), constraints=(/2: [/5 - /5]; tight), fd=()-->(2)]
      │    │    │    └── filters (true)
      │    │    ├── scan xy
      │    │    │    ├── columns: x:6(int!null)
      │    │    │    └── key: (6)
      │    │    └── filters (true)
      │    └── projections
      │         └── u / 1.1 [type=decimal, outer=(8), side-effects]
      └── filters
           └── x = div [type=bool, outer=(6,10), constraints=(/6: (/NULL - ]; /10: (/NULL - ]), fd=(6)==(10), (10)==(6)]

# Don't hoist Project operator in right join case.
opt
SELECT k FROM a
WHERE EXISTS
(
    SELECT * FROM xy RIGHT JOIN (SELECT u, u/1.1 AS div FROM uv WHERE i=5) ON x=div
)
----
project
 ├── columns: k:1(int!null)
 ├── side-effects
 ├── key: (1)
 └── semi-join-apply
      ├── columns: k:1(int!null) i:2(int)
      ├── side-effects
      ├── key: (1)
      ├── fd: (1)-->(2)
      ├── scan a
      │    ├── columns: k:1(int!null) i:2(int)
      │    ├── key: (1)
      │    └── fd: (1)-->(2)
      ├── right-join
      │    ├── columns: x:6(int) div:10(decimal)
      │    ├── outer: (2)
      │    ├── side-effects
      │    ├── scan xy
      │    │    ├── columns: x:6(int!null)
      │    │    └── key: (6)
      │    ├── project
      │    │    ├── columns: div:10(decimal)
      │    │    ├── outer: (2)
      │    │    ├── side-effects
      │    │    ├── select
      │    │    │    ├── columns: u:8(int!null)
      │    │    │    ├── outer: (2)
      │    │    │    ├── key: (8)
      │    │    │    ├── scan uv
      │    │    │    │    ├── columns: u:8(int!null)
      │    │    │    │    └── key: (8)
      │    │    │    └── filters
      │    │    │         └── i = 5 [type=bool, outer=(2), constraints=(/2: [/5 - /5]; tight), fd=()-->(2)]
      │    │    └── projections
      │    │         └── u / 1.1 [type=decimal, outer=(8), side-effects]
      │    └── filters
      │         └── x = div [type=bool, outer=(6,10), constraints=(/6: (/NULL - ]; /10: (/NULL - ]), fd=(6)==(10), (10)==(6)]
      └── filters (true)

# --------------------------------------------------
# TryDecorrelateProjectSelect
# --------------------------------------------------
opt
SELECT k FROM a
WHERE EXISTS
(
    SELECT * FROM xy LEFT JOIN (SELECT u, u+1 AS plus FROM uv WHERE i=5) ON x=plus
)
----
project
 ├── columns: k:1(int!null)
 ├── key: (1)
 └── semi-join-apply
      ├── columns: k:1(int!null) i:2(int)
      ├── key: (1)
      ├── fd: (1)-->(2)
      ├── scan a
      │    ├── columns: k:1(int!null) i:2(int)
      │    ├── key: (1)
      │    └── fd: (1)-->(2)
      ├── left-join
      │    ├── columns: x:6(int!null) plus:10(int)
      │    ├── outer: (2)
      │    ├── scan xy
      │    │    ├── columns: x:6(int!null)
      │    │    └── key: (6)
      │    ├── project
      │    │    ├── columns: plus:10(int)
      │    │    ├── outer: (2)
      │    │    ├── select
      │    │    │    ├── columns: u:8(int!null)
      │    │    │    ├── outer: (2)
      │    │    │    ├── key: (8)
      │    │    │    ├── scan uv
      │    │    │    │    ├── columns: u:8(int!null)
      │    │    │    │    └── key: (8)
      │    │    │    └── filters
      │    │    │         └── i = 5 [type=bool, outer=(2), constraints=(/2: [/5 - /5]; tight), fd=()-->(2)]
      │    │    └── projections
      │    │         └── u + 1 [type=int, outer=(8)]
      │    └── filters
      │         └── x = plus [type=bool, outer=(6,10), constraints=(/6: (/NULL - ]; /10: (/NULL - ]), fd=(6)==(10), (10)==(6)]
      └── filters (true)

# Don't decorrelate FULL JOIN case.
opt
SELECT k FROM a
WHERE EXISTS
(
    SELECT * FROM xy FULL JOIN (SELECT u, u+1 AS plus FROM uv WHERE i=5) ON x=plus
)
----
project
 ├── columns: k:1(int!null)
 ├── key: (1)
 └── semi-join-apply
      ├── columns: k:1(int!null) i:2(int)
      ├── key: (1)
      ├── fd: (1)-->(2)
      ├── scan a
      │    ├── columns: k:1(int!null) i:2(int)
      │    ├── key: (1)
      │    └── fd: (1)-->(2)
      ├── full-join
      │    ├── columns: x:6(int) plus:10(int)
      │    ├── outer: (2)
      │    ├── scan xy
      │    │    ├── columns: x:6(int!null)
      │    │    └── key: (6)
      │    ├── project
      │    │    ├── columns: plus:10(int)
      │    │    ├── outer: (2)
      │    │    ├── select
      │    │    │    ├── columns: u:8(int!null)
      │    │    │    ├── outer: (2)
      │    │    │    ├── key: (8)
      │    │    │    ├── scan uv
      │    │    │    │    ├── columns: u:8(int!null)
      │    │    │    │    └── key: (8)
      │    │    │    └── filters
      │    │    │         └── i = 5 [type=bool, outer=(2), constraints=(/2: [/5 - /5]; tight), fd=()-->(2)]
      │    │    └── projections
      │    │         └── u + 1 [type=int, outer=(8)]
      │    └── filters
      │         └── x = plus [type=bool, outer=(6,10), constraints=(/6: (/NULL - ]; /10: (/NULL - ]), fd=(6)==(10), (10)==(6)]
      └── filters (true)

# --------------------------------------------------
# TryDecorrelateProjectInnerJoin
# --------------------------------------------------
opt expect=TryDecorrelateProjectInnerJoin
SELECT (SELECT sum(y + v) FROM xy, uv WHERE x=u AND x=k) FROM a
----
project
 ├── columns: sum:12(decimal)
 ├── group-by
 │    ├── columns: k:1(int!null) sum:11(decimal)
 │    ├── grouping columns: k:1(int!null)
 │    ├── internal-ordering: +1
 │    ├── key: (1)
 │    ├── fd: (1)-->(11)
 │    ├── left-join (merge)
 │    │    ├── columns: k:1(int!null) x:6(int) column10:10(int)
 │    │    ├── left ordering: +1
 │    │    ├── right ordering: +6
 │    │    ├── key: (1,6)
 │    │    ├── fd: (6)-->(10)
 │    │    ├── ordering: +1
 │    │    ├── scan a
 │    │    │    ├── columns: k:1(int!null)
 │    │    │    ├── key: (1)
 │    │    │    └── ordering: +1
 │    │    ├── project
 │    │    │    ├── columns: column10:10(int) x:6(int!null)
 │    │    │    ├── key: (6)
 │    │    │    ├── fd: (6)-->(10)
 │    │    │    ├── ordering: +6
 │    │    │    ├── inner-join (merge)
 │    │    │    │    ├── columns: x:6(int!null) y:7(int) u:8(int!null) v:9(int)
 │    │    │    │    ├── left ordering: +6
 │    │    │    │    ├── right ordering: +8
 │    │    │    │    ├── key: (8)
 │    │    │    │    ├── fd: (6)-->(7), (8)-->(9), (6)==(8), (8)==(6)
 │    │    │    │    ├── ordering: +(6|8) [actual: +6]
 │    │    │    │    ├── scan xy
 │    │    │    │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    │    │    │    ├── key: (6)
 │    │    │    │    │    ├── fd: (6)-->(7)
 │    │    │    │    │    └── ordering: +6
 │    │    │    │    ├── scan uv
 │    │    │    │    │    ├── columns: u:8(int!null) v:9(int)
 │    │    │    │    │    ├── key: (8)
 │    │    │    │    │    ├── fd: (8)-->(9)
 │    │    │    │    │    └── ordering: +8
 │    │    │    │    └── filters (true)
 │    │    │    └── projections
 │    │    │         └── y + v [type=int, outer=(7,9)]
 │    │    └── filters (true)
 │    └── aggregations
 │         └── sum [type=decimal, outer=(10)]
 │              └── variable: column10 [type=int]
 └── projections
      └── variable: sum [type=decimal, outer=(11)]

# --------------------------------------------------
# TryDecorrelateInnerJoin
# --------------------------------------------------
# Semi-join as outer.
opt expect=TryDecorrelateInnerJoin
SELECT k FROM a
WHERE EXISTS
(
    SELECT * FROM xy INNER JOIN uv ON x=u AND x=k
)
----
semi-join (merge)
 ├── columns: k:1(int!null)
 ├── left ordering: +1
 ├── right ordering: +6
 ├── key: (1)
 ├── scan a
 │    ├── columns: k:1(int!null)
 │    ├── key: (1)
 │    └── ordering: +1
 ├── inner-join (merge)
 │    ├── columns: x:6(int!null) y:7(int) u:8(int!null) v:9(int)
 │    ├── left ordering: +6
 │    ├── right ordering: +8
 │    ├── key: (8)
 │    ├── fd: (6)-->(7), (8)-->(9), (6)==(8), (8)==(6)
 │    ├── ordering: +(6|8) [actual: +6]
 │    ├── scan xy
 │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    ├── key: (6)
 │    │    ├── fd: (6)-->(7)
 │    │    └── ordering: +6
 │    ├── scan uv
 │    │    ├── columns: u:8(int!null) v:9(int)
 │    │    ├── key: (8)
 │    │    ├── fd: (8)-->(9)
 │    │    └── ordering: +8
 │    └── filters (true)
 └── filters (true)

# Anti-join as outer.
opt expect=TryDecorrelateInnerJoin
SELECT k FROM a
WHERE NOT EXISTS
(
    SELECT * FROM xy INNER JOIN uv ON x=u AND x=k
)
----
anti-join (merge)
 ├── columns: k:1(int!null)
 ├── left ordering: +1
 ├── right ordering: +6
 ├── key: (1)
 ├── scan a
 │    ├── columns: k:1(int!null)
 │    ├── key: (1)
 │    └── ordering: +1
 ├── inner-join (merge)
 │    ├── columns: x:6(int!null) y:7(int) u:8(int!null) v:9(int)
 │    ├── left ordering: +6
 │    ├── right ordering: +8
 │    ├── key: (8)
 │    ├── fd: (6)-->(7), (8)-->(9), (6)==(8), (8)==(6)
 │    ├── ordering: +(6|8) [actual: +6]
 │    ├── scan xy
 │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    ├── key: (6)
 │    │    ├── fd: (6)-->(7)
 │    │    └── ordering: +6
 │    ├── scan uv
 │    │    ├── columns: u:8(int!null) v:9(int)
 │    │    ├── key: (8)
 │    │    ├── fd: (8)-->(9)
 │    │    └── ordering: +8
 │    └── filters (true)
 └── filters (true)

# Right-join as outer.
opt expect=TryDecorrelateInnerJoin
SELECT k FROM a
WHERE
(
    SELECT count(*)
    FROM xy
    INNER JOIN uv
    ON x=u AND x=k
) IS DISTINCT FROM 1
----
project
 ├── columns: k:1(int!null)
 ├── key: (1)
 └── select
      ├── columns: k:1(int!null) count_rows:10(int)
      ├── key: (1)
      ├── fd: (1)-->(10)
      ├── group-by
      │    ├── columns: k:1(int!null) count_rows:10(int)
      │    ├── grouping columns: k:1(int!null)
      │    ├── internal-ordering: +1
      │    ├── key: (1)
      │    ├── fd: (1)-->(10)
      │    ├── left-join (merge)
      │    │    ├── columns: k:1(int!null) x:6(int) u:8(int)
      │    │    ├── left ordering: +1
      │    │    ├── right ordering: +6
      │    │    ├── key: (1,8)
      │    │    ├── fd: (6)==(8), (8)==(6)
      │    │    ├── ordering: +1
      │    │    ├── scan a
      │    │    │    ├── columns: k:1(int!null)
      │    │    │    ├── key: (1)
      │    │    │    └── ordering: +1
      │    │    ├── inner-join (merge)
      │    │    │    ├── columns: x:6(int!null) u:8(int!null)
      │    │    │    ├── left ordering: +6
      │    │    │    ├── right ordering: +8
      │    │    │    ├── key: (8)
      │    │    │    ├── fd: (6)==(8), (8)==(6)
      │    │    │    ├── ordering: +(6|8) [actual: +6]
      │    │    │    ├── scan xy
      │    │    │    │    ├── columns: x:6(int!null)
      │    │    │    │    ├── key: (6)
      │    │    │    │    └── ordering: +6
      │    │    │    ├── scan uv
      │    │    │    │    ├── columns: u:8(int!null)
      │    │    │    │    ├── key: (8)
      │    │    │    │    └── ordering: +8
      │    │    │    └── filters (true)
      │    │    └── filters (true)
      │    └── aggregations
      │         └── count [type=int, outer=(6)]
      │              └── variable: x [type=int]
      └── filters
           └── count_rows IS DISTINCT FROM 1 [type=bool, outer=(10), constraints=(/10: [ - /0] [/2 - ]; tight)]

# Can't decorrelate left-join as inner.
opt
SELECT k FROM a
WHERE EXISTS
(
    SELECT * FROM xy LEFT JOIN uv ON x=u AND x=k
)
----
semi-join-apply
 ├── columns: k:1(int!null)
 ├── key: (1)
 ├── scan a
 │    ├── columns: k:1(int!null)
 │    └── key: (1)
 ├── left-join (merge)
 │    ├── columns: x:6(int!null) y:7(int) u:8(int) v:9(int)
 │    ├── left ordering: +6
 │    ├── right ordering: +8
 │    ├── outer: (1)
 │    ├── key: (6,8)
 │    ├── fd: (6)-->(7), (8)-->(9)
 │    ├── scan xy
 │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    ├── key: (6)
 │    │    ├── fd: (6)-->(7)
 │    │    └── ordering: +6
 │    ├── scan uv
 │    │    ├── columns: u:8(int!null) v:9(int)
 │    │    ├── key: (8)
 │    │    ├── fd: (8)-->(9)
 │    │    └── ordering: +8
 │    └── filters
 │         └── x = k [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]
 └── filters (true)

# Can't decorrelate semi-join as inner.
opt
SELECT k
FROM a
WHERE EXISTS
(
    SELECT *
    FROM xy
    WHERE EXISTS
    (
        SELECT * FROM uv INNER JOIN uv AS uv2 ON uv2.u=k
    )
)
----
semi-join-apply
 ├── columns: k:1(int!null)
 ├── key: (1)
 ├── scan a
 │    ├── columns: k:1(int!null)
 │    └── key: (1)
 ├── semi-join
 │    ├── columns: x:6(int!null) y:7(int)
 │    ├── outer: (1)
 │    ├── key: (6)
 │    ├── fd: (6)-->(7)
 │    ├── scan xy
 │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    ├── key: (6)
 │    │    └── fd: (6)-->(7)
 │    ├── inner-join
 │    │    ├── columns: uv.u:8(int!null) uv.v:9(int) uv2.u:10(int!null) uv2.v:11(int)
 │    │    ├── key: (8,10)
 │    │    ├── fd: (8)-->(9), (10)-->(11)
 │    │    ├── scan uv
 │    │    │    ├── columns: uv.u:8(int!null) uv.v:9(int)
 │    │    │    ├── key: (8)
 │    │    │    └── fd: (8)-->(9)
 │    │    ├── scan uv2
 │    │    │    ├── columns: uv2.u:10(int!null) uv2.v:11(int)
 │    │    │    ├── key: (10)
 │    │    │    └── fd: (10)-->(11)
 │    │    └── filters (true)
 │    └── filters
 │         └── uv2.u = k [type=bool, outer=(1,10), constraints=(/1: (/NULL - ]; /10: (/NULL - ]), fd=(1)==(10), (10)==(1)]
 └── filters (true)

# --------------------------------------------------
# TryDecorrelateInnerLeftJoin
# --------------------------------------------------
opt expect=TryDecorrelateInnerLeftJoin
SELECT *
FROM (VALUES (1), (2)) AS v(v1)
WHERE EXISTS(
    SELECT k
    FROM a
    WHERE
    (
        SELECT y FROM xy LEFT JOIN (SELECT v1 FROM uv LIMIT 1) ON x=v1 WHERE x=k
    )=i
)
----
semi-join-apply
 ├── columns: v1:1(int)
 ├── cardinality: [0 - 2]
 ├── values
 │    ├── columns: column1:1(int)
 │    ├── cardinality: [2 - 2]
 │    ├── (1,) [type=tuple{int}]
 │    └── (2,) [type=tuple{int}]
 ├── left-join
 │    ├── columns: k:2(int!null) i:3(int!null) x:7(int!null) y:8(int!null) v1:11(int)
 │    ├── outer: (1)
 │    ├── key: (7)
 │    ├── fd: (2)-->(3), (7)-->(8,11), (2)==(7), (7)==(2), (3)==(8), (8)==(3), ()~~>(11)
 │    ├── inner-join
 │    │    ├── columns: k:2(int!null) i:3(int!null) x:7(int!null) y:8(int!null)
 │    │    ├── key: (7)
 │    │    ├── fd: (2)-->(3), (7)-->(8), (2)==(7), (7)==(2), (3)==(8), (8)==(3)
 │    │    ├── scan a
 │    │    │    ├── columns: k:2(int!null) i:3(int)
 │    │    │    ├── key: (2)
 │    │    │    └── fd: (2)-->(3)
 │    │    ├── scan xy
 │    │    │    ├── columns: x:7(int!null) y:8(int)
 │    │    │    ├── key: (7)
 │    │    │    └── fd: (7)-->(8)
 │    │    └── filters
 │    │         ├── x = k [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)]
 │    │         └── i = y [type=bool, outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)]
 │    ├── project
 │    │    ├── columns: v1:11(int)
 │    │    ├── outer: (1)
 │    │    ├── cardinality: [0 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(11)
 │    │    ├── scan uv
 │    │    │    ├── limit: 1
 │    │    │    └── key: ()
 │    │    └── projections
 │    │         └── variable: column1 [type=int, outer=(1)]
 │    └── filters
 │         └── x = v1 [type=bool, outer=(7,11), constraints=(/7: (/NULL - ]; /11: (/NULL - ]), fd=(7)==(11), (11)==(7)]
 └── filters (true)

opt expect=TryDecorrelateInnerLeftJoin
SELECT *
FROM xy, uv
WHERE (SELECT i FROM a WHERE k=x) IS DISTINCT FROM u
----
project
 ├── columns: x:1(int!null) y:2(int) u:3(int!null) v:4(int)
 ├── fd: (1)-->(2), (3)-->(4)
 └── select
      ├── columns: x:1(int!null) y:2(int) u:3(int!null) v:4(int) k:5(int) i:6(int)
      ├── key: (1,3,5)
      ├── fd: (1)-->(2), (3)-->(4), (5)-->(6)
      ├── left-join
      │    ├── columns: x:1(int!null) y:2(int) u:3(int!null) v:4(int) k:5(int) i:6(int)
      │    ├── key: (1,3,5)
      │    ├── fd: (1)-->(2), (3)-->(4), (5)-->(6)
      │    ├── inner-join
      │    │    ├── columns: x:1(int!null) y:2(int) u:3(int!null) v:4(int)
      │    │    ├── key: (1,3)
      │    │    ├── fd: (1)-->(2), (3)-->(4)
      │    │    ├── scan xy
      │    │    │    ├── columns: x:1(int!null) y:2(int)
      │    │    │    ├── key: (1)
      │    │    │    └── fd: (1)-->(2)
      │    │    ├── scan uv
      │    │    │    ├── columns: u:3(int!null) v:4(int)
      │    │    │    ├── key: (3)
      │    │    │    └── fd: (3)-->(4)
      │    │    └── filters (true)
      │    ├── scan a
      │    │    ├── columns: k:5(int!null) i:6(int)
      │    │    ├── key: (5)
      │    │    └── fd: (5)-->(6)
      │    └── filters
      │         └── k = x [type=bool, outer=(1,5), constraints=(/1: (/NULL - ]; /5: (/NULL - ]), fd=(1)==(5), (5)==(1)]
      └── filters
           └── u IS DISTINCT FROM i [type=bool, outer=(3,6)]

# --------------------------------------------------
# TryDecorrelateGroupBy
# --------------------------------------------------
opt expect=TryDecorrelateGroupBy
SELECT *
FROM a
WHERE EXISTS
(
    SELECT * FROM xy INNER JOIN (SELECT count(*) AS cnt, sum(v) FROM uv WHERE i=5 GROUP BY v) ON x=cnt
)
----
group-by
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── grouping columns: k:1(int!null)
 ├── key: (1)
 ├── fd: ()-->(2), (1)-->(2-5)
 ├── select
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) v:9(int) count_rows:10(int!null)
 │    ├── key: (1,6,9)
 │    ├── fd: ()-->(2), (1)-->(3-5), (1,6,9)-->(3-5,10), (6)==(10), (10)==(6)
 │    ├── group-by
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) v:9(int) count_rows:10(int)
 │    │    ├── grouping columns: k:1(int!null) x:6(int!null) v:9(int)
 │    │    ├── key: (1,6,9)
 │    │    ├── fd: ()-->(2), (1)-->(3-5), (1,6,9)-->(2-5,10)
 │    │    ├── inner-join
 │    │    │    ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) v:9(int)
 │    │    │    ├── fd: ()-->(2), (1)-->(3-5)
 │    │    │    ├── inner-join
 │    │    │    │    ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb) v:9(int)
 │    │    │    │    ├── fd: ()-->(2), (1)-->(3-5)
 │    │    │    │    ├── scan uv
 │    │    │    │    │    └── columns: v:9(int)
 │    │    │    │    ├── select
 │    │    │    │    │    ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb)
 │    │    │    │    │    ├── key: (1)
 │    │    │    │    │    ├── fd: ()-->(2), (1)-->(3-5)
 │    │    │    │    │    ├── scan a
 │    │    │    │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    │    │    │    │    ├── key: (1)
 │    │    │    │    │    │    └── fd: (1)-->(2-5)
 │    │    │    │    │    └── filters
 │    │    │    │    │         └── i = 5 [type=bool, outer=(2), constraints=(/2: [/5 - /5]; tight), fd=()-->(2)]
 │    │    │    │    └── filters (true)
 │    │    │    ├── scan xy
 │    │    │    │    ├── columns: x:6(int!null)
 │    │    │    │    └── key: (6)
 │    │    │    └── filters (true)
 │    │    └── aggregations
 │    │         ├── count-rows [type=int]
 │    │         ├── const-agg [type=int, outer=(2)]
 │    │         │    └── variable: i [type=int]
 │    │         ├── const-agg [type=float, outer=(3)]
 │    │         │    └── variable: f [type=float]
 │    │         ├── const-agg [type=string, outer=(4)]
 │    │         │    └── variable: s [type=string]
 │    │         └── const-agg [type=jsonb, outer=(5)]
 │    │              └── variable: j [type=jsonb]
 │    └── filters
 │         └── x = count_rows [type=bool, outer=(6,10), constraints=(/6: (/NULL - ]; /10: (/NULL - ]), fd=(6)==(10), (10)==(6)]
 └── aggregations
      ├── const-agg [type=int, outer=(2)]
      │    └── variable: i [type=int]
      ├── const-agg [type=float, outer=(3)]
      │    └── variable: f [type=float]
      ├── const-agg [type=string, outer=(4)]
      │    └── variable: s [type=string]
      └── const-agg [type=jsonb, outer=(5)]
           └── variable: j [type=jsonb]

opt expect=TryDecorrelateGroupBy
SELECT *
FROM a
WHERE EXISTS
(
    SELECT * FROM xy INNER JOIN (SELECT count(DISTINCT uv.v) AS cnt, sum(v) FROM uv WHERE i=5 GROUP BY v) ON x=cnt
)
----
group-by
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── grouping columns: k:1(int!null)
 ├── key: (1)
 ├── fd: ()-->(2), (1)-->(2-5)
 ├── select
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) v:9(int) count:10(int!null)
 │    ├── key: (1,6,9)
 │    ├── fd: ()-->(2), (1)-->(3-5), (1,6,9)-->(3-5,10), (6)==(10), (10)==(6)
 │    ├── group-by
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) v:9(int) count:10(int)
 │    │    ├── grouping columns: k:1(int!null) x:6(int!null) v:9(int)
 │    │    ├── key: (1,6,9)
 │    │    ├── fd: ()-->(2), (1)-->(3-5), (1,6,9)-->(2-5,10)
 │    │    ├── inner-join
 │    │    │    ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) v:9(int)
 │    │    │    ├── fd: ()-->(2), (1)-->(3-5)
 │    │    │    ├── inner-join
 │    │    │    │    ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb) v:9(int)
 │    │    │    │    ├── fd: ()-->(2), (1)-->(3-5)
 │    │    │    │    ├── scan uv
 │    │    │    │    │    └── columns: v:9(int)
 │    │    │    │    ├── select
 │    │    │    │    │    ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb)
 │    │    │    │    │    ├── key: (1)
 │    │    │    │    │    ├── fd: ()-->(2), (1)-->(3-5)
 │    │    │    │    │    ├── scan a
 │    │    │    │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    │    │    │    │    ├── key: (1)
 │    │    │    │    │    │    └── fd: (1)-->(2-5)
 │    │    │    │    │    └── filters
 │    │    │    │    │         └── i = 5 [type=bool, outer=(2), constraints=(/2: [/5 - /5]; tight), fd=()-->(2)]
 │    │    │    │    └── filters (true)
 │    │    │    ├── scan xy
 │    │    │    │    ├── columns: x:6(int!null)
 │    │    │    │    └── key: (6)
 │    │    │    └── filters (true)
 │    │    └── aggregations
 │    │         ├── count [type=int, outer=(9)]
 │    │         │    └── agg-distinct [type=int]
 │    │         │         └── variable: v [type=int]
 │    │         ├── const-agg [type=int, outer=(2)]
 │    │         │    └── variable: i [type=int]
 │    │         ├── const-agg [type=float, outer=(3)]
 │    │         │    └── variable: f [type=float]
 │    │         ├── const-agg [type=string, outer=(4)]
 │    │         │    └── variable: s [type=string]
 │    │         └── const-agg [type=jsonb, outer=(5)]
 │    │              └── variable: j [type=jsonb]
 │    └── filters
 │         └── x = count [type=bool, outer=(6,10), constraints=(/6: (/NULL - ]; /10: (/NULL - ]), fd=(6)==(10), (10)==(6)]
 └── aggregations
      ├── const-agg [type=int, outer=(2)]
      │    └── variable: i [type=int]
      ├── const-agg [type=float, outer=(3)]
      │    └── variable: f [type=float]
      ├── const-agg [type=string, outer=(4)]
      │    └── variable: s [type=string]
      └── const-agg [type=jsonb, outer=(5)]
           └── variable: j [type=jsonb]

# Indirectly decorrelate GROUP BY after decorrelating scalar GROUP BY.
opt expect=TryDecorrelateGroupBy
SELECT *
FROM xy, uv
WHERE x=v AND u=(SELECT max(i) FROM a WHERE k=x)
----
project
 ├── columns: x:1(int) y:2(int) u:3(int!null) v:4(int)
 ├── key: (3)
 ├── fd: (1)-->(2), (3)-->(1,2,4), (1)==(4), (4)==(1)
 └── select
      ├── columns: x:1(int) y:2(int) u:3(int!null) v:4(int) max:10(int!null)
      ├── key: (3)
      ├── fd: (1)-->(2), (3)-->(1,2,4), (1)==(4), (4)==(1), (3)==(10), (10)==(3)
      ├── group-by
      │    ├── columns: x:1(int) y:2(int) u:3(int!null) v:4(int) max:10(int)
      │    ├── grouping columns: u:3(int!null)
      │    ├── key: (3)
      │    ├── fd: (1)-->(2), (3)-->(1,2,4,10), (1)==(4), (4)==(1)
      │    ├── inner-join
      │    │    ├── columns: x:1(int!null) y:2(int) u:3(int!null) v:4(int!null) k:5(int!null) i:6(int!null)
      │    │    ├── key: (3)
      │    │    ├── fd: (1)-->(2), (3)-->(4), (1)==(4,5), (4)==(1,5), (5)-->(6), (5)==(1,4)
      │    │    ├── scan uv
      │    │    │    ├── columns: u:3(int!null) v:4(int)
      │    │    │    ├── key: (3)
      │    │    │    └── fd: (3)-->(4)
      │    │    ├── inner-join (merge)
      │    │    │    ├── columns: x:1(int!null) y:2(int) k:5(int!null) i:6(int!null)
      │    │    │    ├── left ordering: +1
      │    │    │    ├── right ordering: +5
      │    │    │    ├── key: (5)
      │    │    │    ├── fd: (1)-->(2), (5)-->(6), (1)==(5), (5)==(1)
      │    │    │    ├── scan xy
      │    │    │    │    ├── columns: x:1(int!null) y:2(int)
      │    │    │    │    ├── key: (1)
      │    │    │    │    ├── fd: (1)-->(2)
      │    │    │    │    └── ordering: +1
      │    │    │    ├── select
      │    │    │    │    ├── columns: k:5(int!null) i:6(int!null)
      │    │    │    │    ├── key: (5)
      │    │    │    │    ├── fd: (5)-->(6)
      │    │    │    │    ├── ordering: +5
      │    │    │    │    ├── scan a
      │    │    │    │    │    ├── columns: k:5(int!null) i:6(int)
      │    │    │    │    │    ├── key: (5)
      │    │    │    │    │    ├── fd: (5)-->(6)
      │    │    │    │    │    └── ordering: +5
      │    │    │    │    └── filters
      │    │    │    │         └── i IS NOT NULL [type=bool, outer=(6), constraints=(/6: (/NULL - ]; tight)]
      │    │    │    └── filters (true)
      │    │    └── filters
      │    │         └── x = v [type=bool, outer=(1,4), constraints=(/1: (/NULL - ]; /4: (/NULL - ]), fd=(1)==(4), (4)==(1)]
      │    └── aggregations
      │         ├── max [type=int, outer=(6)]
      │         │    └── variable: i [type=int]
      │         ├── const-agg [type=int, outer=(4)]
      │         │    └── variable: v [type=int]
      │         ├── const-agg [type=int, outer=(2)]
      │         │    └── variable: y [type=int]
      │         └── const-agg [type=int, outer=(1)]
      │              └── variable: x [type=int]
      └── filters
           └── u = max [type=bool, outer=(3,10), constraints=(/3: (/NULL - ]; /10: (/NULL - ]), fd=(3)==(10), (10)==(3)]

# Indirectly decorrelate GROUP BY after decorrelating scalar GROUP BY. Use
# IS DISTINCT FROM to retain left join.
opt expect=TryDecorrelateGroupBy
SELECT *
FROM xy, uv
WHERE x=v AND (SELECT max(i) FROM a WHERE k=x) IS DISTINCT FROM u
----
project
 ├── columns: x:1(int) y:2(int) u:3(int!null) v:4(int)
 ├── key: (3)
 ├── fd: (1)-->(2), (3)-->(1,2,4), (1)==(4), (4)==(1)
 └── select
      ├── columns: x:1(int) y:2(int) u:3(int!null) v:4(int) max:10(int)
      ├── key: (3)
      ├── fd: (1)-->(2), (3)-->(1,2,4,10), (1)==(4), (4)==(1)
      ├── group-by
      │    ├── columns: x:1(int) y:2(int) u:3(int!null) v:4(int) max:10(int)
      │    ├── grouping columns: u:3(int!null)
      │    ├── key: (3)
      │    ├── fd: (1)-->(2), (3)-->(1,2,4,10), (1)==(4), (4)==(1)
      │    ├── left-join
      │    │    ├── columns: x:1(int!null) y:2(int) u:3(int!null) v:4(int!null) k:5(int) i:6(int)
      │    │    ├── key: (3,5)
      │    │    ├── fd: (1)-->(2), (3)-->(4), (1)==(4), (4)==(1), (5)-->(6)
      │    │    ├── inner-join
      │    │    │    ├── columns: x:1(int!null) y:2(int) u:3(int!null) v:4(int!null)
      │    │    │    ├── key: (3)
      │    │    │    ├── fd: (1)-->(2), (3)-->(4), (1)==(4), (4)==(1)
      │    │    │    ├── scan xy
      │    │    │    │    ├── columns: x:1(int!null) y:2(int)
      │    │    │    │    ├── key: (1)
      │    │    │    │    └── fd: (1)-->(2)
      │    │    │    ├── scan uv
      │    │    │    │    ├── columns: u:3(int!null) v:4(int)
      │    │    │    │    ├── key: (3)
      │    │    │    │    └── fd: (3)-->(4)
      │    │    │    └── filters
      │    │    │         └── x = v [type=bool, outer=(1,4), constraints=(/1: (/NULL - ]; /4: (/NULL - ]), fd=(1)==(4), (4)==(1)]
      │    │    ├── scan a
      │    │    │    ├── columns: k:5(int!null) i:6(int)
      │    │    │    ├── key: (5)
      │    │    │    └── fd: (5)-->(6)
      │    │    └── filters
      │    │         └── k = x [type=bool, outer=(1,5), constraints=(/1: (/NULL - ]; /5: (/NULL - ]), fd=(1)==(5), (5)==(1)]
      │    └── aggregations
      │         ├── max [type=int, outer=(6)]
      │         │    └── variable: i [type=int]
      │         ├── const-agg [type=int, outer=(4)]
      │         │    └── variable: v [type=int]
      │         ├── const-agg [type=int, outer=(2)]
      │         │    └── variable: y [type=int]
      │         └── const-agg [type=int, outer=(1)]
      │              └── variable: x [type=int]
      └── filters
           └── u IS DISTINCT FROM max [type=bool, outer=(3,10)]

# Synthesize key when one is not present.
opt expect=TryDecorrelateGroupBy
SELECT *
FROM
(
    SELECT y, 'foo' AS cst FROM xy
)
WHERE 'bar'=(SELECT max(s) FROM (SELECT * FROM a LIMIT 1) WHERE k=y GROUP BY i)
----
project
 ├── columns: y:2(int) cst:3(string!null)
 ├── fd: ()-->(2,3)
 ├── select
 │    ├── columns: y:2(int) max:9(string!null) rownum:10(int!null)
 │    ├── key: (10)
 │    ├── fd: ()-->(2,9)
 │    ├── group-by
 │    │    ├── columns: y:2(int) max:9(string) rownum:10(int!null)
 │    │    ├── grouping columns: rownum:10(int!null)
 │    │    ├── key: (10)
 │    │    ├── fd: ()-->(2), (10)-->(2,9)
 │    │    ├── inner-join
 │    │    │    ├── columns: y:2(int!null) k:4(int!null) s:7(string) rownum:10(int!null)
 │    │    │    ├── key: (10)
 │    │    │    ├── fd: ()-->(2,4,7), (2)==(4), (4)==(2)
 │    │    │    ├── row-number
 │    │    │    │    ├── columns: y:2(int) rownum:10(int!null)
 │    │    │    │    ├── key: (10)
 │    │    │    │    ├── fd: (10)-->(2)
 │    │    │    │    └── scan xy
 │    │    │    │         └── columns: y:2(int)
 │    │    │    ├── scan a
 │    │    │    │    ├── columns: k:4(int!null) s:7(string)
 │    │    │    │    ├── limit: 1
 │    │    │    │    ├── key: ()
 │    │    │    │    └── fd: ()-->(4,7)
 │    │    │    └── filters
 │    │    │         └── k = y [type=bool, outer=(2,4), constraints=(/2: (/NULL - ]; /4: (/NULL - ]), fd=(2)==(4), (4)==(2)]
 │    │    └── aggregations
 │    │         ├── max [type=string, outer=(7)]
 │    │         │    └── variable: s [type=string]
 │    │         └── const-agg [type=int, outer=(2)]
 │    │              └── variable: y [type=int]
 │    └── filters
 │         └── max = 'bar' [type=bool, outer=(9), constraints=(/9: [/'bar' - /'bar']; tight), fd=()-->(9)]
 └── projections
      └── const: 'foo' [type=string]

# Decorrelate DistinctOn.
opt expect=TryDecorrelateGroupBy
SELECT *
FROM a
WHERE EXISTS
(
    SELECT * FROM xy INNER JOIN (
        SELECT DISTINCT ON (v) u, v FROM uv WHERE i=5
    ) ON x=u
)
----
group-by
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── grouping columns: k:1(int!null)
 ├── key: (1)
 ├── fd: ()-->(2), (1)-->(2-5)
 ├── select
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) u:8(int!null) v:9(int)
 │    ├── key: (1,8)
 │    ├── fd: ()-->(2), (1)-->(3-5), (8)-->(9), (1,6,9)-->(3-5,8), (6)==(8), (8)==(6)
 │    ├── distinct-on
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) u:8(int) v:9(int)
 │    │    ├── grouping columns: k:1(int!null) x:6(int!null) v:9(int)
 │    │    ├── key: (1,6,8)
 │    │    ├── fd: ()-->(2), (1)-->(3-5), (8)-->(9), (1,6,9)-->(2-5,8)
 │    │    ├── inner-join
 │    │    │    ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) u:8(int!null) v:9(int)
 │    │    │    ├── key: (1,6,8)
 │    │    │    ├── fd: ()-->(2), (1)-->(3-5), (8)-->(9)
 │    │    │    ├── inner-join
 │    │    │    │    ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb) u:8(int!null) v:9(int)
 │    │    │    │    ├── key: (1,8)
 │    │    │    │    ├── fd: ()-->(2), (8)-->(9), (1)-->(3-5)
 │    │    │    │    ├── scan uv
 │    │    │    │    │    ├── columns: u:8(int!null) v:9(int)
 │    │    │    │    │    ├── key: (8)
 │    │    │    │    │    └── fd: (8)-->(9)
 │    │    │    │    ├── select
 │    │    │    │    │    ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb)
 │    │    │    │    │    ├── key: (1)
 │    │    │    │    │    ├── fd: ()-->(2), (1)-->(3-5)
 │    │    │    │    │    ├── scan a
 │    │    │    │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    │    │    │    │    ├── key: (1)
 │    │    │    │    │    │    └── fd: (1)-->(2-5)
 │    │    │    │    │    └── filters
 │    │    │    │    │         └── i = 5 [type=bool, outer=(2), constraints=(/2: [/5 - /5]; tight), fd=()-->(2)]
 │    │    │    │    └── filters (true)
 │    │    │    ├── scan xy
 │    │    │    │    ├── columns: x:6(int!null)
 │    │    │    │    └── key: (6)
 │    │    │    └── filters (true)
 │    │    └── aggregations
 │    │         ├── first-agg [type=int, outer=(8)]
 │    │         │    └── variable: u [type=int]
 │    │         ├── const-agg [type=int, outer=(2)]
 │    │         │    └── variable: i [type=int]
 │    │         ├── const-agg [type=float, outer=(3)]
 │    │         │    └── variable: f [type=float]
 │    │         ├── const-agg [type=string, outer=(4)]
 │    │         │    └── variable: s [type=string]
 │    │         └── const-agg [type=jsonb, outer=(5)]
 │    │              └── variable: j [type=jsonb]
 │    └── filters
 │         └── x = u [type=bool, outer=(6,8), constraints=(/6: (/NULL - ]; /8: (/NULL - ]), fd=(6)==(8), (8)==(6)]
 └── aggregations
      ├── const-agg [type=int, outer=(2)]
      │    └── variable: i [type=int]
      ├── const-agg [type=float, outer=(3)]
      │    └── variable: f [type=float]
      ├── const-agg [type=string, outer=(4)]
      │    └── variable: s [type=string]
      └── const-agg [type=jsonb, outer=(5)]
           └── variable: j [type=jsonb]

# --------------------------------------------------
# TryDecorrelateScalarGroupBy
# --------------------------------------------------
opt expect=TryDecorrelateScalarGroupBy
SELECT *
FROM a
WHERE EXISTS
(
    SELECT * FROM xy INNER JOIN (SELECT sum(v), count(*) AS cnt FROM uv WHERE i=5) ON x=cnt
)
----
group-by
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── grouping columns: k:1(int!null)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── select
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) count_rows:11(int!null)
 │    ├── key: (1,6)
 │    ├── fd: (1)-->(2-5), (1,6)-->(2-5,11), (6)==(11), (11)==(6)
 │    ├── group-by
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) count_rows:11(int)
 │    │    ├── grouping columns: k:1(int!null) x:6(int!null)
 │    │    ├── key: (1,6)
 │    │    ├── fd: (1)-->(2-5), (1,6)-->(2-5,11)
 │    │    ├── left-join
 │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) canary:12(bool)
 │    │    │    ├── fd: (1)-->(2-5), ()~~>(12)
 │    │    │    ├── inner-join
 │    │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null)
 │    │    │    │    ├── key: (1,6)
 │    │    │    │    ├── fd: (1)-->(2-5)
 │    │    │    │    ├── scan a
 │    │    │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    │    │    │    ├── key: (1)
 │    │    │    │    │    └── fd: (1)-->(2-5)
 │    │    │    │    ├── scan xy
 │    │    │    │    │    ├── columns: x:6(int!null)
 │    │    │    │    │    └── key: (6)
 │    │    │    │    └── filters (true)
 │    │    │    ├── project
 │    │    │    │    ├── columns: canary:12(bool!null)
 │    │    │    │    ├── fd: ()-->(12)
 │    │    │    │    ├── scan uv
 │    │    │    │    └── projections
 │    │    │    │         └── true [type=bool]
 │    │    │    └── filters
 │    │    │         └── i = 5 [type=bool, outer=(2), constraints=(/2: [/5 - /5]; tight), fd=()-->(2)]
 │    │    └── aggregations
 │    │         ├── count [type=int, outer=(12)]
 │    │         │    └── variable: canary [type=bool]
 │    │         ├── const-agg [type=int, outer=(2)]
 │    │         │    └── variable: i [type=int]
 │    │         ├── const-agg [type=float, outer=(3)]
 │    │         │    └── variable: f [type=float]
 │    │         ├── const-agg [type=string, outer=(4)]
 │    │         │    └── variable: s [type=string]
 │    │         └── const-agg [type=jsonb, outer=(5)]
 │    │              └── variable: j [type=jsonb]
 │    └── filters
 │         └── x = count_rows [type=bool, outer=(6,11), constraints=(/6: (/NULL - ]; /11: (/NULL - ]), fd=(6)==(11), (11)==(6)]
 └── aggregations
      ├── const-agg [type=int, outer=(2)]
      │    └── variable: i [type=int]
      ├── const-agg [type=float, outer=(3)]
      │    └── variable: f [type=float]
      ├── const-agg [type=string, outer=(4)]
      │    └── variable: s [type=string]
      └── const-agg [type=jsonb, outer=(5)]
           └── variable: j [type=jsonb]

# Synthesize key when one is not present.
opt expect=TryDecorrelateScalarGroupBy
SELECT * FROM (SELECT i, 'foo' AS cst FROM a) WHERE 5=(SELECT max(y) FROM xy WHERE x=i)
----
project
 ├── columns: i:2(int) cst:6(string!null)
 ├── fd: ()-->(6)
 ├── select
 │    ├── columns: i:2(int) max:9(int!null) rownum:10(int!null)
 │    ├── key: (10)
 │    ├── fd: ()-->(9), (10)-->(2)
 │    ├── group-by
 │    │    ├── columns: i:2(int) max:9(int) rownum:10(int!null)
 │    │    ├── grouping columns: rownum:10(int!null)
 │    │    ├── key: (10)
 │    │    ├── fd: (10)-->(2,9)
 │    │    ├── inner-join
 │    │    │    ├── columns: i:2(int!null) x:7(int!null) y:8(int!null) rownum:10(int!null)
 │    │    │    ├── key: (10)
 │    │    │    ├── fd: (10)-->(2), (7)-->(8), (2)==(7), (7)==(2)
 │    │    │    ├── row-number
 │    │    │    │    ├── columns: i:2(int) rownum:10(int!null)
 │    │    │    │    ├── key: (10)
 │    │    │    │    ├── fd: (10)-->(2)
 │    │    │    │    └── scan a
 │    │    │    │         └── columns: i:2(int)
 │    │    │    ├── select
 │    │    │    │    ├── columns: x:7(int!null) y:8(int!null)
 │    │    │    │    ├── key: (7)
 │    │    │    │    ├── fd: (7)-->(8)
 │    │    │    │    ├── scan xy
 │    │    │    │    │    ├── columns: x:7(int!null) y:8(int)
 │    │    │    │    │    ├── key: (7)
 │    │    │    │    │    └── fd: (7)-->(8)
 │    │    │    │    └── filters
 │    │    │    │         └── y IS NOT NULL [type=bool, outer=(8), constraints=(/8: (/NULL - ]; tight)]
 │    │    │    └── filters
 │    │    │         └── x = i [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)]
 │    │    └── aggregations
 │    │         ├── max [type=int, outer=(8)]
 │    │         │    └── variable: y [type=int]
 │    │         └── const-agg [type=int, outer=(2)]
 │    │              └── variable: i [type=int]
 │    └── filters
 │         └── max = 5 [type=bool, outer=(9), constraints=(/9: [/5 - /5]; tight), fd=()-->(9)]
 └── projections
      └── const: 'foo' [type=string]

# With an aggregate that can't ignore nulls. xy.y = a.k rejects nulls, so
# there's no canary column to be synthesized.
opt expect=TryDecorrelateScalarGroupBy
SELECT k, (SELECT array_agg(xy.y) FROM xy WHERE xy.y = a.k) FROM a
----
project
 ├── columns: k:1(int!null) array_agg:9(int[])
 ├── key: (1)
 ├── fd: (1)-->(9)
 ├── group-by
 │    ├── columns: k:1(int!null) y:7(int) array_agg:10(int[])
 │    ├── grouping columns: k:1(int!null)
 │    ├── key: (1)
 │    ├── fd: (1)-->(7,10)
 │    ├── left-join
 │    │    ├── columns: k:1(int!null) y:7(int)
 │    │    ├── scan a
 │    │    │    ├── columns: k:1(int!null)
 │    │    │    └── key: (1)
 │    │    ├── scan xy
 │    │    │    └── columns: y:7(int)
 │    │    └── filters
 │    │         └── y = k [type=bool, outer=(1,7), constraints=(/1: (/NULL - ]; /7: (/NULL - ]), fd=(1)==(7), (7)==(1)]
 │    └── aggregations
 │         ├── array-agg [type=int[], outer=(7)]
 │         │    └── variable: y [type=int]
 │         └── any-not-null-agg [type=int, outer=(7)]
 │              └── variable: y [type=int]
 └── projections
      └── CASE WHEN y IS NOT NULL THEN array_agg END [type=int[], outer=(7,10)]

# With multiple columns. Without LATERAL these tests are a bit verbose.
norm expect=TryDecorrelateScalarGroupBy
SELECT k, (SELECT (r, q) FROM (SELECT array_agg(xy.y) r, max(xy.y) q FROM xy WHERE xy.y = a.k)) FROM a
----
project
 ├── columns: k:1(int!null) "?column?":11(tuple{int[], int})
 ├── key: (1)
 ├── fd: (1)-->(11)
 ├── group-by
 │    ├── columns: k:1(int!null) y:7(int) max:9(int) array_agg:12(int[])
 │    ├── grouping columns: k:1(int!null)
 │    ├── key: (1)
 │    ├── fd: (1)-->(7,9,12)
 │    ├── left-join
 │    │    ├── columns: k:1(int!null) y:7(int)
 │    │    ├── scan a
 │    │    │    ├── columns: k:1(int!null)
 │    │    │    └── key: (1)
 │    │    ├── scan xy
 │    │    │    └── columns: y:7(int)
 │    │    └── filters
 │    │         └── y = k [type=bool, outer=(1,7), constraints=(/1: (/NULL - ]; /7: (/NULL - ]), fd=(1)==(7), (7)==(1)]
 │    └── aggregations
 │         ├── array-agg [type=int[], outer=(7)]
 │         │    └── variable: y [type=int]
 │         ├── max [type=int, outer=(7)]
 │         │    └── variable: y [type=int]
 │         └── any-not-null-agg [type=int, outer=(7)]
 │              └── variable: y [type=int]
 └── projections
      └── (CASE WHEN y IS NOT NULL THEN array_agg END, max) [type=tuple{int[], int}, outer=(7,9,12)]


# With an aggregate that can't ignore nulls and when a non-nullable column must be synthesized.
norm expect=TryDecorrelateScalarGroupBy
SELECT k, ARRAY(SELECT y FROM xy WHERE xy.y = a.i OR xy.y IS NULL) FROM a
----
project
 ├── columns: k:1(int!null) array:9(int[])
 ├── key: (1)
 ├── fd: (1)-->(9)
 ├── group-by
 │    ├── columns: k:1(int!null) canary:10(bool) array_agg:11(int[])
 │    ├── grouping columns: k:1(int!null)
 │    ├── key: (1)
 │    ├── fd: ()~~>(10), (1)-->(10,11)
 │    ├── left-join
 │    │    ├── columns: k:1(int!null) i:2(int) y:7(int) canary:10(bool)
 │    │    ├── fd: (1)-->(2), ()~~>(10)
 │    │    ├── scan a
 │    │    │    ├── columns: k:1(int!null) i:2(int)
 │    │    │    ├── key: (1)
 │    │    │    └── fd: (1)-->(2)
 │    │    ├── project
 │    │    │    ├── columns: canary:10(bool!null) y:7(int)
 │    │    │    ├── fd: ()-->(10)
 │    │    │    ├── scan xy
 │    │    │    │    └── columns: y:7(int)
 │    │    │    └── projections
 │    │    │         └── true [type=bool]
 │    │    └── filters
 │    │         └── (y = i) OR (y IS NULL) [type=bool, outer=(2,7)]
 │    └── aggregations
 │         ├── array-agg [type=int[], outer=(7)]
 │         │    └── variable: y [type=int]
 │         └── any-not-null-agg [type=bool, outer=(10)]
 │              └── variable: canary [type=bool]
 └── projections
      └── COALESCE(CASE WHEN canary IS NOT NULL THEN array_agg END, ARRAY[]) [type=int[], outer=(10,11)]

# With an ordering.
norm expect=TryDecorrelateScalarGroupBy
SELECT i, ARRAY(SELECT y FROM xy WHERE xy.y = a.k OR xy.y = NULL ORDER BY y) FROM a
----
project
 ├── columns: i:2(int) array:9(int[])
 ├── group-by
 │    ├── columns: k:1(int!null) i:2(int) canary:10(bool) array_agg:11(int[])
 │    ├── grouping columns: k:1(int!null)
 │    ├── internal-ordering: +7
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,10,11), ()~~>(10)
 │    ├── sort
 │    │    ├── columns: k:1(int!null) i:2(int) y:7(int) canary:10(bool)
 │    │    ├── fd: (1)-->(2), ()~~>(10)
 │    │    ├── ordering: +7
 │    │    └── left-join
 │    │         ├── columns: k:1(int!null) i:2(int) y:7(int) canary:10(bool)
 │    │         ├── fd: (1)-->(2), ()~~>(10)
 │    │         ├── scan a
 │    │         │    ├── columns: k:1(int!null) i:2(int)
 │    │         │    ├── key: (1)
 │    │         │    └── fd: (1)-->(2)
 │    │         ├── project
 │    │         │    ├── columns: canary:10(bool!null) y:7(int)
 │    │         │    ├── fd: ()-->(10)
 │    │         │    ├── scan xy
 │    │         │    │    └── columns: y:7(int)
 │    │         │    └── projections
 │    │         │         └── true [type=bool]
 │    │         └── filters
 │    │              └── (y = k) OR NULL [type=bool, outer=(1,7)]
 │    └── aggregations
 │         ├── array-agg [type=int[], outer=(7)]
 │         │    └── variable: y [type=int]
 │         ├── const-agg [type=int, outer=(2)]
 │         │    └── variable: i [type=int]
 │         └── any-not-null-agg [type=bool, outer=(10)]
 │              └── variable: canary [type=bool]
 └── projections
      └── COALESCE(CASE WHEN canary IS NOT NULL THEN array_agg END, ARRAY[]) [type=int[], outer=(10,11)]

# Nest scalar decorrelation within scalar decorrelation, using IS NULL to force
# use of left joins.
opt expect=TryDecorrelateScalarGroupBy
SELECT *
FROM a
WHERE
(
    SELECT max(y)
    FROM xy
    WHERE
    (
        SELECT max(v) FROM uv WHERE u=k
    ) IS NULL
) IS NULL
----
project
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 └── select
      ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) max:11(int)
      ├── key: (1)
      ├── fd: ()-->(11), (1)-->(2-5)
      ├── group-by
      │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) max:11(int)
      │    ├── grouping columns: k:1(int!null)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-5,11)
      │    ├── left-join-apply
      │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int) y:7(int) max:10(int)
      │    │    ├── key: (1,6)
      │    │    ├── fd: (1)-->(2-5), (1,6)-->(7,10)
      │    │    ├── scan a
      │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    │    │    ├── key: (1)
      │    │    │    └── fd: (1)-->(2-5)
      │    │    ├── group-by
      │    │    │    ├── columns: x:6(int!null) y:7(int) max:10(int)
      │    │    │    ├── grouping columns: x:6(int!null)
      │    │    │    ├── outer: (1)
      │    │    │    ├── key: (6)
      │    │    │    ├── fd: (6)-->(7,10)
      │    │    │    ├── left-join
      │    │    │    │    ├── columns: x:6(int!null) y:7(int) u:8(int) v:9(int)
      │    │    │    │    ├── outer: (1)
      │    │    │    │    ├── key: (6,8)
      │    │    │    │    ├── fd: (6)-->(7), (8)-->(9)
      │    │    │    │    ├── scan xy
      │    │    │    │    │    ├── columns: x:6(int!null) y:7(int)
      │    │    │    │    │    ├── key: (6)
      │    │    │    │    │    └── fd: (6)-->(7)
      │    │    │    │    ├── scan uv
      │    │    │    │    │    ├── columns: u:8(int!null) v:9(int)
      │    │    │    │    │    ├── key: (8)
      │    │    │    │    │    └── fd: (8)-->(9)
      │    │    │    │    └── filters
      │    │    │    │         └── u = k [type=bool, outer=(1,8), constraints=(/1: (/NULL - ]; /8: (/NULL - ]), fd=(1)==(8), (8)==(1)]
      │    │    │    └── aggregations
      │    │    │         ├── max [type=int, outer=(9)]
      │    │    │         │    └── variable: v [type=int]
      │    │    │         └── const-agg [type=int, outer=(7)]
      │    │    │              └── variable: y [type=int]
      │    │    └── filters
      │    │         └── max IS NULL [type=bool, outer=(10), constraints=(/10: [/NULL - /NULL]; tight), fd=()-->(10)]
      │    └── aggregations
      │         ├── max [type=int, outer=(7)]
      │         │    └── variable: y [type=int]
      │         ├── const-agg [type=int, outer=(2)]
      │         │    └── variable: i [type=int]
      │         ├── const-agg [type=float, outer=(3)]
      │         │    └── variable: f [type=float]
      │         ├── const-agg [type=string, outer=(4)]
      │         │    └── variable: s [type=string]
      │         └── const-agg [type=jsonb, outer=(5)]
      │              └── variable: j [type=jsonb]
      └── filters
           └── max IS NULL [type=bool, outer=(11), constraints=(/11: [/NULL - /NULL]; tight), fd=()-->(11)]

# ScalarGroupBy with non-null ignoring and a non-nullable column.
norm expect=TryDecorrelateScalarGroupBy
SELECT *
FROM cd
WHERE
(
    SELECT array_agg(y)
    FROM xy
    WHERE c = x
) = ARRAY[]:::INT[]
----
project
 ├── columns: c:1(int!null) d:2(int)
 ├── key: (1)
 ├── fd: (1)-->(2)
 └── select
      ├── columns: c:1(int!null) d:2(int) array_agg:5(int[]!null)
      ├── key: (1)
      ├── fd: ()-->(5), (1)-->(2)
      ├── project
      │    ├── columns: array_agg:5(int[]) c:1(int!null) d:2(int)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2,5)
      │    ├── group-by
      │    │    ├── columns: c:1(int!null) d:2(int) x:3(int) array_agg:6(int[])
      │    │    ├── grouping columns: c:1(int!null)
      │    │    ├── key: (1)
      │    │    ├── fd: (1)-->(2,3,6)
      │    │    ├── left-join
      │    │    │    ├── columns: c:1(int!null) d:2(int!null) x:3(int) y:4(int)
      │    │    │    ├── key: (1,3)
      │    │    │    ├── fd: (1)-->(2), (3)-->(4)
      │    │    │    ├── scan cd
      │    │    │    │    ├── columns: c:1(int!null) d:2(int!null)
      │    │    │    │    ├── key: (1)
      │    │    │    │    └── fd: (1)-->(2)
      │    │    │    ├── scan xy
      │    │    │    │    ├── columns: x:3(int!null) y:4(int)
      │    │    │    │    ├── key: (3)
      │    │    │    │    └── fd: (3)-->(4)
      │    │    │    └── filters
      │    │    │         └── c = x [type=bool, outer=(1,3), constraints=(/1: (/NULL - ]; /3: (/NULL - ]), fd=(1)==(3), (3)==(1)]
      │    │    └── aggregations
      │    │         ├── array-agg [type=int[], outer=(4)]
      │    │         │    └── variable: y [type=int]
      │    │         ├── const-agg [type=int, outer=(2)]
      │    │         │    └── variable: d [type=int]
      │    │         └── any-not-null-agg [type=int, outer=(3)]
      │    │              └── variable: x [type=int]
      │    └── projections
      │         └── CASE WHEN x IS NOT NULL THEN array_agg END [type=int[], outer=(3,6)]
      └── filters
           └── array_agg = ARRAY[] [type=bool, outer=(5), constraints=(/5: [/ARRAY[] - /ARRAY[]]; tight), fd=()-->(5)]

norm expect=TryDecorrelateScalarGroupBy
SELECT * FROM a WHERE 'foo'=(SELECT concat_agg(y::string) FROM xy WHERE x=k)
----
project
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 └── select
      ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) concat_agg:9(string!null)
      ├── key: (1)
      ├── fd: ()-->(9), (1)-->(2-5)
      ├── project
      │    ├── columns: concat_agg:9(string) k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-5,9)
      │    ├── group-by
      │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) canary:10(bool) concat_agg:11(string)
      │    │    ├── grouping columns: k:1(int!null)
      │    │    ├── key: (1)
      │    │    ├── fd: (1)-->(2-5,10,11), ()~~>(10)
      │    │    ├── left-join
      │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int) column8:8(string) canary:10(bool)
      │    │    │    ├── key: (1,6)
      │    │    │    ├── fd: (1)-->(2-5), (6)-->(8), ()~~>(10), (1,6)-->(10)
      │    │    │    ├── scan a
      │    │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    │    │    │    ├── key: (1)
      │    │    │    │    └── fd: (1)-->(2-5)
      │    │    │    ├── project
      │    │    │    │    ├── columns: canary:10(bool!null) column8:8(string) x:6(int!null)
      │    │    │    │    ├── key: (6)
      │    │    │    │    ├── fd: ()-->(10), (6)-->(8)
      │    │    │    │    ├── scan xy
      │    │    │    │    │    ├── columns: x:6(int!null) y:7(int)
      │    │    │    │    │    ├── key: (6)
      │    │    │    │    │    └── fd: (6)-->(7)
      │    │    │    │    └── projections
      │    │    │    │         ├── true [type=bool]
      │    │    │    │         └── y::STRING [type=string, outer=(7)]
      │    │    │    └── filters
      │    │    │         └── x = k [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]
      │    │    └── aggregations
      │    │         ├── concat-agg [type=string, outer=(8)]
      │    │         │    └── variable: column8 [type=string]
      │    │         ├── const-agg [type=int, outer=(2)]
      │    │         │    └── variable: i [type=int]
      │    │         ├── const-agg [type=float, outer=(3)]
      │    │         │    └── variable: f [type=float]
      │    │         ├── const-agg [type=string, outer=(4)]
      │    │         │    └── variable: s [type=string]
      │    │         ├── const-agg [type=jsonb, outer=(5)]
      │    │         │    └── variable: j [type=jsonb]
      │    │         └── any-not-null-agg [type=bool, outer=(10)]
      │    │              └── variable: canary [type=bool]
      │    └── projections
      │         └── CASE WHEN canary IS NOT NULL THEN concat_agg END [type=string, outer=(10,11)]
      └── filters
           └── concat_agg = 'foo' [type=bool, outer=(9), constraints=(/9: [/'foo' - /'foo']; tight), fd=()-->(9)]

# --------------------------------------------------
# TryDecorrelateSemiJoin
# --------------------------------------------------

# Right input of SemiJoin is GroupBy.
opt expect=TryDecorrelateSemiJoin
SELECT *
FROM xy
WHERE EXISTS
(
    SELECT * FROM a WHERE i=(SELECT max(i) FROM a WHERE f=y::float)
)
----
group-by
 ├── columns: x:1(int!null) y:2(int)
 ├── grouping columns: x:1(int!null)
 ├── key: (1)
 ├── fd: (1)-->(2)
 ├── select
 │    ├── columns: x:1(int!null) y:2(int) k:3(int!null) i:4(int!null) max:13(int!null)
 │    ├── key: (1,3)
 │    ├── fd: (1)-->(2), (3)-->(4), (1,3)-->(2,4,13), (4)==(13), (13)==(4)
 │    ├── group-by
 │    │    ├── columns: x:1(int!null) y:2(int) k:3(int!null) i:4(int) max:13(int)
 │    │    ├── grouping columns: x:1(int!null) k:3(int!null)
 │    │    ├── key: (1,3)
 │    │    ├── fd: (1)-->(2), (3)-->(4), (1,3)-->(2,4,13)
 │    │    ├── inner-join
 │    │    │    ├── columns: x:1(int!null) y:2(int) k:3(int!null) i:4(int) i:9(int!null) f:10(float!null) column14:14(float!null)
 │    │    │    ├── fd: (1)-->(2), (2)-->(14), (3)-->(4), (10)==(14), (14)==(10)
 │    │    │    ├── inner-join
 │    │    │    │    ├── columns: x:1(int!null) y:2(int) i:9(int!null) f:10(float!null) column14:14(float!null)
 │    │    │    │    ├── fd: (1)-->(2), (2)-->(14), (10)==(14), (14)==(10)
 │    │    │    │    ├── project
 │    │    │    │    │    ├── columns: column14:14(float) x:1(int!null) y:2(int)
 │    │    │    │    │    ├── key: (1)
 │    │    │    │    │    ├── fd: (1)-->(2), (2)-->(14)
 │    │    │    │    │    ├── scan xy
 │    │    │    │    │    │    ├── columns: x:1(int!null) y:2(int)
 │    │    │    │    │    │    ├── key: (1)
 │    │    │    │    │    │    └── fd: (1)-->(2)
 │    │    │    │    │    └── projections
 │    │    │    │    │         └── y::FLOAT8 [type=float, outer=(2)]
 │    │    │    │    ├── select
 │    │    │    │    │    ├── columns: i:9(int!null) f:10(float)
 │    │    │    │    │    ├── scan a
 │    │    │    │    │    │    └── columns: i:9(int) f:10(float)
 │    │    │    │    │    └── filters
 │    │    │    │    │         └── i IS NOT NULL [type=bool, outer=(9), constraints=(/9: (/NULL - ]; tight)]
 │    │    │    │    └── filters
 │    │    │    │         └── column14 = f [type=bool, outer=(10,14), constraints=(/10: (/NULL - ]; /14: (/NULL - ]), fd=(10)==(14), (14)==(10)]
 │    │    │    ├── scan a
 │    │    │    │    ├── columns: k:3(int!null) i:4(int)
 │    │    │    │    ├── key: (3)
 │    │    │    │    └── fd: (3)-->(4)
 │    │    │    └── filters (true)
 │    │    └── aggregations
 │    │         ├── max [type=int, outer=(9)]
 │    │         │    └── variable: i [type=int]
 │    │         ├── const-agg [type=int, outer=(4)]
 │    │         │    └── variable: i [type=int]
 │    │         └── const-agg [type=int, outer=(2)]
 │    │              └── variable: y [type=int]
 │    └── filters
 │         └── i = max [type=bool, outer=(4,13), constraints=(/4: (/NULL - ]; /13: (/NULL - ]), fd=(4)==(13), (13)==(4)]
 └── aggregations
      └── const-agg [type=int, outer=(2)]
           └── variable: y [type=int]

# Right input of SemiJoin is DistinctOn.
opt expect=TryDecorrelateSemiJoin
SELECT *
FROM xy
WHERE EXISTS
(
    SELECT * FROM (SELECT DISTINCT ON (f) i FROM a WHERE y > f) WHERE x=i
)
----
group-by
 ├── columns: x:1(int!null) y:2(int)
 ├── grouping columns: x:1(int!null)
 ├── key: (1)
 ├── fd: (1)-->(2)
 ├── select
 │    ├── columns: x:1(int!null) y:2(int) i:4(int!null) f:5(float!null)
 │    ├── key: (1,5)
 │    ├── fd: (1)-->(2), (1,5)-->(2,4), (1)==(4), (4)==(1)
 │    ├── distinct-on
 │    │    ├── columns: x:1(int!null) y:2(int) i:4(int) f:5(float!null)
 │    │    ├── grouping columns: x:1(int!null) f:5(float!null)
 │    │    ├── key: (1,5)
 │    │    ├── fd: (1)-->(2), (1,5)-->(2,4)
 │    │    ├── inner-join
 │    │    │    ├── columns: x:1(int!null) y:2(int!null) i:4(int) f:5(float!null)
 │    │    │    ├── fd: (1)-->(2)
 │    │    │    ├── scan xy
 │    │    │    │    ├── columns: x:1(int!null) y:2(int)
 │    │    │    │    ├── key: (1)
 │    │    │    │    └── fd: (1)-->(2)
 │    │    │    ├── scan a
 │    │    │    │    └── columns: i:4(int) f:5(float)
 │    │    │    └── filters
 │    │    │         └── y > f [type=bool, outer=(2,5), constraints=(/2: (/NULL - ]; /5: (/NULL - ])]
 │    │    └── aggregations
 │    │         ├── first-agg [type=int, outer=(4)]
 │    │         │    └── variable: i [type=int]
 │    │         └── const-agg [type=int, outer=(2)]
 │    │              └── variable: y [type=int]
 │    └── filters
 │         └── x = i [type=bool, outer=(1,4), constraints=(/1: (/NULL - ]; /4: (/NULL - ]), fd=(1)==(4), (4)==(1)]
 └── aggregations
      └── const-agg [type=int, outer=(2)]
           └── variable: y [type=int]

# Right input of SemiJoin is Project.
opt expect=TryDecorrelateSemiJoin
SELECT k FROM a
WHERE EXISTS
(
    SELECT * FROM xy INNER JOIN (SELECT coalesce(u, 10) AS computed FROM uv WHERE u=i) ON x=computed
)
----
project
 ├── columns: k:1(int!null)
 ├── key: (1)
 └── select
      ├── columns: k:1(int!null) x:6(int!null) computed:10(int!null)
      ├── key: (1)
      ├── fd: (1)-->(10), (6)==(10), (10)==(6)
      ├── project
      │    ├── columns: computed:10(int) k:1(int!null) x:6(int!null)
      │    ├── key: (1,6)
      │    ├── fd: (1)-->(10)
      │    ├── inner-join
      │    │    ├── columns: k:1(int!null) i:2(int!null) x:6(int!null) u:8(int!null)
      │    │    ├── key: (1,6)
      │    │    ├── fd: (1)-->(2), (2)==(8), (8)==(2)
      │    │    ├── scan xy
      │    │    │    ├── columns: x:6(int!null)
      │    │    │    └── key: (6)
      │    │    ├── inner-join
      │    │    │    ├── columns: k:1(int!null) i:2(int!null) u:8(int!null)
      │    │    │    ├── key: (1)
      │    │    │    ├── fd: (1)-->(2), (2)==(8), (8)==(2)
      │    │    │    ├── scan uv
      │    │    │    │    ├── columns: u:8(int!null)
      │    │    │    │    └── key: (8)
      │    │    │    ├── scan a
      │    │    │    │    ├── columns: k:1(int!null) i:2(int)
      │    │    │    │    ├── key: (1)
      │    │    │    │    └── fd: (1)-->(2)
      │    │    │    └── filters
      │    │    │         └── u = i [type=bool, outer=(2,8), constraints=(/2: (/NULL - ]; /8: (/NULL - ]), fd=(2)==(8), (8)==(2)]
      │    │    └── filters (true)
      │    └── projections
      │         └── COALESCE(u, 10) [type=int, outer=(8)]
      └── filters
           └── x = computed [type=bool, outer=(6,10), constraints=(/6: (/NULL - ]; /10: (/NULL - ]), fd=(6)==(10), (10)==(6)]

# Right input of SemiJoin is ProjectSet.
opt expect=TryDecorrelateSemiJoin
SELECT * FROM xy WHERE EXISTS(SELECT generate_series(x, 10), generate_series(y, 10))
----
group-by
 ├── columns: x:1(int!null) y:2(int)
 ├── grouping columns: x:1(int!null)
 ├── side-effects
 ├── key: (1)
 ├── fd: (1)-->(2)
 ├── project-set
 │    ├── columns: x:1(int!null) y:2(int) generate_series:3(int) generate_series:4(int)
 │    ├── side-effects
 │    ├── fd: (1)-->(2)
 │    ├── scan xy
 │    │    ├── columns: x:1(int!null) y:2(int)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(2)
 │    └── zip
 │         ├── function: generate_series [type=int, outer=(1), side-effects]
 │         │    ├── variable: x [type=int]
 │         │    └── const: 10 [type=int]
 │         └── function: generate_series [type=int, outer=(2), side-effects]
 │              ├── variable: y [type=int]
 │              └── const: 10 [type=int]
 └── aggregations
      └── const-agg [type=int, outer=(2)]
           └── variable: y [type=int]

# --------------------------------------------------
# TryDecorrelateLimitOne
# --------------------------------------------------

# With inner join.
opt expect=TryDecorrelateLimitOne
SELECT *
FROM a
WHERE EXISTS
(
    SELECT x
    FROM xy
    INNER JOIN (SELECT * FROM uv WHERE v=i LIMIT 1)
    ON x=u
)
----
semi-join
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 ├── inner-join (merge)
 │    ├── columns: x:6(int!null) u:8(int!null) v:9(int)
 │    ├── left ordering: +6
 │    ├── right ordering: +8
 │    ├── key: (8)
 │    ├── fd: (8)-->(9), (6)==(8), (8)==(6)
 │    ├── scan xy
 │    │    ├── columns: x:6(int!null)
 │    │    ├── key: (6)
 │    │    └── ordering: +6
 │    ├── scan uv
 │    │    ├── columns: u:8(int!null) v:9(int)
 │    │    ├── key: (8)
 │    │    ├── fd: (8)-->(9)
 │    │    └── ordering: +8
 │    └── filters (true)
 └── filters
      └── v = i [type=bool, outer=(2,9), constraints=(/2: (/NULL - ]; /9: (/NULL - ]), fd=(2)==(9), (9)==(2)]

# With left join.
opt expect=TryDecorrelateLimitOne
SELECT (SELECT x FROM xy WHERE y=i LIMIT 1) FROM a
----
project
 ├── columns: x:8(int)
 ├── distinct-on
 │    ├── columns: xy.x:6(int) rownum:9(int!null)
 │    ├── grouping columns: rownum:9(int!null)
 │    ├── key: (9)
 │    ├── fd: (9)-->(6)
 │    ├── left-join
 │    │    ├── columns: i:2(int) xy.x:6(int) y:7(int) rownum:9(int!null)
 │    │    ├── key: (6,9)
 │    │    ├── fd: (9)-->(2), (6)-->(7)
 │    │    ├── row-number
 │    │    │    ├── columns: i:2(int) rownum:9(int!null)
 │    │    │    ├── key: (9)
 │    │    │    ├── fd: (9)-->(2)
 │    │    │    └── scan a
 │    │    │         └── columns: i:2(int)
 │    │    ├── scan xy
 │    │    │    ├── columns: xy.x:6(int!null) y:7(int)
 │    │    │    ├── key: (6)
 │    │    │    └── fd: (6)-->(7)
 │    │    └── filters
 │    │         └── y = i [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)]
 │    └── aggregations
 │         └── first-agg [type=int, outer=(6)]
 │              └── variable: xy.x [type=int]
 └── projections
      └── variable: xy.x [type=int, outer=(6)]

# With multiple limited queries.
opt expect=TryDecorrelateLimitOne
SELECT * FROM a WHERE (SELECT x FROM xy WHERE y=i LIMIT 1)=k AND (SELECT u FROM uv WHERE v=i LIMIT 1)=k
----
project
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 └── select
      ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) u:8(int!null)
      ├── key: (1)
      ├── fd: (1)-->(2-5), (1)==(8), (8)==(1)
      ├── distinct-on
      │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) u:8(int)
      │    ├── grouping columns: k:1(int!null)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-5,8)
      │    ├── right-join
      │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) u:8(int) v:9(int)
      │    │    ├── key: (1,8)
      │    │    ├── fd: (1)-->(2-5), (1)==(6), (6)==(1), (8)-->(9)
      │    │    ├── scan uv
      │    │    │    ├── columns: u:8(int!null) v:9(int)
      │    │    │    ├── key: (8)
      │    │    │    └── fd: (8)-->(9)
      │    │    ├── select
      │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null)
      │    │    │    ├── key: (1)
      │    │    │    ├── fd: (1)-->(2-5), (1)==(6), (6)==(1)
      │    │    │    ├── distinct-on
      │    │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int)
      │    │    │    │    ├── grouping columns: k:1(int!null)
      │    │    │    │    ├── key: (1)
      │    │    │    │    ├── fd: (1)-->(2-6)
      │    │    │    │    ├── left-join
      │    │    │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int) y:7(int)
      │    │    │    │    │    ├── key: (1,6)
      │    │    │    │    │    ├── fd: (1)-->(2-5), (6)-->(7)
      │    │    │    │    │    ├── scan a
      │    │    │    │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    │    │    │    │    │    ├── key: (1)
      │    │    │    │    │    │    └── fd: (1)-->(2-5)
      │    │    │    │    │    ├── scan xy
      │    │    │    │    │    │    ├── columns: x:6(int!null) y:7(int)
      │    │    │    │    │    │    ├── key: (6)
      │    │    │    │    │    │    └── fd: (6)-->(7)
      │    │    │    │    │    └── filters
      │    │    │    │    │         └── y = i [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)]
      │    │    │    │    └── aggregations
      │    │    │    │         ├── const-agg [type=int, outer=(2)]
      │    │    │    │         │    └── variable: i [type=int]
      │    │    │    │         ├── const-agg [type=float, outer=(3)]
      │    │    │    │         │    └── variable: f [type=float]
      │    │    │    │         ├── const-agg [type=string, outer=(4)]
      │    │    │    │         │    └── variable: s [type=string]
      │    │    │    │         ├── const-agg [type=jsonb, outer=(5)]
      │    │    │    │         │    └── variable: j [type=jsonb]
      │    │    │    │         └── first-agg [type=int, outer=(6)]
      │    │    │    │              └── variable: x [type=int]
      │    │    │    └── filters
      │    │    │         └── k = x [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]
      │    │    └── filters
      │    │         └── v = i [type=bool, outer=(2,9), constraints=(/2: (/NULL - ]; /9: (/NULL - ]), fd=(2)==(9), (9)==(2)]
      │    └── aggregations
      │         ├── const-agg [type=int, outer=(2)]
      │         │    └── variable: i [type=int]
      │         ├── const-agg [type=float, outer=(3)]
      │         │    └── variable: f [type=float]
      │         ├── const-agg [type=string, outer=(4)]
      │         │    └── variable: s [type=string]
      │         ├── const-agg [type=jsonb, outer=(5)]
      │         │    └── variable: j [type=jsonb]
      │         └── first-agg [type=int, outer=(8)]
      │              └── variable: u [type=int]
      └── filters
           └── k = u [type=bool, outer=(1,8), constraints=(/1: (/NULL - ]; /8: (/NULL - ]), fd=(1)==(8), (8)==(1)]

# With nested limited queries.
opt expect=TryDecorrelateLimitOne
SELECT *
FROM a
WHERE
(
    SELECT x
    FROM xy
    WHERE y=i AND
    (
        SELECT u FROM uv WHERE v=y LIMIT 1
    )=x
    LIMIT 1
)=k
----
project
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 └── select
      ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null)
      ├── key: (1)
      ├── fd: (1)-->(2-5), (1)==(6), (6)==(1)
      ├── distinct-on
      │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int)
      │    ├── grouping columns: k:1(int!null)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-6)
      │    ├── left-join
      │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int) y:7(int) u:8(int)
      │    │    ├── key: (1,6)
      │    │    ├── fd: (1)-->(2-5), (6)-->(7), (6)==(8), (8)==(6)
      │    │    ├── scan a
      │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    │    │    ├── key: (1)
      │    │    │    └── fd: (1)-->(2-5)
      │    │    ├── select
      │    │    │    ├── columns: x:6(int!null) y:7(int) u:8(int!null)
      │    │    │    ├── key: (6)
      │    │    │    ├── fd: (6)-->(7), (6)==(8), (8)==(6)
      │    │    │    ├── distinct-on
      │    │    │    │    ├── columns: x:6(int!null) y:7(int) u:8(int)
      │    │    │    │    ├── grouping columns: x:6(int!null)
      │    │    │    │    ├── key: (6)
      │    │    │    │    ├── fd: (6)-->(7,8)
      │    │    │    │    ├── left-join
      │    │    │    │    │    ├── columns: x:6(int!null) y:7(int) u:8(int) v:9(int)
      │    │    │    │    │    ├── key: (6,8)
      │    │    │    │    │    ├── fd: (6)-->(7), (8)-->(9)
      │    │    │    │    │    ├── scan xy
      │    │    │    │    │    │    ├── columns: x:6(int!null) y:7(int)
      │    │    │    │    │    │    ├── key: (6)
      │    │    │    │    │    │    └── fd: (6)-->(7)
      │    │    │    │    │    ├── scan uv
      │    │    │    │    │    │    ├── columns: u:8(int!null) v:9(int)
      │    │    │    │    │    │    ├── key: (8)
      │    │    │    │    │    │    └── fd: (8)-->(9)
      │    │    │    │    │    └── filters
      │    │    │    │    │         └── v = y [type=bool, outer=(7,9), constraints=(/7: (/NULL - ]; /9: (/NULL - ]), fd=(7)==(9), (9)==(7)]
      │    │    │    │    └── aggregations
      │    │    │    │         ├── const-agg [type=int, outer=(7)]
      │    │    │    │         │    └── variable: y [type=int]
      │    │    │    │         └── first-agg [type=int, outer=(8)]
      │    │    │    │              └── variable: u [type=int]
      │    │    │    └── filters
      │    │    │         └── x = u [type=bool, outer=(6,8), constraints=(/6: (/NULL - ]; /8: (/NULL - ]), fd=(6)==(8), (8)==(6)]
      │    │    └── filters
      │    │         └── y = i [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)]
      │    └── aggregations
      │         ├── const-agg [type=int, outer=(2)]
      │         │    └── variable: i [type=int]
      │         ├── const-agg [type=float, outer=(3)]
      │         │    └── variable: f [type=float]
      │         ├── const-agg [type=string, outer=(4)]
      │         │    └── variable: s [type=string]
      │         ├── const-agg [type=jsonb, outer=(5)]
      │         │    └── variable: j [type=jsonb]
      │         └── first-agg [type=int, outer=(6)]
      │              └── variable: x [type=int]
      └── filters
           └── k = x [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]

# With inner join + ORDER BY.
opt expect=TryDecorrelateLimitOne
SELECT
(
    SELECT v
    FROM uv
    INNER JOIN (SELECT * FROM a WHERE i=x ORDER BY f LIMIT 1)
    ON u=k
    LIMIT 1
)
FROM xy
----
project
 ├── columns: v:10(int)
 ├── distinct-on
 │    ├── columns: x:1(int!null) uv.v:4(int)
 │    ├── grouping columns: x:1(int!null)
 │    ├── key: (1)
 │    ├── fd: (1)-->(4)
 │    ├── left-join
 │    │    ├── columns: x:1(int!null) u:3(int) uv.v:4(int) k:5(int) i:6(int)
 │    │    ├── key: (1,5)
 │    │    ├── fd: (3)-->(4), (5)-->(6), (3)==(5), (5)==(3)
 │    │    ├── scan xy
 │    │    │    ├── columns: x:1(int!null)
 │    │    │    └── key: (1)
 │    │    ├── inner-join (merge)
 │    │    │    ├── columns: u:3(int!null) uv.v:4(int) k:5(int!null) i:6(int)
 │    │    │    ├── left ordering: +3
 │    │    │    ├── right ordering: +5
 │    │    │    ├── key: (5)
 │    │    │    ├── fd: (3)-->(4), (5)-->(6), (3)==(5), (5)==(3)
 │    │    │    ├── scan uv
 │    │    │    │    ├── columns: u:3(int!null) uv.v:4(int)
 │    │    │    │    ├── key: (3)
 │    │    │    │    ├── fd: (3)-->(4)
 │    │    │    │    └── ordering: +3
 │    │    │    ├── scan a
 │    │    │    │    ├── columns: k:5(int!null) i:6(int)
 │    │    │    │    ├── key: (5)
 │    │    │    │    ├── fd: (5)-->(6)
 │    │    │    │    └── ordering: +5
 │    │    │    └── filters (true)
 │    │    └── filters
 │    │         └── i = x [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]
 │    └── aggregations
 │         └── first-agg [type=int, outer=(4)]
 │              └── variable: uv.v [type=int]
 └── projections
      └── variable: uv.v [type=int, outer=(4)]

# With left join + ORDER BY.
opt expect=TryDecorrelateLimitOne
SELECT * FROM xy WHERE (SELECT k FROM a WHERE i=y ORDER BY f,s LIMIT 1)=x
----
project
 ├── columns: x:1(int!null) y:2(int)
 ├── key: (1)
 ├── fd: (1)-->(2)
 └── select
      ├── columns: x:1(int!null) y:2(int) k:3(int!null)
      ├── key: (1)
      ├── fd: (1)-->(2), (1)==(3), (3)==(1)
      ├── distinct-on
      │    ├── columns: x:1(int!null) y:2(int) k:3(int)
      │    ├── grouping columns: x:1(int!null)
      │    ├── internal-ordering: +5,+6 opt(4)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2,3)
      │    ├── sort
      │    │    ├── columns: x:1(int!null) y:2(int) k:3(int) i:4(int) f:5(float) s:6(string)
      │    │    ├── key: (1,3)
      │    │    ├── fd: (1)-->(2), (3)-->(4-6)
      │    │    ├── ordering: +5,+6 opt(4) [actual: +5,+6]
      │    │    └── left-join
      │    │         ├── columns: x:1(int!null) y:2(int) k:3(int) i:4(int) f:5(float) s:6(string)
      │    │         ├── key: (1,3)
      │    │         ├── fd: (1)-->(2), (3)-->(4-6)
      │    │         ├── scan xy
      │    │         │    ├── columns: x:1(int!null) y:2(int)
      │    │         │    ├── key: (1)
      │    │         │    └── fd: (1)-->(2)
      │    │         ├── scan a
      │    │         │    ├── columns: k:3(int!null) i:4(int) f:5(float) s:6(string)
      │    │         │    ├── key: (3)
      │    │         │    └── fd: (3)-->(4-6)
      │    │         └── filters
      │    │              └── i = y [type=bool, outer=(2,4), constraints=(/2: (/NULL - ]; /4: (/NULL - ]), fd=(2)==(4), (4)==(2)]
      │    └── aggregations
      │         ├── const-agg [type=int, outer=(2)]
      │         │    └── variable: y [type=int]
      │         └── first-agg [type=int, outer=(3)]
      │              └── variable: k [type=int]
      └── filters
           └── x = k [type=bool, outer=(1,3), constraints=(/1: (/NULL - ]; /3: (/NULL - ]), fd=(1)==(3), (3)==(1)]

# --------------------------------------------------
# HoistSelectExists
# --------------------------------------------------
opt expect=HoistSelectExists
SELECT * FROM a WHERE EXISTS(SELECT * FROM xy WHERE x=k)
----
semi-join (merge)
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── left ordering: +1
 ├── right ordering: +6
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    └── ordering: +1
 ├── scan xy
 │    ├── columns: x:6(int!null) y:7(int)
 │    ├── key: (6)
 │    ├── fd: (6)-->(7)
 │    └── ordering: +6
 └── filters (true)

# Ensure that EXISTS is hoisted even when it is one of several conjuncts.
opt expect=HoistSelectExists
SELECT * FROM a WHERE s='foo' AND EXISTS(SELECT * FROM xy WHERE x=k) AND i>1
----
semi-join (merge)
 ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string!null) j:5(jsonb)
 ├── left ordering: +1
 ├── right ordering: +6
 ├── key: (1)
 ├── fd: ()-->(4), (1)-->(2,3,5)
 ├── select
 │    ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string!null) j:5(jsonb)
 │    ├── key: (1)
 │    ├── fd: ()-->(4), (1)-->(2,3,5)
 │    ├── ordering: +1 opt(4) [actual: +1]
 │    ├── scan a
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    ├── key: (1)
 │    │    ├── fd: (1)-->(2-5)
 │    │    └── ordering: +1 opt(4) [actual: +1]
 │    └── filters
 │         ├── s = 'foo' [type=bool, outer=(4), constraints=(/4: [/'foo' - /'foo']; tight), fd=()-->(4)]
 │         └── i > 1 [type=bool, outer=(2), constraints=(/2: [/2 - ]; tight)]
 ├── scan xy
 │    ├── columns: x:6(int!null) y:7(int)
 │    ├── key: (6)
 │    ├── fd: (6)-->(7)
 │    └── ordering: +6
 └── filters (true)

# Multiple Exists operators in same Select list.
opt expect=HoistSelectExists
SELECT * FROM a WHERE EXISTS(SELECT * FROM xy WHERE x=k) AND EXISTS(SELECT * FROM xy WHERE x=i)
----
semi-join
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── semi-join
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    ├── scan a
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(2-5)
 │    ├── scan xy
 │    │    ├── columns: x:8(int!null) y:9(int)
 │    │    ├── key: (8)
 │    │    └── fd: (8)-->(9)
 │    └── filters
 │         └── x = i [type=bool, outer=(2,8), constraints=(/2: (/NULL - ]; /8: (/NULL - ]), fd=(2)==(8), (8)==(2)]
 ├── scan xy
 │    ├── columns: x:6(int!null) y:7(int)
 │    ├── key: (6)
 │    └── fd: (6)-->(7)
 └── filters
      └── x = k [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]

# Don't hoist uncorrelated subqueries.
opt expect-not=HoistSelectExists
SELECT * FROM a WHERE EXISTS(SELECT * FROM xy)
----
select
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 └── filters
      └── exists [type=bool, subquery]
           └── scan xy
                ├── columns: x:6(int!null) y:7(int)
                ├── limit: 1
                ├── key: ()
                └── fd: ()-->(6,7)

# Hoist nested EXISTS.
opt expect=HoistSelectExists
SELECT * FROM a WHERE EXISTS(SELECT * FROM xy WHERE EXISTS (SELECT * FROM uv WHERE x=u) AND x=k)
----
semi-join (merge)
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── left ordering: +1
 ├── right ordering: +6
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    └── ordering: +1
 ├── semi-join (merge)
 │    ├── columns: x:6(int!null) y:7(int)
 │    ├── left ordering: +6
 │    ├── right ordering: +8
 │    ├── key: (6)
 │    ├── fd: (6)-->(7)
 │    ├── ordering: +6
 │    ├── scan xy
 │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    ├── key: (6)
 │    │    ├── fd: (6)-->(7)
 │    │    └── ordering: +6
 │    ├── scan uv
 │    │    ├── columns: u:8(int!null) v:9(int)
 │    │    ├── key: (8)
 │    │    ├── fd: (8)-->(9)
 │    │    └── ordering: +8
 │    └── filters (true)
 └── filters (true)

# --------------------------------------------------
# HoistSelectNotExists
# --------------------------------------------------
opt expect=HoistSelectNotExists
SELECT * FROM a WHERE NOT EXISTS(SELECT * FROM xy WHERE x=k)
----
anti-join (merge)
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── left ordering: +1
 ├── right ordering: +6
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    └── ordering: +1
 ├── scan xy
 │    ├── columns: x:6(int!null) y:7(int)
 │    ├── key: (6)
 │    ├── fd: (6)-->(7)
 │    └── ordering: +6
 └── filters (true)

# Ensure that NOT EXISTS is hoisted even when one of several conjuncts.
opt expect=HoistSelectNotExists
SELECT * FROM a WHERE s='foo' AND NOT EXISTS(SELECT * FROM xy WHERE x=k) AND i>1
----
anti-join (merge)
 ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string!null) j:5(jsonb)
 ├── left ordering: +1
 ├── right ordering: +6
 ├── key: (1)
 ├── fd: ()-->(4), (1)-->(2,3,5)
 ├── select
 │    ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string!null) j:5(jsonb)
 │    ├── key: (1)
 │    ├── fd: ()-->(4), (1)-->(2,3,5)
 │    ├── ordering: +1 opt(4) [actual: +1]
 │    ├── scan a
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    ├── key: (1)
 │    │    ├── fd: (1)-->(2-5)
 │    │    └── ordering: +1 opt(4) [actual: +1]
 │    └── filters
 │         ├── s = 'foo' [type=bool, outer=(4), constraints=(/4: [/'foo' - /'foo']; tight), fd=()-->(4)]
 │         └── i > 1 [type=bool, outer=(2), constraints=(/2: [/2 - ]; tight)]
 ├── scan xy
 │    ├── columns: x:6(int!null) y:7(int)
 │    ├── key: (6)
 │    ├── fd: (6)-->(7)
 │    └── ordering: +6
 └── filters (true)

# Multiple Not Exists operators in same Select list.
opt expect=HoistSelectNotExists
SELECT *
FROM a
WHERE NOT EXISTS(SELECT * FROM xy WHERE x=k) AND NOT EXISTS(SELECT * FROM xy WHERE x=i)
----
anti-join
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── anti-join
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    ├── scan a
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(2-5)
 │    ├── scan xy
 │    │    ├── columns: x:8(int!null) y:9(int)
 │    │    ├── key: (8)
 │    │    └── fd: (8)-->(9)
 │    └── filters
 │         └── x = i [type=bool, outer=(2,8), constraints=(/2: (/NULL - ]; /8: (/NULL - ]), fd=(2)==(8), (8)==(2)]
 ├── scan xy
 │    ├── columns: x:6(int!null) y:7(int)
 │    ├── key: (6)
 │    └── fd: (6)-->(7)
 └── filters
      └── x = k [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]

# Don't hoist uncorrelated subqueries.
opt expect-not=HoistSelectNotExists
SELECT * FROM a WHERE NOT EXISTS(SELECT * FROM xy)
----
select
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 └── filters
      └── not [type=bool, subquery]
           └── exists [type=bool]
                └── scan xy
                     ├── columns: x:6(int!null) y:7(int)
                     ├── limit: 1
                     ├── key: ()
                     └── fd: ()-->(6,7)

# --------------------------------------------------
# HoistSelectExists + HoistSelectNotExists
# --------------------------------------------------
opt expect=(HoistSelectExists,HoistSelectNotExists)
SELECT * FROM a WHERE EXISTS(SELECT * FROM xy WHERE x=k) AND NOT EXISTS(SELECT * FROM xy WHERE x=i)
----
semi-join
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── anti-join
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    ├── scan a
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(2-5)
 │    ├── scan xy
 │    │    ├── columns: x:8(int!null) y:9(int)
 │    │    ├── key: (8)
 │    │    └── fd: (8)-->(9)
 │    └── filters
 │         └── x = i [type=bool, outer=(2,8), constraints=(/2: (/NULL - ]; /8: (/NULL - ]), fd=(2)==(8), (8)==(2)]
 ├── scan xy
 │    ├── columns: x:6(int!null) y:7(int)
 │    ├── key: (6)
 │    └── fd: (6)-->(7)
 └── filters
      └── x = k [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]

# --------------------------------------------------
# HoistSelectSubquery
# --------------------------------------------------
opt expect=HoistSelectSubquery
SELECT * FROM a WHERE (SELECT y FROM xy WHERE y=k LIMIT 1) = i
----
project
 ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 └── select
      ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb) y:7(int!null)
      ├── key: (1)
      ├── fd: (1)-->(2-5,7), (2)==(7), (7)==(2)
      ├── distinct-on
      │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) y:7(int)
      │    ├── grouping columns: k:1(int!null)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-5,7)
      │    ├── left-join
      │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) y:7(int)
      │    │    ├── fd: (1)-->(2-5)
      │    │    ├── scan a
      │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    │    │    ├── key: (1)
      │    │    │    └── fd: (1)-->(2-5)
      │    │    ├── scan xy
      │    │    │    └── columns: y:7(int)
      │    │    └── filters
      │    │         └── y = k [type=bool, outer=(1,7), constraints=(/1: (/NULL - ]; /7: (/NULL - ]), fd=(1)==(7), (7)==(1)]
      │    └── aggregations
      │         ├── const-agg [type=int, outer=(2)]
      │         │    └── variable: i [type=int]
      │         ├── const-agg [type=float, outer=(3)]
      │         │    └── variable: f [type=float]
      │         ├── const-agg [type=string, outer=(4)]
      │         │    └── variable: s [type=string]
      │         ├── const-agg [type=jsonb, outer=(5)]
      │         │    └── variable: j [type=jsonb]
      │         └── first-agg [type=int, outer=(7)]
      │              └── variable: y [type=int]
      └── filters
           └── i = y [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)]

# Multiple other conjuncts, including uncorrelated subquery (don't hoist).
opt expect=HoistSelectSubquery
SELECT *
FROM a
WHERE k=10 AND (SELECT y FROM xy WHERE y=k LIMIT 1) = i AND (SELECT x FROM xy LIMIT 1) = 100
----
project
 ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb)
 ├── cardinality: [0 - 1]
 ├── key: ()
 ├── fd: ()-->(1-5)
 └── select
      ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb) y:7(int!null)
      ├── cardinality: [0 - 1]
      ├── key: ()
      ├── fd: ()-->(1-5,7)
      ├── limit
      │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) y:7(int)
      │    ├── cardinality: [0 - 1]
      │    ├── key: ()
      │    ├── fd: ()-->(1-5,7)
      │    ├── left-join (merge)
      │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) y:7(int)
      │    │    ├── left ordering: +1
      │    │    ├── right ordering: +7
      │    │    ├── fd: ()-->(1-5), ()~~>(7)
      │    │    ├── select
      │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    │    │    ├── cardinality: [0 - 1]
      │    │    │    ├── key: ()
      │    │    │    ├── fd: ()-->(1-5)
      │    │    │    ├── scan a
      │    │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    │    │    │    ├── constraint: /1: [/10 - /10]
      │    │    │    │    ├── cardinality: [0 - 1]
      │    │    │    │    ├── key: ()
      │    │    │    │    └── fd: ()-->(1-5)
      │    │    │    └── filters
      │    │    │         └── eq [type=bool, subquery]
      │    │    │              ├── subquery [type=int]
      │    │    │              │    └── scan xy
      │    │    │              │         ├── columns: x:8(int!null)
      │    │    │              │         ├── limit: 1
      │    │    │              │         ├── key: ()
      │    │    │              │         └── fd: ()-->(8)
      │    │    │              └── const: 100 [type=int]
      │    │    ├── select
      │    │    │    ├── columns: y:7(int!null)
      │    │    │    ├── fd: ()-->(7)
      │    │    │    ├── scan xy
      │    │    │    │    └── columns: y:7(int)
      │    │    │    └── filters
      │    │    │         ├── y = 10 [type=bool, outer=(7), constraints=(/7: [/10 - /10]; tight), fd=()-->(7)]
      │    │    │         └── eq [type=bool, subquery]
      │    │    │              ├── subquery [type=int]
      │    │    │              │    └── scan xy
      │    │    │              │         ├── columns: x:8(int!null)
      │    │    │              │         ├── limit: 1
      │    │    │              │         ├── key: ()
      │    │    │              │         └── fd: ()-->(8)
      │    │    │              └── const: 100 [type=int]
      │    │    └── filters (true)
      │    └── const: 1 [type=int]
      └── filters
           └── i = y [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)]

# Multiple correlated subqueries.
opt expect=HoistSelectSubquery
SELECT * FROM a
WHERE (SELECT count(*) FROM xy WHERE y=k) > 0 AND (SELECT y FROM xy WHERE y=k LIMIT 1) = i
----
project
 ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 └── select
      ├── columns: k:1(int!null) i:2(int!null) f:3(float) s:4(string) j:5(jsonb) y:10(int!null)
      ├── key: (1)
      ├── fd: (1)-->(2-5,10), (2)==(10), (10)==(2)
      ├── distinct-on
      │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) y:10(int)
      │    ├── grouping columns: k:1(int!null)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-5,10)
      │    ├── right-join
      │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) count_rows:8(int!null) y:10(int)
      │    │    ├── fd: (1)-->(2-5,8)
      │    │    ├── scan xy
      │    │    │    └── columns: y:10(int)
      │    │    ├── select
      │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) count_rows:8(int!null)
      │    │    │    ├── key: (1)
      │    │    │    ├── fd: (1)-->(2-5,8)
      │    │    │    ├── group-by
      │    │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) count_rows:8(int)
      │    │    │    │    ├── grouping columns: k:1(int!null)
      │    │    │    │    ├── key: (1)
      │    │    │    │    ├── fd: (1)-->(2-5,8)
      │    │    │    │    ├── left-join
      │    │    │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) y:7(int)
      │    │    │    │    │    ├── fd: (1)-->(2-5)
      │    │    │    │    │    ├── scan a
      │    │    │    │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    │    │    │    │    │    ├── key: (1)
      │    │    │    │    │    │    └── fd: (1)-->(2-5)
      │    │    │    │    │    ├── scan xy
      │    │    │    │    │    │    └── columns: y:7(int)
      │    │    │    │    │    └── filters
      │    │    │    │    │         └── y = k [type=bool, outer=(1,7), constraints=(/1: (/NULL - ]; /7: (/NULL - ]), fd=(1)==(7), (7)==(1)]
      │    │    │    │    └── aggregations
      │    │    │    │         ├── count [type=int, outer=(7)]
      │    │    │    │         │    └── variable: y [type=int]
      │    │    │    │         ├── const-agg [type=int, outer=(2)]
      │    │    │    │         │    └── variable: i [type=int]
      │    │    │    │         ├── const-agg [type=float, outer=(3)]
      │    │    │    │         │    └── variable: f [type=float]
      │    │    │    │         ├── const-agg [type=string, outer=(4)]
      │    │    │    │         │    └── variable: s [type=string]
      │    │    │    │         └── const-agg [type=jsonb, outer=(5)]
      │    │    │    │              └── variable: j [type=jsonb]
      │    │    │    └── filters
      │    │    │         └── count_rows > 0 [type=bool, outer=(8), constraints=(/8: [/1 - ]; tight)]
      │    │    └── filters
      │    │         └── y = k [type=bool, outer=(1,10), constraints=(/1: (/NULL - ]; /10: (/NULL - ]), fd=(1)==(10), (10)==(1)]
      │    └── aggregations
      │         ├── const-agg [type=int, outer=(2)]
      │         │    └── variable: i [type=int]
      │         ├── const-agg [type=float, outer=(3)]
      │         │    └── variable: f [type=float]
      │         ├── const-agg [type=string, outer=(4)]
      │         │    └── variable: s [type=string]
      │         ├── const-agg [type=jsonb, outer=(5)]
      │         │    └── variable: j [type=jsonb]
      │         └── first-agg [type=int, outer=(10)]
      │              └── variable: y [type=int]
      └── filters
           └── i = y [type=bool, outer=(2,10), constraints=(/2: (/NULL - ]; /10: (/NULL - ]), fd=(2)==(10), (10)==(2)]

# Subquery nested below interesting scalar operators like cast, function, tuple,
# or, etc).
opt expect=HoistSelectSubquery
SELECT * FROM a WHERE (0, length((SELECT count(*) FROM uv WHERE k=u)::string)) > (0, 1) OR i=1
----
project
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 └── select
      ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) count_rows:8(int)
      ├── key: (1)
      ├── fd: (1)-->(2-5,8)
      ├── group-by
      │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) count_rows:8(int)
      │    ├── grouping columns: k:1(int!null)
      │    ├── internal-ordering: +1
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-5,8)
      │    ├── left-join (merge)
      │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) u:6(int)
      │    │    ├── left ordering: +1
      │    │    ├── right ordering: +6
      │    │    ├── key: (1,6)
      │    │    ├── fd: (1)-->(2-5)
      │    │    ├── ordering: +1
      │    │    ├── scan a
      │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    │    │    ├── key: (1)
      │    │    │    ├── fd: (1)-->(2-5)
      │    │    │    └── ordering: +1
      │    │    ├── scan uv
      │    │    │    ├── columns: u:6(int!null)
      │    │    │    ├── key: (6)
      │    │    │    └── ordering: +6
      │    │    └── filters (true)
      │    └── aggregations
      │         ├── count [type=int, outer=(6)]
      │         │    └── variable: u [type=int]
      │         ├── const-agg [type=int, outer=(2)]
      │         │    └── variable: i [type=int]
      │         ├── const-agg [type=float, outer=(3)]
      │         │    └── variable: f [type=float]
      │         ├── const-agg [type=string, outer=(4)]
      │         │    └── variable: s [type=string]
      │         └── const-agg [type=jsonb, outer=(5)]
      │              └── variable: j [type=jsonb]
      └── filters
           └── ((0, length(count_rows::STRING)) > (0, 1)) OR (i = 1) [type=bool, outer=(2,8)]

# Exists within a disjunction.
opt expect=HoistSelectSubquery
SELECT * FROM a WHERE i=1 OR EXISTS(SELECT * FROM xy WHERE y=i)
----
project
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 └── select
      ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) true_agg:9(bool)
      ├── key: (1)
      ├── fd: (1)-->(2-5,9)
      ├── group-by
      │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) true_agg:9(bool)
      │    ├── grouping columns: k:1(int!null)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-5,9)
      │    ├── left-join
      │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) y:7(int) true:8(bool)
      │    │    ├── fd: (1)-->(2-5), ()~~>(8)
      │    │    ├── scan a
      │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    │    │    ├── key: (1)
      │    │    │    └── fd: (1)-->(2-5)
      │    │    ├── project
      │    │    │    ├── columns: true:8(bool!null) y:7(int)
      │    │    │    ├── fd: ()-->(8)
      │    │    │    ├── scan xy
      │    │    │    │    └── columns: y:7(int)
      │    │    │    └── projections
      │    │    │         └── true [type=bool]
      │    │    └── filters
      │    │         └── y = i [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)]
      │    └── aggregations
      │         ├── const-not-null-agg [type=bool, outer=(8)]
      │         │    └── variable: true [type=bool]
      │         ├── const-agg [type=int, outer=(2)]
      │         │    └── variable: i [type=int]
      │         ├── const-agg [type=float, outer=(3)]
      │         │    └── variable: f [type=float]
      │         ├── const-agg [type=string, outer=(4)]
      │         │    └── variable: s [type=string]
      │         └── const-agg [type=jsonb, outer=(5)]
      │              └── variable: j [type=jsonb]
      └── filters
           └── (i = 1) OR (true_agg IS NOT NULL) [type=bool, outer=(2,9)]

# Any with IS NULL.
opt expect=HoistSelectSubquery
SELECT * FROM a WHERE (i = ANY(SELECT y FROM xy WHERE x=k)) IS NULL
----
project
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 └── select
      ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) case:10(bool)
      ├── key: (1)
      ├── fd: ()-->(10), (1)-->(2-5)
      ├── project
      │    ├── columns: case:10(bool) k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-5,10)
      │    ├── group-by
      │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) bool_or:9(bool)
      │    │    ├── grouping columns: k:1(int!null)
      │    │    ├── internal-ordering: +1
      │    │    ├── key: (1)
      │    │    ├── fd: (1)-->(2-5,9)
      │    │    ├── left-join (merge)
      │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int) y:7(int) notnull:8(bool)
      │    │    │    ├── left ordering: +1
      │    │    │    ├── right ordering: +6
      │    │    │    ├── key: (1,6)
      │    │    │    ├── fd: (1)-->(2-5), (6)-->(7), (7)~~>(8), (1,6)-->(8)
      │    │    │    ├── ordering: +1
      │    │    │    ├── scan a
      │    │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    │    │    │    ├── key: (1)
      │    │    │    │    ├── fd: (1)-->(2-5)
      │    │    │    │    └── ordering: +1
      │    │    │    ├── project
      │    │    │    │    ├── columns: notnull:8(bool) x:6(int!null) y:7(int)
      │    │    │    │    ├── key: (6)
      │    │    │    │    ├── fd: (6)-->(7), (7)-->(8)
      │    │    │    │    ├── ordering: +6
      │    │    │    │    ├── scan xy
      │    │    │    │    │    ├── columns: x:6(int!null) y:7(int)
      │    │    │    │    │    ├── key: (6)
      │    │    │    │    │    ├── fd: (6)-->(7)
      │    │    │    │    │    └── ordering: +6
      │    │    │    │    └── projections
      │    │    │    │         └── y IS NOT NULL [type=bool, outer=(7)]
      │    │    │    └── filters
      │    │    │         └── (i = y) IS NOT false [type=bool, outer=(2,7)]
      │    │    └── aggregations
      │    │         ├── bool-or [type=bool, outer=(8)]
      │    │         │    └── variable: notnull [type=bool]
      │    │         ├── const-agg [type=int, outer=(2)]
      │    │         │    └── variable: i [type=int]
      │    │         ├── const-agg [type=float, outer=(3)]
      │    │         │    └── variable: f [type=float]
      │    │         ├── const-agg [type=string, outer=(4)]
      │    │         │    └── variable: s [type=string]
      │    │         └── const-agg [type=jsonb, outer=(5)]
      │    │              └── variable: j [type=jsonb]
      │    └── projections
      │         └── CASE WHEN bool_or AND (i IS NOT NULL) THEN true WHEN bool_or IS NULL THEN false END [type=bool, outer=(2,9)]
      └── filters
           └── case IS NULL [type=bool, outer=(10), constraints=(/10: [/NULL - /NULL]; tight), fd=()-->(10)]

# Any with uncorrelated subquery (should not be hoisted).
opt
SELECT * FROM a WHERE (i = ANY(SELECT y FROM xy)) IS NULL
----
select
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 └── filters
      └── is [type=bool, outer=(2), correlated-subquery]
           ├── any: eq [type=bool]
           │    ├── scan xy
           │    │    └── columns: y:7(int)
           │    └── variable: i [type=int]
           └── null [type=unknown]

# ALL with non-trivial expression on left.
opt
SELECT i*i/100 < ALL(SELECT y FROM xy WHERE x=k) AS r, s FROM a
----
project
 ├── columns: r:8(bool) s:4(string)
 ├── side-effects
 ├── group-by
 │    ├── columns: k:1(int!null) s:4(string) scalar:9(decimal) bool_or:11(bool)
 │    ├── grouping columns: k:1(int!null)
 │    ├── internal-ordering: +1
 │    ├── side-effects
 │    ├── key: (1)
 │    ├── fd: (1)-->(4,9,11)
 │    ├── left-join (merge)
 │    │    ├── columns: k:1(int!null) s:4(string) x:6(int) y:7(int) scalar:9(decimal) notnull:10(bool)
 │    │    ├── left ordering: +1
 │    │    ├── right ordering: +6
 │    │    ├── side-effects
 │    │    ├── key: (1,6)
 │    │    ├── fd: (1)-->(4,9), (6)-->(7), (7)~~>(10), (1,6)-->(10)
 │    │    ├── ordering: +1
 │    │    ├── project
 │    │    │    ├── columns: scalar:9(decimal) k:1(int!null) s:4(string)
 │    │    │    ├── side-effects
 │    │    │    ├── key: (1)
 │    │    │    ├── fd: (1)-->(4,9)
 │    │    │    ├── ordering: +1
 │    │    │    ├── scan a
 │    │    │    │    ├── columns: k:1(int!null) i:2(int) s:4(string)
 │    │    │    │    ├── key: (1)
 │    │    │    │    ├── fd: (1)-->(2,4)
 │    │    │    │    └── ordering: +1
 │    │    │    └── projections
 │    │    │         └── (i * i) / 100 [type=decimal, outer=(2), side-effects]
 │    │    ├── project
 │    │    │    ├── columns: notnull:10(bool) x:6(int!null) y:7(int)
 │    │    │    ├── key: (6)
 │    │    │    ├── fd: (6)-->(7), (7)-->(10)
 │    │    │    ├── ordering: +6
 │    │    │    ├── scan xy
 │    │    │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    │    │    ├── key: (6)
 │    │    │    │    ├── fd: (6)-->(7)
 │    │    │    │    └── ordering: +6
 │    │    │    └── projections
 │    │    │         └── y IS NOT NULL [type=bool, outer=(7)]
 │    │    └── filters
 │    │         └── (scalar >= y) IS NOT false [type=bool, outer=(7,9)]
 │    └── aggregations
 │         ├── bool-or [type=bool, outer=(10)]
 │         │    └── variable: notnull [type=bool]
 │         ├── const-agg [type=string, outer=(4)]
 │         │    └── variable: s [type=string]
 │         └── const-agg [type=decimal, outer=(9)]
 │              └── variable: scalar [type=decimal]
 └── projections
      └── NOT CASE WHEN bool_or AND (scalar IS NOT NULL) THEN true WHEN bool_or IS NULL THEN false END [type=bool, outer=(9,11)]

# Regress issue #32270: Panic when expression contains both correlated and
# uncorrelated subquery.
opt expect=HoistSelectSubquery
SELECT * FROM a WHERE EXISTS(SELECT * FROM xy) OR EXISTS(SELECT * FROM xy WHERE x=k)
----
project
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 └── select
      ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) true_agg:11(bool)
      ├── key: (1)
      ├── fd: (1)-->(2-5,11)
      ├── group-by
      │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) true_agg:11(bool)
      │    ├── grouping columns: k:1(int!null)
      │    ├── internal-ordering: +1
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-5,11)
      │    ├── left-join (merge)
      │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:8(int) true:10(bool)
      │    │    ├── left ordering: +1
      │    │    ├── right ordering: +8
      │    │    ├── key: (1,8)
      │    │    ├── fd: (1)-->(2-5), ()~~>(10), (1,8)-->(10)
      │    │    ├── ordering: +1
      │    │    ├── scan a
      │    │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    │    │    ├── key: (1)
      │    │    │    ├── fd: (1)-->(2-5)
      │    │    │    └── ordering: +1
      │    │    ├── project
      │    │    │    ├── columns: true:10(bool!null) x:8(int!null)
      │    │    │    ├── key: (8)
      │    │    │    ├── fd: ()-->(10)
      │    │    │    ├── ordering: +8 opt(10) [actual: +8]
      │    │    │    ├── scan xy
      │    │    │    │    ├── columns: x:8(int!null)
      │    │    │    │    ├── key: (8)
      │    │    │    │    └── ordering: +8
      │    │    │    └── projections
      │    │    │         └── true [type=bool]
      │    │    └── filters (true)
      │    └── aggregations
      │         ├── const-not-null-agg [type=bool, outer=(10)]
      │         │    └── variable: true [type=bool]
      │         ├── const-agg [type=int, outer=(2)]
      │         │    └── variable: i [type=int]
      │         ├── const-agg [type=float, outer=(3)]
      │         │    └── variable: f [type=float]
      │         ├── const-agg [type=string, outer=(4)]
      │         │    └── variable: s [type=string]
      │         └── const-agg [type=jsonb, outer=(5)]
      │              └── variable: j [type=jsonb]
      └── filters
           └── or [type=bool, outer=(11), subquery]
                ├── exists [type=bool]
                │    └── scan xy
                │         ├── columns: x:6(int!null) y:7(int)
                │         ├── limit: 1
                │         ├── key: ()
                │         └── fd: ()-->(6,7)
                └── true_agg IS NOT NULL [type=bool]

# --------------------------------------------------
# HoistProjectSubquery
# --------------------------------------------------
opt expect=HoistProjectSubquery
SELECT (SELECT x FROM xy WHERE x=k) FROM a
----
project
 ├── columns: x:8(int)
 ├── left-join (merge)
 │    ├── columns: k:1(int!null) xy.x:6(int)
 │    ├── left ordering: +1
 │    ├── right ordering: +6
 │    ├── key: (1,6)
 │    ├── scan a
 │    │    ├── columns: k:1(int!null)
 │    │    ├── key: (1)
 │    │    └── ordering: +1
 │    ├── scan xy
 │    │    ├── columns: xy.x:6(int!null)
 │    │    ├── key: (6)
 │    │    └── ordering: +6
 │    └── filters (true)
 └── projections
      └── variable: xy.x [type=int, outer=(6)]

# Mixed correlated and uncorrelated subqueries.
opt expect=HoistProjectSubquery
SELECT
    5 AS a,
    (SELECT x FROM xy WHERE x=k),
    (SELECT y FROM xy LIMIT 1),
    5 IN (SELECT y FROM xy) AS b,
    EXISTS(SELECT * FROM xy),
    (SELECT count(*) FROM xy WHERE y=k)
FROM a
----
project
 ├── columns: a:17(int!null) x:18(int) y:19(int) b:20(bool) exists:21(bool) count:22(int)
 ├── fd: ()-->(17,19-21)
 ├── group-by
 │    ├── columns: k:1(int!null) xy.x:6(int) count_rows:16(int)
 │    ├── grouping columns: k:1(int!null) xy.x:6(int)
 │    ├── key: (1,6)
 │    ├── fd: (1,6)-->(16)
 │    ├── left-join
 │    │    ├── columns: k:1(int!null) xy.x:6(int) xy.y:15(int)
 │    │    ├── left-join (merge)
 │    │    │    ├── columns: k:1(int!null) xy.x:6(int)
 │    │    │    ├── left ordering: +1
 │    │    │    ├── right ordering: +6
 │    │    │    ├── key: (1,6)
 │    │    │    ├── scan a
 │    │    │    │    ├── columns: k:1(int!null)
 │    │    │    │    ├── key: (1)
 │    │    │    │    └── ordering: +1
 │    │    │    ├── scan xy
 │    │    │    │    ├── columns: xy.x:6(int!null)
 │    │    │    │    ├── key: (6)
 │    │    │    │    └── ordering: +6
 │    │    │    └── filters (true)
 │    │    ├── scan xy
 │    │    │    └── columns: xy.y:15(int)
 │    │    └── filters
 │    │         └── xy.y = k [type=bool, outer=(1,15), constraints=(/1: (/NULL - ]; /15: (/NULL - ]), fd=(1)==(15), (15)==(1)]
 │    └── aggregations
 │         └── count [type=int, outer=(15)]
 │              └── variable: xy.y [type=int]
 └── projections
      ├── const: 5 [type=int]
      ├── variable: xy.x [type=int, outer=(6)]
      ├── subquery [type=int, subquery]
      │    └── scan xy
      │         ├── columns: xy.y:9(int)
      │         ├── limit: 1
      │         ├── key: ()
      │         └── fd: ()-->(9)
      ├── any: eq [type=bool, subquery]
      │    ├── scan xy
      │    │    └── columns: xy.y:11(int)
      │    └── const: 5 [type=int]
      ├── exists [type=bool, subquery]
      │    └── scan xy
      │         ├── columns: xy.x:12(int!null) xy.y:13(int)
      │         ├── limit: 1
      │         ├── key: ()
      │         └── fd: ()-->(12,13)
      └── variable: count_rows [type=int, outer=(16)]

# Subquery in GroupBy aggregate (optbuilder creates correlated Project).
opt expect=HoistProjectSubquery
SELECT max((SELECT y FROM xy WHERE y=i)) FROM a
----
scalar-group-by
 ├── columns: max:9(int)
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(9)
 ├── project
 │    ├── columns: column8:8(int)
 │    ├── left-join-apply
 │    │    ├── columns: i:2(int) y:7(int)
 │    │    ├── scan a
 │    │    │    └── columns: i:2(int)
 │    │    ├── max1-row
 │    │    │    ├── columns: y:7(int!null)
 │    │    │    ├── outer: (2)
 │    │    │    ├── cardinality: [0 - 1]
 │    │    │    ├── key: ()
 │    │    │    ├── fd: ()-->(7)
 │    │    │    └── select
 │    │    │         ├── columns: y:7(int!null)
 │    │    │         ├── outer: (2)
 │    │    │         ├── fd: ()-->(7)
 │    │    │         ├── scan xy
 │    │    │         │    └── columns: y:7(int)
 │    │    │         └── filters
 │    │    │              └── y = i [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)]
 │    │    └── filters (true)
 │    └── projections
 │         └── variable: y [type=int, outer=(7)]
 └── aggregations
      └── max [type=int, outer=(8)]
           └── variable: column8 [type=int]

# Exists in projection list.
opt expect=HoistProjectSubquery
SELECT EXISTS(SELECT * FROM xy WHERE y=i) FROM a
----
project
 ├── columns: exists:8(bool)
 ├── group-by
 │    ├── columns: true_agg:10(bool) rownum:12(int!null)
 │    ├── grouping columns: rownum:12(int!null)
 │    ├── key: (12)
 │    ├── fd: (12)-->(10)
 │    ├── left-join
 │    │    ├── columns: i:2(int) y:7(int) true:9(bool) rownum:12(int!null)
 │    │    ├── fd: (12)-->(2), ()~~>(9)
 │    │    ├── row-number
 │    │    │    ├── columns: i:2(int) rownum:12(int!null)
 │    │    │    ├── key: (12)
 │    │    │    ├── fd: (12)-->(2)
 │    │    │    └── scan a
 │    │    │         └── columns: i:2(int)
 │    │    ├── project
 │    │    │    ├── columns: true:9(bool!null) y:7(int)
 │    │    │    ├── fd: ()-->(9)
 │    │    │    ├── scan xy
 │    │    │    │    └── columns: y:7(int)
 │    │    │    └── projections
 │    │    │         └── true [type=bool]
 │    │    └── filters
 │    │         └── y = i [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)]
 │    └── aggregations
 │         └── const-not-null-agg [type=bool, outer=(9)]
 │              └── variable: true [type=bool]
 └── projections
      └── true_agg IS NOT NULL [type=bool, outer=(10)]

# Any in projection list.
opt expect=HoistProjectSubquery
SELECT 5 < ANY(SELECT y FROM xy WHERE y=i) AS r FROM a
----
project
 ├── columns: r:8(bool)
 ├── group-by
 │    ├── columns: bool_or:10(bool) rownum:12(int!null)
 │    ├── grouping columns: rownum:12(int!null)
 │    ├── key: (12)
 │    ├── fd: (12)-->(10)
 │    ├── left-join
 │    │    ├── columns: i:2(int) y:7(int) notnull:9(bool) rownum:12(int!null)
 │    │    ├── fd: (12)-->(2), (7)~~>(9)
 │    │    ├── row-number
 │    │    │    ├── columns: i:2(int) rownum:12(int!null)
 │    │    │    ├── key: (12)
 │    │    │    ├── fd: (12)-->(2)
 │    │    │    └── scan a
 │    │    │         └── columns: i:2(int)
 │    │    ├── project
 │    │    │    ├── columns: notnull:9(bool) y:7(int)
 │    │    │    ├── fd: (7)-->(9)
 │    │    │    ├── select
 │    │    │    │    ├── columns: y:7(int)
 │    │    │    │    ├── scan xy
 │    │    │    │    │    └── columns: y:7(int)
 │    │    │    │    └── filters
 │    │    │    │         └── (y > 5) IS NOT false [type=bool, outer=(7)]
 │    │    │    └── projections
 │    │    │         └── y IS NOT NULL [type=bool, outer=(7)]
 │    │    └── filters
 │    │         └── y = i [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)]
 │    └── aggregations
 │         └── bool-or [type=bool, outer=(9)]
 │              └── variable: notnull [type=bool]
 └── projections
      └── CASE WHEN bool_or THEN true WHEN bool_or IS NULL THEN false END [type=bool, outer=(10)]

# Correlated subquery nested in uncorrelated subquery.
opt expect=HoistProjectSubquery
SELECT EXISTS(SELECT EXISTS(SELECT * FROM xy WHERE y=i) FROM a)
----
values
 ├── columns: exists:13(bool)
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(13)
 └── tuple [type=tuple{bool}]
      └── exists [type=bool]
           └── limit
                ├── columns: i:2(int) y:7(int) true:9(bool) rownum:12(int!null)
                ├── cardinality: [0 - 1]
                ├── key: ()
                ├── fd: ()-->(2,7,9,12)
                ├── left-join
                │    ├── columns: i:2(int) y:7(int) true:9(bool) rownum:12(int!null)
                │    ├── fd: (12)-->(2), ()~~>(9)
                │    ├── row-number
                │    │    ├── columns: i:2(int) rownum:12(int!null)
                │    │    ├── key: (12)
                │    │    ├── fd: (12)-->(2)
                │    │    └── scan a
                │    │         └── columns: i:2(int)
                │    ├── project
                │    │    ├── columns: true:9(bool!null) y:7(int)
                │    │    ├── fd: ()-->(9)
                │    │    ├── scan xy
                │    │    │    └── columns: y:7(int)
                │    │    └── projections
                │    │         └── true [type=bool]
                │    └── filters
                │         └── y = i [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)]
                └── const: 1 [type=int]

# Don't hoist uncorrelated subquery.
opt
SELECT i < ANY(SELECT y FROM xy) AS r FROM a
----
project
 ├── columns: r:8(bool)
 ├── scan a
 │    └── columns: i:2(int)
 └── projections
      └── any: lt [type=bool, outer=(2), correlated-subquery]
           ├── scan xy
           │    └── columns: y:7(int)
           └── variable: i [type=int]

# --------------------------------------------------
# HoistJoinSubquery
# --------------------------------------------------
opt expect=HoistJoinSubquery
SELECT i, y FROM a INNER JOIN xy ON (SELECT k+1) = x
----
project
 ├── columns: i:2(int) y:7(int)
 └── inner-join-apply
      ├── columns: k:1(int!null) i:2(int) x:6(int!null) y:7(int) "?column?":8(int)
      ├── key: (1,6)
      ├── fd: (1)-->(2), (1,6)-->(7,8), (6)==(8), (8)==(6)
      ├── scan a
      │    ├── columns: k:1(int!null) i:2(int)
      │    ├── key: (1)
      │    └── fd: (1)-->(2)
      ├── inner-join
      │    ├── columns: x:6(int!null) y:7(int) "?column?":8(int)
      │    ├── outer: (1)
      │    ├── key: (6)
      │    ├── fd: ()-->(8), (6)-->(7)
      │    ├── scan xy
      │    │    ├── columns: x:6(int!null) y:7(int)
      │    │    ├── key: (6)
      │    │    └── fd: (6)-->(7)
      │    ├── values
      │    │    ├── columns: "?column?":8(int)
      │    │    ├── outer: (1)
      │    │    ├── cardinality: [1 - 1]
      │    │    ├── key: ()
      │    │    ├── fd: ()-->(8)
      │    │    └── (k + 1,) [type=tuple{int}]
      │    └── filters (true)
      └── filters
           └── x = ?column? [type=bool, outer=(6,8), constraints=(/6: (/NULL - ]; /8: (/NULL - ]), fd=(6)==(8), (8)==(6)]

# Right join + multiple subqueries.
opt expect=HoistJoinSubquery
SELECT y FROM a RIGHT JOIN xy ON (SELECT k+1) = (SELECT x+1)
----
project
 ├── columns: y:7(int)
 └── right-join-apply
      ├── columns: k:1(int) x:6(int!null) y:7(int) "?column?":8(int) "?column?":9(int)
      ├── key: (1,6)
      ├── fd: (1,6)-->(7-9)
      ├── scan a
      │    ├── columns: k:1(int!null)
      │    └── key: (1)
      ├── inner-join-apply
      │    ├── columns: x:6(int!null) y:7(int) "?column?":8(int) "?column?":9(int)
      │    ├── outer: (1)
      │    ├── key: (6)
      │    ├── fd: ()-->(8), (6)-->(7,9)
      │    ├── inner-join
      │    │    ├── columns: x:6(int!null) y:7(int) "?column?":8(int)
      │    │    ├── outer: (1)
      │    │    ├── key: (6)
      │    │    ├── fd: ()-->(8), (6)-->(7)
      │    │    ├── scan xy
      │    │    │    ├── columns: x:6(int!null) y:7(int)
      │    │    │    ├── key: (6)
      │    │    │    └── fd: (6)-->(7)
      │    │    ├── values
      │    │    │    ├── columns: "?column?":8(int)
      │    │    │    ├── outer: (1)
      │    │    │    ├── cardinality: [1 - 1]
      │    │    │    ├── key: ()
      │    │    │    ├── fd: ()-->(8)
      │    │    │    └── (k + 1,) [type=tuple{int}]
      │    │    └── filters (true)
      │    ├── values
      │    │    ├── columns: "?column?":9(int)
      │    │    ├── outer: (6)
      │    │    ├── cardinality: [1 - 1]
      │    │    ├── key: ()
      │    │    ├── fd: ()-->(9)
      │    │    └── (x + 1,) [type=tuple{int}]
      │    └── filters (true)
      └── filters
           └── ?column? = ?column? [type=bool, outer=(8,9), constraints=(/8: (/NULL - ]; /9: (/NULL - ]), fd=(8)==(9), (9)==(8)]

# Hoist Exists in join filter disjunction.
opt expect=HoistJoinSubquery
SELECT s, x FROM a FULL JOIN xy ON EXISTS(SELECT * FROM uv WHERE u=y) OR k=x
----
project
 ├── columns: s:4(string) x:6(int)
 └── full-join
      ├── columns: k:1(int) s:4(string) x:6(int) exists:12(bool)
      ├── key: (1,6)
      ├── fd: (1)-->(4), (6)-->(12)
      ├── scan a
      │    ├── columns: k:1(int!null) s:4(string)
      │    ├── key: (1)
      │    └── fd: (1)-->(4)
      ├── project
      │    ├── columns: exists:12(bool) x:6(int!null)
      │    ├── key: (6)
      │    ├── fd: (6)-->(12)
      │    ├── group-by
      │    │    ├── columns: x:6(int!null) true_agg:11(bool)
      │    │    ├── grouping columns: x:6(int!null)
      │    │    ├── key: (6)
      │    │    ├── fd: (6)-->(11)
      │    │    ├── left-join
      │    │    │    ├── columns: x:6(int!null) y:7(int) u:8(int) true:10(bool)
      │    │    │    ├── key: (6,8)
      │    │    │    ├── fd: (6)-->(7), ()~~>(10), (6,8)-->(10)
      │    │    │    ├── scan xy
      │    │    │    │    ├── columns: x:6(int!null) y:7(int)
      │    │    │    │    ├── key: (6)
      │    │    │    │    └── fd: (6)-->(7)
      │    │    │    ├── project
      │    │    │    │    ├── columns: true:10(bool!null) u:8(int!null)
      │    │    │    │    ├── key: (8)
      │    │    │    │    ├── fd: ()-->(10)
      │    │    │    │    ├── scan uv
      │    │    │    │    │    ├── columns: u:8(int!null)
      │    │    │    │    │    └── key: (8)
      │    │    │    │    └── projections
      │    │    │    │         └── true [type=bool]
      │    │    │    └── filters
      │    │    │         └── u = y [type=bool, outer=(7,8), constraints=(/7: (/NULL - ]; /8: (/NULL - ]), fd=(7)==(8), (8)==(7)]
      │    │    └── aggregations
      │    │         └── const-not-null-agg [type=bool, outer=(10)]
      │    │              └── variable: true [type=bool]
      │    └── projections
      │         └── true_agg IS NOT NULL [type=bool, outer=(11)]
      └── filters
           └── exists OR (k = x) [type=bool, outer=(1,6,12)]

# Any in Join filter disjunction.
opt expect=HoistJoinSubquery
SELECT j, y FROM a INNER JOIN xy ON x IN (SELECT v FROM uv WHERE u=y AND v=i) OR x IS NULL
----
project
 ├── columns: j:5(jsonb) y:7(int)
 └── select
      ├── columns: j:5(jsonb) x:6(int!null) y:7(int) case:12(bool)
      ├── fd: (6)-->(7)
      ├── project
      │    ├── columns: case:12(bool) j:5(jsonb) x:6(int!null) y:7(int)
      │    ├── fd: (6)-->(7)
      │    ├── group-by
      │    │    ├── columns: k:1(int!null) j:5(jsonb) x:6(int!null) y:7(int) bool_or:11(bool)
      │    │    ├── grouping columns: k:1(int!null) x:6(int!null)
      │    │    ├── key: (1,6)
      │    │    ├── fd: (1)-->(5), (6)-->(7), (1,6)-->(5,7,11)
      │    │    ├── left-join
      │    │    │    ├── columns: k:1(int!null) i:2(int) j:5(jsonb) x:6(int!null) y:7(int) u:8(int) v:9(int) notnull:10(bool)
      │    │    │    ├── key: (1,6,8)
      │    │    │    ├── fd: (1)-->(2,5), (6)-->(7), (8)-->(9), (9)~~>(10), (1,6,8)-->(10)
      │    │    │    ├── inner-join
      │    │    │    │    ├── columns: k:1(int!null) i:2(int) j:5(jsonb) x:6(int!null) y:7(int)
      │    │    │    │    ├── key: (1,6)
      │    │    │    │    ├── fd: (1)-->(2,5), (6)-->(7)
      │    │    │    │    ├── scan a
      │    │    │    │    │    ├── columns: k:1(int!null) i:2(int) j:5(jsonb)
      │    │    │    │    │    ├── key: (1)
      │    │    │    │    │    └── fd: (1)-->(2,5)
      │    │    │    │    ├── scan xy
      │    │    │    │    │    ├── columns: x:6(int!null) y:7(int)
      │    │    │    │    │    ├── key: (6)
      │    │    │    │    │    └── fd: (6)-->(7)
      │    │    │    │    └── filters (true)
      │    │    │    ├── project
      │    │    │    │    ├── columns: notnull:10(bool) u:8(int!null) v:9(int)
      │    │    │    │    ├── key: (8)
      │    │    │    │    ├── fd: (8)-->(9), (9)-->(10)
      │    │    │    │    ├── scan uv
      │    │    │    │    │    ├── columns: u:8(int!null) v:9(int)
      │    │    │    │    │    ├── key: (8)
      │    │    │    │    │    └── fd: (8)-->(9)
      │    │    │    │    └── projections
      │    │    │    │         └── v IS NOT NULL [type=bool, outer=(9)]
      │    │    │    └── filters
      │    │    │         ├── u = y [type=bool, outer=(7,8), constraints=(/7: (/NULL - ]; /8: (/NULL - ]), fd=(7)==(8), (8)==(7)]
      │    │    │         ├── v = i [type=bool, outer=(2,9), constraints=(/2: (/NULL - ]; /9: (/NULL - ]), fd=(2)==(9), (9)==(2)]
      │    │    │         └── (x = v) IS NOT false [type=bool, outer=(6,9)]
      │    │    └── aggregations
      │    │         ├── bool-or [type=bool, outer=(10)]
      │    │         │    └── variable: notnull [type=bool]
      │    │         ├── const-agg [type=int, outer=(7)]
      │    │         │    └── variable: y [type=int]
      │    │         └── const-agg [type=jsonb, outer=(5)]
      │    │              └── variable: j [type=jsonb]
      │    └── projections
      │         └── CASE WHEN bool_or AND (x IS NOT NULL) THEN true WHEN bool_or IS NULL THEN false END [type=bool, outer=(6,11)]
      └── filters
           └── case OR (x IS NULL) [type=bool, outer=(6,12)]


# --------------------------------------------------
# HoistValuesSubquery
# --------------------------------------------------
opt expect=HoistValuesSubquery
SELECT (VALUES ((SELECT i+1 AS r)), (10), ((SELECT k+1 AS s))) FROM a
----
project
 ├── columns: column1:9(int)
 ├── inner-join-apply
 │    ├── columns: k:1(int!null) i:2(int) column1:8(int)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,8)
 │    ├── scan a
 │    │    ├── columns: k:1(int!null) i:2(int)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(2)
 │    ├── max1-row
 │    │    ├── columns: column1:8(int)
 │    │    ├── outer: (1,2)
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(8)
 │    │    └── project
 │    │         ├── columns: column1:8(int)
 │    │         ├── outer: (1,2)
 │    │         ├── cardinality: [3 - 3]
 │    │         └── inner-join-apply
 │    │              ├── columns: r:6(int) s:7(int) column1:8(int)
 │    │              ├── outer: (1,2)
 │    │              ├── cardinality: [3 - 3]
 │    │              ├── fd: ()-->(6,7)
 │    │              ├── inner-join
 │    │              │    ├── columns: r:6(int) s:7(int)
 │    │              │    ├── outer: (1,2)
 │    │              │    ├── cardinality: [1 - 1]
 │    │              │    ├── key: ()
 │    │              │    ├── fd: ()-->(6,7)
 │    │              │    ├── values
 │    │              │    │    ├── columns: r:6(int)
 │    │              │    │    ├── outer: (2)
 │    │              │    │    ├── cardinality: [1 - 1]
 │    │              │    │    ├── key: ()
 │    │              │    │    ├── fd: ()-->(6)
 │    │              │    │    └── (i + 1,) [type=tuple{int}]
 │    │              │    ├── values
 │    │              │    │    ├── columns: s:7(int)
 │    │              │    │    ├── outer: (1)
 │    │              │    │    ├── cardinality: [1 - 1]
 │    │              │    │    ├── key: ()
 │    │              │    │    ├── fd: ()-->(7)
 │    │              │    │    └── (k + 1,) [type=tuple{int}]
 │    │              │    └── filters (true)
 │    │              ├── values
 │    │              │    ├── columns: column1:8(int)
 │    │              │    ├── outer: (6,7)
 │    │              │    ├── cardinality: [3 - 3]
 │    │              │    ├── (r,) [type=tuple{int}]
 │    │              │    ├── (10,) [type=tuple{int}]
 │    │              │    └── (s,) [type=tuple{int}]
 │    │              └── filters (true)
 │    └── filters (true)
 └── projections
      └── variable: column1 [type=int, outer=(8)]

# Exists in values row.
opt expect=HoistValuesSubquery
SELECT (VALUES (EXISTS(SELECT * FROM xy WHERE x=k))) FROM a
----
project
 ├── columns: column1:12(bool)
 ├── inner-join-apply
 │    ├── columns: k:1(int!null) column1:8(bool) exists:11(bool)
 │    ├── key: (1)
 │    ├── fd: (1)-->(8,11)
 │    ├── scan a
 │    │    ├── columns: k:1(int!null)
 │    │    └── key: (1)
 │    ├── inner-join-apply
 │    │    ├── columns: column1:8(bool) exists:11(bool)
 │    │    ├── outer: (1)
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(8,11)
 │    │    ├── project
 │    │    │    ├── columns: exists:11(bool)
 │    │    │    ├── outer: (1)
 │    │    │    ├── cardinality: [1 - 1]
 │    │    │    ├── key: ()
 │    │    │    ├── fd: ()-->(11)
 │    │    │    ├── group-by
 │    │    │    │    ├── columns: true_agg:10(bool)
 │    │    │    │    ├── outer: (1)
 │    │    │    │    ├── cardinality: [1 - 1]
 │    │    │    │    ├── key: ()
 │    │    │    │    ├── fd: ()-->(10)
 │    │    │    │    ├── left-join
 │    │    │    │    │    ├── columns: true:9(bool)
 │    │    │    │    │    ├── outer: (1)
 │    │    │    │    │    ├── cardinality: [1 - 1]
 │    │    │    │    │    ├── key: ()
 │    │    │    │    │    ├── fd: ()-->(9)
 │    │    │    │    │    ├── values
 │    │    │    │    │    │    ├── cardinality: [1 - 1]
 │    │    │    │    │    │    ├── key: ()
 │    │    │    │    │    │    └── tuple [type=tuple]
 │    │    │    │    │    ├── project
 │    │    │    │    │    │    ├── columns: true:9(bool!null)
 │    │    │    │    │    │    ├── outer: (1)
 │    │    │    │    │    │    ├── cardinality: [0 - 1]
 │    │    │    │    │    │    ├── key: ()
 │    │    │    │    │    │    ├── fd: ()-->(9)
 │    │    │    │    │    │    ├── select
 │    │    │    │    │    │    │    ├── columns: x:6(int!null)
 │    │    │    │    │    │    │    ├── outer: (1)
 │    │    │    │    │    │    │    ├── cardinality: [0 - 1]
 │    │    │    │    │    │    │    ├── key: ()
 │    │    │    │    │    │    │    ├── fd: ()-->(6)
 │    │    │    │    │    │    │    ├── scan xy
 │    │    │    │    │    │    │    │    ├── columns: x:6(int!null)
 │    │    │    │    │    │    │    │    └── key: (6)
 │    │    │    │    │    │    │    └── filters
 │    │    │    │    │    │    │         └── x = k [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]
 │    │    │    │    │    │    └── projections
 │    │    │    │    │    │         └── true [type=bool]
 │    │    │    │    │    └── filters (true)
 │    │    │    │    └── aggregations
 │    │    │    │         └── const-not-null-agg [type=bool, outer=(9)]
 │    │    │    │              └── variable: true [type=bool]
 │    │    │    └── projections
 │    │    │         └── true_agg IS NOT NULL [type=bool, outer=(10)]
 │    │    ├── values
 │    │    │    ├── columns: column1:8(bool)
 │    │    │    ├── outer: (11)
 │    │    │    ├── cardinality: [1 - 1]
 │    │    │    ├── key: ()
 │    │    │    ├── fd: ()-->(8)
 │    │    │    └── (exists,) [type=tuple{bool}]
 │    │    └── filters (true)
 │    └── filters (true)
 └── projections
      └── variable: column1 [type=bool, outer=(8)]

# Any in values row.
opt expect=HoistValuesSubquery
SELECT (VALUES (5 IN (SELECT y FROM xy WHERE x=k))) FROM a
----
project
 ├── columns: column1:12(bool)
 ├── inner-join-apply
 │    ├── columns: k:1(int!null) column1:8(bool) case:11(bool)
 │    ├── key: (1)
 │    ├── fd: (1)-->(8,11)
 │    ├── scan a
 │    │    ├── columns: k:1(int!null)
 │    │    └── key: (1)
 │    ├── inner-join-apply
 │    │    ├── columns: column1:8(bool) case:11(bool)
 │    │    ├── outer: (1)
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(8,11)
 │    │    ├── project
 │    │    │    ├── columns: case:11(bool)
 │    │    │    ├── outer: (1)
 │    │    │    ├── cardinality: [1 - 1]
 │    │    │    ├── key: ()
 │    │    │    ├── fd: ()-->(11)
 │    │    │    ├── group-by
 │    │    │    │    ├── columns: bool_or:10(bool)
 │    │    │    │    ├── outer: (1)
 │    │    │    │    ├── cardinality: [1 - 1]
 │    │    │    │    ├── key: ()
 │    │    │    │    ├── fd: ()-->(10)
 │    │    │    │    ├── left-join
 │    │    │    │    │    ├── columns: notnull:9(bool)
 │    │    │    │    │    ├── outer: (1)
 │    │    │    │    │    ├── cardinality: [1 - 1]
 │    │    │    │    │    ├── key: ()
 │    │    │    │    │    ├── fd: ()-->(9)
 │    │    │    │    │    ├── values
 │    │    │    │    │    │    ├── cardinality: [1 - 1]
 │    │    │    │    │    │    ├── key: ()
 │    │    │    │    │    │    └── tuple [type=tuple]
 │    │    │    │    │    ├── project
 │    │    │    │    │    │    ├── columns: notnull:9(bool)
 │    │    │    │    │    │    ├── outer: (1)
 │    │    │    │    │    │    ├── cardinality: [0 - 1]
 │    │    │    │    │    │    ├── key: ()
 │    │    │    │    │    │    ├── fd: ()-->(9)
 │    │    │    │    │    │    ├── select
 │    │    │    │    │    │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    │    │    │    │    │    ├── outer: (1)
 │    │    │    │    │    │    │    ├── cardinality: [0 - 1]
 │    │    │    │    │    │    │    ├── key: ()
 │    │    │    │    │    │    │    ├── fd: ()-->(6,7)
 │    │    │    │    │    │    │    ├── scan xy
 │    │    │    │    │    │    │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    │    │    │    │    │    │    ├── key: (6)
 │    │    │    │    │    │    │    │    └── fd: (6)-->(7)
 │    │    │    │    │    │    │    └── filters
 │    │    │    │    │    │    │         ├── x = k [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]
 │    │    │    │    │    │    │         └── (y = 5) IS NOT false [type=bool, outer=(7)]
 │    │    │    │    │    │    └── projections
 │    │    │    │    │    │         └── y IS NOT NULL [type=bool, outer=(7)]
 │    │    │    │    │    └── filters (true)
 │    │    │    │    └── aggregations
 │    │    │    │         └── bool-or [type=bool, outer=(9)]
 │    │    │    │              └── variable: notnull [type=bool]
 │    │    │    └── projections
 │    │    │         └── CASE WHEN bool_or THEN true WHEN bool_or IS NULL THEN false END [type=bool, outer=(10)]
 │    │    ├── values
 │    │    │    ├── columns: column1:8(bool)
 │    │    │    ├── outer: (11)
 │    │    │    ├── cardinality: [1 - 1]
 │    │    │    ├── key: ()
 │    │    │    ├── fd: ()-->(8)
 │    │    │    └── (case,) [type=tuple{bool}]
 │    │    └── filters (true)
 │    └── filters (true)
 └── projections
      └── variable: column1 [type=bool, outer=(8)]

# ---------------------------------------------------
# HoistProjectSetSubquery + TryDecorrelateProjectSet
# ---------------------------------------------------
opt expect=HoistProjectSetSubquery
SELECT generate_series(1, (SELECT v FROM uv WHERE u=x)) FROM xy
----
project
 ├── columns: generate_series:5(int)
 ├── side-effects
 └── project-set
      ├── columns: v:4(int) generate_series:5(int)
      ├── side-effects
      ├── project
      │    ├── columns: v:4(int)
      │    └── left-join (merge)
      │         ├── columns: x:1(int!null) u:3(int) v:4(int)
      │         ├── left ordering: +1
      │         ├── right ordering: +3
      │         ├── key: (1,3)
      │         ├── fd: (3)-->(4)
      │         ├── scan xy
      │         │    ├── columns: x:1(int!null)
      │         │    ├── key: (1)
      │         │    └── ordering: +1
      │         ├── scan uv
      │         │    ├── columns: u:3(int!null) v:4(int)
      │         │    ├── key: (3)
      │         │    ├── fd: (3)-->(4)
      │         │    └── ordering: +3
      │         └── filters (true)
      └── zip
           └── function: generate_series [type=int, outer=(4), side-effects]
                ├── const: 1 [type=int]
                └── variable: v [type=int]

# Zip correlation within EXISTS.
opt expect=(HoistProjectSetSubquery,TryDecorrelateSemiJoin,TryDecorrelateProjectSet)
SELECT * FROM xy WHERE EXISTS(SELECT * FROM generate_series(1, (SELECT v FROM uv WHERE u=x)))
----
group-by
 ├── columns: x:1(int!null) y:2(int)
 ├── grouping columns: x:1(int!null)
 ├── side-effects
 ├── key: (1)
 ├── fd: (1)-->(2)
 ├── project-set
 │    ├── columns: x:1(int!null) y:2(int) v:4(int) generate_series:5(int)
 │    ├── side-effects
 │    ├── fd: (1)-->(2)
 │    ├── project
 │    │    ├── columns: x:1(int!null) y:2(int) v:4(int)
 │    │    ├── fd: (1)-->(2)
 │    │    └── left-join (merge)
 │    │         ├── columns: x:1(int!null) y:2(int) u:3(int) v:4(int)
 │    │         ├── left ordering: +1
 │    │         ├── right ordering: +3
 │    │         ├── key: (1,3)
 │    │         ├── fd: (1)-->(2), (3)-->(4)
 │    │         ├── scan xy
 │    │         │    ├── columns: x:1(int!null) y:2(int)
 │    │         │    ├── key: (1)
 │    │         │    ├── fd: (1)-->(2)
 │    │         │    └── ordering: +1
 │    │         ├── scan uv
 │    │         │    ├── columns: u:3(int!null) v:4(int)
 │    │         │    ├── key: (3)
 │    │         │    ├── fd: (3)-->(4)
 │    │         │    └── ordering: +3
 │    │         └── filters (true)
 │    └── zip
 │         └── function: generate_series [type=int, outer=(4), side-effects]
 │              ├── const: 1 [type=int]
 │              └── variable: v [type=int]
 └── aggregations
      └── const-agg [type=int, outer=(2)]
           └── variable: y [type=int]

# Function contains multiple subqueries in arguments.
opt expect=HoistProjectSetSubquery
SELECT generate_series((select y FROM xy WHERE x=k), (SELECT v FROM uv WHERE u=k)) FROM a
----
project
 ├── columns: generate_series:10(int)
 ├── side-effects
 └── project-set
      ├── columns: y:7(int) v:9(int) generate_series:10(int)
      ├── side-effects
      ├── project
      │    ├── columns: y:7(int) v:9(int)
      │    └── left-join (merge)
      │         ├── columns: k:1(int!null) x:6(int) y:7(int) u:8(int) v:9(int)
      │         ├── left ordering: +1
      │         ├── right ordering: +8
      │         ├── key: (1,6,8)
      │         ├── fd: (6)-->(7), (8)-->(9)
      │         ├── left-join (merge)
      │         │    ├── columns: k:1(int!null) x:6(int) y:7(int)
      │         │    ├── left ordering: +1
      │         │    ├── right ordering: +6
      │         │    ├── key: (1,6)
      │         │    ├── fd: (6)-->(7)
      │         │    ├── ordering: +1
      │         │    ├── scan a
      │         │    │    ├── columns: k:1(int!null)
      │         │    │    ├── key: (1)
      │         │    │    └── ordering: +1
      │         │    ├── scan xy
      │         │    │    ├── columns: x:6(int!null) y:7(int)
      │         │    │    ├── key: (6)
      │         │    │    ├── fd: (6)-->(7)
      │         │    │    └── ordering: +6
      │         │    └── filters (true)
      │         ├── scan uv
      │         │    ├── columns: u:8(int!null) v:9(int)
      │         │    ├── key: (8)
      │         │    ├── fd: (8)-->(9)
      │         │    └── ordering: +8
      │         └── filters (true)
      └── zip
           └── function: generate_series [type=int, outer=(7,9), side-effects]
                ├── variable: y [type=int]
                └── variable: v [type=int]

# Multiple functions.
opt expect=HoistProjectSetSubquery
SELECT
    generate_series(1, (SELECT v FROM uv WHERE u=k)),
    information_schema._pg_expandarray(ARRAY[(SELECT x FROM xy WHERE x=k)])
FROM a
----
project
 ├── columns: generate_series:8(int) information_schema._pg_expandarray:13(tuple{int AS x, int AS n})
 ├── side-effects
 ├── project-set
 │    ├── columns: v:7(int) generate_series:8(int) xy.x:9(int) x:11(int) n:12(int)
 │    ├── side-effects
 │    ├── project
 │    │    ├── columns: v:7(int) xy.x:9(int)
 │    │    └── left-join (merge)
 │    │         ├── columns: k:1(int!null) u:6(int) v:7(int) xy.x:9(int)
 │    │         ├── left ordering: +1
 │    │         ├── right ordering: +9
 │    │         ├── key: (1,6,9)
 │    │         ├── fd: (6)-->(7)
 │    │         ├── left-join (merge)
 │    │         │    ├── columns: k:1(int!null) u:6(int) v:7(int)
 │    │         │    ├── left ordering: +1
 │    │         │    ├── right ordering: +6
 │    │         │    ├── key: (1,6)
 │    │         │    ├── fd: (6)-->(7)
 │    │         │    ├── ordering: +1
 │    │         │    ├── scan a
 │    │         │    │    ├── columns: k:1(int!null)
 │    │         │    │    ├── key: (1)
 │    │         │    │    └── ordering: +1
 │    │         │    ├── scan uv
 │    │         │    │    ├── columns: u:6(int!null) v:7(int)
 │    │         │    │    ├── key: (6)
 │    │         │    │    ├── fd: (6)-->(7)
 │    │         │    │    └── ordering: +6
 │    │         │    └── filters (true)
 │    │         ├── scan xy
 │    │         │    ├── columns: xy.x:9(int!null)
 │    │         │    ├── key: (9)
 │    │         │    └── ordering: +9
 │    │         └── filters (true)
 │    └── zip
 │         ├── function: generate_series [type=int, outer=(7), side-effects]
 │         │    ├── const: 1 [type=int]
 │         │    └── variable: v [type=int]
 │         └── function: information_schema._pg_expandarray [type=tuple{int AS x, int AS n}, outer=(9), side-effects]
 │              └── ARRAY[xy.x] [type=int[]]
 └── projections
      └── ((x, n) AS x, n) [type=tuple{int AS x, int AS n}, outer=(11,12)]

opt expect=HoistProjectSetSubquery
SELECT a, generate_series(1, (SELECT a)) FROM (VALUES (1)) AS v (a)
----
project
 ├── columns: a:1(int) generate_series:3(int)
 ├── side-effects
 ├── fd: ()-->(1)
 └── project-set
      ├── columns: column1:1(int) a:2(int) generate_series:3(int)
      ├── side-effects
      ├── fd: ()-->(1,2)
      ├── inner-join-apply
      │    ├── columns: column1:1(int) a:2(int)
      │    ├── cardinality: [1 - 1]
      │    ├── key: ()
      │    ├── fd: ()-->(1,2)
      │    ├── values
      │    │    ├── columns: column1:1(int)
      │    │    ├── cardinality: [1 - 1]
      │    │    ├── key: ()
      │    │    ├── fd: ()-->(1)
      │    │    └── (1,) [type=tuple{int}]
      │    ├── values
      │    │    ├── columns: a:2(int)
      │    │    ├── outer: (1)
      │    │    ├── cardinality: [1 - 1]
      │    │    ├── key: ()
      │    │    ├── fd: ()-->(2)
      │    │    └── (column1,) [type=tuple{int}]
      │    └── filters (true)
      └── zip
           └── function: generate_series [type=int, outer=(2), side-effects]
                ├── const: 1 [type=int]
                └── variable: a [type=int]

opt expect=HoistProjectSetSubquery
SELECT a, generate_series(1, (SELECT a)), generate_series(1, (SELECT a)) FROM (VALUES (1)) AS v (a)
----
project
 ├── columns: a:1(int) generate_series:3(int) generate_series:5(int)
 ├── side-effects
 ├── fd: ()-->(1)
 └── project-set
      ├── columns: column1:1(int) a:2(int) generate_series:3(int) a:4(int) generate_series:5(int)
      ├── side-effects
      ├── fd: ()-->(1,2,4)
      ├── inner-join-apply
      │    ├── columns: column1:1(int) a:2(int) a:4(int)
      │    ├── cardinality: [1 - 1]
      │    ├── key: ()
      │    ├── fd: ()-->(1,2,4)
      │    ├── inner-join-apply
      │    │    ├── columns: column1:1(int) a:2(int)
      │    │    ├── cardinality: [1 - 1]
      │    │    ├── key: ()
      │    │    ├── fd: ()-->(1,2)
      │    │    ├── values
      │    │    │    ├── columns: column1:1(int)
      │    │    │    ├── cardinality: [1 - 1]
      │    │    │    ├── key: ()
      │    │    │    ├── fd: ()-->(1)
      │    │    │    └── (1,) [type=tuple{int}]
      │    │    ├── values
      │    │    │    ├── columns: a:2(int)
      │    │    │    ├── outer: (1)
      │    │    │    ├── cardinality: [1 - 1]
      │    │    │    ├── key: ()
      │    │    │    ├── fd: ()-->(2)
      │    │    │    └── (column1,) [type=tuple{int}]
      │    │    └── filters (true)
      │    ├── values
      │    │    ├── columns: a:4(int)
      │    │    ├── outer: (1)
      │    │    ├── cardinality: [1 - 1]
      │    │    ├── key: ()
      │    │    ├── fd: ()-->(4)
      │    │    └── (column1,) [type=tuple{int}]
      │    └── filters (true)
      └── zip
           ├── function: generate_series [type=int, outer=(2), side-effects]
           │    ├── const: 1 [type=int]
           │    └── variable: a [type=int]
           └── function: generate_series [type=int, outer=(4), side-effects]
                ├── const: 1 [type=int]
                └── variable: a [type=int]

exec-ddl
CREATE TABLE articles (
  id INT PRIMARY KEY,
  body STRING,
  description STRING,
  title STRING,
  slug STRING,
  tag_list STRING[],
  user_id STRING,
  created_at TIMESTAMP,
  updated_at TIMESTAMP
)
----
TABLE articles
 ├── id int not null
 ├── body string
 ├── description string
 ├── title string
 ├── slug string
 ├── tag_list string[]
 ├── user_id string
 ├── created_at timestamp
 ├── updated_at timestamp
 └── INDEX primary
      └── id int not null

# Regression test for #31706.
opt expect=(TryDecorrelateSemiJoin,TryDecorrelateProjectSet)
SELECT a0.id, a0.body, a0.description, a0.title, a0.slug, a0.tag_list, a0.user_id, a0.created_at, a0.updated_at
    FROM articles AS a0
   WHERE EXISTS(SELECT * FROM unnest(a0.tag_list) AS tag WHERE tag = 'dragons')
ORDER BY a0.created_at
   LIMIT 10
  OFFSET 0;
----
limit
 ├── columns: id:1(int!null) body:2(string) description:3(string) title:4(string) slug:5(string) tag_list:6(string[]) user_id:7(string) created_at:8(timestamp) updated_at:9(timestamp)
 ├── internal-ordering: +8
 ├── cardinality: [0 - 10]
 ├── side-effects
 ├── key: (1)
 ├── fd: (1)-->(2-9)
 ├── ordering: +8
 ├── offset
 │    ├── columns: id:1(int!null) body:2(string) description:3(string) title:4(string) slug:5(string) tag_list:6(string[]) user_id:7(string) created_at:8(timestamp) updated_at:9(timestamp)
 │    ├── internal-ordering: +8
 │    ├── side-effects
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-9)
 │    ├── ordering: +8
 │    ├── sort
 │    │    ├── columns: id:1(int!null) body:2(string) description:3(string) title:4(string) slug:5(string) tag_list:6(string[]) user_id:7(string) created_at:8(timestamp) updated_at:9(timestamp)
 │    │    ├── side-effects
 │    │    ├── key: (1)
 │    │    ├── fd: (1)-->(2-9)
 │    │    ├── ordering: +8
 │    │    └── group-by
 │    │         ├── columns: id:1(int!null) body:2(string) description:3(string) title:4(string) slug:5(string) tag_list:6(string[]) user_id:7(string) created_at:8(timestamp) updated_at:9(timestamp)
 │    │         ├── grouping columns: id:1(int!null)
 │    │         ├── side-effects
 │    │         ├── key: (1)
 │    │         ├── fd: (1)-->(2-9)
 │    │         ├── select
 │    │         │    ├── columns: id:1(int!null) body:2(string) description:3(string) title:4(string) slug:5(string) tag_list:6(string[]) user_id:7(string) created_at:8(timestamp) updated_at:9(timestamp) unnest:10(string!null)
 │    │         │    ├── side-effects
 │    │         │    ├── fd: ()-->(10), (1)-->(2-9)
 │    │         │    ├── project-set
 │    │         │    │    ├── columns: id:1(int!null) body:2(string) description:3(string) title:4(string) slug:5(string) tag_list:6(string[]) user_id:7(string) created_at:8(timestamp) updated_at:9(timestamp) unnest:10(string)
 │    │         │    │    ├── side-effects
 │    │         │    │    ├── fd: (1)-->(2-9)
 │    │         │    │    ├── scan a0
 │    │         │    │    │    ├── columns: id:1(int!null) body:2(string) description:3(string) title:4(string) slug:5(string) tag_list:6(string[]) user_id:7(string) created_at:8(timestamp) updated_at:9(timestamp)
 │    │         │    │    │    ├── key: (1)
 │    │         │    │    │    └── fd: (1)-->(2-9)
 │    │         │    │    └── zip
 │    │         │    │         └── function: unnest [type=string, outer=(6), side-effects]
 │    │         │    │              └── variable: tag_list [type=string[]]
 │    │         │    └── filters
 │    │         │         └── unnest = 'dragons' [type=bool, outer=(10), constraints=(/10: [/'dragons' - /'dragons']; tight), fd=()-->(10)]
 │    │         └── aggregations
 │    │              ├── const-agg [type=string, outer=(2)]
 │    │              │    └── variable: body [type=string]
 │    │              ├── const-agg [type=string, outer=(3)]
 │    │              │    └── variable: description [type=string]
 │    │              ├── const-agg [type=string, outer=(4)]
 │    │              │    └── variable: title [type=string]
 │    │              ├── const-agg [type=string, outer=(5)]
 │    │              │    └── variable: slug [type=string]
 │    │              ├── const-agg [type=string[], outer=(6)]
 │    │              │    └── variable: tag_list [type=string[]]
 │    │              ├── const-agg [type=string, outer=(7)]
 │    │              │    └── variable: user_id [type=string]
 │    │              ├── const-agg [type=timestamp, outer=(8)]
 │    │              │    └── variable: created_at [type=timestamp]
 │    │              └── const-agg [type=timestamp, outer=(9)]
 │    │                   └── variable: updated_at [type=timestamp]
 │    └── const: 0 [type=int]
 └── const: 10 [type=int]

# TODO(justin): figure out how to get this to decorrelate again.
opt
SELECT * FROM articles, xy WHERE EXISTS(
  SELECT * FROM ROWS FROM (generate_series(x, id), length(title), upper(title), unnest(tag_list))
)
----
project
 ├── columns: id:1(int!null) body:2(string) description:3(string) title:4(string) slug:5(string) tag_list:6(string[]) user_id:7(string) created_at:8(timestamp) updated_at:9(timestamp) x:10(int!null) y:11(int)
 ├── side-effects
 ├── key: (1,10)
 ├── fd: (1)-->(2-9), (1,10)-->(2-9,11)
 └── select
      ├── columns: id:1(int!null) body:2(string) description:3(string) title:4(string) slug:5(string) tag_list:6(string[]) user_id:7(string) created_at:8(timestamp) updated_at:9(timestamp) x:10(int!null) y:11(int) true_agg:17(bool!null)
      ├── side-effects
      ├── key: (1,10)
      ├── fd: (1)-->(2-9), (1,10)-->(2-9,11,17)
      ├── group-by
      │    ├── columns: id:1(int!null) body:2(string) description:3(string) title:4(string) slug:5(string) tag_list:6(string[]) user_id:7(string) created_at:8(timestamp) updated_at:9(timestamp) x:10(int!null) y:11(int) true_agg:17(bool)
      │    ├── grouping columns: id:1(int!null) x:10(int!null)
      │    ├── side-effects
      │    ├── key: (1,10)
      │    ├── fd: (1)-->(2-9), (1,10)-->(2-9,11,17)
      │    ├── inner-join-apply
      │    │    ├── columns: id:1(int!null) body:2(string) description:3(string) title:4(string) slug:5(string) tag_list:6(string[]) user_id:7(string) created_at:8(timestamp) updated_at:9(timestamp) x:10(int!null) y:11(int) true:16(bool)
      │    │    ├── side-effects
      │    │    ├── fd: (1)-->(2-9), (1,10)-->(11)
      │    │    ├── scan articles
      │    │    │    ├── columns: id:1(int!null) body:2(string) description:3(string) title:4(string) slug:5(string) tag_list:6(string[]) user_id:7(string) created_at:8(timestamp) updated_at:9(timestamp)
      │    │    │    ├── key: (1)
      │    │    │    └── fd: (1)-->(2-9)
      │    │    ├── left-join-apply
      │    │    │    ├── columns: x:10(int!null) y:11(int) true:16(bool)
      │    │    │    ├── outer: (1,4,6)
      │    │    │    ├── side-effects
      │    │    │    ├── fd: (10)-->(11)
      │    │    │    ├── scan xy
      │    │    │    │    ├── columns: x:10(int!null) y:11(int)
      │    │    │    │    ├── key: (10)
      │    │    │    │    └── fd: (10)-->(11)
      │    │    │    ├── project
      │    │    │    │    ├── columns: true:16(bool!null)
      │    │    │    │    ├── outer: (1,4,6,10)
      │    │    │    │    ├── side-effects
      │    │    │    │    ├── fd: ()-->(16)
      │    │    │    │    ├── project-set
      │    │    │    │    │    ├── columns: generate_series:12(int) length:13(int) upper:14(string) unnest:15(string)
      │    │    │    │    │    ├── outer: (1,4,6,10)
      │    │    │    │    │    ├── side-effects
      │    │    │    │    │    ├── values
      │    │    │    │    │    │    ├── cardinality: [1 - 1]
      │    │    │    │    │    │    ├── key: ()
      │    │    │    │    │    │    └── tuple [type=tuple]
      │    │    │    │    │    └── zip
      │    │    │    │    │         ├── function: generate_series [type=int, outer=(1,10), side-effects]
      │    │    │    │    │         │    ├── variable: x [type=int]
      │    │    │    │    │         │    └── variable: id [type=int]
      │    │    │    │    │         ├── function: length [type=int, outer=(4)]
      │    │    │    │    │         │    └── variable: title [type=string]
      │    │    │    │    │         ├── function: upper [type=string, outer=(4)]
      │    │    │    │    │         │    └── variable: title [type=string]
      │    │    │    │    │         └── function: unnest [type=string, outer=(6), side-effects]
      │    │    │    │    │              └── variable: tag_list [type=string[]]
      │    │    │    │    └── projections
      │    │    │    │         └── true [type=bool]
      │    │    │    └── filters (true)
      │    │    └── filters (true)
      │    └── aggregations
      │         ├── const-not-null-agg [type=bool, outer=(16)]
      │         │    └── variable: true [type=bool]
      │         ├── const-agg [type=int, outer=(11)]
      │         │    └── variable: y [type=int]
      │         ├── const-agg [type=string, outer=(2)]
      │         │    └── variable: body [type=string]
      │         ├── const-agg [type=string, outer=(3)]
      │         │    └── variable: description [type=string]
      │         ├── const-agg [type=string, outer=(4)]
      │         │    └── variable: title [type=string]
      │         ├── const-agg [type=string, outer=(5)]
      │         │    └── variable: slug [type=string]
      │         ├── const-agg [type=string[], outer=(6)]
      │         │    └── variable: tag_list [type=string[]]
      │         ├── const-agg [type=string, outer=(7)]
      │         │    └── variable: user_id [type=string]
      │         ├── const-agg [type=timestamp, outer=(8)]
      │         │    └── variable: created_at [type=timestamp]
      │         └── const-agg [type=timestamp, outer=(9)]
      │              └── variable: updated_at [type=timestamp]
      └── filters
           └── true_agg IS NOT NULL [type=bool, outer=(17), constraints=(/17: (/NULL - ]; tight)]

opt expect=TryDecorrelateProjectSet
SELECT id FROM articles WHERE title = ANY(
  SELECT unnest FROM ROWS FROM (upper(title), unnest(tag_list), generate_series(0,1), lower('ABC'))
)
----
distinct-on
 ├── columns: id:1(int!null)
 ├── grouping columns: id:1(int!null)
 ├── side-effects
 ├── key: (1)
 └── select
      ├── columns: id:1(int!null) title:4(string!null) tag_list:6(string[]) upper:10(string) unnest:11(string!null) generate_series:12(int) lower:13(string)
      ├── side-effects
      ├── fd: (1)-->(4,6), (4)==(11), (11)==(4)
      ├── project-set
      │    ├── columns: id:1(int!null) title:4(string) tag_list:6(string[]) upper:10(string) unnest:11(string) generate_series:12(int) lower:13(string)
      │    ├── side-effects
      │    ├── fd: (1)-->(4,6)
      │    ├── scan articles
      │    │    ├── columns: id:1(int!null) title:4(string) tag_list:6(string[])
      │    │    ├── key: (1)
      │    │    └── fd: (1)-->(4,6)
      │    └── zip
      │         ├── function: upper [type=string, outer=(4)]
      │         │    └── variable: title [type=string]
      │         ├── function: unnest [type=string, outer=(6), side-effects]
      │         │    └── variable: tag_list [type=string[]]
      │         ├── function: generate_series [type=int, side-effects]
      │         │    ├── const: 0 [type=int]
      │         │    └── const: 1 [type=int]
      │         └── const: 'abc' [type=string]
      └── filters
           └── title = unnest [type=bool, outer=(4,11), constraints=(/4: (/NULL - ]; /11: (/NULL - ]), fd=(4)==(11), (11)==(4)]

# --------------------------------------------------
# NormalizeSelectAnyFilter + NormalizeJoinAnyFilter
# --------------------------------------------------
opt expect=NormalizeSelectAnyFilter
SELECT * FROM a WHERE i IN (SELECT y FROM xy)
----
semi-join
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 ├── scan xy
 │    └── columns: y:7(int)
 └── filters
      └── i = y [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)]

# Any is one of several conjuncts.
opt expect=NormalizeSelectAnyFilter
SELECT * FROM a WHERE k=10 AND i < ANY(SELECT y FROM xy) AND s='foo'
----
semi-join
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string!null) j:5(jsonb)
 ├── cardinality: [0 - 1]
 ├── key: ()
 ├── fd: ()-->(1-5)
 ├── select
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string!null) j:5(jsonb)
 │    ├── cardinality: [0 - 1]
 │    ├── key: ()
 │    ├── fd: ()-->(1-5)
 │    ├── scan a
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    ├── constraint: /1: [/10 - /10]
 │    │    ├── cardinality: [0 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(1-5)
 │    └── filters
 │         └── s = 'foo' [type=bool, outer=(4), constraints=(/4: [/'foo' - /'foo']; tight), fd=()-->(4)]
 ├── scan xy
 │    └── columns: y:7(int)
 └── filters
      └── i < y [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ])]

# Multiple ANY conjuncts.
opt expect=NormalizeSelectAnyFilter
SELECT * FROM a WHERE i < ANY(SELECT y FROM xy) AND s = ANY(SELECT y::string FROM xy)
----
semi-join
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── semi-join
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    ├── scan a
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(2-5)
 │    ├── project
 │    │    ├── columns: y:10(string)
 │    │    ├── scan xy
 │    │    │    └── columns: xy.y:9(int)
 │    │    └── projections
 │    │         └── xy.y::STRING [type=string, outer=(9)]
 │    └── filters
 │         └── s = y [type=bool, outer=(4,10), constraints=(/4: (/NULL - ]; /10: (/NULL - ]), fd=(4)==(10), (10)==(4)]
 ├── scan xy
 │    └── columns: xy.y:7(int)
 └── filters
      └── i < xy.y [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ])]

# Don't hoist uncorrelated ANY (but rewrite it to EXISTS).
opt expect=NormalizeSelectAnyFilter
SELECT * FROM a WHERE 5 IN (SELECT y FROM xy)
----
select
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 └── filters
      └── exists [type=bool, subquery]
           └── limit
                ├── columns: y:7(int!null)
                ├── cardinality: [0 - 1]
                ├── key: ()
                ├── fd: ()-->(7)
                ├── select
                │    ├── columns: y:7(int!null)
                │    ├── fd: ()-->(7)
                │    ├── scan xy
                │    │    └── columns: y:7(int)
                │    └── filters
                │         └── y = 5 [type=bool, outer=(7), constraints=(/7: [/5 - /5]; tight), fd=()-->(7)]
                └── const: 1 [type=int]

# ANY in Join On condition.
opt expect=NormalizeJoinAnyFilter
SELECT * FROM a INNER JOIN xy ON i IN (SELECT v FROM uv) AND k=x
----
inner-join
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) y:7(int)
 ├── key: (6)
 ├── fd: (1)-->(2-5), (6)-->(7), (1)==(6), (6)==(1)
 ├── semi-join
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    ├── scan a
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(2-5)
 │    ├── scan uv
 │    │    └── columns: v:9(int)
 │    └── filters
 │         └── i = v [type=bool, outer=(2,9), constraints=(/2: (/NULL - ]; /9: (/NULL - ]), fd=(2)==(9), (9)==(2)]
 ├── scan xy
 │    ├── columns: x:6(int!null) y:7(int)
 │    ├── key: (6)
 │    └── fd: (6)-->(7)
 └── filters
      └── k = x [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]

# --------------------------------------------------
# NormalizeSelectNotAnyFilter + NormalizeJoinNotAnyFilter
# --------------------------------------------------
opt expect=NormalizeSelectNotAnyFilter
SELECT * FROM a WHERE i NOT IN (SELECT y FROM xy)
----
anti-join
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 ├── scan xy
 │    └── columns: y:7(int)
 └── filters
      └── (i = y) IS NOT false [type=bool, outer=(2,7)]

# NOT ANY is one of several conjuncts. Note that i > ALL(...) gets mapped to
# NOT i <= ANY(...) by optbuilder.
opt expect=NormalizeSelectNotAnyFilter
SELECT * FROM a WHERE k > 1 AND k < 5 AND i > ALL(SELECT y FROM xy)
----
anti-join
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── constraint: /1: [/2 - /4]
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 ├── scan xy
 │    └── columns: y:7(int)
 └── filters
      └── (i <= y) IS NOT false [type=bool, outer=(2,7)]

# Multiple NOT ANY conjuncts.
opt expect=NormalizeSelectNotAnyFilter
SELECT * FROM a WHERE i < ALL(SELECT y FROM xy) AND s <> ALL(SELECT y::string FROM xy)
----
anti-join
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── anti-join
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    ├── scan a
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(2-5)
 │    ├── project
 │    │    ├── columns: y:10(string)
 │    │    ├── scan xy
 │    │    │    └── columns: xy.y:9(int)
 │    │    └── projections
 │    │         └── xy.y::STRING [type=string, outer=(9)]
 │    └── filters
 │         └── (s = y) IS NOT false [type=bool, outer=(4,10)]
 ├── scan xy
 │    └── columns: xy.y:7(int)
 └── filters
      └── (i >= xy.y) IS NOT false [type=bool, outer=(2,7)]

# Don't hoist uncorrelated NOT ANY (but rewrite it to NOT EXISTS).
opt expect=NormalizeSelectNotAnyFilter
SELECT * FROM a WHERE 5 NOT IN (SELECT y FROM xy)
----
select
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 └── filters
      └── not [type=bool, subquery]
           └── exists [type=bool]
                └── limit
                     ├── columns: y:7(int)
                     ├── cardinality: [0 - 1]
                     ├── key: ()
                     ├── fd: ()-->(7)
                     ├── select
                     │    ├── columns: y:7(int)
                     │    ├── scan xy
                     │    │    └── columns: y:7(int)
                     │    └── filters
                     │         └── (y = 5) IS NOT false [type=bool, outer=(7)]
                     └── const: 1 [type=int]

# NOT ANY in Join On condition.
opt expect=NormalizeJoinNotAnyFilter
SELECT * FROM a INNER JOIN xy ON i NOT IN (SELECT v FROM uv) AND k=x
----
inner-join
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) y:7(int)
 ├── key: (6)
 ├── fd: (1)-->(2-5), (6)-->(7), (1)==(6), (6)==(1)
 ├── anti-join
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    ├── scan a
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(2-5)
 │    ├── scan uv
 │    │    └── columns: v:9(int)
 │    └── filters
 │         └── (i = v) IS NOT false [type=bool, outer=(2,9)]
 ├── scan xy
 │    ├── columns: x:6(int!null) y:7(int)
 │    ├── key: (6)
 │    └── fd: (6)-->(7)
 └── filters
      └── k = x [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]

# --------------------------------------------------
# NormalizeSelectAnyFilter + NormalizeSelectNotAnyFilter
# --------------------------------------------------
opt expect=(NormalizeSelectAnyFilter,NormalizeSelectNotAnyFilter)
SELECT * FROM a WHERE i = ANY(SELECT y FROM xy) AND s <> ALL(SELECT y::string FROM xy)
----
semi-join
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── anti-join
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    ├── scan a
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(2-5)
 │    ├── project
 │    │    ├── columns: y:10(string)
 │    │    ├── scan xy
 │    │    │    └── columns: xy.y:9(int)
 │    │    └── projections
 │    │         └── xy.y::STRING [type=string, outer=(9)]
 │    └── filters
 │         └── (s = y) IS NOT false [type=bool, outer=(4,10)]
 ├── scan xy
 │    └── columns: xy.y:7(int)
 └── filters
      └── i = xy.y [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)]
