optsteps
SELECT 1
----
================================================================================
Initial expression
  Cost: 0.05
================================================================================
  project
   ├── columns: "?column?":1(int!null)
   ├── cardinality: [1 - 1]
   ├── key: ()
   ├── fd: ()-->(1)
   ├── values
   │    ├── cardinality: [1 - 1]
   │    ├── key: ()
   │    └── tuple [type=tuple]
   └── projections
        └── const: 1 [as="?column?":1, type=int]
================================================================================
MergeProjectWithValues
  Cost: 0.02
================================================================================
  -project
  +values
    ├── columns: "?column?":1(int!null)
    ├── cardinality: [1 - 1]
    ├── key: ()
    ├── fd: ()-->(1)
  - ├── values
  - │    ├── cardinality: [1 - 1]
  - │    ├── key: ()
  - │    └── tuple [type=tuple]
  - └── projections
  -      └── const: 1 [as="?column?":1, type=int]
  + └── tuple [type=tuple{int}]
  +      └── const: 1 [type=int]
================================================================================
Final best expression
  Cost: 0.02
================================================================================
  values
   ├── columns: "?column?":1(int!null)
   ├── cardinality: [1 - 1]
   ├── key: ()
   ├── fd: ()-->(1)
   └── tuple [type=tuple{int}]
        └── const: 1 [type=int]

exec-ddl
CREATE TABLE ab (a INT PRIMARY KEY, b INT, INDEX(b))
----

optsteps
SELECT * FROM ab WHERE b=1
----
================================================================================
Initial expression
  Cost: 1098.77
================================================================================
  project
   ├── columns: a:1(int!null) b:2(int!null)
   ├── key: (1)
   ├── fd: ()-->(2)
   └── select
        ├── columns: a:1(int!null) b:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
        ├── key: (1)
        ├── fd: ()-->(2), (1)-->(3,4)
        ├── scan ab
        │    ├── columns: a:1(int!null) b:2(int) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
        │    ├── key: (1)
        │    └── fd: (1)-->(2-4)
        └── filters
             └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
                  ├── variable: b:2 [type=int]
                  └── const: 1 [type=int]
================================================================================
PruneSelectCols
  Cost: 1078.57
================================================================================
   project
    ├── columns: a:1(int!null) b:2(int!null)
    ├── key: (1)
    ├── fd: ()-->(2)
    └── select
  -      ├── columns: a:1(int!null) b:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
  +      ├── columns: a:1(int!null) b:2(int!null)
         ├── key: (1)
  -      ├── fd: ()-->(2), (1)-->(3,4)
  +      ├── fd: ()-->(2)
         ├── scan ab
  -      │    ├── columns: a:1(int!null) b:2(int) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
  +      │    ├── columns: a:1(int!null) b:2(int)
         │    ├── key: (1)
  -      │    └── fd: (1)-->(2-4)
  +      │    └── fd: (1)-->(2)
         └── filters
              └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
                   ├── variable: b:2 [type=int]
                   └── const: 1 [type=int]
================================================================================
EliminateProject
  Cost: 1078.45
================================================================================
  -project
  +select
    ├── columns: a:1(int!null) b:2(int!null)
    ├── key: (1)
    ├── fd: ()-->(2)
  - └── select
  -      ├── columns: a:1(int!null) b:2(int!null)
  -      ├── key: (1)
  -      ├── fd: ()-->(2)
  -      ├── scan ab
  -      │    ├── columns: a:1(int!null) b:2(int)
  -      │    ├── key: (1)
  -      │    └── fd: (1)-->(2)
  -      └── filters
  -           └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
  -                ├── variable: b:2 [type=int]
  -                └── const: 1 [type=int]
  + ├── scan ab
  + │    ├── columns: a:1(int!null) b:2(int)
  + │    ├── key: (1)
  + │    └── fd: (1)-->(2)
  + └── filters
  +      └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
  +           ├── variable: b:2 [type=int]
  +           └── const: 1 [type=int]
--------------------------------------------------------------------------------
GenerateIndexScans (higher cost)
--------------------------------------------------------------------------------
   select
    ├── columns: a:1(int!null) b:2(int!null)
    ├── key: (1)
    ├── fd: ()-->(2)
  - ├── scan ab
  + ├── scan ab@ab_b_idx
    │    ├── columns: a:1(int!null) b:2(int)
    │    ├── key: (1)
    │    └── fd: (1)-->(2)
    └── filters
         └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
              ├── variable: b:2 [type=int]
              └── const: 1 [type=int]
--------------------------------------------------------------------------------
GeneratePartialIndexScans (no changes)
--------------------------------------------------------------------------------
================================================================================
GenerateConstrainedScans
  Cost: 28.42
================================================================================
  -select
  +scan ab@ab_b_idx
    ├── columns: a:1(int!null) b:2(int!null)
  + ├── constraint: /2/1: [/1 - /1]
    ├── key: (1)
  - ├── fd: ()-->(2)
  - ├── scan ab
  - │    ├── columns: a:1(int!null) b:2(int)
  - │    ├── key: (1)
  - │    └── fd: (1)-->(2)
  - └── filters
  -      └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
  -           ├── variable: b:2 [type=int]
  -           └── const: 1 [type=int]
  + └── fd: ()-->(2)
--------------------------------------------------------------------------------
GenerateZigzagJoins (no changes)
--------------------------------------------------------------------------------
================================================================================
Final best expression
  Cost: 28.42
================================================================================
  scan ab@ab_b_idx
   ├── columns: a:1(int!null) b:2(int!null)
   ├── constraint: /2/1: [/1 - /1]
   ├── key: (1)
   └── fd: ()-->(2)

normsteps
SELECT * FROM ab WHERE b=1
----
================================================================================
Initial expression
  Cost: 1098.77
================================================================================
  project
   ├── columns: a:1(int!null) b:2(int!null)
   ├── key: (1)
   ├── fd: ()-->(2)
   └── select
        ├── columns: a:1(int!null) b:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
        ├── key: (1)
        ├── fd: ()-->(2), (1)-->(3,4)
        ├── scan ab
        │    ├── columns: a:1(int!null) b:2(int) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
        │    ├── key: (1)
        │    └── fd: (1)-->(2-4)
        └── filters
             └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
                  ├── variable: b:2 [type=int]
                  └── const: 1 [type=int]
================================================================================
PruneSelectCols
  Cost: 1078.57
================================================================================
   project
    ├── columns: a:1(int!null) b:2(int!null)
    ├── key: (1)
    ├── fd: ()-->(2)
    └── select
  -      ├── columns: a:1(int!null) b:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
  +      ├── columns: a:1(int!null) b:2(int!null)
         ├── key: (1)
  -      ├── fd: ()-->(2), (1)-->(3,4)
  +      ├── fd: ()-->(2)
         ├── scan ab
  -      │    ├── columns: a:1(int!null) b:2(int) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
  +      │    ├── columns: a:1(int!null) b:2(int)
         │    ├── key: (1)
  -      │    └── fd: (1)-->(2-4)
  +      │    └── fd: (1)-->(2)
         └── filters
              └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
                   ├── variable: b:2 [type=int]
                   └── const: 1 [type=int]
================================================================================
EliminateProject
  Cost: 1078.45
================================================================================
  -project
  +select
    ├── columns: a:1(int!null) b:2(int!null)
    ├── key: (1)
    ├── fd: ()-->(2)
  - └── select
  -      ├── columns: a:1(int!null) b:2(int!null)
  -      ├── key: (1)
  -      ├── fd: ()-->(2)
  -      ├── scan ab
  -      │    ├── columns: a:1(int!null) b:2(int)
  -      │    ├── key: (1)
  -      │    └── fd: (1)-->(2)
  -      └── filters
  -           └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
  -                ├── variable: b:2 [type=int]
  -                └── const: 1 [type=int]
  + ├── scan ab
  + │    ├── columns: a:1(int!null) b:2(int)
  + │    ├── key: (1)
  + │    └── fd: (1)-->(2)
  + └── filters
  +      └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
  +           ├── variable: b:2 [type=int]
  +           └── const: 1 [type=int]
================================================================================
Final best expression
  Cost: 1078.45
================================================================================
  select
   ├── columns: a:1(int!null) b:2(int!null)
   ├── key: (1)
   ├── fd: ()-->(2)
   ├── scan ab@ab_b_idx
   │    ├── columns: a:1(int!null) b:2(int)
   │    ├── key: (1)
   │    └── fd: (1)-->(2)
   └── filters
        └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
             ├── variable: b:2 [type=int]
             └── const: 1 [type=int]

exec-ddl
CREATE TABLE customers (
    id INT8 NOT NULL,
    name STRING NOT NULL,
    address STRING NULL,
    CONSTRAINT "primary" PRIMARY KEY (id ASC),
    FAMILY "primary" (id, name, address)
)
----

exec-ddl
CREATE TABLE orders (
    id INT8 NOT NULL,
    customer_id INT8 NULL,
    status STRING NOT NULL,
    CONSTRAINT "primary" PRIMARY KEY (id ASC),
    CONSTRAINT fk_customer_id_ref_customers FOREIGN KEY (customer_id) REFERENCES customers(id),
    INDEX orders_auto_index_fk_customer_id_ref_customers (customer_id ASC),
    FAMILY "primary" (id, customer_id, status),
    CONSTRAINT check_status CHECK (status IN ('open':::STRING, 'complete':::STRING, 'cancelled':::STRING))
)
----

# Verify that we don't crash when a normalization rule runs on a constraint
# expression that is attached to the TableMeta but otherwise not used.
# In this example, the rule is NormalizeInConst.
optsteps
SELECT * FROM orders LEFT JOIN customers ON customer_id = customers.id
----
================================================================================
Initial expression
  Cost: 2267.83
================================================================================
  project
   ├── columns: id:1(int!null) customer_id:2(int) status:3(string!null) id:6(int) name:7(string) address:8(string)
   ├── key: (1)
   ├── fd: (1)-->(2,3,6-8), (6)-->(7,8)
   └── left-join (hash)
        ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null) orders.crdb_internal_mvcc_timestamp:4(decimal) orders.tableoid:5(oid) customers.id:6(int) name:7(string) address:8(string) customers.crdb_internal_mvcc_timestamp:9(decimal) customers.tableoid:10(oid)
        ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-more)
        ├── key: (1)
        ├── fd: (1)-->(2-10), (6)-->(7-10)
        ├── scan orders
        │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null) orders.crdb_internal_mvcc_timestamp:4(decimal) orders.tableoid:5(oid)
        │    ├── check constraint expressions
        │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
        │    │         ├── variable: status:3 [type=string]
        │    │         └── tuple [type=tuple{string, string, string}]
        │    │              ├── const: 'open' [type=string]
        │    │              ├── const: 'complete' [type=string]
        │    │              └── const: 'cancelled' [type=string]
        │    ├── key: (1)
        │    └── fd: (1)-->(2-5)
        ├── scan customers
        │    ├── columns: customers.id:6(int!null) name:7(string!null) address:8(string) customers.crdb_internal_mvcc_timestamp:9(decimal) customers.tableoid:10(oid)
        │    ├── key: (6)
        │    └── fd: (6)-->(7-10)
        └── filters
             └── eq [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
                  ├── variable: customer_id:2 [type=int]
                  └── variable: customers.id:6 [type=int]
================================================================================
NormalizeInConst
  Cost: 2267.83
================================================================================
   project
    ├── columns: id:1(int!null) customer_id:2(int) status:3(string!null) id:6(int) name:7(string) address:8(string)
    ├── key: (1)
    ├── fd: (1)-->(2,3,6-8), (6)-->(7,8)
    └── left-join (hash)
         ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null) orders.crdb_internal_mvcc_timestamp:4(decimal) orders.tableoid:5(oid) customers.id:6(int) name:7(string) address:8(string) customers.crdb_internal_mvcc_timestamp:9(decimal) customers.tableoid:10(oid)
         ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-more)
         ├── key: (1)
         ├── fd: (1)-->(2-10), (6)-->(7-10)
         ├── scan orders
         │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null) orders.crdb_internal_mvcc_timestamp:4(decimal) orders.tableoid:5(oid)
         │    ├── check constraint expressions
         │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
         │    │         ├── variable: status:3 [type=string]
         │    │         └── tuple [type=tuple{string, string, string}]
  -      │    │              ├── const: 'open' [type=string]
  +      │    │              ├── const: 'cancelled' [type=string]
         │    │              ├── const: 'complete' [type=string]
  -      │    │              └── const: 'cancelled' [type=string]
  +      │    │              └── const: 'open' [type=string]
         │    ├── key: (1)
         │    └── fd: (1)-->(2-5)
         ├── scan customers
         │    ├── columns: customers.id:6(int!null) name:7(string!null) address:8(string) customers.crdb_internal_mvcc_timestamp:9(decimal) customers.tableoid:10(oid)
         │    ├── key: (6)
         │    └── fd: (6)-->(7-10)
         └── filters
              └── eq [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
                   ├── variable: customer_id:2 [type=int]
                   └── variable: customers.id:6 [type=int]
================================================================================
PruneJoinLeftCols
  Cost: 2247.63
================================================================================
   project
    ├── columns: id:1(int!null) customer_id:2(int) status:3(string!null) id:6(int) name:7(string) address:8(string)
    ├── key: (1)
    ├── fd: (1)-->(2,3,6-8), (6)-->(7,8)
    └── left-join (hash)
  -      ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null) orders.crdb_internal_mvcc_timestamp:4(decimal) orders.tableoid:5(oid) customers.id:6(int) name:7(string) address:8(string) customers.crdb_internal_mvcc_timestamp:9(decimal) customers.tableoid:10(oid)
  +      ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null) customers.id:6(int) name:7(string) address:8(string) customers.crdb_internal_mvcc_timestamp:9(decimal) customers.tableoid:10(oid)
         ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-more)
         ├── key: (1)
  -      ├── fd: (1)-->(2-10), (6)-->(7-10)
  +      ├── fd: (1)-->(2,3,6-10), (6)-->(7-10)
         ├── scan orders
  -      │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null) orders.crdb_internal_mvcc_timestamp:4(decimal) orders.tableoid:5(oid)
  +      │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
         │    ├── check constraint expressions
         │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
         │    │         ├── variable: status:3 [type=string]
         │    │         └── tuple [type=tuple{string, string, string}]
         │    │              ├── const: 'cancelled' [type=string]
         │    │              ├── const: 'complete' [type=string]
         │    │              └── const: 'open' [type=string]
         │    ├── key: (1)
  -      │    └── fd: (1)-->(2-5)
  +      │    └── fd: (1)-->(2,3)
         ├── scan customers
         │    ├── columns: customers.id:6(int!null) name:7(string!null) address:8(string) customers.crdb_internal_mvcc_timestamp:9(decimal) customers.tableoid:10(oid)
         │    ├── key: (6)
         │    └── fd: (6)-->(7-10)
         └── filters
              └── eq [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
                   ├── variable: customer_id:2 [type=int]
                   └── variable: customers.id:6 [type=int]
================================================================================
PruneJoinRightCols
  Cost: 2227.43
================================================================================
   project
    ├── columns: id:1(int!null) customer_id:2(int) status:3(string!null) id:6(int) name:7(string) address:8(string)
    ├── key: (1)
    ├── fd: (1)-->(2,3,6-8), (6)-->(7,8)
    └── left-join (hash)
  -      ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null) customers.id:6(int) name:7(string) address:8(string) customers.crdb_internal_mvcc_timestamp:9(decimal) customers.tableoid:10(oid)
  +      ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null) customers.id:6(int) name:7(string) address:8(string)
         ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-more)
         ├── key: (1)
  -      ├── fd: (1)-->(2,3,6-10), (6)-->(7-10)
  +      ├── fd: (1)-->(2,3,6-8), (6)-->(7,8)
         ├── scan orders
         │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
         │    ├── check constraint expressions
         │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
         │    │         ├── variable: status:3 [type=string]
         │    │         └── tuple [type=tuple{string, string, string}]
         │    │              ├── const: 'cancelled' [type=string]
         │    │              ├── const: 'complete' [type=string]
         │    │              └── const: 'open' [type=string]
         │    ├── key: (1)
         │    └── fd: (1)-->(2,3)
         ├── scan customers
  -      │    ├── columns: customers.id:6(int!null) name:7(string!null) address:8(string) customers.crdb_internal_mvcc_timestamp:9(decimal) customers.tableoid:10(oid)
  +      │    ├── columns: customers.id:6(int!null) name:7(string!null) address:8(string)
         │    ├── key: (6)
  -      │    └── fd: (6)-->(7-10)
  +      │    └── fd: (6)-->(7,8)
         └── filters
              └── eq [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
                   ├── variable: customer_id:2 [type=int]
                   └── variable: customers.id:6 [type=int]
================================================================================
EliminateProject
  Cost: 2217.41
================================================================================
  -project
  +left-join (hash)
    ├── columns: id:1(int!null) customer_id:2(int) status:3(string!null) id:6(int) name:7(string) address:8(string)
  + ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-more)
    ├── key: (1)
    ├── fd: (1)-->(2,3,6-8), (6)-->(7,8)
  - └── left-join (hash)
  -      ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null) customers.id:6(int) name:7(string) address:8(string)
  -      ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-more)
  -      ├── key: (1)
  -      ├── fd: (1)-->(2,3,6-8), (6)-->(7,8)
  -      ├── scan orders
  -      │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
  -      │    ├── check constraint expressions
  -      │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
  -      │    │         ├── variable: status:3 [type=string]
  -      │    │         └── tuple [type=tuple{string, string, string}]
  -      │    │              ├── const: 'cancelled' [type=string]
  -      │    │              ├── const: 'complete' [type=string]
  -      │    │              └── const: 'open' [type=string]
  -      │    ├── key: (1)
  -      │    └── fd: (1)-->(2,3)
  -      ├── scan customers
  -      │    ├── columns: customers.id:6(int!null) name:7(string!null) address:8(string)
  -      │    ├── key: (6)
  -      │    └── fd: (6)-->(7,8)
  -      └── filters
  -           └── eq [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
  -                ├── variable: customer_id:2 [type=int]
  -                └── variable: customers.id:6 [type=int]
  + ├── scan orders
  + │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
  + │    ├── check constraint expressions
  + │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
  + │    │         ├── variable: status:3 [type=string]
  + │    │         └── tuple [type=tuple{string, string, string}]
  + │    │              ├── const: 'cancelled' [type=string]
  + │    │              ├── const: 'complete' [type=string]
  + │    │              └── const: 'open' [type=string]
  + │    ├── key: (1)
  + │    └── fd: (1)-->(2,3)
  + ├── scan customers
  + │    ├── columns: customers.id:6(int!null) name:7(string!null) address:8(string)
  + │    ├── key: (6)
  + │    └── fd: (6)-->(7,8)
  + └── filters
  +      └── eq [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
  +           ├── variable: customer_id:2 [type=int]
  +           └── variable: customers.id:6 [type=int]
--------------------------------------------------------------------------------
GenerateIndexScans (no changes)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
GenerateIndexScans (no changes)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
ReorderJoins (no changes)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
CommuteLeftJoin (higher cost)
--------------------------------------------------------------------------------
  -left-join (hash)
  +right-join (hash)
    ├── columns: id:1(int!null) customer_id:2(int) status:3(string!null) id:6(int) name:7(string) address:8(string)
  - ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-more)
    ├── key: (1)
    ├── fd: (1)-->(2,3,6-8), (6)-->(7,8)
  + ├── scan customers
  + │    ├── columns: customers.id:6(int!null) name:7(string!null) address:8(string)
  + │    ├── key: (6)
  + │    └── fd: (6)-->(7,8)
    ├── scan orders
    │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
    │    ├── check constraint expressions
    │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
    │    │         ├── variable: status:3 [type=string]
    │    │         └── tuple [type=tuple{string, string, string}]
    │    │              ├── const: 'cancelled' [type=string]
    │    │              ├── const: 'complete' [type=string]
    │    │              └── const: 'open' [type=string]
    │    ├── key: (1)
    │    └── fd: (1)-->(2,3)
  - ├── scan customers
  - │    ├── columns: customers.id:6(int!null) name:7(string!null) address:8(string)
  - │    ├── key: (6)
  - │    └── fd: (6)-->(7,8)
    └── filters
         └── eq [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
              ├── variable: customer_id:2 [type=int]
              └── variable: customers.id:6 [type=int]
--------------------------------------------------------------------------------
GenerateMergeJoins (no changes)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
GenerateLookupJoins (higher cost)
--------------------------------------------------------------------------------
  -left-join (hash)
  +left-join (lookup customers)
    ├── columns: id:1(int!null) customer_id:2(int) status:3(string!null) id:6(int) name:7(string) address:8(string)
  - ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-more)
  + ├── key columns: [2] = [6]
  + ├── lookup columns are key
    ├── key: (1)
    ├── fd: (1)-->(2,3,6-8), (6)-->(7,8)
    ├── scan orders
    │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
    │    ├── check constraint expressions
    │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
    │    │         ├── variable: status:3 [type=string]
    │    │         └── tuple [type=tuple{string, string, string}]
    │    │              ├── const: 'cancelled' [type=string]
    │    │              ├── const: 'complete' [type=string]
    │    │              └── const: 'open' [type=string]
    │    ├── key: (1)
    │    └── fd: (1)-->(2,3)
  - ├── scan customers
  - │    ├── columns: customers.id:6(int!null) name:7(string!null) address:8(string)
  - │    ├── key: (6)
  - │    └── fd: (6)-->(7,8)
  - └── filters
  -      └── eq [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
  -           ├── variable: customer_id:2 [type=int]
  -           └── variable: customers.id:6 [type=int]
  + └── filters (true)
--------------------------------------------------------------------------------
GenerateMergeJoins (higher cost)
--------------------------------------------------------------------------------
  -left-join (lookup customers)
  +right-join (merge)
    ├── columns: id:1(int!null) customer_id:2(int) status:3(string!null) id:6(int) name:7(string) address:8(string)
  - ├── key columns: [2] = [6]
  - ├── lookup columns are key
  + ├── left ordering: +6
  + ├── right ordering: +2
    ├── key: (1)
    ├── fd: (1)-->(2,3,6-8), (6)-->(7,8)
  - ├── scan orders
  + ├── scan customers
  + │    ├── columns: customers.id:6(int!null) name:7(string!null) address:8(string)
  + │    ├── key: (6)
  + │    ├── fd: (6)-->(7,8)
  + │    └── ordering: +6
  + ├── sort
    │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
  - │    ├── check constraint expressions
  - │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
  - │    │         ├── variable: status:3 [type=string]
  - │    │         └── tuple [type=tuple{string, string, string}]
  - │    │              ├── const: 'cancelled' [type=string]
  - │    │              ├── const: 'complete' [type=string]
  - │    │              └── const: 'open' [type=string]
    │    ├── key: (1)
  - │    └── fd: (1)-->(2,3)
  + │    ├── fd: (1)-->(2,3)
  + │    ├── ordering: +2
  + │    └── scan orders
  + │         ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
  + │         ├── check constraint expressions
  + │         │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
  + │         │         ├── variable: status:3 [type=string]
  + │         │         └── tuple [type=tuple{string, string, string}]
  + │         │              ├── const: 'cancelled' [type=string]
  + │         │              ├── const: 'complete' [type=string]
  + │         │              └── const: 'open' [type=string]
  + │         ├── key: (1)
  + │         └── fd: (1)-->(2,3)
    └── filters (true)
================================================================================
Final best expression
  Cost: 2217.41
================================================================================
  left-join (hash)
   ├── columns: id:1(int!null) customer_id:2(int) status:3(string!null) id:6(int) name:7(string) address:8(string)
   ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-more)
   ├── key: (1)
   ├── fd: (1)-->(2,3,6-8), (6)-->(7,8)
   ├── scan orders
   │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
   │    ├── check constraint expressions
   │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
   │    │         ├── variable: status:3 [type=string]
   │    │         └── tuple [type=tuple{string, string, string}]
   │    │              ├── const: 'cancelled' [type=string]
   │    │              ├── const: 'complete' [type=string]
   │    │              └── const: 'open' [type=string]
   │    ├── key: (1)
   │    └── fd: (1)-->(2,3)
   ├── scan customers
   │    ├── columns: customers.id:6(int!null) name:7(string!null) address:8(string)
   │    ├── key: (6)
   │    └── fd: (6)-->(7,8)
   └── filters
        └── eq [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
             ├── variable: customer_id:2 [type=int]
             └── variable: customers.id:6 [type=int]

exec-ddl
CREATE TABLE comp (
  k INT,
  c BOOL AS (k IN (1,3,2)) STORED,
  INDEX (c, k)
)
----

# Verify that we don't crash when a normalization rule runs on a computed
# column expression that is attached to the TableMeta but otherwise not used.
# In this example, the rule is NormalizeInConst.
optsteps
SELECT * FROM comp WHERE k=1
----
================================================================================
Initial expression
  Cost: 1118.97
================================================================================
  project
   ├── columns: k:1(int!null) c:2(bool)
   ├── fd: ()-->(1,2)
   └── select
        ├── columns: k:1(int!null) c:2(bool) rowid:3(int!null) crdb_internal_mvcc_timestamp:4(decimal) tableoid:5(oid)
        ├── key: (3)
        ├── fd: ()-->(1,2), (3)-->(4,5)
        ├── scan comp
        │    ├── columns: k:1(int) c:2(bool) rowid:3(int!null) crdb_internal_mvcc_timestamp:4(decimal) tableoid:5(oid)
        │    ├── computed column expressions
        │    │    └── c:2
        │    │         └── in [type=bool]
        │    │              ├── variable: k:1 [type=int]
        │    │              └── tuple [type=tuple{int, int, int}]
        │    │                   ├── const: 1 [type=int]
        │    │                   ├── const: 3 [type=int]
        │    │                   └── const: 2 [type=int]
        │    ├── key: (3)
        │    └── fd: (3)-->(1,2,4,5), (1)-->(2)
        └── filters
             └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
                  ├── variable: k:1 [type=int]
                  └── const: 1 [type=int]
================================================================================
NormalizeInConst
  Cost: 1118.97
================================================================================
   project
    ├── columns: k:1(int!null) c:2(bool)
    ├── fd: ()-->(1,2)
    └── select
         ├── columns: k:1(int!null) c:2(bool) rowid:3(int!null) crdb_internal_mvcc_timestamp:4(decimal) tableoid:5(oid)
         ├── key: (3)
         ├── fd: ()-->(1,2), (3)-->(4,5)
         ├── scan comp
         │    ├── columns: k:1(int) c:2(bool) rowid:3(int!null) crdb_internal_mvcc_timestamp:4(decimal) tableoid:5(oid)
         │    ├── computed column expressions
         │    │    └── c:2
         │    │         └── in [type=bool]
         │    │              ├── variable: k:1 [type=int]
         │    │              └── tuple [type=tuple{int, int, int}]
         │    │                   ├── const: 1 [type=int]
  -      │    │                   ├── const: 3 [type=int]
  -      │    │                   └── const: 2 [type=int]
  +      │    │                   ├── const: 2 [type=int]
  +      │    │                   └── const: 3 [type=int]
         │    ├── key: (3)
         │    └── fd: (3)-->(1,2,4,5), (1)-->(2)
         └── filters
              └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
                   ├── variable: k:1 [type=int]
                   └── const: 1 [type=int]
================================================================================
PruneSelectCols
  Cost: 1088.67
================================================================================
   project
    ├── columns: k:1(int!null) c:2(bool)
    ├── fd: ()-->(1,2)
    └── select
  -      ├── columns: k:1(int!null) c:2(bool) rowid:3(int!null) crdb_internal_mvcc_timestamp:4(decimal) tableoid:5(oid)
  -      ├── key: (3)
  -      ├── fd: ()-->(1,2), (3)-->(4,5)
  +      ├── columns: k:1(int!null) c:2(bool)
  +      ├── fd: ()-->(1,2)
         ├── scan comp
  -      │    ├── columns: k:1(int) c:2(bool) rowid:3(int!null) crdb_internal_mvcc_timestamp:4(decimal) tableoid:5(oid)
  +      │    ├── columns: k:1(int) c:2(bool)
         │    ├── computed column expressions
         │    │    └── c:2
         │    │         └── in [type=bool]
         │    │              ├── variable: k:1 [type=int]
         │    │              └── tuple [type=tuple{int, int, int}]
         │    │                   ├── const: 1 [type=int]
         │    │                   ├── const: 2 [type=int]
         │    │                   └── const: 3 [type=int]
  -      │    ├── key: (3)
  -      │    └── fd: (3)-->(1,2,4,5), (1)-->(2)
  +      │    └── fd: (1)-->(2)
         └── filters
              └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
                   ├── variable: k:1 [type=int]
                   └── const: 1 [type=int]
================================================================================
EliminateProject
  Cost: 1088.55
================================================================================
  -project
  +select
    ├── columns: k:1(int!null) c:2(bool)
    ├── fd: ()-->(1,2)
  - └── select
  -      ├── columns: k:1(int!null) c:2(bool)
  -      ├── fd: ()-->(1,2)
  -      ├── scan comp
  -      │    ├── columns: k:1(int) c:2(bool)
  -      │    ├── computed column expressions
  -      │    │    └── c:2
  -      │    │         └── in [type=bool]
  -      │    │              ├── variable: k:1 [type=int]
  -      │    │              └── tuple [type=tuple{int, int, int}]
  -      │    │                   ├── const: 1 [type=int]
  -      │    │                   ├── const: 2 [type=int]
  -      │    │                   └── const: 3 [type=int]
  -      │    └── fd: (1)-->(2)
  -      └── filters
  -           └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
  -                ├── variable: k:1 [type=int]
  -                └── const: 1 [type=int]
  + ├── scan comp
  + │    ├── columns: k:1(int) c:2(bool)
  + │    ├── computed column expressions
  + │    │    └── c:2
  + │    │         └── in [type=bool]
  + │    │              ├── variable: k:1 [type=int]
  + │    │              └── tuple [type=tuple{int, int, int}]
  + │    │                   ├── const: 1 [type=int]
  + │    │                   ├── const: 2 [type=int]
  + │    │                   └── const: 3 [type=int]
  + │    └── fd: (1)-->(2)
  + └── filters
  +      └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
  +           ├── variable: k:1 [type=int]
  +           └── const: 1 [type=int]
--------------------------------------------------------------------------------
GenerateIndexScans (higher cost)
--------------------------------------------------------------------------------
   select
    ├── columns: k:1(int!null) c:2(bool)
    ├── fd: ()-->(1,2)
  - ├── scan comp
  + ├── scan comp@comp_c_k_idx
    │    ├── columns: k:1(int) c:2(bool)
  - │    ├── computed column expressions
  - │    │    └── c:2
  - │    │         └── in [type=bool]
  - │    │              ├── variable: k:1 [type=int]
  - │    │              └── tuple [type=tuple{int, int, int}]
  - │    │                   ├── const: 1 [type=int]
  - │    │                   ├── const: 2 [type=int]
  - │    │                   └── const: 3 [type=int]
    │    └── fd: (1)-->(2)
    └── filters
         └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
              ├── variable: k:1 [type=int]
              └── const: 1 [type=int]
--------------------------------------------------------------------------------
GeneratePartialIndexScans (no changes)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
GenerateConstrainedScans (no changes)
--------------------------------------------------------------------------------
================================================================================
FoldComparison
  Cost: 28.52
================================================================================
  -select
  +scan comp@comp_c_k_idx
    ├── columns: k:1(int!null) c:2(bool)
  - ├── fd: ()-->(1,2)
  - ├── scan comp
  - │    ├── columns: k:1(int) c:2(bool)
  - │    ├── computed column expressions
  - │    │    └── c:2
  - │    │         └── in [type=bool]
  - │    │              ├── variable: k:1 [type=int]
  - │    │              └── tuple [type=tuple{int, int, int}]
  - │    │                   ├── const: 1 [type=int]
  - │    │                   ├── const: 2 [type=int]
  - │    │                   └── const: 3 [type=int]
  - │    └── fd: (1)-->(2)
  - └── filters
  -      └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
  -           ├── variable: k:1 [type=int]
  -           └── const: 1 [type=int]
  + ├── constraint: /2/1/3: [/true/1 - /true/1]
  + └── fd: ()-->(1,2)
--------------------------------------------------------------------------------
FoldComparison (normalization of expression outside of memo)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
GenerateZigzagJoins (no changes)
--------------------------------------------------------------------------------
================================================================================
Final best expression
  Cost: 28.52
================================================================================
  scan comp@comp_c_k_idx
   ├── columns: k:1(int!null) c:2(bool)
   ├── constraint: /2/1/3: [/true/1 - /true/1]
   └── fd: ()-->(1,2)

exec-ddl
CREATE TABLE t (i INT)
----

# Verify that CheckExpr is not run during optsteps in order to prevent crashing
# on a partially normalized expressions. CheckExpr would cause a crash in this
# test because the RangeExpr in the InlineConstVar step does not have tight
# constraints.
optsteps
SELECT i FROM (SELECT i FROM t WHERE i > 10 AND i < 20) AS t2 WHERE i = 5
----
================================================================================
Initial expression
  Cost: 1100.50
================================================================================
  select
   ├── columns: i:1(int!null)
   ├── fd: ()-->(1)
   ├── project
   │    ├── columns: i:1(int!null)
   │    └── select
   │         ├── columns: i:1(int!null) rowid:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
   │         ├── key: (2)
   │         ├── fd: (2)-->(1,3,4)
   │         ├── scan t
   │         │    ├── columns: i:1(int) rowid:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
   │         │    ├── key: (2)
   │         │    └── fd: (2)-->(1,3,4)
   │         └── filters
   │              └── and [type=bool, outer=(1), constraints=(/1: [/11 - /19]; tight)]
   │                   ├── gt [type=bool]
   │                   │    ├── variable: i:1 [type=int]
   │                   │    └── const: 10 [type=int]
   │                   └── lt [type=bool]
   │                        ├── variable: i:1 [type=int]
   │                        └── const: 20 [type=int]
   └── filters
        └── eq [type=bool, outer=(1), constraints=(/1: [/5 - /5]; tight), fd=()-->(1)]
             ├── variable: i:1 [type=int]
             └── const: 5 [type=int]
================================================================================
SimplifySelectFilters
  Cost: 1105.38
================================================================================
   select
    ├── columns: i:1(int!null)
    ├── fd: ()-->(1)
    ├── project
    │    ├── columns: i:1(int!null)
    │    └── select
    │         ├── columns: i:1(int!null) rowid:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
    │         ├── key: (2)
    │         ├── fd: (2)-->(1,3,4)
    │         ├── scan t
    │         │    ├── columns: i:1(int) rowid:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
    │         │    ├── key: (2)
    │         │    └── fd: (2)-->(1,3,4)
    │         └── filters
  - │              └── and [type=bool, outer=(1), constraints=(/1: [/11 - /19]; tight)]
  - │                   ├── gt [type=bool]
  - │                   │    ├── variable: i:1 [type=int]
  - │                   │    └── const: 10 [type=int]
  - │                   └── lt [type=bool]
  - │                        ├── variable: i:1 [type=int]
  - │                        └── const: 20 [type=int]
  + │              ├── gt [type=bool, outer=(1), constraints=(/1: [/11 - ]; tight)]
  + │              │    ├── variable: i:1 [type=int]
  + │              │    └── const: 10 [type=int]
  + │              └── lt [type=bool, outer=(1), constraints=(/1: (/NULL - /19]; tight)]
  + │                   ├── variable: i:1 [type=int]
  + │                   └── const: 20 [type=int]
    └── filters
         └── eq [type=bool, outer=(1), constraints=(/1: [/5 - /5]; tight), fd=()-->(1)]
              ├── variable: i:1 [type=int]
              └── const: 5 [type=int]
================================================================================
ConsolidateSelectFilters
  Cost: 1100.50
================================================================================
   select
    ├── columns: i:1(int!null)
    ├── fd: ()-->(1)
    ├── project
    │    ├── columns: i:1(int!null)
    │    └── select
    │         ├── columns: i:1(int!null) rowid:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
    │         ├── key: (2)
    │         ├── fd: (2)-->(1,3,4)
    │         ├── scan t
    │         │    ├── columns: i:1(int) rowid:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
    │         │    ├── key: (2)
    │         │    └── fd: (2)-->(1,3,4)
    │         └── filters
  - │              ├── gt [type=bool, outer=(1), constraints=(/1: [/11 - ]; tight)]
  - │              │    ├── variable: i:1 [type=int]
  - │              │    └── const: 10 [type=int]
  - │              └── lt [type=bool, outer=(1), constraints=(/1: (/NULL - /19]; tight)]
  - │                   ├── variable: i:1 [type=int]
  - │                   └── const: 20 [type=int]
  + │              └── range [type=bool, outer=(1), constraints=(/1: [/11 - /19]; tight)]
  + │                   └── and [type=bool]
  + │                        ├── gt [type=bool]
  + │                        │    ├── variable: i:1 [type=int]
  + │                        │    └── const: 10 [type=int]
  + │                        └── lt [type=bool]
  + │                             ├── variable: i:1 [type=int]
  + │                             └── const: 20 [type=int]
    └── filters
         └── eq [type=bool, outer=(1), constraints=(/1: [/5 - /5]; tight), fd=()-->(1)]
              ├── variable: i:1 [type=int]
              └── const: 5 [type=int]
================================================================================
PruneSelectCols
  Cost: 1070.20
================================================================================
   select
    ├── columns: i:1(int!null)
    ├── fd: ()-->(1)
    ├── project
    │    ├── columns: i:1(int!null)
    │    └── select
  - │         ├── columns: i:1(int!null) rowid:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
  - │         ├── key: (2)
  - │         ├── fd: (2)-->(1,3,4)
  + │         ├── columns: i:1(int!null)
    │         ├── scan t
  - │         │    ├── columns: i:1(int) rowid:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
  - │         │    ├── key: (2)
  - │         │    └── fd: (2)-->(1,3,4)
  + │         │    └── columns: i:1(int)
    │         └── filters
    │              └── range [type=bool, outer=(1), constraints=(/1: [/11 - /19]; tight)]
    │                   └── and [type=bool]
    │                        ├── gt [type=bool]
    │                        │    ├── variable: i:1 [type=int]
    │                        │    └── const: 10 [type=int]
    │                        └── lt [type=bool]
    │                             ├── variable: i:1 [type=int]
    │                             └── const: 20 [type=int]
    └── filters
         └── eq [type=bool, outer=(1), constraints=(/1: [/5 - /5]; tight), fd=()-->(1)]
              ├── variable: i:1 [type=int]
              └── const: 5 [type=int]
================================================================================
EliminateProject
  Cost: 1069.28
================================================================================
   select
    ├── columns: i:1(int!null)
    ├── fd: ()-->(1)
  - ├── project
  + ├── select
    │    ├── columns: i:1(int!null)
  - │    └── select
  - │         ├── columns: i:1(int!null)
  - │         ├── scan t
  - │         │    └── columns: i:1(int)
  - │         └── filters
  - │              └── range [type=bool, outer=(1), constraints=(/1: [/11 - /19]; tight)]
  - │                   └── and [type=bool]
  - │                        ├── gt [type=bool]
  - │                        │    ├── variable: i:1 [type=int]
  - │                        │    └── const: 10 [type=int]
  - │                        └── lt [type=bool]
  - │                             ├── variable: i:1 [type=int]
  - │                             └── const: 20 [type=int]
  + │    ├── scan t
  + │    │    └── columns: i:1(int)
  + │    └── filters
  + │         └── range [type=bool, outer=(1), constraints=(/1: [/11 - /19]; tight)]
  + │              └── and [type=bool]
  + │                   ├── gt [type=bool]
  + │                   │    ├── variable: i:1 [type=int]
  + │                   │    └── const: 10 [type=int]
  + │                   └── lt [type=bool]
  + │                        ├── variable: i:1 [type=int]
  + │                        └── const: 20 [type=int]
    └── filters
         └── eq [type=bool, outer=(1), constraints=(/1: [/5 - /5]; tight), fd=()-->(1)]
              ├── variable: i:1 [type=int]
              └── const: 5 [type=int]
================================================================================
MergeSelects
  Cost: 1068.36
================================================================================
   select
    ├── columns: i:1(int!null)
    ├── fd: ()-->(1)
  - ├── select
  - │    ├── columns: i:1(int!null)
  - │    ├── scan t
  - │    │    └── columns: i:1(int)
  - │    └── filters
  - │         └── range [type=bool, outer=(1), constraints=(/1: [/11 - /19]; tight)]
  - │              └── and [type=bool]
  - │                   ├── gt [type=bool]
  - │                   │    ├── variable: i:1 [type=int]
  - │                   │    └── const: 10 [type=int]
  - │                   └── lt [type=bool]
  - │                        ├── variable: i:1 [type=int]
  - │                        └── const: 20 [type=int]
  + ├── scan t
  + │    └── columns: i:1(int)
    └── filters
  +      ├── range [type=bool, outer=(1), constraints=(/1: [/11 - /19]; tight)]
  +      │    └── and [type=bool]
  +      │         ├── gt [type=bool]
  +      │         │    ├── variable: i:1 [type=int]
  +      │         │    └── const: 10 [type=int]
  +      │         └── lt [type=bool]
  +      │              ├── variable: i:1 [type=int]
  +      │              └── const: 20 [type=int]
         └── eq [type=bool, outer=(1), constraints=(/1: [/5 - /5]; tight), fd=()-->(1)]
              ├── variable: i:1 [type=int]
              └── const: 5 [type=int]
================================================================================
InlineConstVar
  Cost: 1068.36
================================================================================
   select
    ├── columns: i:1(int!null)
    ├── fd: ()-->(1)
    ├── scan t
    │    └── columns: i:1(int)
    └── filters
  -      ├── range [type=bool, outer=(1), constraints=(/1: [/11 - /19]; tight)]
  +      ├── range [type=bool]
         │    └── and [type=bool]
         │         ├── gt [type=bool]
  -      │         │    ├── variable: i:1 [type=int]
  +      │         │    ├── const: 5 [type=int]
         │         │    └── const: 10 [type=int]
         │         └── lt [type=bool]
  -      │              ├── variable: i:1 [type=int]
  +      │              ├── const: 5 [type=int]
         │              └── const: 20 [type=int]
         └── eq [type=bool, outer=(1), constraints=(/1: [/5 - /5]; tight), fd=()-->(1)]
              ├── variable: i:1 [type=int]
              └── const: 5 [type=int]
================================================================================
FoldComparison
  Cost: 1068.35
================================================================================
   select
    ├── columns: i:1(int!null)
  + ├── cardinality: [0 - 0]
    ├── fd: ()-->(1)
    ├── scan t
    │    └── columns: i:1(int)
    └── filters
  -      ├── range [type=bool]
  +      ├── range [type=bool, constraints=(contradiction; tight)]
         │    └── and [type=bool]
  -      │         ├── gt [type=bool]
  -      │         │    ├── const: 5 [type=int]
  -      │         │    └── const: 10 [type=int]
  +      │         ├── false [type=bool]
         │         └── lt [type=bool]
         │              ├── const: 5 [type=int]
         │              └── const: 20 [type=int]
         └── eq [type=bool, outer=(1), constraints=(/1: [/5 - /5]; tight), fd=()-->(1)]
              ├── variable: i:1 [type=int]
              └── const: 5 [type=int]
================================================================================
FoldComparison
  Cost: 1068.35
================================================================================
   select
    ├── columns: i:1(int!null)
    ├── cardinality: [0 - 0]
    ├── fd: ()-->(1)
    ├── scan t
    │    └── columns: i:1(int)
    └── filters
         ├── range [type=bool, constraints=(contradiction; tight)]
         │    └── and [type=bool]
         │         ├── false [type=bool]
  -      │         └── lt [type=bool]
  -      │              ├── const: 5 [type=int]
  -      │              └── const: 20 [type=int]
  +      │         └── true [type=bool]
         └── eq [type=bool, outer=(1), constraints=(/1: [/5 - /5]; tight), fd=()-->(1)]
              ├── variable: i:1 [type=int]
              └── const: 5 [type=int]
================================================================================
SimplifyAndTrue
  Cost: 1068.35
================================================================================
   select
    ├── columns: i:1(int!null)
    ├── cardinality: [0 - 0]
    ├── fd: ()-->(1)
    ├── scan t
    │    └── columns: i:1(int)
    └── filters
         ├── range [type=bool, constraints=(contradiction; tight)]
  -      │    └── and [type=bool]
  -      │         ├── false [type=bool]
  -      │         └── true [type=bool]
  +      │    └── false [type=bool]
         └── eq [type=bool, outer=(1), constraints=(/1: [/5 - /5]; tight), fd=()-->(1)]
              ├── variable: i:1 [type=int]
              └── const: 5 [type=int]
================================================================================
SimplifyRange
  Cost: 1068.35
================================================================================
   select
    ├── columns: i:1(int!null)
    ├── cardinality: [0 - 0]
    ├── fd: ()-->(1)
    ├── scan t
    │    └── columns: i:1(int)
    └── filters
  -      ├── range [type=bool, constraints=(contradiction; tight)]
  -      │    └── false [type=bool]
  +      ├── false [type=bool, constraints=(contradiction; tight)]
         └── eq [type=bool, outer=(1), constraints=(/1: [/5 - /5]; tight), fd=()-->(1)]
              ├── variable: i:1 [type=int]
              └── const: 5 [type=int]
================================================================================
SimplifySelectFilters
  Cost: 1068.34
================================================================================
   select
  - ├── columns: i:1(int!null)
  + ├── columns: i:1(int)
    ├── cardinality: [0 - 0]
  - ├── fd: ()-->(1)
    ├── scan t
    │    └── columns: i:1(int)
    └── filters
  -      ├── false [type=bool, constraints=(contradiction; tight)]
  -      └── eq [type=bool, outer=(1), constraints=(/1: [/5 - /5]; tight), fd=()-->(1)]
  -           ├── variable: i:1 [type=int]
  -           └── const: 5 [type=int]
  +      └── false [type=bool, constraints=(contradiction; tight)]
================================================================================
SimplifyZeroCardinalityGroup
  Cost: 0.01
================================================================================
  -select
  - ├── columns: i:1(int)
  +values
  + ├── columns: i:1(int!null)
    ├── cardinality: [0 - 0]
  - ├── scan t
  - │    └── columns: i:1(int)
  - └── filters
  -      └── false [type=bool, constraints=(contradiction; tight)]
  + ├── key: ()
  + └── fd: ()-->(1)
================================================================================
Final best expression
  Cost: 0.01
================================================================================
  values
   ├── columns: i:1(int!null)
   ├── cardinality: [0 - 0]
   ├── key: ()
   └── fd: ()-->(1)

optsteps split-diff
SELECT 1
----
================================================================================
Initial expression
  Cost: 0.05
================================================================================
  project
   ├── columns: "?column?":1(int!null)
   ├── cardinality: [1 - 1]
   ├── key: ()
   ├── fd: ()-->(1)
   ├── values
   │    ├── cardinality: [1 - 1]
   │    ├── key: ()
   │    └── tuple [type=tuple]
   └── projections
        └── const: 1 [as="?column?":1, type=int]
================================================================================
MergeProjectWithValues
  Cost: 0.02
================================================================================
<<<<<<< before
  project
   ├── columns: "?column?":1(int!null)
   ├── cardinality: [1 - 1]
   ├── key: ()
   ├── fd: ()-->(1)
   ├── values
   │    ├── cardinality: [1 - 1]
   │    ├── key: ()
   │    └── tuple [type=tuple]
   └── projections
        └── const: 1 [as="?column?":1, type=int]
=======
  values
   ├── columns: "?column?":1(int!null)
   ├── cardinality: [1 - 1]
   ├── key: ()
   ├── fd: ()-->(1)
   └── tuple [type=tuple{int}]
        └── const: 1 [type=int]
>>>>>>> after
================================================================================
Final best expression
  Cost: 0.02
================================================================================
  values
   ├── columns: "?column?":1(int!null)
   ├── cardinality: [1 - 1]
   ├── key: ()
   ├── fd: ()-->(1)
   └── tuple [type=tuple{int}]
        └── const: 1 [type=int]

exec-ddl
CREATE TABLE chk (
	id INT PRIMARY KEY,
	a INT,
	CHECK (a > 1+1)
)
----

# Verify that we don't crash when a normalization rule runs on a constraint
# expression that is never attached to the TableMeta.
# In this example, the rule is FoldBinary and the check constraint is not
# attached to the TableMeta because it may evaluate to NULL, which is considered
# a passing CHECK constraint and therefore the expression is not a universal
# truth about the contents of the table.
optsteps
SELECT id FROM chk
----
================================================================================
Initial expression
  Cost: 1098.64
================================================================================
  project
   ├── columns: id:1(int!null)
   ├── key: (1)
   └── scan chk
        ├── columns: id:1(int!null) a:2(int) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
        ├── key: (1)
        └── fd: (1)-->(2-4)
--------------------------------------------------------------------------------
FoldBinary (normalization of expression outside of memo)
--------------------------------------------------------------------------------
================================================================================
PruneScanCols
  Cost: 1068.34
================================================================================
   project
    ├── columns: id:1(int!null)
    ├── key: (1)
    └── scan chk
  -      ├── columns: id:1(int!null) a:2(int) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
  -      ├── key: (1)
  -      └── fd: (1)-->(2-4)
  +      ├── columns: id:1(int!null)
  +      └── key: (1)
================================================================================
EliminateProject
  Cost: 1058.32
================================================================================
  -project
  +scan chk
    ├── columns: id:1(int!null)
  - ├── key: (1)
  - └── scan chk
  -      ├── columns: id:1(int!null)
  -      └── key: (1)
  + └── key: (1)
--------------------------------------------------------------------------------
GenerateIndexScans (no changes)
--------------------------------------------------------------------------------
================================================================================
Final best expression
  Cost: 1058.32
================================================================================
  scan chk
   ├── columns: id:1(int!null)
   └── key: (1)
