exec-ddl
CREATE TABLE a (x INT PRIMARY KEY, y INT, s STRING, d DECIMAL NOT NULL, b BOOL, UNIQUE (s DESC, d))
----
TABLE a
 ├── x int not null
 ├── y int
 ├── s string
 ├── d decimal not null
 ├── b bool
 ├── INDEX primary
 │    └── x int not null
 └── INDEX secondary
      ├── s string desc
      ├── d decimal 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
  }
]'
----

build
SELECT * FROM a
----
scan a
 ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) b:5(bool)
 ├── stats: [rows=2000]
 ├── key: (1)
 └── fd: (1)-->(2-5), (3,4)~~>(1,2,5)

# Check that boolean columns have distinct count 2 when there are no stats
# available.
build
SELECT * FROM a WHERE b
----
select
 ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) b:5(bool!null)
 ├── stats: [rows=990, distinct(1)=990, null(1)=0, distinct(4)=199.804688, null(4)=0, distinct(5)=1, null(5)=0]
 ├── key: (1)
 ├── fd: ()-->(5), (1)-->(2-4), (3,4)~~>(1,2)
 ├── scan a
 │    ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) b:5(bool)
 │    ├── stats: [rows=2000, distinct(1)=2000, null(1)=0, distinct(4)=200, null(4)=0, distinct(5)=2, null(5)=20]
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5), (3,4)~~>(1,2,5)
 └── filters
      └── variable: b [type=bool, outer=(5), constraints=(/5: [/true - /true]; tight), fd=()-->(5)]

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": ["x","y"],
    "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": ["y"],
    "created_at": "2018-01-01 2:00:00.00000+00:00",
    "row_count": 3000,
    "distinct_count": 500
  },
  {
    "columns": ["s"],
    "created_at": "2018-01-01 2:00:00.00000+00:00",
    "row_count": 3000,
    "distinct_count": 2
  }
]'
----

build
SELECT * FROM a
----
scan a
 ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) b:5(bool)
 ├── stats: [rows=3000]
 ├── key: (1)
 └── fd: (1)-->(2-5), (3,4)~~>(1,2,5)

# Test constrained scan.
opt
SELECT s, x FROM a WHERE x > 0 AND x <= 100
----
scan a
 ├── columns: s:3(string) x:1(int!null)
 ├── constraint: /1: [/1 - /100]
 ├── stats: [rows=150, distinct(1)=100, null(1)=0]
 ├── key: (1)
 └── fd: (1)-->(3)

# Test limited scan.
opt
SELECT s, x FROM a WHERE x > 5 AND x <= 10 LIMIT 2
----
scan a
 ├── columns: s:3(string) x:1(int!null)
 ├── constraint: /1: [/6 - /10]
 ├── limit: 2
 ├── stats: [rows=2]
 ├── key: (1)
 └── fd: (1)-->(3)

opt
SELECT count(*), y, x FROM a WHERE x > 0 AND x <= 100 GROUP BY x, y
----
group-by
 ├── columns: count:6(int) y:2(int) x:1(int!null)
 ├── grouping columns: x:1(int!null)
 ├── internal-ordering: +1
 ├── stats: [rows=100, distinct(1)=100, null(1)=0]
 ├── key: (1)
 ├── fd: (1)-->(2,6)
 ├── scan a
 │    ├── columns: x:1(int!null) y:2(int)
 │    ├── constraint: /1: [/1 - /100]
 │    ├── stats: [rows=150, distinct(1)=100, null(1)=0]
 │    ├── key: (1)
 │    ├── fd: (1)-->(2)
 │    └── ordering: +1
 └── aggregations
      ├── count-rows [type=int]
      └── const-agg [type=int, outer=(2)]
           └── variable: y [type=int]

# Test calculation of multi-column stats.
opt
SELECT y, s FROM a GROUP BY y, s
----
distinct-on
 ├── columns: y:2(int) s:3(string)
 ├── grouping columns: y:2(int) s:3(string)
 ├── stats: [rows=1000, distinct(2,3)=1000, null(2,3)=0]
 ├── key: (2,3)
 └── scan a
      ├── columns: y:2(int) s:3(string)
      └── stats: [rows=3000, distinct(2,3)=1000, null(2,3)=0]

opt
SELECT s, d, x FROM a WHERE (s <= 'aaa') OR (s >= 'bar' AND s <= 'foo')
----
select
 ├── columns: s:3(string) d:4(decimal!null) x:1(int!null)
 ├── stats: [rows=1000, distinct(1)=911.337892, null(1)=0, distinct(4)=294.797541, null(4)=0]
 ├── key: (1)
 ├── fd: (1)-->(3,4), (3,4)~~>(1)
 ├── scan a@secondary
 │    ├── columns: x:1(int!null) s:3(string!null) d:4(decimal!null)
 │    ├── constraint: /-3/4: [/'foo' - /'bar'] [/'aaa' - /NULL)
 │    ├── stats: [rows=1000, distinct(1)=911.337892, null(1)=0, distinct(3)=2, null(3)=0, distinct(4)=294.797541, null(4)=0]
 │    ├── key: (1)
 │    └── fd: (1)-->(3,4), (3,4)-->(1)
 └── filters
      └── (s <= 'aaa') OR (s >= 'bar') [type=bool, outer=(3)]

opt
SELECT s, d, x FROM a WHERE (s <= 'aaa') OR (s >= 'bar' AND s <= 'foo') OR s IS NULL
----
select
 ├── columns: s:3(string) d:4(decimal!null) x:1(int!null)
 ├── stats: [rows=1000, distinct(1)=911.337892, null(1)=0, distinct(4)=294.797541, null(4)=0]
 ├── key: (1)
 ├── fd: (1)-->(3,4), (3,4)~~>(1)
 ├── scan a@secondary
 │    ├── columns: x:1(int!null) s:3(string) d:4(decimal!null)
 │    ├── constraint: /-3/4: [/'foo' - /'bar'] [/'aaa' - /NULL]
 │    ├── stats: [rows=1000, distinct(1)=911.337892, null(1)=0, distinct(4)=294.797541, null(4)=0]
 │    ├── key: (1)
 │    └── fd: (1)-->(3,4), (3,4)~~>(1)
 └── filters
      └── ((s <= 'aaa') OR ((s >= 'bar') AND (s <= 'foo'))) OR (s IS NULL) [type=bool, outer=(3)]

opt
SELECT s, d, x FROM a WHERE s IS NOT NULL
----
scan a@secondary
 ├── columns: s:3(string!null) d:4(decimal!null) x:1(int!null)
 ├── constraint: /-3/4: [ - /NULL)
 ├── stats: [rows=3000, distinct(1)=2000, null(1)=0, distinct(3)=2, null(3)=0, distinct(4)=300, null(4)=0]
 ├── key: (1)
 └── fd: (1)-->(3,4), (3,4)-->(1)

opt
SELECT s, d, x FROM a WHERE (s >= 'bar' AND s <= 'foo') OR (s >= 'foobar')
----
select
 ├── columns: s:3(string) d:4(decimal!null) x:1(int!null)
 ├── stats: [rows=1000, distinct(1)=911.337892, null(1)=0, distinct(4)=294.797541, null(4)=0]
 ├── key: (1)
 ├── fd: (1)-->(3,4), (3,4)~~>(1)
 ├── scan a@secondary
 │    ├── columns: x:1(int!null) s:3(string!null) d:4(decimal!null)
 │    ├── constraint: /-3/4: [ - /'foobar'] [/'foo' - /'bar']
 │    ├── stats: [rows=1000, distinct(1)=911.337892, null(1)=0, distinct(3)=2, null(3)=0, distinct(4)=294.797541, null(4)=0]
 │    ├── key: (1)
 │    └── fd: (1)-->(3,4), (3,4)-->(1)
 └── filters
      └── (s <= 'foo') OR (s >= 'foobar') [type=bool, outer=(3)]

opt
SELECT * FROM a WHERE ((s >= 'bar' AND s <= 'foo') OR (s >= 'foobar')) AND d > 5.0
----
index-join a
 ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) b:5(bool)
 ├── stats: [rows=333.333333, distinct(1)=323.895037, null(1)=0, distinct(4)=207.616156, null(4)=0]
 ├── key: (1)
 ├── fd: (1)-->(2-5), (3,4)~~>(1,2,5)
 └── select
      ├── columns: x:1(int!null) s:3(string!null) d:4(decimal!null)
      ├── stats: [rows=111.111111, distinct(1)=110.489355, null(1)=0, distinct(3)=2, null(3)=0, distinct(4)=97.0976681, null(4)=0]
      ├── key: (1)
      ├── fd: (1)-->(3,4), (3,4)-->(1)
      ├── scan a@secondary
      │    ├── columns: x:1(int!null) s:3(string!null) d:4(decimal!null)
      │    ├── constraint: /-3/4: [ - /'foobar'] (/'foo'/5.0 - /'bar']
      │    ├── stats: [rows=1000, distinct(1)=911.337892, null(1)=0, distinct(3)=2, null(3)=0, distinct(4)=294.797541, null(4)=0]
      │    ├── key: (1)
      │    └── fd: (1)-->(3,4), (3,4)-->(1)
      └── filters
           ├── (s <= 'foo') OR (s >= 'foobar') [type=bool, outer=(3)]
           └── d > 5.0 [type=bool, outer=(4), constraints=(/4: (/5.0 - ]; tight)]

opt
SELECT * FROM a WHERE ((s >= 'bar' AND s <= 'foo') OR (s >= 'foobar')) AND d <= 5.0 AND s IS NOT NULL
----
index-join a
 ├── columns: x:1(int!null) y:2(int) s:3(string!null) d:4(decimal!null) b:5(bool)
 ├── stats: [rows=333.333333, distinct(1)=323.895037, null(1)=0, distinct(3)=2, null(3)=0, distinct(4)=207.616156, null(4)=0]
 ├── key: (1)
 ├── fd: (1)-->(2-5), (3,4)-->(1,2,5)
 └── select
      ├── columns: x:1(int!null) s:3(string!null) d:4(decimal!null)
      ├── stats: [rows=37.037037, distinct(1)=36.9747958, null(1)=0, distinct(3)=1.99999999, null(3)=0, distinct(4)=35.7721483, null(4)=0]
      ├── key: (1)
      ├── fd: (1)-->(3,4), (3,4)-->(1)
      ├── scan a@secondary
      │    ├── columns: x:1(int!null) s:3(string!null) d:4(decimal!null)
      │    ├── constraint: /-3/4: [ - /'foobar'/5.0] [/'foo' - /'bar'/5.0]
      │    ├── stats: [rows=333.333333, distinct(1)=323.895037, null(1)=0, distinct(3)=2, null(3)=0, distinct(4)=207.616156, null(4)=0]
      │    ├── key: (1)
      │    └── fd: (1)-->(3,4), (3,4)-->(1)
      └── filters
           ├── (s <= 'foo') OR (s >= 'foobar') [type=bool, outer=(3)]
           └── d <= 5.0 [type=bool, outer=(4), constraints=(/4: (/NULL - /5.0]; tight)]

# Bump up null counts.

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": ["x","y"],
    "created_at": "2018-01-01 1:00:00.00000+00:00",
    "row_count": 2000,
    "distinct_count": 2000,
    "null_count": 300
  },
  {
    "columns": ["y"],
    "created_at": "2018-01-01 1:30:00.00000+00:00",
    "row_count": 2000,
    "distinct_count": 400,
    "null_count": 800
  },
  {
    "columns": ["y"],
    "created_at": "2018-01-01 2:00:00.00000+00:00",
    "row_count": 3000,
    "distinct_count": 500,
    "null_count": 1000
  },
  {
    "columns": ["s"],
    "created_at": "2018-01-01 2:00:00.00000+00:00",
    "row_count": 3000,
    "distinct_count": 2,
    "null_count": 1000
  },
  {
    "columns": ["b"],
    "created_at": "2018-01-01 2:00:00.00000+00:00",
    "row_count": 3000,
    "distinct_count": 2,
    "null_count": 1500
  }
]'
----

# Test calculation of multi-column stats.
opt colstat=2 colstat=3 colstat=5 colstat=(2,3,5) colstat=(2,3) colstat=(3,5)
SELECT y,s,b FROM a
----
scan a
 ├── columns: y:2(int) s:3(string) b:5(bool)
 └── stats: [rows=3000, distinct(2)=500, null(2)=1000, distinct(3)=2, null(3)=1000, distinct(5)=2, null(5)=1500, distinct(2,3)=1000, null(2,3)=1666.66667, distinct(3,5)=4, null(3,5)=2000, distinct(2,3,5)=2000, null(2,3,5)=2333.33333]

opt colstat=1 colstat=3 colstat=5 colstat=(1,3,5) colstat=(1,3) colstat=(3,5)
SELECT x,y,s FROM a
----
scan a
 ├── columns: x:1(int!null) y:2(int) s:3(string)
 ├── stats: [rows=3000, distinct(1)=2000, null(1)=0, distinct(3)=2, null(3)=1000, distinct(5)=2, null(5)=1500, distinct(1,3)=2000, null(1,3)=1000, distinct(3,5)=4, null(3,5)=2000, distinct(1,3,5)=1000, null(1,3,5)=2000]
 ├── key: (1)
 └── fd: (1)-->(2,3)

opt
SELECT y, s FROM a GROUP BY y, s
----
distinct-on
 ├── columns: y:2(int) s:3(string)
 ├── grouping columns: y:2(int) s:3(string)
 ├── stats: [rows=1000, distinct(2,3)=1000, null(2,3)=1000]
 ├── key: (2,3)
 └── scan a
      ├── columns: y:2(int) s:3(string)
      └── stats: [rows=3000, distinct(2,3)=1000, null(2,3)=1666.66667]

opt
SELECT s, d, x FROM a WHERE ((s <= 'aaa') OR (s >= 'bar' AND s <= 'foo')) AND s IS NOT NULL
----
select
 ├── columns: s:3(string!null) d:4(decimal!null) x:1(int!null)
 ├── stats: [rows=666.666667, distinct(1)=666.666667, null(1)=0, distinct(3)=2, null(3)=0, distinct(4)=294.797541, null(4)=0]
 ├── key: (1)
 ├── fd: (1)-->(3,4), (3,4)-->(1)
 ├── scan a@secondary
 │    ├── columns: x:1(int!null) s:3(string!null) d:4(decimal!null)
 │    ├── constraint: /-3/4: [/'foo' - /'bar'] [/'aaa' - /NULL)
 │    ├── stats: [rows=666.666667, distinct(1)=666.666667, null(1)=0, distinct(3)=2, null(3)=0, distinct(4)=294.797541, null(4)=0]
 │    ├── key: (1)
 │    └── fd: (1)-->(3,4), (3,4)-->(1)
 └── filters
      └── (s <= 'aaa') OR (s >= 'bar') [type=bool, outer=(3)]

opt
SELECT s, d, x FROM a WHERE (s <= 'aaa') OR (s >= 'bar' AND s <= 'foo') OR s IS NULL
----
select
 ├── columns: s:3(string) d:4(decimal!null) x:1(int!null)
 ├── stats: [rows=1000, distinct(1)=911.337892, null(1)=0, distinct(4)=294.797541, null(4)=0]
 ├── key: (1)
 ├── fd: (1)-->(3,4), (3,4)~~>(1)
 ├── scan a@secondary
 │    ├── columns: x:1(int!null) s:3(string) d:4(decimal!null)
 │    ├── constraint: /-3/4: [/'foo' - /'bar'] [/'aaa' - /NULL]
 │    ├── stats: [rows=1000, distinct(1)=911.337892, null(1)=0, distinct(4)=294.797541, null(4)=0]
 │    ├── key: (1)
 │    └── fd: (1)-->(3,4), (3,4)~~>(1)
 └── filters
      └── ((s <= 'aaa') OR ((s >= 'bar') AND (s <= 'foo'))) OR (s IS NULL) [type=bool, outer=(3)]

opt
SELECT s, d, x FROM a WHERE s IS NOT NULL
----
scan a@secondary
 ├── columns: s:3(string!null) d:4(decimal!null) x:1(int!null)
 ├── constraint: /-3/4: [ - /NULL)
 ├── stats: [rows=2000, distinct(1)=2000, null(1)=0, distinct(3)=2, null(3)=0, distinct(4)=300, null(4)=0]
 ├── key: (1)
 └── fd: (1)-->(3,4), (3,4)-->(1)

opt
SELECT s, d, x FROM a WHERE ((s >= 'bar' AND s <= 'foo') OR (s >= 'foobar')) AND s IS NOT NULL
----
select
 ├── columns: s:3(string!null) d:4(decimal!null) x:1(int!null)
 ├── stats: [rows=666.666667, distinct(1)=666.666667, null(1)=0, distinct(3)=2, null(3)=0, distinct(4)=294.797541, null(4)=0]
 ├── key: (1)
 ├── fd: (1)-->(3,4), (3,4)-->(1)
 ├── scan a@secondary
 │    ├── columns: x:1(int!null) s:3(string!null) d:4(decimal!null)
 │    ├── constraint: /-3/4: [ - /'foobar'] [/'foo' - /'bar']
 │    ├── stats: [rows=666.666667, distinct(1)=666.666667, null(1)=0, distinct(3)=2, null(3)=0, distinct(4)=294.797541, null(4)=0]
 │    ├── key: (1)
 │    └── fd: (1)-->(3,4), (3,4)-->(1)
 └── filters
      └── (s <= 'foo') OR (s >= 'foobar') [type=bool, outer=(3)]

opt
SELECT * FROM a WHERE ((s >= 'bar' AND s <= 'foo') OR (s >= 'foobar')) AND d <= 5.0 AND s IS NOT NULL
----
index-join a
 ├── columns: x:1(int!null) y:2(int) s:3(string!null) d:4(decimal!null) b:5(bool)
 ├── stats: [rows=222.222222, distinct(1)=222.222222, null(1)=0, distinct(3)=2, null(3)=0, distinct(4)=207.616156, null(4)=0]
 ├── key: (1)
 ├── fd: (1)-->(2-5), (3,4)-->(1,2,5)
 └── select
      ├── columns: x:1(int!null) s:3(string!null) d:4(decimal!null)
      ├── stats: [rows=24.691358, distinct(1)=24.691358, null(1)=0, distinct(3)=1.99999586, null(3)=0, distinct(4)=24.5913408, null(4)=0]
      ├── key: (1)
      ├── fd: (1)-->(3,4), (3,4)-->(1)
      ├── scan a@secondary
      │    ├── columns: x:1(int!null) s:3(string!null) d:4(decimal!null)
      │    ├── constraint: /-3/4: [ - /'foobar'/5.0] [/'foo' - /'bar'/5.0]
      │    ├── stats: [rows=222.222222, distinct(1)=222.222222, null(1)=0, distinct(3)=2, null(3)=0, distinct(4)=207.616156, null(4)=0]
      │    ├── key: (1)
      │    └── fd: (1)-->(3,4), (3,4)-->(1)
      └── filters
           ├── (s <= 'foo') OR (s >= 'foobar') [type=bool, outer=(3)]
           └── d <= 5.0 [type=bool, outer=(4), constraints=(/4: (/NULL - /5.0]; tight)]

exec-ddl
CREATE TABLE abcde (
  a INT PRIMARY KEY,
  b INT,
  c STRING,
  d INT,
  e INT,
  INDEX bad(b, d),
  INDEX good(b, c, d)
)
----
TABLE abcde
 ├── a int not null
 ├── b int
 ├── c string
 ├── d int
 ├── e int
 ├── INDEX primary
 │    └── a int not null
 ├── INDEX bad
 │    ├── b int
 │    ├── d int
 │    └── a int not null
 └── INDEX good
      ├── b int
      ├── c string
      ├── d int
      └── a int not null

# Regression test for #31929. Ensure that the good index is chosen.
opt
SELECT * FROM abcde WHERE b = 1 AND c LIKE '+1-1000%'
----
index-join abcde
 ├── columns: a:1(int!null) b:2(int!null) c:3(string!null) d:4(int) e:5(int)
 ├── stats: [rows=1.089, distinct(1)=1.089, null(1)=0, distinct(2)=1, null(2)=0, distinct(3)=1.089, null(3)=0]
 ├── key: (1)
 ├── fd: ()-->(2), (1)-->(3-5)
 └── scan abcde@good
      ├── columns: a:1(int!null) b:2(int!null) c:3(string!null) d:4(int)
      ├── constraint: /2/3/4/1: [/1/'+1-1000' - /1/'+1-1001')
      ├── stats: [rows=1.089, distinct(1)=1.089, null(1)=0, distinct(2)=1, null(2)=0, distinct(3)=1.089, null(3)=0]
      ├── key: (1)
      └── fd: ()-->(2), (1)-->(3,4)

exec-ddl
CREATE SEQUENCE seq
----
SEQUENCE t.public.seq

opt
SELECT * FROM seq
----
sequence-select t.public.seq
 ├── columns: last_value:1(int!null) log_cnt:2(int!null) is_called:3(bool!null)
 ├── cardinality: [1 - 1]
 ├── stats: [rows=1]
 ├── key: ()
 └── fd: ()-->(1-3)
