exec-ddl
CREATE TABLE xyzs (x INT PRIMARY KEY, y INT, z FLOAT NOT NULL, s STRING, UNIQUE (s DESC, z))
----

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

# Group-by with interesting aggregate expressions.
build
SELECT y, sum(z), x, FALSE, avg(z) FILTER (WHERE z>0), string_agg(DISTINCT s, ',')
FROM xyzs
WHERE s IS NOT NULL
GROUP BY x, y
----
project
 ├── columns: y:2(int) sum:7(float!null) x:1(int!null) bool:12(bool!null) avg:9(float) string_agg:11(string!null)
 ├── key: (1)
 ├── fd: ()-->(12), (1)-->(2,7,9,11)
 ├── prune: (1,2,7,9,11,12)
 ├── interesting orderings: (+1 opt(12))
 ├── group-by (hash)
 │    ├── columns: x:1(int!null) y:2(int) sum:7(float!null) avg:9(float) string_agg:11(string!null)
 │    ├── grouping columns: x:1(int!null) y:2(int)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,7,9,11)
 │    ├── prune: (7,9,11)
 │    ├── interesting orderings: (+1)
 │    ├── project
 │    │    ├── columns: column8:8(bool!null) column10:10(string!null) x:1(int!null) y:2(int) z:3(float!null) s:4(string!null)
 │    │    ├── key: (1)
 │    │    ├── fd: ()-->(10), (1)-->(2-4,8), (3,4)-->(1,2,8), (3)-->(8)
 │    │    ├── prune: (1-4,8,10)
 │    │    ├── interesting orderings: (+1 opt(10)) (-4,+3 opt(10))
 │    │    ├── select
 │    │    │    ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string!null) crdb_internal_mvcc_timestamp:5(decimal) tableoid:6(oid)
 │    │    │    ├── key: (1)
 │    │    │    ├── fd: (1)-->(2-6), (3,4)-->(1,2,5,6)
 │    │    │    ├── prune: (1-3,5,6)
 │    │    │    ├── interesting orderings: (+1) (-4,+3)
 │    │    │    ├── scan xyzs
 │    │    │    │    ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string) 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) (-4,+3,+1)
 │    │    │    └── filters
 │    │    │         └── is-not [type=bool, outer=(4), constraints=(/4: (/NULL - ]; tight)]
 │    │    │              ├── variable: s:4 [type=string]
 │    │    │              └── null [type=unknown]
 │    │    └── projections
 │    │         ├── gt [as=column8:8, type=bool, outer=(3)]
 │    │         │    ├── variable: z:3 [type=float]
 │    │         │    └── const: 0.0 [type=float]
 │    │         └── const: ',' [as=column10:10, type=string]
 │    └── aggregations
 │         ├── sum [as=sum:7, type=float, outer=(3)]
 │         │    └── variable: z:3 [type=float]
 │         ├── agg-filter [as=avg:9, type=float, outer=(3,8)]
 │         │    ├── avg [type=float]
 │         │    │    └── variable: z:3 [type=float]
 │         │    └── variable: column8:8 [type=bool]
 │         └── agg-distinct [as=string_agg:11, type=string, outer=(4,10)]
 │              └── string-agg [type=string]
 │                   ├── variable: s:4 [type=string]
 │                   └── variable: column10:10 [type=string]
 └── projections
      └── false [as=bool:12, type=bool]

# Scalar groupby.
build
SELECT sum(x), max(y), count(x) FROM xyzs
----
scalar-group-by
 ├── columns: sum:7(decimal) max:8(int) count:9(int!null)
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(7-9)
 ├── prune: (7-9)
 ├── project
 │    ├── columns: x:1(int!null) y:2(int)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2)
 │    ├── prune: (1,2)
 │    ├── interesting orderings: (+1)
 │    └── scan xyzs
 │         ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string) 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) (-4,+3,+1)
 └── aggregations
      ├── sum [as=sum:7, type=decimal, outer=(1)]
      │    └── variable: x:1 [type=int]
      ├── max [as=max:8, type=int, outer=(2)]
      │    └── variable: y:2 [type=int]
      └── count [as=count:9, type=int, outer=(1)]
           └── variable: x:1 [type=int]

# Group by unique index columns.
build
SELECT s FROM xyzs GROUP BY z, s
----
project
 ├── columns: s:4(string)
 ├── prune: (4)
 ├── interesting orderings: (-4)
 └── group-by (hash)
      ├── columns: z:3(float!null) s:4(string)
      ├── grouping columns: z:3(float!null) s:4(string)
      ├── key: (3,4)
      ├── interesting orderings: (-4,+3)
      └── project
           ├── columns: z:3(float!null) s:4(string)
           ├── lax-key: (3,4)
           ├── prune: (3,4)
           ├── interesting orderings: (-4,+3)
           └── scan xyzs
                ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string) 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) (-4,+3,+1)

# Group by columns that otherwise wouldn't be weak key.
build
SELECT y, sum(z) FROM xyzs GROUP BY z, y
----
project
 ├── columns: y:2(int) sum:7(float!null)
 ├── prune: (2,7)
 └── group-by (hash)
      ├── columns: y:2(int) z:3(float!null) sum:7(float!null)
      ├── grouping columns: y:2(int) z:3(float!null)
      ├── key: (2,3)
      ├── fd: (2,3)-->(7)
      ├── prune: (7)
      ├── project
      │    ├── columns: y:2(int) z:3(float!null)
      │    ├── prune: (2,3)
      │    └── scan xyzs
      │         ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string) 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) (-4,+3,+1)
      └── aggregations
           └── sum [as=sum:7, type=float, outer=(3)]
                └── variable: z:3 [type=float]

# Group by column that is subset of unique index.
build
SELECT z, max(s) FROM xyzs GROUP BY z
----
group-by (hash)
 ├── columns: z:3(float!null) max:7(string)
 ├── grouping columns: z:3(float!null)
 ├── key: (3)
 ├── fd: (3)-->(7)
 ├── prune: (7)
 ├── project
 │    ├── columns: z:3(float!null) s:4(string)
 │    ├── lax-key: (3,4)
 │    ├── prune: (3,4)
 │    ├── interesting orderings: (-4,+3)
 │    └── scan xyzs
 │         ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string) 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) (-4,+3,+1)
 └── aggregations
      └── max [as=max:7, type=string, outer=(4)]
           └── variable: s:4 [type=string]

# Group by all columns.
build
SELECT s FROM xyzs GROUP BY xyzs.*
----
project
 ├── columns: s:4(string)
 ├── prune: (4)
 ├── interesting orderings: (-4)
 └── group-by (hash)
      ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string)
      ├── grouping columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string)
      ├── key: (1)
      ├── fd: (1)-->(2-4), (3,4)~~>(1,2)
      ├── interesting orderings: (+1) (-4,+3,+1)
      └── project
           ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string)
           ├── key: (1)
           ├── fd: (1)-->(2-4), (3,4)~~>(1,2)
           ├── prune: (1-4)
           ├── interesting orderings: (+1) (-4,+3,+1)
           └── scan xyzs
                ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string) 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) (-4,+3,+1)

# Propagate outer columns.
build
SELECT (SELECT sum(x) FROM (SELECT y, u FROM kuv) GROUP BY u) FROM xyzs GROUP BY y
----
project
 ├── columns: sum:16(decimal)
 ├── prune: (16)
 ├── group-by (hash)
 │    ├── columns: xyzs.y:2(int) sum:14(decimal!null)
 │    ├── grouping columns: xyzs.y:2(int)
 │    ├── key: (2)
 │    ├── fd: (2)-->(14)
 │    ├── prune: (14)
 │    ├── project
 │    │    ├── columns: x:13(int!null) xyzs.y:2(int)
 │    │    ├── key: (13)
 │    │    ├── fd: (13)-->(2)
 │    │    ├── prune: (2,13)
 │    │    ├── interesting orderings: (+13)
 │    │    ├── scan xyzs
 │    │    │    ├── columns: xyzs.x:1(int!null) xyzs.y:2(int) z:3(float!null) s:4(string) xyzs.crdb_internal_mvcc_timestamp:5(decimal) xyzs.tableoid:6(oid)
 │    │    │    ├── key: (1)
 │    │    │    ├── fd: (1)-->(2-6), (3,4)~~>(1,2,5,6)
 │    │    │    ├── prune: (1-6)
 │    │    │    └── interesting orderings: (+1) (-4,+3,+1)
 │    │    └── projections
 │    │         └── variable: xyzs.x:1 [as=x:13, type=int, outer=(1)]
 │    └── aggregations
 │         └── sum [as=sum:14, type=decimal, outer=(13)]
 │              └── variable: x:13 [type=int]
 └── projections
      └── subquery [as=sum:16, type=decimal, outer=(2,14), correlated-subquery]
           └── max1-row
                ├── columns: sum:15(decimal)
                ├── error: "more than one row returned by a subquery used as an expression"
                ├── outer: (2,14)
                ├── cardinality: [0 - 1]
                ├── key: ()
                ├── fd: ()-->(15)
                └── project
                     ├── columns: sum:15(decimal)
                     ├── outer: (2,14)
                     ├── fd: ()-->(15)
                     ├── prune: (15)
                     ├── group-by (hash)
                     │    ├── columns: u:8(float)
                     │    ├── grouping columns: u:8(float)
                     │    ├── outer: (2)
                     │    ├── key: (8)
                     │    └── project
                     │         ├── columns: u:8(float)
                     │         ├── outer: (2)
                     │         ├── prune: (8)
                     │         └── project
                     │              ├── columns: y:12(int) u:8(float)
                     │              ├── outer: (2)
                     │              ├── fd: ()-->(12)
                     │              ├── prune: (8,12)
                     │              ├── 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)
                     │              └── projections
                     │                   └── variable: xyzs.y:2 [as=y:12, type=int, outer=(2)]
                     └── projections
                          └── variable: sum:14 [as=sum:15, type=decimal, outer=(14)]

# Calculate groupby cardinality.
build
SELECT * FROM (VALUES (1), (2), (1), (NULL)) GROUP BY column1
----
group-by (hash)
 ├── columns: column1:1(int)
 ├── grouping columns: column1:1(int)
 ├── cardinality: [1 - 4]
 ├── immutable
 ├── key: (1)
 └── values
      ├── columns: column1:1(int)
      ├── cardinality: [4 - 4]
      ├── immutable
      ├── prune: (1)
      ├── tuple [type=tuple{int}]
      │    └── const: 1 [type=int]
      ├── tuple [type=tuple{int}]
      │    └── const: 2 [type=int]
      ├── tuple [type=tuple{int}]
      │    └── const: 1 [type=int]
      └── tuple [type=tuple{int}]
           └── cast: INT8 [type=int]
                └── null [type=unknown]

# GroupBy with empty grouping columns.
opt
SELECT x, count(y) FROM xyzs GROUP BY x HAVING x=1
----
group-by (streaming)
 ├── columns: x:1(int!null) count:7(int!null)
 ├── cardinality: [0 - 1]
 ├── key: ()
 ├── fd: ()-->(1,7)
 ├── prune: (1,7)
 ├── scan xyzs
 │    ├── columns: x:1(int!null) y:2(int)
 │    ├── constraint: /1: [/1 - /1]
 │    ├── cardinality: [0 - 1]
 │    ├── key: ()
 │    ├── fd: ()-->(1,2)
 │    └── prune: (2)
 └── aggregations
      ├── count [as=count:7, type=int, outer=(2)]
      │    └── variable: y:2 [type=int]
      └── const-agg [as=x:1, type=int, outer=(1)]
           └── variable: x:1 [type=int]


# Even with non-NULL input, some aggregates can still be NULL.
build
SELECT variance(x), stddev(x), corr(x, y)
FROM xyzs
GROUP BY x, y
----
project
 ├── columns: variance:7(decimal) stddev:8(decimal) corr:9(float)
 ├── prune: (7-9)
 └── group-by (hash)
      ├── columns: x:1(int!null) y:2(int) variance:7(decimal) stddev:8(decimal) corr:9(float)
      ├── grouping columns: x:1(int!null) y:2(int)
      ├── key: (1)
      ├── fd: (1)-->(2,7-9)
      ├── prune: (7-9)
      ├── interesting orderings: (+1)
      ├── project
      │    ├── columns: x:1(int!null) y:2(int)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2)
      │    ├── prune: (1,2)
      │    ├── interesting orderings: (+1)
      │    └── scan xyzs
      │         ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string) 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) (-4,+3,+1)
      └── aggregations
           ├── variance [as=variance:7, type=decimal, outer=(1)]
           │    └── variable: x:1 [type=int]
           ├── std-dev [as=stddev:8, type=decimal, outer=(1)]
           │    └── variable: x:1 [type=int]
           └── corr [as=corr:9, type=float, outer=(1,2)]
                ├── variable: x:1 [type=int]
                └── variable: y:2 [type=int]

# Regression test for #84957 - the aggregation output columns should not be
# marked as non-null.
#
# st_makeline with a non-null input of the wrong type (postgres returns null
# here rather than an error).
build
SELECT st_makeline('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))') FROM generate_series(1, 5) g(t) GROUP BY t;
----
project
 ├── columns: st_makeline:3(geometry)
 ├── immutable
 ├── prune: (3)
 └── group-by (hash)
      ├── columns: generate_series:1(int) st_makeline:3(geometry)
      ├── grouping columns: generate_series:1(int)
      ├── immutable
      ├── key: (1)
      ├── fd: (1)-->(3)
      ├── prune: (3)
      ├── project
      │    ├── columns: column2:2(geometry!null) generate_series:1(int)
      │    ├── immutable
      │    ├── fd: ()-->(2)
      │    ├── prune: (1,2)
      │    ├── project-set
      │    │    ├── columns: generate_series:1(int)
      │    │    ├── immutable
      │    │    ├── values
      │    │    │    ├── cardinality: [1 - 1]
      │    │    │    ├── key: ()
      │    │    │    └── tuple [type=tuple]
      │    │    └── zip
      │    │         └── function: generate_series [type=int, immutable]
      │    │              ├── const: 1 [type=int]
      │    │              └── const: 5 [type=int]
      │    └── projections
      │         └── const: '0103000000010000000500000000000000000000000000000000000000000000000000F03F0000000000000000000000000000F03F000000000000F03F0000000000000000000000000000F03F00000000000000000000000000000000' [as=column2:2, type=geometry]
      └── aggregations
           └── s-t-make-line [as=st_makeline:3, type=geometry, outer=(2)]
                └── variable: column2:2 [type=geometry]

# st_extent with an empty (but non-null) input.
build
SELECT st_extent('0101000000000000000000F87F000000000000F87F') FROM generate_series(1, 5) g(t) GROUP BY t;
----
project
 ├── columns: st_extent:3(box2d)
 ├── immutable
 ├── prune: (3)
 └── group-by (hash)
      ├── columns: generate_series:1(int) st_extent:3(box2d)
      ├── grouping columns: generate_series:1(int)
      ├── immutable
      ├── key: (1)
      ├── fd: (1)-->(3)
      ├── prune: (3)
      ├── project
      │    ├── columns: column2:2(geometry!null) generate_series:1(int)
      │    ├── immutable
      │    ├── fd: ()-->(2)
      │    ├── prune: (1,2)
      │    ├── project-set
      │    │    ├── columns: generate_series:1(int)
      │    │    ├── immutable
      │    │    ├── values
      │    │    │    ├── cardinality: [1 - 1]
      │    │    │    ├── key: ()
      │    │    │    └── tuple [type=tuple]
      │    │    └── zip
      │    │         └── function: generate_series [type=int, immutable]
      │    │              ├── const: 1 [type=int]
      │    │              └── const: 5 [type=int]
      │    └── projections
      │         └── const: '0101000000000000000000F87F000000000000F87F' [as=column2:2, type=geometry]
      └── aggregations
           └── s-t-extent [as=st_extent:3, type=box2d, outer=(2)]
                └── variable: column2:2 [type=geometry]
