exec-ddl
CREATE TABLE a (x INT, y INT)
----
TABLE a
 ├── x int
 ├── y int
 ├── rowid int not null (hidden)
 └── INDEX primary
      └── rowid int not null (hidden)

exec-ddl
CREATE TABLE kuv (k INT PRIMARY KEY, u FLOAT, v STRING)
----
TABLE kuv
 ├── k int not null
 ├── u float
 ├── v string
 └── INDEX primary
      └── k int not null

opt
SELECT * FROM a WHERE x > 1
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── prune: (2)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── gt [type=bool, outer=(1), constraints=(/1: [/2 - ]; tight)]
           ├── variable: x [type=int]
           └── const: 1 [type=int]

# Verify that 1 is determined to be constant (from the intersection of the
# constraints).
opt
SELECT * FROM a WHERE x > 0 AND x < 2
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── fd: ()-->(1)
 ├── prune: (2)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── range [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
           └── and [type=bool]
                ├── gt [type=bool]
                │    ├── variable: x [type=int]
                │    └── const: 0 [type=int]
                └── lt [type=bool]
                     ├── variable: x [type=int]
                     └── const: 2 [type=int]

opt
SELECT * FROM a WHERE x >= 1
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── prune: (2)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── ge [type=bool, outer=(1), constraints=(/1: [/1 - ]; tight)]
           ├── variable: x [type=int]
           └── const: 1 [type=int]

opt
SELECT * FROM a WHERE x < 1
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── prune: (2)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── lt [type=bool, outer=(1), constraints=(/1: (/NULL - /0]; tight)]
           ├── variable: x [type=int]
           └── const: 1 [type=int]

opt
SELECT * FROM a WHERE x <= 1
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── prune: (2)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── le [type=bool, outer=(1), constraints=(/1: (/NULL - /1]; tight)]
           ├── variable: x [type=int]
           └── const: 1 [type=int]

opt
SELECT * FROM a WHERE x = 1
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── fd: ()-->(1)
 ├── prune: (2)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
           ├── variable: x [type=int]
           └── const: 1 [type=int]

opt
SELECT * FROM a WHERE x > 1 AND x < 5
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── prune: (2)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── range [type=bool, outer=(1), constraints=(/1: [/2 - /4]; tight)]
           └── and [type=bool]
                ├── gt [type=bool]
                │    ├── variable: x [type=int]
                │    └── const: 1 [type=int]
                └── lt [type=bool]
                     ├── variable: x [type=int]
                     └── const: 5 [type=int]

opt
SELECT * FROM a WHERE x = 1 AND y = 5
----
select
 ├── columns: x:1(int!null) y:2(int!null)
 ├── fd: ()-->(1,2)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      ├── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
      │    ├── variable: x [type=int]
      │    └── const: 1 [type=int]
      └── eq [type=bool, outer=(2), constraints=(/2: [/5 - /5]; tight), fd=()-->(2)]
           ├── variable: y [type=int]
           └── const: 5 [type=int]

opt
SELECT * FROM a WHERE x > 1 AND x < 5 AND y >= 7 AND y <= 9
----
select
 ├── columns: x:1(int!null) y:2(int!null)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      ├── range [type=bool, outer=(1), constraints=(/1: [/2 - /4]; tight)]
      │    └── and [type=bool]
      │         ├── gt [type=bool]
      │         │    ├── variable: x [type=int]
      │         │    └── const: 1 [type=int]
      │         └── lt [type=bool]
      │              ├── variable: x [type=int]
      │              └── const: 5 [type=int]
      └── range [type=bool, outer=(2), constraints=(/2: [/7 - /9]; tight)]
           └── and [type=bool]
                ├── ge [type=bool]
                │    ├── variable: y [type=int]
                │    └── const: 7 [type=int]
                └── le [type=bool]
                     ├── variable: y [type=int]
                     └── const: 9 [type=int]

# Verify the resulting constraints are not tight.
opt
SELECT * FROM a WHERE x > 1 AND x < 5 AND x + y = 5
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      ├── range [type=bool, outer=(1), constraints=(/1: [/2 - /4]; tight)]
      │    └── and [type=bool]
      │         ├── gt [type=bool]
      │         │    ├── variable: x [type=int]
      │         │    └── const: 1 [type=int]
      │         └── lt [type=bool]
      │              ├── variable: x [type=int]
      │              └── const: 5 [type=int]
      └── eq [type=bool, outer=(1,2)]
           ├── plus [type=int]
           │    ├── variable: x [type=int]
           │    └── variable: y [type=int]
           └── const: 5 [type=int]

opt
SELECT * FROM a WHERE x > 1 AND x + y >= 5 AND x + y <= 7
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      ├── gt [type=bool, outer=(1), constraints=(/1: [/2 - ]; tight)]
      │    ├── variable: x [type=int]
      │    └── const: 1 [type=int]
      ├── ge [type=bool, outer=(1,2)]
      │    ├── plus [type=int]
      │    │    ├── variable: x [type=int]
      │    │    └── variable: y [type=int]
      │    └── const: 5 [type=int]
      └── le [type=bool, outer=(1,2)]
           ├── plus [type=int]
           │    ├── variable: x [type=int]
           │    └── variable: y [type=int]
           └── const: 7 [type=int]

# Verify that we ignore some mixed-type comparisons.
opt
SELECT * FROM a WHERE x > 1.5
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── prune: (2)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── gt [type=bool, outer=(1), constraints=(/1: (/NULL - ])]
           ├── variable: x [type=int]
           └── const: 1.5 [type=decimal]

# This is a safe mixed-type comparison.
opt
SELECT * FROM kuv WHERE u > 1::INT
----
select
 ├── columns: k:1(int!null) u:2(float!null) v:3(string)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── prune: (1,3)
 ├── interesting orderings: (+1)
 ├── scan kuv
 │    ├── columns: k:1(int!null) u:2(float) v:3(string)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1)
 └── filters
      └── gt [type=bool, outer=(2), constraints=(/2: [/1.0000000000000002 - ]; tight)]
           ├── variable: u [type=float]
           └── const: 1.0 [type=float]

opt
SELECT * FROM kuv WHERE v <= 'foo' AND v >= 'bar'
----
select
 ├── columns: k:1(int!null) u:2(float) v:3(string!null)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── prune: (1,2)
 ├── interesting orderings: (+1)
 ├── scan kuv
 │    ├── columns: k:1(int!null) u:2(float) v:3(string)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1)
 └── filters
      └── range [type=bool, outer=(3), constraints=(/3: [/'bar' - /'foo']; tight)]
           └── and [type=bool]
                ├── le [type=bool]
                │    ├── variable: v [type=string]
                │    └── const: 'foo' [type=string]
                └── ge [type=bool]
                     ├── variable: v [type=string]
                     └── const: 'bar' [type=string]

# Test IN.
opt
SELECT * FROM a WHERE x IN (1, 2, 3, NULL)
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── prune: (2)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── in [type=bool, outer=(1), constraints=(/1: [/1 - /1] [/2 - /2] [/3 - /3]; tight)]
           ├── variable: x [type=int]
           └── tuple [type=tuple{unknown, int, int, int}]
                ├── null [type=unknown]
                ├── const: 1 [type=int]
                ├── const: 2 [type=int]
                └── const: 3 [type=int]

opt
SELECT * FROM a WHERE rowid IS NULL
----
project
 ├── columns: x:1(int) y:2(int)
 ├── cardinality: [0 - 1]
 ├── key: ()
 ├── fd: ()-->(1,2)
 ├── prune: (1,2)
 └── scan a
      ├── columns: x:1(int) y:2(int) rowid:3(int!null)
      ├── constraint: /3: contradiction
      ├── cardinality: [0 - 1]
      ├── key: ()
      ├── fd: ()-->(1-3)
      ├── prune: (1-3)
      └── interesting orderings: (+3)

# Test IN in combination with another condition on the same column (which rules
# out some of the entries in the IN condition).
opt
SELECT * FROM a WHERE x IN (1, 3, 5, 7, 9) AND x > 6
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── prune: (2)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── range [type=bool, outer=(1), constraints=(/1: [/7 - /7] [/9 - /9]; tight)]
           └── and [type=bool]
                ├── in [type=bool]
                │    ├── variable: x [type=int]
                │    └── tuple [type=tuple{int, int, int, int, int}]
                │         ├── const: 1 [type=int]
                │         ├── const: 3 [type=int]
                │         ├── const: 5 [type=int]
                │         ├── const: 7 [type=int]
                │         └── const: 9 [type=int]
                └── gt [type=bool]
                     ├── variable: x [type=int]
                     └── const: 6 [type=int]

# Test IN in combination with a condition on another column.
opt
SELECT * FROM a WHERE x IN (1, 3) AND y > 4
----
select
 ├── columns: x:1(int!null) y:2(int!null)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      ├── in [type=bool, outer=(1), constraints=(/1: [/1 - /1] [/3 - /3]; tight)]
      │    ├── variable: x [type=int]
      │    └── tuple [type=tuple{int, int}]
      │         ├── const: 1 [type=int]
      │         └── const: 3 [type=int]
      └── gt [type=bool, outer=(2), constraints=(/2: [/5 - ]; tight)]
           ├── variable: y [type=int]
           └── const: 4 [type=int]

# Test tuple inequality.
opt
SELECT * FROM a WHERE (x, y) > (1, 2)
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── gt [type=bool, outer=(1,2), constraints=(/1/2: [/1/3 - ]; tight)]
           ├── tuple [type=tuple{int, int}]
           │    ├── variable: x [type=int]
           │    └── variable: y [type=int]
           └── tuple [type=tuple{int, int}]
                ├── const: 1 [type=int]
                └── const: 2 [type=int]

opt
SELECT * FROM a WHERE (x, y) >= (1, 2)
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── ge [type=bool, outer=(1,2), constraints=(/1/2: [/1/2 - ]; tight)]
           ├── tuple [type=tuple{int, int}]
           │    ├── variable: x [type=int]
           │    └── variable: y [type=int]
           └── tuple [type=tuple{int, int}]
                ├── const: 1 [type=int]
                └── const: 2 [type=int]

opt
SELECT * FROM a WHERE (x, y) < (1, 2)
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── lt [type=bool, outer=(1,2), constraints=(/1/2: (/NULL - /1/1]; tight)]
           ├── tuple [type=tuple{int, int}]
           │    ├── variable: x [type=int]
           │    └── variable: y [type=int]
           └── tuple [type=tuple{int, int}]
                ├── const: 1 [type=int]
                └── const: 2 [type=int]

opt
SELECT * FROM a WHERE (x, y) <= (1, 2)
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── le [type=bool, outer=(1,2), constraints=(/1/2: (/NULL - /1/2]; tight)]
           ├── tuple [type=tuple{int, int}]
           │    ├── variable: x [type=int]
           │    └── variable: y [type=int]
           └── tuple [type=tuple{int, int}]
                ├── const: 1 [type=int]
                └── const: 2 [type=int]

# Test that we ignore tuple inequalities when the types don't match up.
opt
SELECT * FROM a WHERE (x, y) >= (1, 2.5)
----
select
 ├── columns: x:1(int) y:2(int)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── ge [type=bool, outer=(1,2)]
           ├── tuple [type=tuple{int, int}]
           │    ├── variable: x [type=int]
           │    └── variable: y [type=int]
           └── tuple [type=tuple{int, decimal}]
                ├── const: 1 [type=int]
                └── const: 2.5 [type=decimal]

# Test that we ignore tuple inequalities when they contain NULLs.
opt
SELECT * FROM a WHERE (x, y) >= (1, NULL)
----
select
 ├── columns: x:1(int) y:2(int)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── ge [type=bool, outer=(1,2)]
           ├── tuple [type=tuple{int, int}]
           │    ├── variable: x [type=int]
           │    └── variable: y [type=int]
           └── tuple [type=tuple{int, unknown}]
                ├── const: 1 [type=int]
                └── null [type=unknown]

# Test that we ignore tuple inequalities when we have something other than
# simple variables in the left tuple.
opt
SELECT * FROM a WHERE (x, 1) >= (1, 2)
----
select
 ├── columns: x:1(int) y:2(int)
 ├── prune: (2)
 ├── scan a
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── ge [type=bool, outer=(1)]
           ├── tuple [type=tuple{int, int}]
           │    ├── variable: x [type=int]
           │    └── const: 1 [type=int]
           └── tuple [type=tuple{int, int}]
                ├── const: 1 [type=int]
                └── const: 2 [type=int]

exec-ddl
CREATE TABLE abc (a INT, b BOOL, c STRING)
----
TABLE abc
 ├── a int
 ├── b bool
 ├── c string
 ├── rowid int not null (hidden)
 └── INDEX primary
      └── rowid int not null (hidden)

opt
SELECT * FROM abc WHERE a != 5
----
select
 ├── columns: a:1(int!null) b:2(bool) c:3(string)
 ├── prune: (2,3)
 ├── scan abc
 │    ├── columns: a:1(int) b:2(bool) c:3(string)
 │    └── prune: (1-3)
 └── filters
      └── ne [type=bool, outer=(1), constraints=(/1: (/NULL - /4] [/6 - ]; tight)]
           ├── variable: a [type=int]
           └── const: 5 [type=int]

opt
SELECT * FROM abc WHERE a IS DISTINCT FROM 5
----
select
 ├── columns: a:1(int) b:2(bool) c:3(string)
 ├── prune: (2,3)
 ├── scan abc
 │    ├── columns: a:1(int) b:2(bool) c:3(string)
 │    └── prune: (1-3)
 └── filters
      └── is-not [type=bool, outer=(1), constraints=(/1: [ - /4] [/6 - ]; tight)]
           ├── variable: a [type=int]
           └── const: 5 [type=int]

opt
SELECT * FROM abc WHERE b != true
----
select
 ├── columns: a:1(int) b:2(bool!null) c:3(string)
 ├── prune: (1,3)
 ├── scan abc
 │    ├── columns: a:1(int) b:2(bool) c:3(string)
 │    └── prune: (1-3)
 └── filters
      └── ne [type=bool, outer=(2), constraints=(/2: (/NULL - /false]; tight)]
           ├── variable: b [type=bool]
           └── true [type=bool]

opt
SELECT * FROM abc WHERE b != false
----
select
 ├── columns: a:1(int) b:2(bool!null) c:3(string)
 ├── prune: (1,3)
 ├── scan abc
 │    ├── columns: a:1(int) b:2(bool) c:3(string)
 │    └── prune: (1-3)
 └── filters
      └── ne [type=bool, outer=(2), constraints=(/2: [/true - ]; tight)]
           ├── variable: b [type=bool]
           └── false [type=bool]

opt
SELECT * FROM abc WHERE b IS NOT true
----
select
 ├── columns: a:1(int) b:2(bool) c:3(string)
 ├── prune: (1,3)
 ├── scan abc
 │    ├── columns: a:1(int) b:2(bool) c:3(string)
 │    └── prune: (1-3)
 └── filters
      └── is-not [type=bool, outer=(2), constraints=(/2: [ - /false]; tight)]
           ├── variable: b [type=bool]
           └── true [type=bool]

opt
SELECT * FROM abc WHERE b IS NOT false
----
select
 ├── columns: a:1(int) b:2(bool) c:3(string)
 ├── prune: (1,3)
 ├── scan abc
 │    ├── columns: a:1(int) b:2(bool) c:3(string)
 │    └── prune: (1-3)
 └── filters
      └── is-not [type=bool, outer=(2), constraints=(/2: [ - /false) [/true - ]; tight)]
           ├── variable: b [type=bool]
           └── false [type=bool]

opt
SELECT * FROM abc WHERE b
----
select
 ├── columns: a:1(int) b:2(bool!null) c:3(string)
 ├── fd: ()-->(2)
 ├── prune: (1,3)
 ├── scan abc
 │    ├── columns: a:1(int) b:2(bool) c:3(string)
 │    └── prune: (1-3)
 └── filters
      └── variable: b [type=bool, outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)]

opt
SELECT * FROM abc WHERE NOT b
----
select
 ├── columns: a:1(int) b:2(bool!null) c:3(string)
 ├── fd: ()-->(2)
 ├── prune: (1,3)
 ├── scan abc
 │    ├── columns: a:1(int) b:2(bool) c:3(string)
 │    └── prune: (1-3)
 └── filters
      └── not [type=bool, outer=(2), constraints=(/2: [/false - /false]; tight), fd=()-->(2)]
           └── variable: b [type=bool]

opt
SELECT * FROM abc WHERE a > 5 AND b
----
select
 ├── columns: a:1(int!null) b:2(bool!null) c:3(string)
 ├── fd: ()-->(2)
 ├── prune: (3)
 ├── scan abc
 │    ├── columns: a:1(int) b:2(bool) c:3(string)
 │    └── prune: (1-3)
 └── filters
      ├── gt [type=bool, outer=(1), constraints=(/1: [/6 - ]; tight)]
      │    ├── variable: a [type=int]
      │    └── const: 5 [type=int]
      └── variable: b [type=bool, outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)]

opt
SELECT * FROM abc WHERE c != 'foo'
----
select
 ├── columns: a:1(int) b:2(bool) c:3(string!null)
 ├── prune: (1,2)
 ├── scan abc
 │    ├── columns: a:1(int) b:2(bool) c:3(string)
 │    └── prune: (1-3)
 └── filters
      └── ne [type=bool, outer=(3), constraints=(/3: (/NULL - /'foo') [/e'foo\x00' - ]; tight)]
           ├── variable: c [type=string]
           └── const: 'foo' [type=string]

opt
SELECT * FROM abc WHERE c IS DISTINCT FROM 'foo'
----
select
 ├── columns: a:1(int) b:2(bool) c:3(string)
 ├── prune: (1,2)
 ├── scan abc
 │    ├── columns: a:1(int) b:2(bool) c:3(string)
 │    └── prune: (1-3)
 └── filters
      └── is-not [type=bool, outer=(3), constraints=(/3: [ - /'foo') [/e'foo\x00' - ]; tight)]
           ├── variable: c [type=string]
           └── const: 'foo' [type=string]

opt
SELECT * FROM (SELECT (x, y) AS col FROM a) WHERE col > (1, 2)
----
select
 ├── columns: col:4(tuple{int, int}!null)
 ├── project
 │    ├── columns: col:4(tuple{int, int})
 │    ├── prune: (4)
 │    ├── scan a
 │    │    ├── columns: x:1(int) y:2(int)
 │    │    └── prune: (1,2)
 │    └── projections
 │         └── tuple [type=tuple{int, int}, outer=(1,2)]
 │              ├── variable: x [type=int]
 │              └── variable: y [type=int]
 └── filters
      └── gt [type=bool, outer=(4), constraints=(/4: [/(1, 3) - ]; tight)]
           ├── variable: col [type=tuple{int, int}]
           └── tuple [type=tuple{int, int}]
                ├── const: 1 [type=int]
                └── const: 2 [type=int]

exec-ddl
CREATE TABLE c
(
    k INT PRIMARY KEY,
    u INT,
    v INT,
    INDEX v (v, u)
)
----
TABLE c
 ├── k int not null
 ├── u int
 ├── v int
 ├── INDEX primary
 │    └── k int not null
 └── INDEX v
      ├── v int
      ├── u int
      └── k int not null

opt
SELECT * FROM c WHERE (v, u) IN ((1, 2), (3, 50), (5, 100))
----
scan c@v
 ├── columns: k:1(int!null) u:2(int!null) v:3(int!null)
 ├── constraint: /3/2/1: [/1/2 - /1/2] [/3/50 - /3/50] [/5/100 - /5/100]
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── prune: (1)
 └── interesting orderings: (+1) (+3,+2,+1)

opt format=hide-qual
SELECT * FROM c WHERE (v, u) IN ((1, 2), (1, 3), (1, 4))
----
scan c@v
 ├── columns: k:1(int!null) u:2(int!null) v:3(int!null)
 ├── constraint: /3/2/1: [/1/2 - /1/4]
 ├── stats: [rows=0.29403, distinct(1)=0.29403, null(1)=0, distinct(2)=0.29403, null(2)=0, distinct(3)=0.29403, null(3)=0]
 ├── cost: 0.3216718
 ├── key: (1)
 ├── fd: ()-->(3), (1)-->(2)
 ├── prune: (1)
 └── interesting orderings: (+1) (+3,+2,+1)

opt format=hide-qual
SELECT * FROM c WHERE (v, u) IN ((1, 2), (3, 2), (5, 2))
----
scan c@v
 ├── columns: k:1(int!null) u:2(int!null) v:3(int!null)
 ├── constraint: /3/2/1: [/1/2 - /1/2] [/3/2 - /3/2] [/5/2 - /5/2]
 ├── stats: [rows=0.29403, distinct(1)=0.29403, null(1)=0, distinct(2)=0.29403, null(2)=0, distinct(3)=0.29403, null(3)=0]
 ├── cost: 0.3216718
 ├── key: (1)
 ├── fd: ()-->(2), (1)-->(3)
 ├── prune: (1)
 └── interesting orderings: (+1) (+3,+2,+1)

opt format=hide-qual
SELECT * FROM c WHERE (v, u) IN ((1, 2), (1, 2), (1, 2))
----
scan c@v
 ├── columns: k:1(int!null) u:2(int!null) v:3(int!null)
 ├── constraint: /3/2/1: [/1/2 - /1/2]
 ├── stats: [rows=0.09801, distinct(1)=0.09801, null(1)=0, distinct(2)=0.09801, null(2)=0, distinct(3)=0.09801, null(3)=0]
 ├── cost: 0.1138906
 ├── key: (1)
 ├── fd: ()-->(2,3)
 ├── prune: (1)
 └── interesting orderings: (+1) (+3,+2,+1)

opt format=hide-qual
SELECT * FROM c WHERE (v, u) IN ((1, 2), (1, 3), (1, 4))
----
scan c@v
 ├── columns: k:1(int!null) u:2(int!null) v:3(int!null)
 ├── constraint: /3/2/1: [/1/2 - /1/4]
 ├── stats: [rows=0.29403, distinct(1)=0.29403, null(1)=0, distinct(2)=0.29403, null(2)=0, distinct(3)=0.29403, null(3)=0]
 ├── cost: 0.3216718
 ├── key: (1)
 ├── fd: ()-->(3), (1)-->(2)
 ├── prune: (1)
 └── interesting orderings: (+1) (+3,+2,+1)

opt format=hide-qual
SELECT * FROM c WHERE (v, u) IN ((1, 2), (3, 2), (5, 2))
----
scan c@v
 ├── columns: k:1(int!null) u:2(int!null) v:3(int!null)
 ├── constraint: /3/2/1: [/1/2 - /1/2] [/3/2 - /3/2] [/5/2 - /5/2]
 ├── stats: [rows=0.29403, distinct(1)=0.29403, null(1)=0, distinct(2)=0.29403, null(2)=0, distinct(3)=0.29403, null(3)=0]
 ├── cost: 0.3216718
 ├── key: (1)
 ├── fd: ()-->(2), (1)-->(3)
 ├── prune: (1)
 └── interesting orderings: (+1) (+3,+2,+1)

opt format=hide-qual
SELECT * FROM c WHERE (v, u) IN ((1, 2), (1, 2), (1, 2))
----
scan c@v
 ├── columns: k:1(int!null) u:2(int!null) v:3(int!null)
 ├── constraint: /3/2/1: [/1/2 - /1/2]
 ├── stats: [rows=0.09801, distinct(1)=0.09801, null(1)=0, distinct(2)=0.09801, null(2)=0, distinct(3)=0.09801, null(3)=0]
 ├── cost: 0.1138906
 ├── key: (1)
 ├── fd: ()-->(2,3)
 ├── prune: (1)
 └── interesting orderings: (+1) (+3,+2,+1)

# A tuple with NULL in it can't match anything, so it should be excluded from the constraints.
opt
SELECT * FROM c WHERE (v, u) IN ((1, 2), (3, 50), (5, NULL))
----
scan c@v
 ├── columns: k:1(int!null) u:2(int!null) v:3(int!null)
 ├── constraint: /3/2/1: [/1/2 - /1/2] [/3/50 - /3/50]
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── prune: (1)
 └── interesting orderings: (+1) (+3,+2,+1)

# TODO(justin): ideally we would be normalizing away the 2 on the LHS here to
# get v = 1 and tight spans.
opt
SELECT * FROM c WHERE (v, 2) IN ((1, 2), (3, 50), (5, 100))
----
select
 ├── columns: k:1(int!null) u:2(int) v:3(int!null)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── prune: (1,2)
 ├── interesting orderings: (+1) (+3,+2,+1)
 ├── scan c@v
 │    ├── columns: k:1(int!null) u:2(int) v:3(int!null)
 │    ├── constraint: /3/2/1: [/1 - /1] [/3 - /3] [/5 - /5]
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1) (+3,+2,+1)
 └── filters
      └── in [type=bool, outer=(3), constraints=(/3: [/1 - /1] [/3 - /3] [/5 - /5])]
           ├── tuple [type=tuple{int, int}]
           │    ├── variable: v [type=int]
           │    └── const: 2 [type=int]
           └── tuple [type=tuple{tuple{int, int}, tuple{int, int}, tuple{int, int}}]
                ├── tuple [type=tuple{int, int}]
                │    ├── const: 1 [type=int]
                │    └── const: 2 [type=int]
                ├── tuple [type=tuple{int, int}]
                │    ├── const: 3 [type=int]
                │    └── const: 50 [type=int]
                └── tuple [type=tuple{int, int}]
                     ├── const: 5 [type=int]
                     └── const: 100 [type=int]

# TODO(justin): in a perfect world we would be able to somehow transform this
# filter to (v, u) IN ((1, 1), (3, 47), (5, 95)) in order to get tight spans.
# This could be achieved via row-reduction.
opt
SELECT * FROM c WHERE (v, u + v) IN ((1, 2), (3, 50), (5, 100))
----
select
 ├── columns: k:1(int!null) u:2(int) v:3(int!null)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── prune: (1)
 ├── interesting orderings: (+1) (+3,+2,+1)
 ├── scan c@v
 │    ├── columns: k:1(int!null) u:2(int) v:3(int!null)
 │    ├── constraint: /3/2/1: [/1 - /1] [/3 - /3] [/5 - /5]
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1) (+3,+2,+1)
 └── filters
      └── in [type=bool, outer=(2,3), constraints=(/3: [/1 - /1] [/3 - /3] [/5 - /5])]
           ├── tuple [type=tuple{int, int}]
           │    ├── variable: v [type=int]
           │    └── plus [type=int]
           │         ├── variable: u [type=int]
           │         └── variable: v [type=int]
           └── tuple [type=tuple{tuple{int, int}, tuple{int, int}, tuple{int, int}}]
                ├── tuple [type=tuple{int, int}]
                │    ├── const: 1 [type=int]
                │    └── const: 2 [type=int]
                ├── tuple [type=tuple{int, int}]
                │    ├── const: 3 [type=int]
                │    └── const: 50 [type=int]
                └── tuple [type=tuple{int, int}]
                     ├── const: 5 [type=int]
                     └── const: 100 [type=int]

opt
SELECT * FROM c WHERE (v, u) IN ((1, 2), (k, 50), (5, 100))
----
select
 ├── columns: k:1(int!null) u:2(int!null) v:3(int)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── interesting orderings: (+1) (+3,+2,+1)
 ├── scan c
 │    ├── columns: k:1(int!null) u:2(int) v:3(int)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1) (+3,+2,+1)
 └── filters
      └── in [type=bool, outer=(1-3), constraints=(/2: [/2 - /2] [/50 - /50] [/100 - /100])]
           ├── tuple [type=tuple{int, int}]
           │    ├── variable: v [type=int]
           │    └── variable: u [type=int]
           └── tuple [type=tuple{tuple{int, int}, tuple{int, int}, tuple{int, int}}]
                ├── tuple [type=tuple{int, int}]
                │    ├── const: 1 [type=int]
                │    └── const: 2 [type=int]
                ├── tuple [type=tuple{int, int}]
                │    ├── variable: k [type=int]
                │    └── const: 50 [type=int]
                └── tuple [type=tuple{int, int}]
                     ├── const: 5 [type=int]
                     └── const: 100 [type=int]

exec-ddl
CREATE TABLE d
(
    k INT PRIMARY KEY,
    p INT,
    q INT
)
----
TABLE d
 ├── k int not null
 ├── p int
 ├── q int
 └── INDEX primary
      └── k int not null

opt format=hide-qual
SELECT * FROM d WHERE (p, q) IN ((1, 2), (1, 3), (1, 4))
----
select
 ├── columns: k:1(int!null) p:2(int!null) q:3(int!null)
 ├── stats: [rows=0.29403, distinct(1)=0.29403, null(1)=0, distinct(2)=0.29403, null(2)=0, distinct(3)=0.29403, null(3)=0]
 ├── cost: 1070.03
 ├── key: (1)
 ├── fd: ()-->(2), (1)-->(3)
 ├── prune: (1)
 ├── interesting orderings: (+1)
 ├── scan d
 │    ├── columns: k:1(int!null) p:2(int) q:3(int)
 │    ├── stats: [rows=1000, distinct(1)=1000, null(1)=0, distinct(2)=100, null(2)=10, distinct(3)=100, null(3)=10]
 │    ├── cost: 1060.02
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1)
 └── filters
      └── in [type=bool, outer=(2,3), constraints=(/2/3: [/1/2 - /1/2] [/1/3 - /1/3] [/1/4 - /1/4]; /3: [/2 - /2] [/3 - /3] [/4 - /4]; tight), fd=()-->(2)]
           ├── tuple [type=tuple{int, int}]
           │    ├── variable: p [type=int]
           │    └── variable: q [type=int]
           └── tuple [type=tuple{tuple{int, int}, tuple{int, int}, tuple{int, int}}]
                ├── tuple [type=tuple{int, int}]
                │    ├── const: 1 [type=int]
                │    └── const: 2 [type=int]
                ├── tuple [type=tuple{int, int}]
                │    ├── const: 1 [type=int]
                │    └── const: 3 [type=int]
                └── tuple [type=tuple{int, int}]
                     ├── const: 1 [type=int]
                     └── const: 4 [type=int]

opt format=hide-qual
SELECT * FROM d WHERE (p, q) IN ((2, 1), (3, 1), (4, 1))
----
select
 ├── columns: k:1(int!null) p:2(int!null) q:3(int!null)
 ├── stats: [rows=0.29403, distinct(1)=0.29403, null(1)=0, distinct(2)=0.29403, null(2)=0, distinct(3)=0.29403, null(3)=0]
 ├── cost: 1070.03
 ├── key: (1)
 ├── fd: ()-->(3), (1)-->(2)
 ├── prune: (1)
 ├── interesting orderings: (+1)
 ├── scan d
 │    ├── columns: k:1(int!null) p:2(int) q:3(int)
 │    ├── stats: [rows=1000, distinct(1)=1000, null(1)=0, distinct(2)=100, null(2)=10, distinct(3)=100, null(3)=10]
 │    ├── cost: 1060.02
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1)
 └── filters
      └── in [type=bool, outer=(2,3), constraints=(/2/3: [/2/1 - /2/1] [/3/1 - /3/1] [/4/1 - /4/1]; /3: [/1 - /1]; tight), fd=()-->(3)]
           ├── tuple [type=tuple{int, int}]
           │    ├── variable: p [type=int]
           │    └── variable: q [type=int]
           └── tuple [type=tuple{tuple{int, int}, tuple{int, int}, tuple{int, int}}]
                ├── tuple [type=tuple{int, int}]
                │    ├── const: 2 [type=int]
                │    └── const: 1 [type=int]
                ├── tuple [type=tuple{int, int}]
                │    ├── const: 3 [type=int]
                │    └── const: 1 [type=int]
                └── tuple [type=tuple{int, int}]
                     ├── const: 4 [type=int]
                     └── const: 1 [type=int]

exec-ddl
CREATE TABLE e
(
    k INT PRIMARY KEY,
    t TIMESTAMP,
    d TIMESTAMP,
    INDEX (t),
    INDEX (d)
)
----
TABLE e
 ├── k int not null
 ├── t timestamp
 ├── d timestamp
 ├── INDEX primary
 │    └── k int not null
 ├── INDEX secondary
 │    ├── t timestamp
 │    └── k int not null
 └── INDEX secondary
      ├── d timestamp
      └── k int not null

opt
SELECT k FROM e WHERE d > '2018-07-01' AND d < '2018-07-01'::DATE + '1w'::INTERVAL
----
project
 ├── columns: k:1(int!null)
 ├── key: (1)
 ├── prune: (1)
 ├── interesting orderings: (+1)
 └── scan e@secondary
      ├── columns: k:1(int!null) d:3(timestamp!null)
      ├── constraint: /3/1: [/'2018-07-01 00:00:00.000001+00:00' - /'2018-07-07 23:59:59.999999+00:00']
      ├── key: (1)
      ├── fd: (1)-->(3)
      ├── prune: (1,3)
      └── interesting orderings: (+1) (+3,+1)

# Verify constraints for tuple IN (tuple, ..), when the tuples are not sorted.
opt
SELECT * FROM (SELECT (x, y) AS foo FROM a) WHERE foo IN ((3, 4), (1, 2))
----
select
 ├── columns: foo:4(tuple{int, int}!null)
 ├── project
 │    ├── columns: foo:4(tuple{int, int})
 │    ├── prune: (4)
 │    ├── scan a
 │    │    ├── columns: x:1(int) y:2(int)
 │    │    └── prune: (1,2)
 │    └── projections
 │         └── tuple [type=tuple{int, int}, outer=(1,2)]
 │              ├── variable: x [type=int]
 │              └── variable: y [type=int]
 └── filters
      └── in [type=bool, outer=(4), constraints=(/4: [/(1, 2) - /(1, 2)] [/(3, 4) - /(3, 4)]; tight)]
           ├── variable: foo [type=tuple{int, int}]
           └── tuple [type=tuple{tuple{int, int}, tuple{int, int}}]
                ├── tuple [type=tuple{int, int}]
                │    ├── const: 3 [type=int]
                │    └── const: 4 [type=int]
                └── tuple [type=tuple{int, int}]
                     ├── const: 1 [type=int]
                     └── const: 2 [type=int]

# Tests for string operators (LIKE, SIMILAR TO).
opt
SELECT * FROM kuv WHERE v LIKE 'ABC%'
----
select
 ├── columns: k:1(int!null) u:2(float) v:3(string!null)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── prune: (1,2)
 ├── interesting orderings: (+1)
 ├── scan kuv
 │    ├── columns: k:1(int!null) u:2(float) v:3(string)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1)
 └── filters
      └── like [type=bool, outer=(3), constraints=(/3: [/'ABC' - /'ABD'); tight)]
           ├── variable: v [type=string]
           └── const: 'ABC%' [type=string]

opt
SELECT * FROM kuv WHERE v LIKE 'ABC_'
----
select
 ├── columns: k:1(int!null) u:2(float) v:3(string!null)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── prune: (1,2)
 ├── interesting orderings: (+1)
 ├── scan kuv
 │    ├── columns: k:1(int!null) u:2(float) v:3(string)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1)
 └── filters
      └── like [type=bool, outer=(3), constraints=(/3: [/'ABC' - /'ABD'))]
           ├── variable: v [type=string]
           └── const: 'ABC_' [type=string]

opt
SELECT * FROM kuv WHERE v LIKE 'ABC%Z'
----
select
 ├── columns: k:1(int!null) u:2(float) v:3(string!null)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── prune: (1,2)
 ├── interesting orderings: (+1)
 ├── scan kuv
 │    ├── columns: k:1(int!null) u:2(float) v:3(string)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1)
 └── filters
      └── like [type=bool, outer=(3), constraints=(/3: [/'ABC' - /'ABD'))]
           ├── variable: v [type=string]
           └── const: 'ABC%Z' [type=string]

opt
SELECT * FROM kuv WHERE v LIKE 'ABC'
----
select
 ├── columns: k:1(int!null) u:2(float) v:3(string!null)
 ├── key: (1)
 ├── fd: ()-->(3), (1)-->(2)
 ├── prune: (1,2)
 ├── interesting orderings: (+1)
 ├── scan kuv
 │    ├── columns: k:1(int!null) u:2(float) v:3(string)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1)
 └── filters
      └── like [type=bool, outer=(3), constraints=(/3: [/'ABC' - /'ABC']; tight), fd=()-->(3)]
           ├── variable: v [type=string]
           └── const: 'ABC' [type=string]

opt
SELECT * FROM kuv WHERE v LIKE '%'
----
select
 ├── columns: k:1(int!null) u:2(float) v:3(string!null)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── prune: (1,2)
 ├── interesting orderings: (+1)
 ├── scan kuv
 │    ├── columns: k:1(int!null) u:2(float) v:3(string)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1)
 └── filters
      └── like [type=bool, outer=(3), constraints=(/3: (/NULL - ])]
           ├── variable: v [type=string]
           └── const: '%' [type=string]

opt
SELECT * FROM kuv WHERE v LIKE '%XY'
----
select
 ├── columns: k:1(int!null) u:2(float) v:3(string!null)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── prune: (1,2)
 ├── interesting orderings: (+1)
 ├── scan kuv
 │    ├── columns: k:1(int!null) u:2(float) v:3(string)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1)
 └── filters
      └── like [type=bool, outer=(3), constraints=(/3: (/NULL - ])]
           ├── variable: v [type=string]
           └── const: '%XY' [type=string]

opt
SELECT * FROM kuv WHERE v SIMILAR TO 'ABC.*'
----
select
 ├── columns: k:1(int!null) u:2(float) v:3(string!null)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── prune: (1,2)
 ├── interesting orderings: (+1)
 ├── scan kuv
 │    ├── columns: k:1(int!null) u:2(float) v:3(string)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1)
 └── filters
      └── similar-to [type=bool, outer=(3), constraints=(/3: [/'ABC' - /'ABD'))]
           ├── variable: v [type=string]
           └── const: 'ABC.*' [type=string]

opt
SELECT * FROM kuv WHERE v SIMILAR TO 'ABC.*Z'
----
select
 ├── columns: k:1(int!null) u:2(float) v:3(string!null)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── prune: (1,2)
 ├── interesting orderings: (+1)
 ├── scan kuv
 │    ├── columns: k:1(int!null) u:2(float) v:3(string)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1)
 └── filters
      └── similar-to [type=bool, outer=(3), constraints=(/3: [/'ABC' - /'ABD'))]
           ├── variable: v [type=string]
           └── const: 'ABC.*Z' [type=string]

opt
SELECT * FROM kuv WHERE v SIMILAR TO 'ABC'
----
select
 ├── columns: k:1(int!null) u:2(float) v:3(string!null)
 ├── key: (1)
 ├── fd: ()-->(3), (1)-->(2)
 ├── prune: (1,2)
 ├── interesting orderings: (+1)
 ├── scan kuv
 │    ├── columns: k:1(int!null) u:2(float) v:3(string)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1)
 └── filters
      └── similar-to [type=bool, outer=(3), constraints=(/3: [/'ABC' - /'ABC']; tight), fd=()-->(3)]
           ├── variable: v [type=string]
           └── const: 'ABC' [type=string]

opt
SELECT * FROM kuv WHERE v SIMILAR TO '(ABC|ABCDEF).*'
----
select
 ├── columns: k:1(int!null) u:2(float) v:3(string!null)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── prune: (1,2)
 ├── interesting orderings: (+1)
 ├── scan kuv
 │    ├── columns: k:1(int!null) u:2(float) v:3(string)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1)
 └── filters
      └── similar-to [type=bool, outer=(3), constraints=(/3: [/'ABC' - /'ABD'))]
           ├── variable: v [type=string]
           └── const: '(ABC|ABCDEF).*' [type=string]

opt
SELECT * FROM kuv WHERE v SIMILAR TO '.*'
----
select
 ├── columns: k:1(int!null) u:2(float) v:3(string!null)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── prune: (1,2)
 ├── interesting orderings: (+1)
 ├── scan kuv
 │    ├── columns: k:1(int!null) u:2(float) v:3(string)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3)
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1)
 └── filters
      └── similar-to [type=bool, outer=(3), constraints=(/3: [/'' - ])]
           ├── variable: v [type=string]
           └── const: '.*' [type=string]
