exec-ddl
CREATE TABLE xysd (x INT PRIMARY KEY, y INT, s STRING, d DECIMAL NOT NULL, UNIQUE (s DESC, d))
----

exec-ddl
CREATE TABLE kuv (k INT PRIMARY KEY, u FLOAT, v STRING)
----

exec-ddl
CREATE TABLE ab (a INT, b INT, UNIQUE (a, b))
----

build
SELECT y, x+1 AS a, 1 AS b, x FROM xysd
----
project
 ├── columns: y:2(int) a:7(int!null) b:8(int!null) x:1(int!null)
 ├── immutable
 ├── key: (1)
 ├── fd: ()-->(8), (1)-->(2,7)
 ├── prune: (1,2,7,8)
 ├── interesting orderings: (+1 opt(8))
 ├── scan xysd
 │    ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) crdb_internal_mvcc_timestamp:5(decimal) tableoid:6(oid)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-6), (3,4)~~>(1,2,5,6)
 │    ├── prune: (1-6)
 │    └── interesting orderings: (+1) (-3,+4,+1)
 └── projections
      ├── plus [as=a:7, type=int, outer=(1), immutable]
      │    ├── variable: x:1 [type=int]
      │    └── const: 1 [type=int]
      └── const: 1 [as=b:8, type=int]

build
SELECT s FROM xysd
----
project
 ├── columns: s:3(string)
 ├── prune: (3)
 ├── interesting orderings: (-3)
 └── scan xysd
      ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) crdb_internal_mvcc_timestamp:5(decimal) tableoid:6(oid)
      ├── key: (1)
      ├── fd: (1)-->(2-6), (3,4)~~>(1,2,5,6)
      ├── prune: (1-6)
      └── interesting orderings: (+1) (-3,+4,+1)

# Propagate outer columns.
build
SELECT * FROM xysd WHERE (SELECT (SELECT y) FROM kuv WHERE k=x) > 5
----
project
 ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null)
 ├── key: (1)
 ├── fd: (1)-->(2-4), (3,4)~~>(1,2)
 ├── prune: (1-4)
 ├── interesting orderings: (+1) (-3,+4,+1)
 └── select
      ├── columns: x:1(int!null) xysd.y:2(int) s:3(string) d:4(decimal!null) xysd.crdb_internal_mvcc_timestamp:5(decimal) xysd.tableoid:6(oid)
      ├── key: (1)
      ├── fd: (1)-->(2-6), (3,4)~~>(1,2,5,6)
      ├── prune: (3-6)
      ├── interesting orderings: (+1) (-3,+4,+1)
      ├── scan xysd
      │    ├── columns: x:1(int!null) xysd.y:2(int) s:3(string) d:4(decimal!null) xysd.crdb_internal_mvcc_timestamp:5(decimal) xysd.tableoid:6(oid)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-6), (3,4)~~>(1,2,5,6)
      │    ├── prune: (1-6)
      │    └── interesting orderings: (+1) (-3,+4,+1)
      └── filters
           └── gt [type=bool, outer=(1,2), correlated-subquery]
                ├── subquery [type=int]
                │    └── max1-row
                │         ├── columns: y:13(int)
                │         ├── error: "more than one row returned by a subquery used as an expression"
                │         ├── outer: (1,2)
                │         ├── cardinality: [0 - 1]
                │         ├── key: ()
                │         ├── fd: ()-->(13)
                │         └── project
                │              ├── columns: y:13(int)
                │              ├── outer: (1,2)
                │              ├── cardinality: [0 - 1]
                │              ├── key: ()
                │              ├── fd: ()-->(13)
                │              ├── prune: (13)
                │              ├── select
                │              │    ├── columns: k:7(int!null) u:8(float) v:9(string) kuv.crdb_internal_mvcc_timestamp:10(decimal) kuv.tableoid:11(oid)
                │              │    ├── outer: (1)
                │              │    ├── cardinality: [0 - 1]
                │              │    ├── key: ()
                │              │    ├── fd: ()-->(7-11)
                │              │    ├── prune: (8-11)
                │              │    ├── scan kuv
                │              │    │    ├── columns: k:7(int!null) u:8(float) v:9(string) kuv.crdb_internal_mvcc_timestamp:10(decimal) kuv.tableoid:11(oid)
                │              │    │    ├── key: (7)
                │              │    │    ├── fd: (7)-->(8-11)
                │              │    │    ├── prune: (7-11)
                │              │    │    └── interesting orderings: (+7)
                │              │    └── filters
                │              │         └── eq [type=bool, outer=(1,7), constraints=(/1: (/NULL - ]; /7: (/NULL - ]), fd=(1)==(7), (7)==(1)]
                │              │              ├── variable: k:7 [type=int]
                │              │              └── variable: x:1 [type=int]
                │              └── projections
                │                   └── subquery [as=y:13, type=int, outer=(2), correlated-subquery]
                │                        └── max1-row
                │                             ├── columns: y:12(int)
                │                             ├── error: "more than one row returned by a subquery used as an expression"
                │                             ├── outer: (2)
                │                             ├── cardinality: [1 - 1]
                │                             ├── key: ()
                │                             ├── fd: ()-->(12)
                │                             └── project
                │                                  ├── columns: y:12(int)
                │                                  ├── outer: (2)
                │                                  ├── cardinality: [1 - 1]
                │                                  ├── key: ()
                │                                  ├── fd: ()-->(12)
                │                                  ├── prune: (12)
                │                                  ├── values
                │                                  │    ├── cardinality: [1 - 1]
                │                                  │    ├── key: ()
                │                                  │    └── tuple [type=tuple]
                │                                  └── projections
                │                                       └── variable: xysd.y:2 [as=y:12, type=int, outer=(2)]
                └── const: 5 [type=int]

# Pass through cardinality.
build
SELECT x, y FROM (SELECT * FROM xysd LIMIT 10)
----
project
 ├── columns: x:1(int!null) y:2(int)
 ├── cardinality: [0 - 10]
 ├── key: (1)
 ├── fd: (1)-->(2)
 ├── prune: (1,2)
 ├── interesting orderings: (+1)
 └── limit
      ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null)
      ├── cardinality: [0 - 10]
      ├── key: (1)
      ├── fd: (1)-->(2-4), (3,4)~~>(1,2)
      ├── prune: (1-4)
      ├── interesting orderings: (+1) (-3,+4,+1)
      ├── project
      │    ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-4), (3,4)~~>(1,2)
      │    ├── limit hint: 10.00
      │    ├── prune: (1-4)
      │    ├── interesting orderings: (+1) (-3,+4,+1)
      │    └── scan xysd
      │         ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) crdb_internal_mvcc_timestamp:5(decimal) tableoid:6(oid)
      │         ├── key: (1)
      │         ├── fd: (1)-->(2-6), (3,4)~~>(1,2,5,6)
      │         ├── limit hint: 10.00
      │         ├── prune: (1-6)
      │         └── interesting orderings: (+1) (-3,+4,+1)
      └── const: 10 [type=int]

# Constant null and not-null columns.
build
SELECT 1 AS a, 'foo' AS b, NULL AS c, 1::decimal + NULL AS d, NULL::STRING AS e FROM xysd
----
project
 ├── columns: a:7(int!null) b:8(string!null) c:9(unknown) d:9(unknown) e:10(string)
 ├── immutable
 ├── fd: ()-->(7-10)
 ├── prune: (7-10)
 ├── scan xysd
 │    ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) crdb_internal_mvcc_timestamp:5(decimal) tableoid:6(oid)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-6), (3,4)~~>(1,2,5,6)
 │    ├── prune: (1-6)
 │    └── interesting orderings: (+1) (-3,+4,+1)
 └── projections
      ├── const: 1 [as=a:7, type=int]
      ├── const: 'foo' [as=b:8, type=string]
      ├── null [as=c:9, type=unknown]
      └── cast: STRING [as=e:10, type=string, immutable]
           └── null [type=unknown]

# Project constant over input with no needed columns and ensure that there is
# no key on the output (because it will have duplicates).
opt
SELECT 1 FROM (SELECT x FROM xysd)
----
project
 ├── columns: "?column?":7(int!null)
 ├── fd: ()-->(7)
 ├── prune: (7)
 ├── scan xysd@xysd_s_d_key
 └── projections
      └── const: 1 [as="?column?":7, type=int]

# Project simple variable reference after constant folding; should be not-null
# if the column it refers to is not-null.
norm
SELECT CASE WHEN true THEN x END FROM xysd
----
project
 ├── columns: case:7(int!null)
 ├── key: (7)
 ├── prune: (7)
 ├── interesting orderings: (+7)
 ├── scan xysd
 │    ├── columns: x:1(int!null)
 │    ├── key: (1)
 │    ├── prune: (1)
 │    └── interesting orderings: (+1)
 └── projections
      └── variable: x:1 [as=case:7, type=int, outer=(1)]

# Project correlated subquery.
build
SELECT k, (SELECT y FROM xysd WHERE x=k) FROM kuv
----
project
 ├── columns: k:1(int!null) y:12(int)
 ├── key: (1)
 ├── fd: (1)-->(12)
 ├── prune: (1,12)
 ├── interesting orderings: (+1)
 ├── scan kuv
 │    ├── columns: k:1(int!null) u:2(float) v:3(string) kuv.crdb_internal_mvcc_timestamp:4(decimal) kuv.tableoid:5(oid)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    ├── prune: (1-5)
 │    └── interesting orderings: (+1)
 └── projections
      └── subquery [as=y:12, type=int, outer=(1), correlated-subquery]
           └── max1-row
                ├── columns: xysd.y:7(int)
                ├── error: "more than one row returned by a subquery used as an expression"
                ├── outer: (1)
                ├── cardinality: [0 - 1]
                ├── key: ()
                ├── fd: ()-->(7)
                └── project
                     ├── columns: xysd.y:7(int)
                     ├── outer: (1)
                     ├── cardinality: [0 - 1]
                     ├── key: ()
                     ├── fd: ()-->(7)
                     ├── prune: (7)
                     └── select
                          ├── columns: x:6(int!null) xysd.y:7(int) s:8(string) d:9(decimal!null) xysd.crdb_internal_mvcc_timestamp:10(decimal) xysd.tableoid:11(oid)
                          ├── outer: (1)
                          ├── cardinality: [0 - 1]
                          ├── key: ()
                          ├── fd: ()-->(6-11)
                          ├── prune: (7-11)
                          ├── scan xysd
                          │    ├── columns: x:6(int!null) xysd.y:7(int) s:8(string) d:9(decimal!null) xysd.crdb_internal_mvcc_timestamp:10(decimal) xysd.tableoid:11(oid)
                          │    ├── key: (6)
                          │    ├── fd: (6)-->(7-11), (8,9)~~>(6,7,10,11)
                          │    ├── prune: (6-11)
                          │    └── interesting orderings: (+6) (-8,+9,+6)
                          └── filters
                               └── eq [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]
                                    ├── variable: x:6 [type=int]
                                    └── variable: k:1 [type=int]

# Project nested correlated subquery.
build
SELECT k, EXISTS(SELECT EXISTS(SELECT y FROM xysd WHERE x=k) FROM xysd) FROM kuv
----
project
 ├── columns: k:1(int!null) exists:21(bool)
 ├── key: (1)
 ├── fd: (1)-->(21)
 ├── prune: (1,21)
 ├── interesting orderings: (+1)
 ├── scan kuv
 │    ├── columns: k:1(int!null) u:2(float) v:3(string) kuv.crdb_internal_mvcc_timestamp:4(decimal) kuv.tableoid:5(oid)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    ├── prune: (1-5)
 │    └── interesting orderings: (+1)
 └── projections
      └── exists [as=exists:21, type=bool, outer=(1), correlated-subquery]
           └── project
                ├── columns: exists:19(bool)
                ├── outer: (1)
                ├── prune: (19)
                ├── scan xysd
                │    ├── columns: x:6(int!null) y:7(int) s:8(string) d:9(decimal!null) xysd.crdb_internal_mvcc_timestamp:10(decimal) xysd.tableoid:11(oid)
                │    ├── key: (6)
                │    ├── fd: (6)-->(7-11), (8,9)~~>(6,7,10,11)
                │    ├── prune: (6-11)
                │    └── interesting orderings: (+6) (-8,+9,+6)
                └── projections
                     └── exists [as=exists:19, type=bool, outer=(1), correlated-subquery]
                          └── project
                               ├── columns: y:13(int)
                               ├── outer: (1)
                               ├── cardinality: [0 - 1]
                               ├── key: ()
                               ├── fd: ()-->(13)
                               ├── prune: (13)
                               └── select
                                    ├── columns: x:12(int!null) y:13(int) s:14(string) d:15(decimal!null) xysd.crdb_internal_mvcc_timestamp:16(decimal) xysd.tableoid:17(oid)
                                    ├── outer: (1)
                                    ├── cardinality: [0 - 1]
                                    ├── key: ()
                                    ├── fd: ()-->(12-17)
                                    ├── prune: (13-17)
                                    ├── scan xysd
                                    │    ├── columns: x:12(int!null) y:13(int) s:14(string) d:15(decimal!null) xysd.crdb_internal_mvcc_timestamp:16(decimal) xysd.tableoid:17(oid)
                                    │    ├── key: (12)
                                    │    ├── fd: (12)-->(13-17), (14,15)~~>(12,13,16,17)
                                    │    ├── prune: (12-17)
                                    │    └── interesting orderings: (+12) (-14,+15,+12)
                                    └── filters
                                         └── eq [type=bool, outer=(1,12), constraints=(/1: (/NULL - ]; /12: (/NULL - ]), fd=(1)==(12), (12)==(1)]
                                              ├── variable: x:12 [type=int]
                                              └── variable: k:1 [type=int]

# We have the FD: y --> y::TEXT.
build
SELECT y, y::TEXT FROM xysd
----
project
 ├── columns: y:2(int) y:7(string)
 ├── immutable
 ├── fd: (2)-->(7)
 ├── prune: (2,7)
 ├── scan xysd
 │    ├── columns: x:1(int!null) xysd.y:2(int) s:3(string) d:4(decimal!null) crdb_internal_mvcc_timestamp:5(decimal) tableoid:6(oid)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-6), (3,4)~~>(1,2,5,6)
 │    ├── prune: (1-6)
 │    └── interesting orderings: (+1) (-3,+4,+1)
 └── projections
      └── cast: STRING [as=y:7, type=string, outer=(2), immutable]
           └── variable: xysd.y:2 [type=int]

# We don't have the FD: d --> d::TEXT because d is a composite type.
# For example, d=1 is equal to d=1.0 but d::TEXT differs.
build
SELECT d, d::TEXT FROM xysd
----
project
 ├── columns: d:4(decimal!null) d:7(string!null)
 ├── immutable
 ├── prune: (4,7)
 ├── scan xysd
 │    ├── columns: x:1(int!null) y:2(int) s:3(string) xysd.d:4(decimal!null) crdb_internal_mvcc_timestamp:5(decimal) tableoid:6(oid)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-6), (3,4)~~>(1,2,5,6)
 │    ├── prune: (1-6)
 │    └── interesting orderings: (+1) (-3,+4,+1)
 └── projections
      └── cast: STRING [as=d:7, type=string, outer=(4), immutable]
           └── variable: xysd.d:4 [type=decimal]

# We have the FD d --> d+1.
build
SELECT d, d+1 FROM xysd
----
project
 ├── columns: d:4(decimal!null) "?column?":7(decimal!null)
 ├── immutable
 ├── fd: (4)-->(7)
 ├── prune: (4,7)
 ├── scan xysd
 │    ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) crdb_internal_mvcc_timestamp:5(decimal) tableoid:6(oid)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-6), (3,4)~~>(1,2,5,6)
 │    ├── prune: (1-6)
 │    └── interesting orderings: (+1) (-3,+4,+1)
 └── projections
      └── plus [as="?column?":7, type=decimal, outer=(4), immutable]
           ├── variable: d:4 [type=decimal]
           └── const: 1 [type=decimal]

# We have the equality relation between the synthesized column and the column
# it refers to.
norm
SELECT x, CASE WHEN true THEN x END FROM xysd
----
project
 ├── columns: x:1(int!null) case:7(int!null)
 ├── key: (1)
 ├── fd: (1)==(7), (7)==(1)
 ├── prune: (1,7)
 ├── interesting orderings: (+(1|7))
 ├── scan xysd
 │    ├── columns: x:1(int!null)
 │    ├── key: (1)
 │    ├── prune: (1)
 │    └── interesting orderings: (+1)
 └── projections
      └── variable: x:1 [as=case:7, type=int, outer=(1)]


# Verify that a,b form a key.
norm
SELECT a, b FROM ab WHERE a IS NOT NULL and b IS NOT NULL
----
select
 ├── columns: a:1(int!null) b:2(int!null)
 ├── key: (1,2)
 ├── interesting orderings: (+1,+2)
 ├── scan ab
 │    ├── columns: a:1(int) b:2(int)
 │    ├── lax-key: (1,2)
 │    ├── prune: (1,2)
 │    └── interesting orderings: (+1,+2)
 └── filters
      ├── is-not [type=bool, outer=(1), constraints=(/1: (/NULL - ]; tight)]
      │    ├── variable: a:1 [type=int]
      │    └── null [type=unknown]
      └── is-not [type=bool, outer=(2), constraints=(/2: (/NULL - ]; tight)]
           ├── variable: b:2 [type=int]
           └── null [type=unknown]

norm
SELECT a, b FROM ab WHERE (a, b) IN ((1, 1), (2, 2))
----
select
 ├── columns: a:1(int!null) b:2(int!null)
 ├── cardinality: [0 - 2]
 ├── key: (1,2)
 ├── interesting orderings: (+1,+2)
 ├── scan ab
 │    ├── columns: a:1(int) b:2(int)
 │    ├── lax-key: (1,2)
 │    ├── prune: (1,2)
 │    └── interesting orderings: (+1,+2)
 └── filters
      └── in [type=bool, outer=(1,2), constraints=(/1/2: [/1/1 - /1/1] [/2/2 - /2/2]; /2: [/1 - /1] [/2 - /2]; tight)]
           ├── tuple [type=tuple{int, int}]
           │    ├── variable: a:1 [type=int]
           │    └── variable: b:2 [type=int]
           └── tuple [type=tuple{tuple{int, int}, tuple{int, int}}]
                ├── tuple [type=tuple{int, int}]
                │    ├── const: 1 [type=int]
                │    └── const: 1 [type=int]
                └── tuple [type=tuple{int, int}]
                     ├── const: 2 [type=int]
                     └── const: 2 [type=int]
