exec-ddl
CREATE TABLE a (x INT PRIMARY KEY, y INT, f FLOAT, s STRING)
----
TABLE a
 ├── x int not null
 ├── y int
 ├── f float
 ├── s string
 └── INDEX primary
      └── x int not null

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

# --------------------------------------------------
# EliminateProject
# --------------------------------------------------

# Same order, same names.
opt expect=EliminateProject
SELECT x, y FROM a
----
scan a
 ├── columns: x:1(int!null) y:2(int)
 ├── key: (1)
 └── fd: (1)-->(2)

# Different order, aliased names.
opt expect=EliminateProject
SELECT a.y AS aliasy, a.x FROM a
----
scan a
 ├── columns: aliasy:2(int) x:1(int!null)
 ├── key: (1)
 └── fd: (1)-->(2)

# Reordered, duplicate, aliased columns.
opt expect=EliminateProject
SELECT a.y AS alias1, a.x, a.y AS alias1, a.x FROM a
----
scan a
 ├── columns: alias1:2(int) x:1(int!null) alias1:2(int) x:1(int!null)
 ├── key: (1)
 └── fd: (1)-->(2)

# Added column (projection should not be eliminated).
opt expect-not=EliminateProject
SELECT *, 1 r FROM a
----
project
 ├── columns: x:1(int!null) y:2(int) f:3(float) s:4(string) r:5(int!null)
 ├── key: (1)
 ├── fd: ()-->(5), (1)-->(2-4)
 ├── scan a
 │    ├── columns: x:1(int!null) y:2(int) f:3(float) s:4(string)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-4)
 └── projections
      └── const: 1 [type=int]

# --------------------------------------------------
# MergeProjects
# --------------------------------------------------

# Inner project has no synthesized columns.
opt expect=MergeProjects
SELECT y+1 AS r FROM (SELECT a.y FROM a, b WHERE a.x=b.x) a
----
project
 ├── columns: r:7(int)
 ├── inner-join (merge)
 │    ├── columns: a.x:1(int!null) y:2(int) b.x:5(int!null)
 │    ├── left ordering: +1
 │    ├── right ordering: +5
 │    ├── key: (5)
 │    ├── fd: (1)-->(2), (1)==(5), (5)==(1)
 │    ├── scan a
 │    │    ├── columns: a.x:1(int!null) y:2(int)
 │    │    ├── key: (1)
 │    │    ├── fd: (1)-->(2)
 │    │    └── ordering: +1
 │    ├── scan b
 │    │    ├── columns: b.x:5(int!null)
 │    │    ├── key: (5)
 │    │    └── ordering: +5
 │    └── filters (true)
 └── projections
      └── y + 1 [type=int, outer=(2)]

# Outer and inner projections have synthesized columns.
opt expect=MergeProjects
SELECT y1, f+1 FROM (SELECT y+1 AS y1, f FROM a)
----
project
 ├── columns: y1:5(int) "?column?":6(float)
 ├── scan a
 │    └── columns: y:2(int) f:3(float)
 └── projections
      ├── f + 1.0 [type=float, outer=(3)]
      └── y + 1 [type=int, outer=(2)]

# Multiple synthesized columns in both outer and inner projections.
opt expect=MergeProjects
SELECT y1, f+1, x2, s||'foo' FROM (SELECT y+1 AS y1, f, s, x*2 AS x2 FROM a)
----
project
 ├── columns: y1:5(int) "?column?":7(float) x2:6(int) "?column?":8(string)
 ├── scan a
 │    ├── columns: x:1(int!null) y:2(int) f:3(float) s:4(string)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-4)
 └── projections
      ├── f + 1.0 [type=float, outer=(3)]
      ├── s || 'foo' [type=string, outer=(4)]
      ├── y + 1 [type=int, outer=(2)]
      └── x * 2 [type=int, outer=(1)]

# Outer project selects subset of inner columns.
opt expect=MergeProjects
SELECT y1 FROM (SELECT y+1 AS y1, f*2 AS f2 FROM a)
----
project
 ├── columns: y1:5(int)
 ├── scan a
 │    └── columns: y:2(int)
 └── projections
      └── y + 1 [type=int, outer=(2)]

# Don't merge, since outer depends on inner.
opt expect-not=MergeProjects
SELECT y1*2, y1/2 FROM (SELECT y+1 AS y1 FROM a)
----
project
 ├── columns: "?column?":6(int) "?column?":7(decimal)
 ├── side-effects
 ├── project
 │    ├── columns: y1:5(int)
 │    ├── scan a
 │    │    └── columns: y:2(int)
 │    └── projections
 │         └── y + 1 [type=int, outer=(2)]
 └── projections
      ├── y1 * 2 [type=int, outer=(5)]
      └── y1 / 2 [type=decimal, outer=(5), side-effects]

# Discard all inner columns.
opt expect=MergeProjects
SELECT 1 r FROM (SELECT y+1, x FROM a) a
----
project
 ├── columns: r:6(int!null)
 ├── fd: ()-->(6)
 ├── scan a
 └── projections
      └── const: 1 [type=int]

# --------------------------------------------------
# MergeProjectWithValues
# --------------------------------------------------

opt expect=MergeProjectWithValues
SELECT column1, 3 FROM (VALUES (1, 2))
----
values
 ├── columns: column1:1(int) "?column?":3(int)
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1,3)
 └── (1, 3) [type=tuple{int, int}]

# Only passthrough columns.
opt expect=MergeProjectWithValues
SELECT column1, column3 FROM (VALUES (1, 2, 3))
----
values
 ├── columns: column1:1(int) column3:3(int)
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1,3)
 └── (1, 3) [type=tuple{int, int}]

# Only synthesized columns.
opt expect=MergeProjectWithValues
SELECT 4, 5 FROM (VALUES (1, 2, 3))
----
values
 ├── columns: "?column?":4(int) "?column?":5(int)
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(4,5)
 └── (4, 5) [type=tuple{int, int}]

# Don't trigger rule when there is more than one Values row.
opt expect-not=MergeProjectWithValues
SELECT column1, 3 FROM (VALUES (1, 2), (1, 4))
----
project
 ├── columns: column1:1(int) "?column?":3(int!null)
 ├── cardinality: [2 - 2]
 ├── fd: ()-->(3)
 ├── values
 │    ├── columns: column1:1(int)
 │    ├── cardinality: [2 - 2]
 │    ├── (1,) [type=tuple{int}]
 │    └── (1,) [type=tuple{int}]
 └── projections
      └── const: 3 [type=int]

# Don't trigger rule when Project column depends on Values column.
opt expect-not=MergeProjectWithValues
SELECT column1+1, 3 FROM (VALUES ($1::int, $2::int))
----
project
 ├── columns: "?column?":3(int) "?column?":4(int!null)
 ├── cardinality: [1 - 1]
 ├── has-placeholder
 ├── key: ()
 ├── fd: ()-->(3,4)
 ├── values
 │    ├── columns: column1:1(int)
 │    ├── cardinality: [1 - 1]
 │    ├── has-placeholder
 │    ├── key: ()
 │    ├── fd: ()-->(1)
 │    └── ($1::INT8,) [type=tuple{int}]
 └── projections
      ├── column1 + 1 [type=int, outer=(1)]
      └── const: 3 [type=int]
