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

opt
SELECT * FROM t WHERE a = NULL
----
values
 ├── columns: a:1(int) b:2(bool) c:3(string)
 ├── cardinality: [0 - 0]
 ├── key: ()
 ├── fd: ()-->(1-3)
 └── prune: (1-3)

opt
SELECT * FROM t WHERE a < NULL
----
values
 ├── columns: a:1(int) b:2(bool) c:3(string)
 ├── cardinality: [0 - 0]
 ├── key: ()
 ├── fd: ()-->(1-3)
 └── prune: (1-3)

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

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

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

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

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

# Test that we get a not-NULL constraint on x.
opt
SELECT * FROM xy WHERE x > abs(y)
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── scan xy
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── gt [type=bool, outer=(1,2), constraints=(/1: (/NULL - ])]
           ├── variable: x [type=int]
           └── function: abs [type=int]
                └── variable: y [type=int]

# Test that we get a not-NULL constraint on x.
opt
SELECT * FROM xy WHERE sin(x::float)::int < x
----
select
 ├── columns: x:1(int!null) y:2(int)
 ├── prune: (2)
 ├── scan xy
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── gt [type=bool, outer=(1), constraints=(/1: (/NULL - ])]
           ├── variable: x [type=int]
           └── cast: INT8 [type=int]
                └── function: sin [type=float]
                     └── cast: FLOAT8 [type=float]
                          └── variable: x [type=int]

# Test that we get a not-NULL constraint on x and y.
opt
SELECT * FROM xy WHERE x > y
----
select
 ├── columns: x:1(int!null) y:2(int!null)
 ├── scan xy
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── gt [type=bool, outer=(1,2), constraints=(/1: (/NULL - ]; /2: (/NULL - ])]
           ├── variable: x [type=int]
           └── variable: y [type=int]

# Test that we get a not-NULL constraint on x and y.
opt
SELECT * FROM xy WHERE x = y
----
select
 ├── columns: x:1(int!null) y:2(int!null)
 ├── fd: (1)==(2), (2)==(1)
 ├── scan xy
 │    ├── columns: x:1(int) y:2(int)
 │    └── prune: (1,2)
 └── filters
      └── eq [type=bool, outer=(1,2), constraints=(/1: (/NULL - ]; /2: (/NULL - ]), fd=(1)==(2), (2)==(1)]
           ├── variable: x [type=int]
           └── variable: y [type=int]
