# LogicTest: local

# Note that we use EXPLAIN (opt) in these tests because the standard explain
# prints spans after they have been converted into keys. Once converted into
# keys, enum datums are not human readable. EXPLAIN (OPT) prints these enums
# as datums, so we can more clearly see what spans are being generated.

statement ok
CREATE TYPE greeting AS ENUM ('hello', 'howdy', 'hi');
CREATE TABLE t (x greeting PRIMARY KEY, y greeting, INDEX i (y), FAMILY (x, y));
INSERT INTO t VALUES ('hello', 'howdy'), ('howdy', 'hi')

# Test that we calculate the correct stats and cardinality.
query T
EXPLAIN (OPT,VERBOSE) SELECT * FROM t
----
scan t
 ├── columns: x:1 y:2
 ├── check constraint expressions
 │    └── x:1 IN ('hello', 'howdy', 'hi') [outer=(1), constraints=(/1: [/'hello' - /'hello'] [/'howdy' - /'howdy'] [/'hi' - /'hi']; tight)]
 ├── cardinality: [0 - 3]
 ├── stats: [rows=3]
 ├── cost: 22.05
 ├── key: (1)
 ├── fd: (1)-->(2)
 ├── distribution: test
 └── prune: (1,2)

query T
EXPLAIN (OPT) SELECT * FROM t WHERE x = 'hello'
----
scan t
 └── constraint: /1: [/'hello' - /'hello']

query T
EXPLAIN (OPT) SELECT * FROM t WHERE x = 'hello' OR x = 'hi'
----
scan t
 └── constraint: /1
      ├── [/'hello' - /'hello']
      └── [/'hi' - /'hi']

query T
EXPLAIN (OPT) SELECT * FROM t WHERE x > 'hello'
----
scan t
 └── constraint: /1: [/'howdy' - /'hi']

# Test that we can perform constrained scans using secondary indexes too.
query T
EXPLAIN (OPT) SELECT * FROM t WHERE y = 'hello'
----
scan t@i
 └── constraint: /2/1: [/'hello'/'hello' - /'hello'/'hi']

query T
EXPLAIN (OPT) SELECT * FROM t WHERE y > 'hello' AND y < 'hi'
----
scan t@i
 └── constraint: /2/1: [/'howdy'/'hello' - /'howdy'/'hi']

query T
EXPLAIN (opt) SELECT * FROM t WHERE x IN ('hello', 'hi')
----
scan t
 └── constraint: /1
      ├── [/'hello' - /'hello']
      └── [/'hi' - /'hi']

statement ok
CREATE TABLE checks (x greeting NOT NULL, y int, INDEX (x, y))

# Check that inferred check constraints from enum columns are used in plans.
query T
EXPLAIN (OPT) SELECT x, y FROM checks WHERE y = 2
----
scan checks@checks_x_y_idx
 └── constraint: /1/2/3
      ├── [/'hello'/2 - /'hello'/2]
      ├── [/'howdy'/2 - /'howdy'/2]
      └── [/'hi'/2 - /'hi'/2]


# Test that changes to enums are picked up in plans.
statement ok
BEGIN;
ALTER TYPE greeting ADD VALUE 'cheers'

query T
EXPLAIN (opt) SELECT x, y FROM checks WHERE y = 2
----
scan checks@checks_x_y_idx
 └── constraint: /1/2/3
      ├── [/'hello'/2 - /'hello'/2]
      ├── [/'howdy'/2 - /'howdy'/2]
      ├── [/'hi'/2 - /'hi'/2]
      └── [/'cheers'/2 - /'cheers'/2]

statement ok
ROLLBACK

statement ok
ALTER TYPE greeting ADD VALUE 'cheers'

query T
EXPLAIN (opt) SELECT x, y FROM checks WHERE y = 2
----
scan checks@checks_x_y_idx
 └── constraint: /1/2/3
      ├── [/'hello'/2 - /'hello'/2]
      ├── [/'howdy'/2 - /'howdy'/2]
      ├── [/'hi'/2 - /'hi'/2]
      └── [/'cheers'/2 - /'cheers'/2]

# Test that we calculate the correct stats and cardinality.
query T
EXPLAIN (OPT,VERBOSE) SELECT DISTINCT x FROM checks
----
distinct-on
 ├── columns: x:1
 ├── grouping columns: x:1
 ├── internal-ordering: +1
 ├── cardinality: [0 - 4]
 ├── stats: [rows=4, distinct(1)=4, null(1)=0]
 ├── cost: 1118.88
 ├── key: (1)
 ├── distribution: test
 └── scan checks@checks_x_y_idx
      ├── columns: x:1
      ├── stats: [rows=1000, distinct(1)=4, null(1)=0]
      ├── cost: 1108.82
      ├── ordering: +1
      ├── distribution: test
      ├── prune: (1)
      └── interesting orderings: (+1)

# Test that a limited, ordered scan is efficient.
statement ok
CREATE TABLE composite_key (x greeting, y INT, PRIMARY KEY (x, y), FAMILY (x, y));

query T
EXPLAIN (opt) SELECT * FROM composite_key ORDER BY y LIMIT 5
----
limit
 ├── union-all
 │    ├── union-all
 │    │    ├── scan composite_key
 │    │    │    ├── constraint: /8/9: [/'hello' - /'hello']
 │    │    │    └── limit: 5
 │    │    └── scan composite_key
 │    │         ├── constraint: /14/15: [/'howdy' - /'howdy']
 │    │         └── limit: 5
 │    └── union-all
 │         ├── scan composite_key
 │         │    ├── constraint: /20/21: [/'hi' - /'hi']
 │         │    └── limit: 5
 │         └── scan composite_key
 │              ├── constraint: /26/27: [/'cheers' - /'cheers']
 │              └── limit: 5
 └── 5

statement ok
CREATE TABLE nulls (x greeting, y int, INDEX (x, y))

# Test that we calculate the correct stats and cardinality including null values.
query T
EXPLAIN (OPT,VERBOSE) SELECT x FROM nulls WHERE y < 0 UNION SELECT x FROM nulls WHERE y > 10
----
union
 ├── columns: x:15
 ├── left columns: nulls.x:1
 ├── right columns: nulls.x:8
 ├── internal-ordering: +15
 ├── cardinality: [0 - 5]
 ├── stats: [rows=5, distinct(15)=5, null(15)=1]
 ├── cost: 2284.86667
 ├── key: (15)
 ├── distribution: test
 ├── interesting orderings: (+15)
 ├── project
 │    ├── columns: nulls.x:1
 │    ├── stats: [rows=333.3333, distinct(1)=5, null(1)=3.33333]
 │    ├── cost: 1142.40333
 │    ├── ordering: +1
 │    ├── distribution: test
 │    ├── interesting orderings: (+1)
 │    └── select
 │         ├── columns: nulls.x:1 y:2
 │         ├── stats: [rows=333.3333, distinct(1)=5, null(1)=3.33333, distinct(2)=33.3333, null(2)=0]
 │         ├── cost: 1139.05
 │         ├── ordering: +1
 │         ├── distribution: test
 │         ├── interesting orderings: (+1,+2)
 │         ├── scan nulls@nulls_x_y_idx
 │         │    ├── columns: nulls.x:1 y:2
 │         │    ├── stats: [rows=1000, distinct(1)=5, null(1)=10, distinct(2)=100, null(2)=10]
 │         │    ├── cost: 1129.02
 │         │    ├── ordering: +1
 │         │    ├── distribution: test
 │         │    ├── prune: (1,2)
 │         │    └── interesting orderings: (+1,+2)
 │         └── filters
 │              └── y:2 < 0 [outer=(2), constraints=(/2: (/NULL - /-1]; tight)]
 └── project
      ├── columns: nulls.x:8
      ├── stats: [rows=333.3333, distinct(8)=5, null(8)=3.33333]
      ├── cost: 1142.40333
      ├── ordering: +8
      ├── distribution: test
      ├── interesting orderings: (+8)
      └── select
           ├── columns: nulls.x:8 y:9
           ├── stats: [rows=333.3333, distinct(8)=5, null(8)=3.33333, distinct(9)=33.3333, null(9)=0]
           ├── cost: 1139.05
           ├── ordering: +8
           ├── distribution: test
           ├── interesting orderings: (+8,+9)
           ├── scan nulls@nulls_x_y_idx
           │    ├── columns: nulls.x:8 y:9
           │    ├── stats: [rows=1000, distinct(8)=5, null(8)=10, distinct(9)=100, null(9)=10]
           │    ├── cost: 1129.02
           │    ├── ordering: +8
           │    ├── distribution: test
           │    ├── prune: (8,9)
           │    └── interesting orderings: (+8,+9)
           └── filters
                └── y:9 > 10 [outer=(9), constraints=(/9: [/11 - ]; tight)]
