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=1000, distinct(5)=1]
 ├── 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(5)=2]
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5), (3,4)~~>(1,2,5)
 └── filters [type=bool, outer=(5), constraints=(/5: [/true - /true]; tight), fd=()-->(5)]
      └── variable: b [type=bool, outer=(5), constraints=(/5: [/true - /true]; tight)]

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]
 ├── 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]
 ├── 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]
 │    ├── key: (1)
 │    ├── fd: (1)-->(2)
 │    └── ordering: +1
 └── aggregations [outer=(2)]
      ├── count-rows [type=int]
      └── const-agg [type=int, outer=(2)]
           └── variable: y [type=int, outer=(2)]

# 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]
 ├── key: (2,3)
 └── scan a
      ├── columns: y:2(int) s:3(string)
      └── stats: [rows=3000, distinct(2,3)=1000]

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]
 ├── 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]
 │    ├── key: (1)
 │    └── fd: (1)-->(3,4), (3,4)-->(1)
 └── filters [type=bool, outer=(3)]
      └── (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]
 ├── 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]
 │    ├── key: (1)
 │    └── fd: (1)-->(3,4), (3,4)~~>(1)
 └── filters [type=bool, outer=(3)]
      └── ((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=2687.87538]
 ├── 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]
 ├── 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]
 │    ├── key: (1)
 │    └── fd: (1)-->(3,4), (3,4)-->(1)
 └── filters [type=bool, outer=(3)]
      └── (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]
 ├── 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]
      ├── 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]
      │    ├── key: (1)
      │    └── fd: (1)-->(3,4), (3,4)-->(1)
      └── filters [type=bool, outer=(3,4), constraints=(/4: (/5.0 - ])]
           ├── (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
----
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]
 ├── 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]
      ├── 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]
      │    ├── key: (1)
      │    └── fd: (1)-->(3,4), (3,4)-->(1)
      └── filters [type=bool, outer=(3,4), constraints=(/4: (/NULL - /5.0])]
           ├── (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.11111111, distinct(2)=1]
 ├── 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.11111111, distinct(2)=1]
      ├── key: (1)
      └── fd: ()-->(2), (1)-->(3,4)
