github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/testutils/opttester/testdata/opt-steps (about)

     1  optsteps
     2  SELECT 1
     3  ----
     4  ================================================================================
     5  Initial expression
     6    Cost: 0.05
     7  ================================================================================
     8    project
     9     ├── columns: "?column?":1(int!null)
    10     ├── cardinality: [1 - 1]
    11     ├── key: ()
    12     ├── fd: ()-->(1)
    13     ├── values
    14     │    ├── cardinality: [1 - 1]
    15     │    ├── key: ()
    16     │    └── tuple [type=tuple]
    17     └── projections
    18          └── const: 1 [as="?column?":1, type=int]
    19  ================================================================================
    20  MergeProjectWithValues
    21    Cost: 0.02
    22  ================================================================================
    23    -project
    24    +values
    25      ├── columns: "?column?":1(int!null)
    26      ├── cardinality: [1 - 1]
    27      ├── key: ()
    28      ├── fd: ()-->(1)
    29    - ├── values
    30    - │    ├── cardinality: [1 - 1]
    31    - │    ├── key: ()
    32    - │    └── tuple [type=tuple]
    33    - └── projections
    34    -      └── const: 1 [as="?column?":1, type=int]
    35    + └── tuple [type=tuple{int}]
    36    +      └── const: 1 [type=int]
    37  ================================================================================
    38  Final best expression
    39    Cost: 0.02
    40  ================================================================================
    41    values
    42     ├── columns: "?column?":1(int!null)
    43     ├── cardinality: [1 - 1]
    44     ├── key: ()
    45     ├── fd: ()-->(1)
    46     └── tuple [type=tuple{int}]
    47          └── const: 1 [type=int]
    48  
    49  exec-ddl
    50  CREATE TABLE ab (a INT PRIMARY KEY, b INT, INDEX(b))
    51  ----
    52  
    53  optsteps
    54  SELECT * FROM ab WHERE b=1
    55  ----
    56  ================================================================================
    57  Initial expression
    58    Cost: 1050.03
    59  ================================================================================
    60    select
    61     ├── columns: a:1(int!null) b:2(int!null)
    62     ├── key: (1)
    63     ├── fd: ()-->(2)
    64     ├── scan ab
    65     │    ├── columns: a:1(int!null) b:2(int)
    66     │    ├── key: (1)
    67     │    └── fd: (1)-->(2)
    68     └── filters
    69          └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
    70               ├── variable: b:2 [type=int]
    71               └── const: 1 [type=int]
    72  --------------------------------------------------------------------------------
    73  GenerateIndexScans (higher cost)
    74  --------------------------------------------------------------------------------
    75     select
    76      ├── columns: a:1(int!null) b:2(int!null)
    77      ├── key: (1)
    78      ├── fd: ()-->(2)
    79    - ├── scan ab
    80    + ├── scan ab@secondary
    81      │    ├── columns: a:1(int!null) b:2(int)
    82      │    ├── key: (1)
    83      │    └── fd: (1)-->(2)
    84      └── filters
    85           └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
    86                ├── variable: b:2 [type=int]
    87                └── const: 1 [type=int]
    88  --------------------------------------------------------------------------------
    89  GenerateZigzagJoins (no changes)
    90  --------------------------------------------------------------------------------
    91  ================================================================================
    92  GenerateConstrainedScans
    93    Cost: 10.41
    94  ================================================================================
    95    -select
    96    +scan ab@secondary
    97      ├── columns: a:1(int!null) b:2(int!null)
    98    + ├── constraint: /2/1: [/1 - /1]
    99      ├── key: (1)
   100    - ├── fd: ()-->(2)
   101    - ├── scan ab
   102    - │    ├── columns: a:1(int!null) b:2(int)
   103    - │    ├── key: (1)
   104    - │    └── fd: (1)-->(2)
   105    - └── filters
   106    -      └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
   107    -           ├── variable: b:2 [type=int]
   108    -           └── const: 1 [type=int]
   109    + └── fd: ()-->(2)
   110  ================================================================================
   111  Final best expression
   112    Cost: 10.41
   113  ================================================================================
   114    scan ab@secondary
   115     ├── columns: a:1(int!null) b:2(int!null)
   116     ├── constraint: /2/1: [/1 - /1]
   117     ├── key: (1)
   118     └── fd: ()-->(2)
   119  
   120  exec-ddl
   121  CREATE TABLE customers (
   122      id INT8 NOT NULL,
   123      name STRING NOT NULL,
   124      address STRING NULL,
   125      CONSTRAINT "primary" PRIMARY KEY (id ASC),
   126      FAMILY "primary" (id, name, address)
   127  )
   128  ----
   129  
   130  exec-ddl
   131  CREATE TABLE orders (
   132      id INT8 NOT NULL,
   133      customer_id INT8 NULL,
   134      status STRING NOT NULL,
   135      CONSTRAINT "primary" PRIMARY KEY (id ASC),
   136      CONSTRAINT fk_customer_id_ref_customers FOREIGN KEY (customer_id) REFERENCES customers(id),
   137      INDEX orders_auto_index_fk_customer_id_ref_customers (customer_id ASC),
   138      FAMILY "primary" (id, customer_id, status),
   139      CONSTRAINT check_status CHECK (status IN ('open':::STRING, 'complete':::STRING, 'cancelled':::STRING))
   140  )
   141  ----
   142  
   143  # Verify that we don't crash when a normalization rule runs on a constraint
   144  # expression that is attached to the TableMeta but otherwise not used.
   145  # In this example, the rule is NormalizeInConst.
   146  optsteps
   147  SELECT * FROM orders LEFT JOIN customers ON customer_id = customers.id
   148  ----
   149  ================================================================================
   150  Initial expression
   151    Cost: 2160.05
   152  ================================================================================
   153    left-join (hash)
   154     ├── columns: id:1(int!null) customer_id:2(int) status:3(string!null) id:4(int) name:5(string) address:6(string)
   155     ├── key: (1)
   156     ├── fd: (1)-->(2-6), (4)-->(5,6)
   157     ├── scan orders
   158     │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
   159     │    ├── check constraint expressions
   160     │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
   161     │    │         ├── variable: status:3 [type=string]
   162     │    │         └── tuple [type=tuple{string, string, string}]
   163     │    │              ├── const: 'open' [type=string]
   164     │    │              ├── const: 'complete' [type=string]
   165     │    │              └── const: 'cancelled' [type=string]
   166     │    ├── key: (1)
   167     │    └── fd: (1)-->(2,3)
   168     ├── scan customers
   169     │    ├── columns: customers.id:4(int!null) name:5(string!null) address:6(string)
   170     │    ├── key: (4)
   171     │    └── fd: (4)-->(5,6)
   172     └── filters
   173          └── eq [type=bool, outer=(2,4), constraints=(/2: (/NULL - ]; /4: (/NULL - ]), fd=(2)==(4), (4)==(2)]
   174               ├── variable: customer_id:2 [type=int]
   175               └── variable: customers.id:4 [type=int]
   176  ================================================================================
   177  NormalizeInConst
   178    Cost: 2160.05
   179  ================================================================================
   180     left-join (hash)
   181      ├── columns: id:1(int!null) customer_id:2(int) status:3(string!null) id:4(int) name:5(string) address:6(string)
   182      ├── key: (1)
   183      ├── fd: (1)-->(2-6), (4)-->(5,6)
   184      ├── scan orders
   185      │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
   186      │    ├── check constraint expressions
   187      │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
   188      │    │         ├── variable: status:3 [type=string]
   189      │    │         └── tuple [type=tuple{string, string, string}]
   190    - │    │              ├── const: 'open' [type=string]
   191    + │    │              ├── const: 'cancelled' [type=string]
   192      │    │              ├── const: 'complete' [type=string]
   193    - │    │              └── const: 'cancelled' [type=string]
   194    + │    │              └── const: 'open' [type=string]
   195      │    ├── key: (1)
   196      │    └── fd: (1)-->(2,3)
   197      ├── scan customers
   198      │    ├── columns: customers.id:4(int!null) name:5(string!null) address:6(string)
   199      │    ├── key: (4)
   200      │    └── fd: (4)-->(5,6)
   201      └── filters
   202           └── eq [type=bool, outer=(2,4), constraints=(/2: (/NULL - ]; /4: (/NULL - ]), fd=(2)==(4), (4)==(2)]
   203                ├── variable: customer_id:2 [type=int]
   204                └── variable: customers.id:4 [type=int]
   205  --------------------------------------------------------------------------------
   206  GenerateIndexScans (no changes)
   207  --------------------------------------------------------------------------------
   208  --------------------------------------------------------------------------------
   209  GenerateIndexScans (no changes)
   210  --------------------------------------------------------------------------------
   211  --------------------------------------------------------------------------------
   212  CommuteLeftJoin (higher cost)
   213  --------------------------------------------------------------------------------
   214    -left-join (hash)
   215    +right-join (hash)
   216      ├── columns: id:1(int!null) customer_id:2(int) status:3(string!null) id:4(int) name:5(string) address:6(string)
   217      ├── key: (1)
   218      ├── fd: (1)-->(2-6), (4)-->(5,6)
   219    + ├── scan customers
   220    + │    ├── columns: customers.id:4(int!null) name:5(string!null) address:6(string)
   221    + │    ├── key: (4)
   222    + │    └── fd: (4)-->(5,6)
   223      ├── scan orders
   224      │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
   225      │    ├── check constraint expressions
   226      │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
   227      │    │         ├── variable: status:3 [type=string]
   228      │    │         └── tuple [type=tuple{string, string, string}]
   229      │    │              ├── const: 'cancelled' [type=string]
   230      │    │              ├── const: 'complete' [type=string]
   231      │    │              └── const: 'open' [type=string]
   232      │    ├── key: (1)
   233      │    └── fd: (1)-->(2,3)
   234    - ├── scan customers
   235    - │    ├── columns: customers.id:4(int!null) name:5(string!null) address:6(string)
   236    - │    ├── key: (4)
   237    - │    └── fd: (4)-->(5,6)
   238      └── filters
   239           └── eq [type=bool, outer=(2,4), constraints=(/2: (/NULL - ]; /4: (/NULL - ]), fd=(2)==(4), (4)==(2)]
   240                ├── variable: customer_id:2 [type=int]
   241                └── variable: customers.id:4 [type=int]
   242  --------------------------------------------------------------------------------
   243  GenerateMergeJoins (no changes)
   244  --------------------------------------------------------------------------------
   245  --------------------------------------------------------------------------------
   246  GenerateLookupJoins (higher cost)
   247  --------------------------------------------------------------------------------
   248    -left-join (hash)
   249    +left-join (lookup customers)
   250      ├── columns: id:1(int!null) customer_id:2(int) status:3(string!null) id:4(int) name:5(string) address:6(string)
   251    + ├── key columns: [2] = [4]
   252    + ├── lookup columns are key
   253      ├── key: (1)
   254      ├── fd: (1)-->(2-6), (4)-->(5,6)
   255      ├── scan orders
   256      │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
   257      │    ├── check constraint expressions
   258      │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
   259      │    │         ├── variable: status:3 [type=string]
   260      │    │         └── tuple [type=tuple{string, string, string}]
   261      │    │              ├── const: 'cancelled' [type=string]
   262      │    │              ├── const: 'complete' [type=string]
   263      │    │              └── const: 'open' [type=string]
   264      │    ├── key: (1)
   265      │    └── fd: (1)-->(2,3)
   266    - ├── scan customers
   267    - │    ├── columns: customers.id:4(int!null) name:5(string!null) address:6(string)
   268    - │    ├── key: (4)
   269    - │    └── fd: (4)-->(5,6)
   270    - └── filters
   271    -      └── eq [type=bool, outer=(2,4), constraints=(/2: (/NULL - ]; /4: (/NULL - ]), fd=(2)==(4), (4)==(2)]
   272    -           ├── variable: customer_id:2 [type=int]
   273    -           └── variable: customers.id:4 [type=int]
   274    + └── filters (true)
   275  --------------------------------------------------------------------------------
   276  GenerateMergeJoins (higher cost)
   277  --------------------------------------------------------------------------------
   278    -left-join (lookup customers)
   279    +right-join (merge)
   280      ├── columns: id:1(int!null) customer_id:2(int) status:3(string!null) id:4(int) name:5(string) address:6(string)
   281    - ├── key columns: [2] = [4]
   282    - ├── lookup columns are key
   283    + ├── left ordering: +4
   284    + ├── right ordering: +2
   285      ├── key: (1)
   286      ├── fd: (1)-->(2-6), (4)-->(5,6)
   287    - ├── scan orders
   288    + ├── scan customers
   289    + │    ├── columns: customers.id:4(int!null) name:5(string!null) address:6(string)
   290    + │    ├── key: (4)
   291    + │    ├── fd: (4)-->(5,6)
   292    + │    └── ordering: +4
   293    + ├── sort
   294      │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
   295    - │    ├── check constraint expressions
   296    - │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
   297    - │    │         ├── variable: status:3 [type=string]
   298    - │    │         └── tuple [type=tuple{string, string, string}]
   299    - │    │              ├── const: 'cancelled' [type=string]
   300    - │    │              ├── const: 'complete' [type=string]
   301    - │    │              └── const: 'open' [type=string]
   302      │    ├── key: (1)
   303    - │    └── fd: (1)-->(2,3)
   304    + │    ├── fd: (1)-->(2,3)
   305    + │    ├── ordering: +2
   306    + │    └── scan orders
   307    + │         ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
   308    + │         ├── check constraint expressions
   309    + │         │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
   310    + │         │         ├── variable: status:3 [type=string]
   311    + │         │         └── tuple [type=tuple{string, string, string}]
   312    + │         │              ├── const: 'cancelled' [type=string]
   313    + │         │              ├── const: 'complete' [type=string]
   314    + │         │              └── const: 'open' [type=string]
   315    + │         ├── key: (1)
   316    + │         └── fd: (1)-->(2,3)
   317      └── filters (true)
   318  ================================================================================
   319  Final best expression
   320    Cost: 2160.05
   321  ================================================================================
   322    left-join (hash)
   323     ├── columns: id:1(int!null) customer_id:2(int) status:3(string!null) id:4(int) name:5(string) address:6(string)
   324     ├── key: (1)
   325     ├── fd: (1)-->(2-6), (4)-->(5,6)
   326     ├── scan orders
   327     │    ├── columns: orders.id:1(int!null) customer_id:2(int) status:3(string!null)
   328     │    ├── check constraint expressions
   329     │    │    └── in [type=bool, outer=(3), constraints=(/3: [/'cancelled' - /'cancelled'] [/'complete' - /'complete'] [/'open' - /'open']; tight)]
   330     │    │         ├── variable: status:3 [type=string]
   331     │    │         └── tuple [type=tuple{string, string, string}]
   332     │    │              ├── const: 'cancelled' [type=string]
   333     │    │              ├── const: 'complete' [type=string]
   334     │    │              └── const: 'open' [type=string]
   335     │    ├── key: (1)
   336     │    └── fd: (1)-->(2,3)
   337     ├── scan customers
   338     │    ├── columns: customers.id:4(int!null) name:5(string!null) address:6(string)
   339     │    ├── key: (4)
   340     │    └── fd: (4)-->(5,6)
   341     └── filters
   342          └── eq [type=bool, outer=(2,4), constraints=(/2: (/NULL - ]; /4: (/NULL - ]), fd=(2)==(4), (4)==(2)]
   343               ├── variable: customer_id:2 [type=int]
   344               └── variable: customers.id:4 [type=int]
   345  
   346  exec-ddl
   347  CREATE TABLE comp (
   348    k INT,
   349    c BOOL AS (k IN (1,3,2)) STORED,
   350    INDEX (c, k)
   351  )
   352  ----
   353  
   354  # Verify that we don't crash when a normalization rule runs on a computed
   355  # column expression that is attached to the TableMeta but otherwise not used.
   356  # In this example, the rule is NormalizeInConst.
   357  optsteps
   358  SELECT * FROM comp WHERE k=1
   359  ----
   360  ================================================================================
   361  Initial expression
   362    Cost: 1070.14
   363  ================================================================================
   364    project
   365     ├── columns: k:1(int!null) c:2(bool)
   366     ├── fd: ()-->(1)
   367     └── select
   368          ├── columns: k:1(int!null) c:2(bool) rowid:3(int!null)
   369          ├── key: (3)
   370          ├── fd: ()-->(1), (3)-->(2)
   371          ├── scan comp
   372          │    ├── columns: k:1(int) c:2(bool) rowid:3(int!null)
   373          │    ├── computed column expressions
   374          │    │    └── c:2
   375          │    │         └── in [type=bool]
   376          │    │              ├── variable: k:1 [type=int]
   377          │    │              └── tuple [type=tuple{int, int, int}]
   378          │    │                   ├── const: 1 [type=int]
   379          │    │                   ├── const: 3 [type=int]
   380          │    │                   └── const: 2 [type=int]
   381          │    ├── key: (3)
   382          │    └── fd: (3)-->(1,2)
   383          └── filters
   384               └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
   385                    ├── variable: k:1 [type=int]
   386                    └── const: 1 [type=int]
   387  ================================================================================
   388  NormalizeInConst
   389    Cost: 1070.14
   390  ================================================================================
   391     project
   392      ├── columns: k:1(int!null) c:2(bool)
   393      ├── fd: ()-->(1)
   394      └── select
   395           ├── columns: k:1(int!null) c:2(bool) rowid:3(int!null)
   396           ├── key: (3)
   397           ├── fd: ()-->(1), (3)-->(2)
   398           ├── scan comp
   399           │    ├── columns: k:1(int) c:2(bool) rowid:3(int!null)
   400           │    ├── computed column expressions
   401           │    │    └── c:2
   402           │    │         └── in [type=bool]
   403           │    │              ├── variable: k:1 [type=int]
   404           │    │              └── tuple [type=tuple{int, int, int}]
   405           │    │                   ├── const: 1 [type=int]
   406    -      │    │                   ├── const: 3 [type=int]
   407    -      │    │                   └── const: 2 [type=int]
   408    +      │    │                   ├── const: 2 [type=int]
   409    +      │    │                   └── const: 3 [type=int]
   410           │    ├── key: (3)
   411           │    └── fd: (3)-->(1,2)
   412           └── filters
   413                └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
   414                     ├── variable: k:1 [type=int]
   415                     └── const: 1 [type=int]
   416  ================================================================================
   417  PruneSelectCols
   418    Cost: 1060.14
   419  ================================================================================
   420     project
   421      ├── columns: k:1(int!null) c:2(bool)
   422      ├── fd: ()-->(1)
   423      └── select
   424    -      ├── columns: k:1(int!null) c:2(bool) rowid:3(int!null)
   425    -      ├── key: (3)
   426    -      ├── fd: ()-->(1), (3)-->(2)
   427    +      ├── columns: k:1(int!null) c:2(bool)
   428    +      ├── fd: ()-->(1)
   429           ├── scan comp
   430    -      │    ├── columns: k:1(int) c:2(bool) rowid:3(int!null)
   431    -      │    ├── computed column expressions
   432    -      │    │    └── c:2
   433    -      │    │         └── in [type=bool]
   434    -      │    │              ├── variable: k:1 [type=int]
   435    -      │    │              └── tuple [type=tuple{int, int, int}]
   436    -      │    │                   ├── const: 1 [type=int]
   437    -      │    │                   ├── const: 2 [type=int]
   438    -      │    │                   └── const: 3 [type=int]
   439    -      │    ├── key: (3)
   440    -      │    └── fd: (3)-->(1,2)
   441    +      │    ├── columns: k:1(int) c:2(bool)
   442    +      │    └── computed column expressions
   443    +      │         └── c:2
   444    +      │              └── in [type=bool]
   445    +      │                   ├── variable: k:1 [type=int]
   446    +      │                   └── tuple [type=tuple{int, int, int}]
   447    +      │                        ├── const: 1 [type=int]
   448    +      │                        ├── const: 2 [type=int]
   449    +      │                        └── const: 3 [type=int]
   450           └── filters
   451                └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
   452                     ├── variable: k:1 [type=int]
   453                     └── const: 1 [type=int]
   454  ================================================================================
   455  EliminateProject
   456    Cost: 1060.03
   457  ================================================================================
   458    -project
   459    +select
   460      ├── columns: k:1(int!null) c:2(bool)
   461      ├── fd: ()-->(1)
   462    - └── select
   463    -      ├── columns: k:1(int!null) c:2(bool)
   464    -      ├── fd: ()-->(1)
   465    -      ├── scan comp
   466    -      │    ├── columns: k:1(int) c:2(bool)
   467    -      │    └── computed column expressions
   468    -      │         └── c:2
   469    -      │              └── in [type=bool]
   470    -      │                   ├── variable: k:1 [type=int]
   471    -      │                   └── tuple [type=tuple{int, int, int}]
   472    -      │                        ├── const: 1 [type=int]
   473    -      │                        ├── const: 2 [type=int]
   474    -      │                        └── const: 3 [type=int]
   475    -      └── filters
   476    -           └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
   477    -                ├── variable: k:1 [type=int]
   478    -                └── const: 1 [type=int]
   479    + ├── scan comp
   480    + │    ├── columns: k:1(int) c:2(bool)
   481    + │    └── computed column expressions
   482    + │         └── c:2
   483    + │              └── in [type=bool]
   484    + │                   ├── variable: k:1 [type=int]
   485    + │                   └── tuple [type=tuple{int, int, int}]
   486    + │                        ├── const: 1 [type=int]
   487    + │                        ├── const: 2 [type=int]
   488    + │                        └── const: 3 [type=int]
   489    + └── filters
   490    +      └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
   491    +           ├── variable: k:1 [type=int]
   492    +           └── const: 1 [type=int]
   493  --------------------------------------------------------------------------------
   494  GenerateIndexScans (higher cost)
   495  --------------------------------------------------------------------------------
   496     select
   497      ├── columns: k:1(int!null) c:2(bool)
   498      ├── fd: ()-->(1)
   499    - ├── scan comp
   500    - │    ├── columns: k:1(int) c:2(bool)
   501    - │    └── computed column expressions
   502    - │         └── c:2
   503    - │              └── in [type=bool]
   504    - │                   ├── variable: k:1 [type=int]
   505    - │                   └── tuple [type=tuple{int, int, int}]
   506    - │                        ├── const: 1 [type=int]
   507    - │                        ├── const: 2 [type=int]
   508    - │                        └── const: 3 [type=int]
   509    + ├── scan comp@secondary
   510    + │    └── columns: k:1(int) c:2(bool)
   511      └── filters
   512           └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
   513                ├── variable: k:1 [type=int]
   514                └── const: 1 [type=int]
   515  --------------------------------------------------------------------------------
   516  GenerateZigzagJoins (no changes)
   517  --------------------------------------------------------------------------------
   518  --------------------------------------------------------------------------------
   519  GenerateConstrainedScans (no changes)
   520  --------------------------------------------------------------------------------
   521  ================================================================================
   522  FoldComparison
   523    Cost: 10.51
   524  ================================================================================
   525    -select
   526    +scan comp@secondary
   527      ├── columns: k:1(int!null) c:2(bool)
   528    - ├── fd: ()-->(1)
   529    - ├── scan comp
   530    - │    ├── columns: k:1(int) c:2(bool)
   531    - │    └── computed column expressions
   532    - │         └── c:2
   533    - │              └── in [type=bool]
   534    - │                   ├── variable: k:1 [type=int]
   535    - │                   └── tuple [type=tuple{int, int, int}]
   536    - │                        ├── const: 1 [type=int]
   537    - │                        ├── const: 2 [type=int]
   538    - │                        └── const: 3 [type=int]
   539    - └── filters
   540    -      └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
   541    -           ├── variable: k:1 [type=int]
   542    -           └── const: 1 [type=int]
   543    + ├── constraint: /2/1/3: [/true/1 - /true/1]
   544    + └── fd: ()-->(1)
   545  ================================================================================
   546  Final best expression
   547    Cost: 10.51
   548  ================================================================================
   549    scan comp@secondary
   550     ├── columns: k:1(int!null) c:2(bool)
   551     ├── constraint: /2/1/3: [/true/1 - /true/1]
   552     └── fd: ()-->(1)