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

exec-ddl
CREATE TABLE b (x STRING PRIMARY KEY, z DECIMAL NOT NULL)
----
TABLE b
 ├── x string not null
 ├── z decimal not null
 └── INDEX primary
      └── x string not null

build
SELECT y, b.x, y+1 AS c
FROM a, b
WHERE a.y>1 AND a.x::string=b.x
ORDER BY y
LIMIT 10
----
limit
 ├── columns: y:2(int!null) x:3(string!null) c:5(int)
 ├── internal-ordering: +2
 ├── cardinality: [0 - 10]
 ├── fd: (2)-->(5)
 ├── ordering: +2
 ├── sort
 │    ├── columns: y:2(int!null) b.x:3(string!null) c:5(int)
 │    ├── fd: (2)-->(5)
 │    ├── ordering: +2
 │    └── project
 │         ├── columns: c:5(int) y:2(int!null) b.x:3(string!null)
 │         ├── fd: (2)-->(5)
 │         ├── select
 │         │    ├── columns: a.x:1(int!null) y:2(int!null) b.x:3(string!null) z:4(decimal!null)
 │         │    ├── key: (1,3)
 │         │    ├── fd: (1)-->(2), (3)-->(4)
 │         │    ├── inner-join
 │         │    │    ├── columns: a.x:1(int!null) y:2(int) b.x:3(string!null) z:4(decimal!null)
 │         │    │    ├── key: (1,3)
 │         │    │    ├── fd: (1)-->(2), (3)-->(4)
 │         │    │    ├── scan a
 │         │    │    │    ├── columns: a.x:1(int!null) y:2(int)
 │         │    │    │    ├── key: (1)
 │         │    │    │    └── fd: (1)-->(2)
 │         │    │    ├── scan b
 │         │    │    │    ├── columns: b.x:3(string!null) z:4(decimal!null)
 │         │    │    │    ├── key: (3)
 │         │    │    │    └── fd: (3)-->(4)
 │         │    │    └── filters (true)
 │         │    └── filters
 │         │         └── and [type=bool, outer=(1-3), constraints=(/2: [/2 - ])]
 │         │              ├── gt [type=bool]
 │         │              │    ├── variable: y [type=int]
 │         │              │    └── const: 1 [type=int]
 │         │              └── eq [type=bool]
 │         │                   ├── cast: STRING [type=string]
 │         │                   │    └── variable: a.x [type=int]
 │         │                   └── variable: b.x [type=string]
 │         └── projections
 │              └── plus [type=int, outer=(2)]
 │                   ├── variable: y [type=int]
 │                   └── const: 1 [type=int]
 └── const: 10 [type=int]

opt
SELECT y, b.x, y+1 AS c
FROM a, b
WHERE a.y>1 AND a.x::string=b.x
ORDER BY y
LIMIT 10
----
project
 ├── columns: y:2(int!null) x:3(string!null) c:6(int)
 ├── cardinality: [0 - 10]
 ├── fd: (2)-->(6)
 ├── ordering: +2
 ├── limit
 │    ├── columns: y:2(int!null) b.x:3(string!null) column5:5(string!null)
 │    ├── internal-ordering: +2
 │    ├── cardinality: [0 - 10]
 │    ├── fd: (3)==(5), (5)==(3)
 │    ├── ordering: +2
 │    ├── sort
 │    │    ├── columns: y:2(int!null) b.x:3(string!null) column5:5(string!null)
 │    │    ├── fd: (3)==(5), (5)==(3)
 │    │    ├── ordering: +2
 │    │    └── inner-join
 │    │         ├── columns: y:2(int!null) b.x:3(string!null) column5:5(string!null)
 │    │         ├── fd: (3)==(5), (5)==(3)
 │    │         ├── scan b
 │    │         │    ├── columns: b.x:3(string!null)
 │    │         │    └── key: (3)
 │    │         ├── project
 │    │         │    ├── columns: column5:5(string) y:2(int!null)
 │    │         │    ├── select
 │    │         │    │    ├── columns: a.x:1(int!null) y:2(int!null)
 │    │         │    │    ├── key: (1)
 │    │         │    │    ├── fd: (1)-->(2)
 │    │         │    │    ├── scan a
 │    │         │    │    │    ├── columns: a.x:1(int!null) y:2(int)
 │    │         │    │    │    ├── key: (1)
 │    │         │    │    │    └── fd: (1)-->(2)
 │    │         │    │    └── filters
 │    │         │    │         └── gt [type=bool, outer=(2), constraints=(/2: [/2 - ]; tight)]
 │    │         │    │              ├── variable: y [type=int]
 │    │         │    │              └── const: 1 [type=int]
 │    │         │    └── projections
 │    │         │         └── cast: STRING [type=string, outer=(1)]
 │    │         │              └── variable: a.x [type=int]
 │    │         └── filters
 │    │              └── eq [type=bool, outer=(3,5), constraints=(/3: (/NULL - ]; /5: (/NULL - ]), fd=(3)==(5), (5)==(3)]
 │    │                   ├── variable: column5 [type=string]
 │    │                   └── variable: b.x [type=string]
 │    └── const: 10 [type=int]
 └── projections
      └── plus [type=int, outer=(2)]
           ├── variable: y [type=int]
           └── const: 1 [type=int]

memo
SELECT y, b.x, y+1 AS c
FROM a, b
WHERE a.y>1 AND a.x::string=b.x
ORDER BY y
LIMIT 10
----
memo (optimized, ~16KB, required=[presentation: y:2,x:3,c:6] [ordering: +2])
 ├── G1: (project G2 G3 y x)
 │    ├── [presentation: y:2,x:3,c:6] [ordering: +2]
 │    │    ├── best: (project G2="[ordering: +2]" G3 y x)
 │    │    └── cost: 2170.39
 │    └── []
 │         ├── best: (project G2 G3 y x)
 │         └── cost: 2170.39
 ├── G2: (limit G4 G5 ordering=+2)
 │    ├── [ordering: +2]
 │    │    ├── best: (limit G4="[ordering: +2]" G5 ordering=+2)
 │    │    └── cost: 2170.18
 │    └── []
 │         ├── best: (limit G4="[ordering: +2]" G5 ordering=+2)
 │         └── cost: 2170.18
 ├── G3: (projections G6)
 ├── G4: (inner-join G7 G8 G9) (inner-join G8 G7 G9) (lookup-join G7 G10 b,keyCols=[5],outCols=(2,3,5)) (merge-join G8 G7 G10 inner-join,+3,+5)
 │    ├── [ordering: +2]
 │    │    ├── best: (sort G4)
 │    │    └── cost: 2170.07
 │    └── []
 │         ├── best: (inner-join G8 G7 G9)
 │         └── cost: 2108.24
 ├── G5: (const 10)
 ├── G6: (plus G11 G12)
 ├── G7: (project G13 G14 y)
 │    ├── [ordering: +2]
 │    │    ├── best: (sort G7)
 │    │    └── cost: 1118.47
 │    ├── [ordering: +5]
 │    │    ├── best: (sort G7)
 │    │    └── cost: 1118.47
 │    └── []
 │         ├── best: (project G13 G14 y)
 │         └── cost: 1056.64
 ├── G8: (scan b,cols=(3))
 │    ├── [ordering: +3]
 │    │    ├── best: (scan b,cols=(3))
 │    │    └── cost: 1030.02
 │    └── []
 │         ├── best: (scan b,cols=(3))
 │         └── cost: 1030.02
 ├── G9: (filters G15)
 ├── G10: (filters)
 ├── G11: (variable y)
 ├── G12: (const 1)
 ├── G13: (select G16 G17)
 │    ├── [ordering: +2]
 │    │    ├── best: (sort G13)
 │    │    └── cost: 1111.86
 │    └── []
 │         ├── best: (select G16 G17)
 │         └── cost: 1050.03
 ├── G14: (projections G18)
 ├── G15: (eq G19 G20)
 ├── G16: (scan a)
 │    ├── [ordering: +2]
 │    │    ├── best: (sort G16)
 │    │    └── cost: 1259.35
 │    └── []
 │         ├── best: (scan a)
 │         └── cost: 1040.02
 ├── G17: (filters G21)
 ├── G18: (cast G22 STRING)
 ├── G19: (variable column5)
 ├── G20: (variable b.x)
 ├── G21: (gt G11 G12)
 └── G22: (variable a.x)

# Test interning of expressions.
memo
SELECT 1 AS a, 1+z AS b, left(x, 10)::TIMESTAMP AS c, left(x, 10)::TIMESTAMPTZ AS d
FROM b
WHERE z=1 AND concat(x, 'foo', x)=concat(x, 'foo', x)
----
memo (optimized, ~4KB, required=[presentation: a:3,b:4,c:5,d:6])
 ├── G1: (project G2 G3)
 │    └── [presentation: a:3,b:4,c:5,d:6]
 │         ├── best: (project G2 G3)
 │         └── cost: 1050.21
 ├── G2: (select G4 G5)
 │    └── []
 │         ├── best: (select G4 G5)
 │         └── cost: 1050.03
 ├── G3: (projections G6 G7 G8 G9)
 ├── G4: (scan b)
 │    └── []
 │         ├── best: (scan b)
 │         └── cost: 1040.02
 ├── G5: (filters G10 G11)
 ├── G6: (const 1)
 ├── G7: (plus G12 G13)
 ├── G8: (cast G14 TIMESTAMP)
 ├── G9: (cast G14 TIMESTAMPTZ)
 ├── G10: (eq G12 G13)
 ├── G11: (eq G15 G15)
 ├── G12: (variable z)
 ├── G13: (const 1)
 ├── G14: (function G16 left)
 ├── G15: (function G17 concat)
 ├── G16: (scalar-list G18 G19)
 ├── G17: (scalar-list G18 G20 G18)
 ├── G18: (variable x)
 ├── G19: (const 10)
 └── G20: (const 'foo')

# Test topological sorting
memo
SELECT x FROM a WHERE x = 1 AND x+y = 1
----
memo (optimized, ~4KB, required=[presentation: x:1])
 ├── G1: (project G2 G3 x)
 │    └── [presentation: x:1]
 │         ├── best: (project G2 G3 x)
 │         └── cost: 1.08
 ├── G2: (select G4 G5) (select G6 G7)
 │    └── []
 │         ├── best: (select G6 G7)
 │         └── cost: 1.07
 ├── G3: (projections)
 ├── G4: (scan a)
 │    └── []
 │         ├── best: (scan a)
 │         └── cost: 1040.02
 ├── G5: (filters G8 G9)
 ├── G6: (scan a,constrained)
 │    └── []
 │         ├── best: (scan a,constrained)
 │         └── cost: 1.05
 ├── G7: (filters G9)
 ├── G8: (eq G10 G11)
 ├── G9: (eq G12 G11)
 ├── G10: (variable x)
 ├── G11: (const 1)
 ├── G12: (plus G10 G13)
 └── G13: (variable y)

memo 
SELECT x, y FROM a UNION SELECT x+1, y+1 FROM a
----
memo (optimized, ~4KB, required=[presentation: x:7,y:8])
 ├── G1: (union G2 G3)
 │    └── [presentation: x:7,y:8]
 │         ├── best: (union G2 G3)
 │         └── cost: 2150.06
 ├── G2: (scan a)
 │    └── []
 │         ├── best: (scan a)
 │         └── cost: 1040.02
 ├── G3: (project G4 G5)
 │    └── []
 │         ├── best: (project G4 G5)
 │         └── cost: 1070.03
 ├── G4: (scan a)
 │    └── []
 │         ├── best: (scan a)
 │         └── cost: 1040.02
 ├── G5: (projections G6 G7)
 ├── G6: (plus G8 G9)
 ├── G7: (plus G10 G9)
 ├── G8: (variable a.x)
 ├── G9: (const 1)
 └── G10: (variable a.y)

memo
SELECT array_agg(x) FROM (SELECT * FROM a)
----
memo (optimized, ~3KB, required=[presentation: array_agg:3])
 ├── G1: (scalar-group-by G2 G3 cols=())
 │    └── [presentation: array_agg:3]
 │         ├── best: (scalar-group-by G2 G3 cols=())
 │         └── cost: 1040.04
 ├── G2: (scan a,cols=(1))
 │    └── []
 │         ├── best: (scan a,cols=(1))
 │         └── cost: 1030.02
 ├── G3: (aggregations G4)
 ├── G4: (array-agg G5)
 └── G5: (variable x)

memo
SELECT array_agg(x) FROM (SELECT * FROM a) GROUP BY y
----
memo (optimized, ~3KB, required=[presentation: array_agg:3])
 ├── G1: (project G2 G3 array_agg)
 │    └── [presentation: array_agg:3]
 │         ├── best: (project G2 G3 array_agg)
 │         └── cost: 1072.04
 ├── G2: (group-by G4 G5 cols=(2))
 │    └── []
 │         ├── best: (group-by G4 G5 cols=(2))
 │         └── cost: 1071.03
 ├── G3: (projections)
 ├── G4: (scan a)
 │    └── []
 │         ├── best: (scan a)
 │         └── cost: 1040.02
 ├── G5: (aggregations G6)
 ├── G6: (array-agg G7)
 └── G7: (variable x)

memo
SELECT array_agg(x) FROM (SELECT * FROM a ORDER BY y)
----
memo (optimized, ~2KB, required=[presentation: array_agg:3])
 ├── G1: (scalar-group-by G2 G3 cols=(),ordering=+2)
 │    └── [presentation: array_agg:3]
 │         ├── best: (scalar-group-by G2="[ordering: +2]" G3 cols=(),ordering=+2)
 │         └── cost: 1269.37
 ├── G2: (scan a)
 │    ├── [ordering: +2]
 │    │    ├── best: (sort G2)
 │    │    └── cost: 1259.35
 │    └── []
 │         ├── best: (scan a)
 │         └── cost: 1040.02
 ├── G3: (aggregations G4)
 ├── G4: (array-agg G5)
 └── G5: (variable x)

memo
SELECT DISTINCT field FROM [EXPLAIN SELECT 123 AS k]
----
memo (optimized, ~5KB, required=[presentation: field:3])
 ├── G1: (distinct-on G2 G3 cols=(3))
 │    └── [presentation: field:3]
 │         ├── best: (distinct-on G2 G3 cols=(3))
 │         └── cost: 0.04
 ├── G2: (explain G4 [presentation: k:1])
 │    └── []
 │         ├── best: (explain G4="[presentation: k:1]" [presentation: k:1])
 │         └── cost: 0.03
 ├── G3: (aggregations)
 ├── G4: (values G5 id=v1)
 │    └── [presentation: k:1]
 │         ├── best: (values G5 id=v1)
 │         └── cost: 0.02
 ├── G5: (scalar-list G6)
 ├── G6: (tuple G7)
 ├── G7: (scalar-list G8)
 └── G8: (const 123)

memo
SELECT DISTINCT tag FROM [SHOW TRACE FOR SESSION]
----
memo (optimized, ~2KB, required=[presentation: tag:4])
 ├── G1: (distinct-on G2 G3 cols=(4))
 │    └── [presentation: tag:4]
 │         ├── best: (distinct-on G2 G3 cols=(4))
 │         └── cost: 0.02
 ├── G2: (show-trace-for-session &{TRACE false [1 2 3 4 5 6 7]})
 │    └── []
 │         ├── best: (show-trace-for-session &{TRACE false [1 2 3 4 5 6 7]})
 │         └── cost: 0.01
 └── G3: (aggregations)
