exec-ddl
CREATE TABLE xy (x INT PRIMARY KEY, y INT)
----

opt colstat=1 colstat=2 colstat=3 colstat=4 colstat=5
SELECT a.*, b.*, c.* FROM upper('abc') a
JOIN ROWS FROM (upper('def'), generate_series(1, 3), upper('ghi')) b ON true
JOIN generate_series(1, 4) c ON true
----
inner-join (cross)
 ├── columns: a:1(string) upper:2(string) generate_series:3(int) upper:4(string) c:5(int)
 ├── immutable
 ├── stats: [rows=100, distinct(1)=1, null(1)=0, distinct(2)=1, null(2)=90, distinct(3)=7, null(3)=1, distinct(4)=1, null(4)=90, distinct(5)=7, null(5)=1]
 ├── inner-join (cross)
 │    ├── columns: upper:1(string) upper:2(string) generate_series:3(int) upper:4(string)
 │    ├── immutable
 │    ├── stats: [rows=10, distinct(1)=1, null(1)=0, distinct(2)=1, null(2)=9, distinct(3)=7, null(3)=0.1, distinct(4)=1, null(4)=9]
 │    ├── project-set
 │    │    ├── columns: upper:2(string) generate_series:3(int) upper:4(string)
 │    │    ├── immutable
 │    │    ├── stats: [rows=10, distinct(2)=1, null(2)=9, distinct(3)=7, null(3)=0.1, distinct(4)=1, null(4)=9]
 │    │    ├── values
 │    │    │    ├── cardinality: [1 - 1]
 │    │    │    ├── stats: [rows=1]
 │    │    │    ├── key: ()
 │    │    │    └── () [type=tuple]
 │    │    └── zip
 │    │         ├── 'DEF' [type=string]
 │    │         ├── generate_series(1, 3) [type=int, immutable]
 │    │         └── 'GHI' [type=string]
 │    ├── project-set
 │    │    ├── columns: upper:1(string)
 │    │    ├── stats: [rows=1, distinct(1)=1, null(1)=0]
 │    │    ├── values
 │    │    │    ├── cardinality: [1 - 1]
 │    │    │    ├── stats: [rows=1]
 │    │    │    ├── key: ()
 │    │    │    └── () [type=tuple]
 │    │    └── zip
 │    │         └── 'ABC' [type=string]
 │    └── filters (true)
 ├── project-set
 │    ├── columns: generate_series:5(int)
 │    ├── immutable
 │    ├── stats: [rows=10, distinct(5)=7, null(5)=0.1]
 │    ├── values
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── stats: [rows=1]
 │    │    ├── key: ()
 │    │    └── () [type=tuple]
 │    └── zip
 │         └── generate_series(1, 4) [type=int, immutable]
 └── filters (true)

opt
SELECT * FROM (SELECT * FROM upper('abc') a, generate_series(1, 2) b) GROUP BY a, b
----
distinct-on
 ├── columns: a:1(string) b:2(int)
 ├── grouping columns: upper:1(string) generate_series:2(int)
 ├── immutable
 ├── stats: [rows=7, distinct(1,2)=7, null(1,2)=0]
 ├── key: (1,2)
 └── inner-join (cross)
      ├── columns: upper:1(string) generate_series:2(int)
      ├── immutable
      ├── stats: [rows=10, distinct(1,2)=7, null(1,2)=0]
      ├── project-set
      │    ├── columns: generate_series:2(int)
      │    ├── immutable
      │    ├── stats: [rows=10, distinct(2)=7, null(2)=0.1]
      │    ├── values
      │    │    ├── cardinality: [1 - 1]
      │    │    ├── stats: [rows=1]
      │    │    ├── key: ()
      │    │    └── () [type=tuple]
      │    └── zip
      │         └── generate_series(1, 2) [type=int, immutable]
      ├── project-set
      │    ├── columns: upper:1(string)
      │    ├── stats: [rows=1, distinct(1)=1, null(1)=0]
      │    ├── values
      │    │    ├── cardinality: [1 - 1]
      │    │    ├── stats: [rows=1]
      │    │    ├── key: ()
      │    │    └── () [type=tuple]
      │    └── zip
      │         └── 'ABC' [type=string]
      └── filters (true)

opt colstat=3 colstat=(1,2,3)
SELECT unnest(ARRAY[x,y]) FROM xy
----
project
 ├── columns: unnest:5(int)
 ├── stats: [rows=2000, distinct(3)=1, null(3)=2000, distinct(1-3)=1000, null(1-3)=0]
 └── inner-join-apply
      ├── columns: x:1(int!null) y:2(int) unnest:5(int)
      ├── stats: [rows=2000, distinct(1,2)=1000, null(1,2)=0]
      ├── fd: (1)-->(2)
      ├── scan xy
      │    ├── columns: x:1(int!null) y:2(int)
      │    ├── stats: [rows=1000, distinct(1,2)=1000, null(1,2)=0]
      │    ├── key: (1)
      │    └── fd: (1)-->(2)
      ├── values
      │    ├── columns: unnest:5(int)
      │    ├── outer: (1,2)
      │    ├── cardinality: [2 - 2]
      │    ├── stats: [rows=2]
      │    ├── (x:1,) [type=tuple{int}]
      │    └── (y:2,) [type=tuple{int}]
      └── filters (true)

opt colstat=5 colstat=6 colstat=(5, 6) colstat=(1, 5) colstat=(2, 6)
SELECT xy.*, generate_series(x, y), generate_series(0, 1) FROM xy
----
project-set
 ├── columns: x:1(int!null) y:2(int) generate_series:5(int) generate_series:6(int)
 ├── immutable
 ├── stats: [rows=10000, distinct(5)=700, null(5)=100, distinct(6)=7, null(6)=100, distinct(1,5)=10000, null(1,5)=0, distinct(2,6)=700, null(2,6)=1, distinct(5,6)=4900, null(5,6)=1]
 ├── fd: (1)-->(2)
 ├── scan xy
 │    ├── columns: x:1(int!null) y:2(int)
 │    ├── stats: [rows=1000, distinct(1)=1000, null(1)=0, distinct(2)=100, null(2)=10]
 │    ├── key: (1)
 │    └── fd: (1)-->(2)
 └── zip
      ├── generate_series(x:1, y:2) [type=int, outer=(1,2), immutable]
      └── generate_series(0, 1) [type=int, immutable]

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
)
----

# The following queries test the statistics for four different types of Zip
# functions:
#   1. correlated scalar functions -- upper(title)
#   2. correlated generator functions -- unnest(tag_list)
#   4. uncorrelated scalar functions -- lower('ABC')
#   3. uncorrelated generator functions -- generate_series(0,1)
#
# They need to be tested with different queries at the moment due to
# limitations with our testing infrastructure.

opt
SELECT id FROM articles WHERE title = ANY(
  SELECT upper 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)
 ├── immutable
 ├── stats: [rows=9.856012, distinct(1)=9.85601, null(1)=0]
 ├── key: (1)
 └── select
      ├── columns: id:1(int!null) title:4(string!null) tag_list:6(string[]) upper:12(string!null) unnest:13(string) generate_series:14(int) lower:15(string)
      ├── immutable
      ├── stats: [rows=9.9, distinct(1)=9.85601, null(1)=0, distinct(4)=9.9, null(4)=0, distinct(12)=9.9, null(12)=0]
      ├── fd: (1)-->(4,6), (4)==(12), (12)==(4)
      ├── project-set
      │    ├── columns: id:1(int!null) title:4(string) tag_list:6(string[]) upper:12(string) unnest:13(string) generate_series:14(int) lower:15(string)
      │    ├── immutable
      │    ├── stats: [rows=10000, distinct(1)=1000, null(1)=0, distinct(4)=100, null(4)=100, distinct(12)=100, null(12)=9000]
      │    ├── fd: (1)-->(4,6)
      │    ├── scan articles
      │    │    ├── columns: id:1(int!null) title:4(string) tag_list:6(string[])
      │    │    ├── stats: [rows=1000, distinct(1)=1000, null(1)=0, distinct(4)=100, null(4)=10]
      │    │    ├── key: (1)
      │    │    └── fd: (1)-->(4,6)
      │    └── zip
      │         ├── upper(title:4) [type=string, outer=(4), immutable]
      │         ├── unnest(tag_list:6) [type=string, outer=(6), immutable]
      │         ├── generate_series(0, 1) [type=int, immutable]
      │         └── 'abc' [type=string]
      └── filters
           └── title:4 = upper:12 [type=bool, outer=(4,12), constraints=(/4: (/NULL - ]; /12: (/NULL - ]), fd=(4)==(12), (12)==(4)]

opt
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)
 ├── immutable
 ├── stats: [rows=13.91354, distinct(1)=13.9135, null(1)=0]
 ├── key: (1)
 └── select
      ├── columns: id:1(int!null) title:4(string!null) tag_list:6(string[]) upper:12(string) unnest:13(string!null) generate_series:14(int) lower:15(string)
      ├── immutable
      ├── stats: [rows=14.00143, distinct(1)=13.9135, null(1)=0, distinct(4)=14.0014, null(4)=0, distinct(13)=14.0014, null(13)=0]
      ├── fd: (1)-->(4,6), (4)==(13), (13)==(4)
      ├── project-set
      │    ├── columns: id:1(int!null) title:4(string) tag_list:6(string[]) upper:12(string) unnest:13(string) generate_series:14(int) lower:15(string)
      │    ├── immutable
      │    ├── stats: [rows=10000, distinct(1)=1000, null(1)=0, distinct(4)=100, null(4)=100, distinct(13)=700, null(13)=100]
      │    ├── fd: (1)-->(4,6)
      │    ├── scan articles
      │    │    ├── columns: id:1(int!null) title:4(string) tag_list:6(string[])
      │    │    ├── stats: [rows=1000, distinct(1)=1000, null(1)=0, distinct(4)=100, null(4)=10]
      │    │    ├── key: (1)
      │    │    └── fd: (1)-->(4,6)
      │    └── zip
      │         ├── upper(title:4) [type=string, outer=(4), immutable]
      │         ├── unnest(tag_list:6) [type=string, outer=(6), immutable]
      │         ├── generate_series(0, 1) [type=int, immutable]
      │         └── 'abc' [type=string]
      └── filters
           └── title:4 = unnest:13 [type=bool, outer=(4,13), constraints=(/4: (/NULL - ]; /13: (/NULL - ]), fd=(4)==(13), (13)==(4)]

opt
SELECT id FROM articles WHERE id = ANY(
  SELECT generate_series 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)
 ├── immutable
 ├── stats: [rows=6, distinct(1)=6, null(1)=0]
 ├── key: (1)
 └── select
      ├── columns: id:1(int!null) title:4(string) tag_list:6(string[]) upper:12(string) unnest:13(string) generate_series:14(int!null) lower:15(string)
      ├── immutable
      ├── stats: [rows=9.9, distinct(1)=6, null(1)=0, distinct(14)=6, null(14)=0]
      ├── fd: (1)-->(4,6), (1)==(14), (14)==(1)
      ├── project-set
      │    ├── columns: id:1(int!null) title:4(string) tag_list:6(string[]) upper:12(string) unnest:13(string) generate_series:14(int) lower:15(string)
      │    ├── immutable
      │    ├── stats: [rows=10000, distinct(1)=1000, null(1)=0, distinct(14)=7, null(14)=100]
      │    ├── fd: (1)-->(4,6)
      │    ├── scan articles
      │    │    ├── columns: id:1(int!null) title:4(string) tag_list:6(string[])
      │    │    ├── stats: [rows=1000, distinct(1)=1000, null(1)=0]
      │    │    ├── key: (1)
      │    │    └── fd: (1)-->(4,6)
      │    └── zip
      │         ├── upper(title:4) [type=string, outer=(4), immutable]
      │         ├── unnest(tag_list:6) [type=string, outer=(6), immutable]
      │         ├── generate_series(0, 1) [type=int, immutable]
      │         └── 'abc' [type=string]
      └── filters
           └── id:1 = generate_series:14 [type=bool, outer=(1,14), constraints=(/1: (/NULL - ]; /14: (/NULL - ]), fd=(1)==(14), (14)==(1)]

opt
SELECT id FROM articles WHERE title = ANY(
  SELECT lower 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)
 ├── immutable
 ├── stats: [rows=9.856012, distinct(1)=9.85601, null(1)=0]
 ├── key: (1)
 └── select
      ├── columns: id:1(int!null) title:4(string!null) tag_list:6(string[]) upper:12(string) unnest:13(string) generate_series:14(int) lower:15(string!null)
      ├── immutable
      ├── stats: [rows=9.9, distinct(1)=9.85601, null(1)=0, distinct(4)=1e-10, null(4)=0, distinct(15)=1e-10, null(15)=0]
      ├── fd: (1)-->(4,6), (4)==(15), (15)==(4)
      ├── project-set
      │    ├── columns: id:1(int!null) title:4(string) tag_list:6(string[]) upper:12(string) unnest:13(string) generate_series:14(int) lower:15(string)
      │    ├── immutable
      │    ├── stats: [rows=10000, distinct(1)=1000, null(1)=0, distinct(4)=100, null(4)=100, distinct(15)=1, null(15)=9000]
      │    ├── fd: (1)-->(4,6)
      │    ├── scan articles
      │    │    ├── columns: id:1(int!null) title:4(string) tag_list:6(string[])
      │    │    ├── stats: [rows=1000, distinct(1)=1000, null(1)=0, distinct(4)=100, null(4)=10]
      │    │    ├── key: (1)
      │    │    └── fd: (1)-->(4,6)
      │    └── zip
      │         ├── upper(title:4) [type=string, outer=(4), immutable]
      │         ├── unnest(tag_list:6) [type=string, outer=(6), immutable]
      │         ├── generate_series(0, 1) [type=int, immutable]
      │         └── 'abc' [type=string]
      └── filters
           └── title:4 = lower:15 [type=bool, outer=(4,15), constraints=(/4: (/NULL - ]; /15: (/NULL - ]), fd=(4)==(15), (15)==(4)]
