exec-ddl
CREATE TABLE a (x INT PRIMARY KEY, y INT, z FLOAT NOT NULL, s STRING, UNIQUE (s DESC, z))
----
TABLE a
 ├── x int not null
 ├── y int
 ├── z float not null
 ├── s string
 ├── INDEX primary
 │    └── x int not null
 └── INDEX secondary
      ├── s string desc
      ├── z float not null
      └── x int not null (storing)

exec-ddl
ALTER TABLE a INJECT STATISTICS '[
  {
    "columns": ["x"],
    "created_at": "2018-01-01 1:00:00.00000+00:00",
    "row_count": 2000,
    "distinct_count": 2000
  },
  {
    "columns": ["y"],
    "created_at": "2018-01-01 1:30:00.00000+00:00",
    "row_count": 2000,
    "distinct_count": 400
  },
  {
    "columns": ["s"],
    "created_at": "2018-01-01 1:30:00.00000+00:00",
    "row_count": 2000,
    "distinct_count": 10
  },
  {
    "columns": ["s","y","z"],
    "created_at": "2018-01-01 1:40:00.00000+00:00",
    "row_count": 2000,
    "distinct_count": 600
  }
]'
----

# No aggregate.
build
SELECT x FROM a GROUP BY x, y
----
project
 ├── columns: x:1(int!null)
 ├── stats: [rows=2000]
 ├── key: (1)
 └── group-by
      ├── columns: x:1(int!null) y:2(int)
      ├── grouping columns: x:1(int!null) y:2(int)
      ├── stats: [rows=2000, distinct(1,2)=2000]
      ├── key: (1)
      ├── fd: (1)-->(2)
      └── project
           ├── columns: x:1(int!null) y:2(int)
           ├── stats: [rows=2000, distinct(1,2)=2000]
           ├── key: (1)
           ├── fd: (1)-->(2)
           └── scan a
                ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string)
                ├── stats: [rows=2000, distinct(1,2)=2000]
                ├── key: (1)
                └── fd: (1)-->(2-4), (3,4)~~>(1,2)

# Group by single column key.
build
SELECT max(y) FROM a GROUP BY x
----
project
 ├── columns: max:5(int)
 ├── stats: [rows=2000]
 └── group-by
      ├── columns: x:1(int!null) max:5(int)
      ├── grouping columns: x:1(int!null)
      ├── stats: [rows=2000, distinct(1)=2000]
      ├── key: (1)
      ├── fd: (1)-->(5)
      ├── project
      │    ├── columns: x:1(int!null) y:2(int)
      │    ├── stats: [rows=2000, distinct(1)=2000]
      │    ├── key: (1)
      │    ├── fd: (1)-->(2)
      │    └── scan a
      │         ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string)
      │         ├── stats: [rows=2000, distinct(1)=2000]
      │         ├── key: (1)
      │         └── fd: (1)-->(2-4), (3,4)~~>(1,2)
      └── aggregations [outer=(2)]
           └── max [type=int, outer=(2)]
                └── variable: y [type=int, outer=(2)]

# Group by non-key.
build
SELECT y, sum(z) FROM a GROUP BY y
----
group-by
 ├── columns: y:2(int) sum:5(float)
 ├── grouping columns: y:2(int)
 ├── stats: [rows=400, distinct(2)=400]
 ├── key: (2)
 ├── fd: (2)-->(5)
 ├── project
 │    ├── columns: y:2(int) z:3(float!null)
 │    ├── stats: [rows=2000, distinct(2)=400]
 │    └── scan a
 │         ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string)
 │         ├── stats: [rows=2000, distinct(2)=400]
 │         ├── key: (1)
 │         └── fd: (1)-->(2-4), (3,4)~~>(1,2)
 └── aggregations [outer=(3)]
      └── sum [type=float, outer=(3)]
           └── variable: z [type=float, outer=(3)]

build
SELECT max(x) FROM a GROUP BY y, z, s
----
project
 ├── columns: max:5(int)
 ├── stats: [rows=600]
 └── group-by
      ├── columns: y:2(int) z:3(float!null) s:4(string) max:5(int)
      ├── grouping columns: y:2(int) z:3(float!null) s:4(string)
      ├── stats: [rows=600, distinct(2-4)=600]
      ├── key: (2-4)
      ├── fd: (3,4)~~>(2), (2-4)-->(5)
      ├── scan a
      │    ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string)
      │    ├── stats: [rows=2000, distinct(2-4)=600]
      │    ├── key: (1)
      │    └── fd: (1)-->(2-4), (3,4)~~>(1,2)
      └── aggregations [outer=(1)]
           └── max [type=int, outer=(1)]
                └── variable: x [type=int, outer=(1)]

build
SELECT min(x) FROM a GROUP BY y, z
----
project
 ├── columns: min:5(int)
 ├── stats: [rows=2000]
 └── group-by
      ├── columns: y:2(int) z:3(float!null) min:5(int)
      ├── grouping columns: y:2(int) z:3(float!null)
      ├── stats: [rows=2000, distinct(2,3)=2000]
      ├── key: (2,3)
      ├── fd: (2,3)-->(5)
      ├── project
      │    ├── columns: x:1(int!null) y:2(int) z:3(float!null)
      │    ├── stats: [rows=2000, distinct(2,3)=2000]
      │    ├── key: (1)
      │    ├── fd: (1)-->(2,3)
      │    └── scan a
      │         ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string)
      │         ├── stats: [rows=2000, distinct(2,3)=2000]
      │         ├── key: (1)
      │         └── fd: (1)-->(2-4), (3,4)~~>(1,2)
      └── aggregations [outer=(1)]
           └── min [type=int, outer=(1)]
                └── variable: x [type=int, outer=(1)]

build
SELECT max(x) FROM a GROUP BY y, z, s HAVING s IN ('a', 'b')
----
project
 ├── columns: max:5(int)
 ├── stats: [rows=120]
 └── select
      ├── columns: y:2(int) z:3(float!null) s:4(string!null) max:5(int)
      ├── stats: [rows=120, distinct(4)=2]
      ├── key: (3,4)
      ├── fd: (3,4)-->(2), (2-4)-->(5)
      ├── group-by
      │    ├── columns: y:2(int) z:3(float!null) s:4(string) max:5(int)
      │    ├── grouping columns: y:2(int) z:3(float!null) s:4(string)
      │    ├── stats: [rows=600, distinct(4)=10, distinct(2-4)=600]
      │    ├── key: (2-4)
      │    ├── fd: (3,4)~~>(2), (2-4)-->(5)
      │    ├── scan a
      │    │    ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string)
      │    │    ├── stats: [rows=2000, distinct(4)=10, distinct(2-4)=600]
      │    │    ├── key: (1)
      │    │    └── fd: (1)-->(2-4), (3,4)~~>(1,2)
      │    └── aggregations [outer=(1)]
      │         └── max [type=int, outer=(1)]
      │              └── variable: x [type=int, outer=(1)]
      └── filters [type=bool, outer=(4), constraints=(/4: [/'a' - /'a'] [/'b' - /'b']; tight)]
           └── s IN ('a', 'b') [type=bool, outer=(4), constraints=(/4: [/'a' - /'a'] [/'b' - /'b']; tight)]

# Estimate the distinct count for an aggregate column.
build
SELECT sum(x), s FROM a GROUP BY s HAVING sum(x) = 5
----
select
 ├── columns: sum:5(decimal!null) s:4(string)
 ├── stats: [rows=1, distinct(5)=1]
 ├── key: (4)
 ├── fd: ()-->(5)
 ├── group-by
 │    ├── columns: s:4(string) sum:5(decimal)
 │    ├── grouping columns: s:4(string)
 │    ├── stats: [rows=10, distinct(4)=10, distinct(5)=10]
 │    ├── key: (4)
 │    ├── fd: (4)-->(5)
 │    ├── project
 │    │    ├── columns: x:1(int!null) s:4(string)
 │    │    ├── stats: [rows=2000, distinct(4)=10]
 │    │    ├── key: (1)
 │    │    ├── fd: (1)-->(4)
 │    │    └── scan a
 │    │         ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string)
 │    │         ├── stats: [rows=2000, distinct(4)=10]
 │    │         ├── key: (1)
 │    │         └── fd: (1)-->(2-4), (3,4)~~>(1,2)
 │    └── aggregations [outer=(1)]
 │         └── sum [type=decimal, outer=(1)]
 │              └── variable: x [type=int, outer=(1)]
 └── filters [type=bool, outer=(5), constraints=(/5: [/5 - /5]; tight), fd=()-->(5)]
      └── sum = 5 [type=bool, outer=(5), constraints=(/5: [/5 - /5]; tight)]

# Scalar GroupBy.
build
SELECT max(y), sum(z) FROM a HAVING sum(z) = 5.0
----
select
 ├── columns: max:5(int) sum:6(float!null)
 ├── cardinality: [0 - 1]
 ├── stats: [rows=1, distinct(6)=1]
 ├── key: ()
 ├── fd: ()-->(5,6)
 ├── scalar-group-by
 │    ├── columns: max:5(int) sum:6(float)
 │    ├── cardinality: [1 - 1]
 │    ├── stats: [rows=1, distinct(6)=1]
 │    ├── key: ()
 │    ├── fd: ()-->(5,6)
 │    ├── project
 │    │    ├── columns: y:2(int) z:3(float!null)
 │    │    ├── stats: [rows=2000]
 │    │    └── scan a
 │    │         ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string)
 │    │         ├── stats: [rows=2000]
 │    │         ├── key: (1)
 │    │         └── fd: (1)-->(2-4), (3,4)~~>(1,2)
 │    └── aggregations [outer=(2,3)]
 │         ├── max [type=int, outer=(2)]
 │         │    └── variable: y [type=int, outer=(2)]
 │         └── sum [type=float, outer=(3)]
 │              └── variable: z [type=float, outer=(3)]
 └── filters [type=bool, outer=(6), constraints=(/6: [/5.0 - /5.0]; tight), fd=()-->(6)]
      └── sum = 5.0 [type=bool, outer=(6), constraints=(/6: [/5.0 - /5.0]; tight)]
