github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/xform/testdata/rules/select (about)

     1  exec-ddl
     2  CREATE TABLE a
     3  (
     4      k INT PRIMARY KEY,
     5      u INT,
     6      v INT,
     7      INDEX u(u) STORING (v),
     8      UNIQUE INDEX v(v) STORING (u)
     9  )
    10  ----
    11  
    12  exec-ddl
    13  CREATE TABLE b
    14  (
    15      k INT PRIMARY KEY,
    16      u INT,
    17      v INT,
    18      j JSONB,
    19      INDEX u(u),
    20      UNIQUE INDEX v(v),
    21      INVERTED INDEX inv_idx(j)
    22  )
    23  ----
    24  
    25  exec-ddl
    26  CREATE TABLE c
    27  (
    28      k INT PRIMARY KEY,
    29      a INT[],
    30      u INT,
    31      INVERTED INDEX inv_idx(a),
    32      INDEX u(u)
    33  )
    34  ----
    35  
    36  exec-ddl
    37  CREATE TABLE d
    38  (
    39      k INT PRIMARY KEY,
    40      u INT,
    41      v INT,
    42      w INT,
    43      INDEX u(u),
    44      INDEX v(v)
    45  )
    46  ----
    47  
    48  exec-ddl
    49  CREATE TABLE e
    50  (
    51      k INT PRIMARY KEY,
    52      u INT,
    53      v INT,
    54      w INT,
    55      INDEX uw(u, w),
    56      INDEX vw(v, w)
    57  )
    58  ----
    59  
    60  exec-ddl
    61  CREATE TABLE f
    62  (
    63      k INT,
    64      j INT,
    65      u INT,
    66      v INT,
    67      CONSTRAINT pk PRIMARY KEY (k, j),
    68      INDEX u(u),
    69      INDEX v(v)
    70  )
    71  ----
    72  
    73  exec-ddl
    74  CREATE TABLE no_explicit_primary_key
    75  (
    76      k INT,
    77      u INT,
    78      v INT,
    79      INDEX u(u),
    80      INDEX v(v)
    81  )
    82  ----
    83  
    84  # --------------------------------------------------
    85  # GenerateConstrainedScans
    86  # --------------------------------------------------
    87  
    88  opt
    89  SELECT k FROM a WHERE k = 1
    90  ----
    91  scan a
    92   ├── columns: k:1!null
    93   ├── constraint: /1: [/1 - /1]
    94   ├── cardinality: [0 - 1]
    95   ├── key: ()
    96   └── fd: ()-->(1)
    97  
    98  memo
    99  SELECT k FROM a WHERE k = 1
   100  ----
   101  memo (optimized, ~5KB, required=[presentation: k:1])
   102   ├── G1: (select G2 G3) (scan a,cols=(1),constrained)
   103   │    └── [presentation: k:1]
   104   │         ├── best: (scan a,cols=(1),constrained)
   105   │         └── cost: 1.05
   106   ├── G2: (scan a,cols=(1)) (scan a@u,cols=(1)) (scan a@v,cols=(1))
   107   │    └── []
   108   │         ├── best: (scan a,cols=(1))
   109   │         └── cost: 1040.02
   110   ├── G3: (filters G4)
   111   ├── G4: (eq G5 G6)
   112   ├── G5: (variable k)
   113   └── G6: (const 1)
   114  
   115  opt
   116  SELECT k FROM a WHERE v > 1
   117  ----
   118  project
   119   ├── columns: k:1!null
   120   ├── key: (1)
   121   └── scan a@v
   122        ├── columns: k:1!null v:3!null
   123        ├── constraint: /3: [/2 - ]
   124        ├── key: (1)
   125        └── fd: (1)-->(3), (3)-->(1)
   126  
   127  memo
   128  SELECT k FROM a WHERE v > 1
   129  ----
   130  memo (optimized, ~6KB, required=[presentation: k:1])
   131   ├── G1: (project G2 G3 k)
   132   │    └── [presentation: k:1]
   133   │         ├── best: (project G2 G3 k)
   134   │         └── cost: 350.17
   135   ├── G2: (select G4 G5) (scan a@v,cols=(1,3),constrained)
   136   │    └── []
   137   │         ├── best: (scan a@v,cols=(1,3),constrained)
   138   │         └── cost: 346.86
   139   ├── G3: (projections)
   140   ├── G4: (scan a,cols=(1,3)) (scan a@u,cols=(1,3)) (scan a@v,cols=(1,3))
   141   │    └── []
   142   │         ├── best: (scan a,cols=(1,3))
   143   │         └── cost: 1050.02
   144   ├── G5: (filters G6)
   145   ├── G6: (gt G7 G8)
   146   ├── G7: (variable v)
   147   └── G8: (const 1)
   148  
   149  opt
   150  SELECT k FROM a WHERE u = 1 AND k = 5
   151  ----
   152  project
   153   ├── columns: k:1!null
   154   ├── cardinality: [0 - 1]
   155   ├── key: ()
   156   ├── fd: ()-->(1)
   157   └── scan a@u
   158        ├── columns: k:1!null u:2!null
   159        ├── constraint: /2/1: [/1/5 - /1/5]
   160        ├── cardinality: [0 - 1]
   161        ├── key: ()
   162        └── fd: ()-->(1,2)
   163  
   164  memo
   165  SELECT k FROM a WHERE u = 1 AND k = 5
   166  ----
   167  memo (optimized, ~7KB, required=[presentation: k:1])
   168   ├── G1: (project G2 G3 k)
   169   │    └── [presentation: k:1]
   170   │         ├── best: (project G2 G3 k)
   171   │         └── cost: 1.07
   172   ├── G2: (select G4 G5) (select G6 G7) (scan a@u,cols=(1,2),constrained)
   173   │    └── []
   174   │         ├── best: (scan a@u,cols=(1,2),constrained)
   175   │         └── cost: 1.05
   176   ├── G3: (projections)
   177   ├── G4: (scan a,cols=(1,2)) (scan a@u,cols=(1,2)) (scan a@v,cols=(1,2))
   178   │    └── []
   179   │         ├── best: (scan a,cols=(1,2))
   180   │         └── cost: 1050.02
   181   ├── G5: (filters G8 G9)
   182   ├── G6: (scan a,cols=(1,2),constrained)
   183   │    └── []
   184   │         ├── best: (scan a,cols=(1,2),constrained)
   185   │         └── cost: 1.06
   186   ├── G7: (filters G8)
   187   ├── G8: (eq G10 G11)
   188   ├── G9: (eq G12 G13)
   189   ├── G10: (variable u)
   190   ├── G11: (const 1)
   191   ├── G12: (variable k)
   192   └── G13: (const 5)
   193  
   194  # Constraint + remaining filter.
   195  opt
   196  SELECT k FROM a WHERE u = 1 AND k+u = 1
   197  ----
   198  project
   199   ├── columns: k:1!null
   200   ├── cardinality: [0 - 1]
   201   ├── key: ()
   202   ├── fd: ()-->(1)
   203   └── scan a@u
   204        ├── columns: k:1!null u:2!null
   205        ├── constraint: /2/1: [/1/0 - /1/0]
   206        ├── cardinality: [0 - 1]
   207        ├── key: ()
   208        └── fd: ()-->(1,2)
   209  
   210  memo
   211  SELECT k FROM a WHERE u = 1 AND k+u = 1
   212  ----
   213  memo (optimized, ~8KB, required=[presentation: k:1])
   214   ├── G1: (project G2 G3 k)
   215   │    └── [presentation: k:1]
   216   │         ├── best: (project G2 G3 k)
   217   │         └── cost: 1.07
   218   ├── G2: (select G4 G5) (select G6 G7) (scan a@u,cols=(1,2),constrained)
   219   │    └── []
   220   │         ├── best: (scan a@u,cols=(1,2),constrained)
   221   │         └── cost: 1.05
   222   ├── G3: (projections)
   223   ├── G4: (scan a,cols=(1,2)) (scan a@u,cols=(1,2)) (scan a@v,cols=(1,2))
   224   │    └── []
   225   │         ├── best: (scan a,cols=(1,2))
   226   │         └── cost: 1050.02
   227   ├── G5: (filters G8 G9)
   228   ├── G6: (scan a,cols=(1,2),constrained)
   229   │    └── []
   230   │         ├── best: (scan a,cols=(1,2),constrained)
   231   │         └── cost: 1.06
   232   ├── G7: (filters G8)
   233   ├── G8: (eq G10 G11)
   234   ├── G9: (eq G12 G13)
   235   ├── G10: (variable u)
   236   ├── G11: (const 1)
   237   ├── G12: (variable k)
   238   └── G13: (const 0)
   239  
   240  opt
   241  SELECT k FROM a WHERE u = 1 AND v = 5
   242  ----
   243  project
   244   ├── columns: k:1!null
   245   ├── cardinality: [0 - 1]
   246   ├── key: ()
   247   ├── fd: ()-->(1)
   248   └── select
   249        ├── columns: k:1!null u:2!null v:3!null
   250        ├── cardinality: [0 - 1]
   251        ├── key: ()
   252        ├── fd: ()-->(1-3)
   253        ├── scan a@v
   254        │    ├── columns: k:1!null u:2 v:3!null
   255        │    ├── constraint: /3: [/5 - /5]
   256        │    ├── cardinality: [0 - 1]
   257        │    ├── key: ()
   258        │    └── fd: ()-->(1-3)
   259        └── filters
   260             └── u:2 = 1 [outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
   261  
   262  memo
   263  SELECT k FROM a WHERE u = 1 AND v = 5
   264  ----
   265  memo (optimized, ~6KB, required=[presentation: k:1])
   266   ├── G1: (project G2 G3 k)
   267   │    └── [presentation: k:1]
   268   │         ├── best: (project G2 G3 k)
   269   │         └── cost: 1.11
   270   ├── G2: (select G4 G5) (select G6 G7) (select G8 G9)
   271   │    └── []
   272   │         ├── best: (select G8 G9)
   273   │         └── cost: 1.09
   274   ├── G3: (projections)
   275   ├── G4: (scan a) (scan a@u) (scan a@v)
   276   │    └── []
   277   │         ├── best: (scan a)
   278   │         └── cost: 1060.02
   279   ├── G5: (filters G10 G11)
   280   ├── G6: (scan a@u,constrained)
   281   │    └── []
   282   │         ├── best: (scan a@u,constrained)
   283   │         └── cost: 10.61
   284   ├── G7: (filters G11)
   285   ├── G8: (scan a@v,constrained)
   286   │    └── []
   287   │         ├── best: (scan a@v,constrained)
   288   │         └── cost: 1.07
   289   ├── G9: (filters G10)
   290   ├── G10: (eq G12 G13)
   291   ├── G11: (eq G14 G15)
   292   ├── G12: (variable u)
   293   ├── G13: (const 1)
   294   ├── G14: (variable v)
   295   └── G15: (const 5)
   296  
   297  # Only not-null constraint is pushed down.
   298  opt
   299  SELECT k FROM a WHERE u=v
   300  ----
   301  project
   302   ├── columns: k:1!null
   303   ├── key: (1)
   304   └── select
   305        ├── columns: k:1!null u:2!null v:3!null
   306        ├── key: (1)
   307        ├── fd: (1)-->(2,3), (3)-->(1), (2)==(3), (3)==(2)
   308        ├── scan a@u
   309        │    ├── columns: k:1!null u:2!null v:3
   310        │    ├── constraint: /2/1: (/NULL - ]
   311        │    ├── key: (1)
   312        │    └── fd: (1)-->(2,3), (3)~~>(1,2)
   313        └── filters
   314             └── u:2 = v:3 [outer=(2,3), constraints=(/2: (/NULL - ]; /3: (/NULL - ]), fd=(2)==(3), (3)==(2)]
   315  
   316  # Don't push constraint into already limited scan.
   317  opt
   318  SELECT k FROM (SELECT k FROM a ORDER BY u LIMIT 1) a WHERE k = 1
   319  ----
   320  project
   321   ├── columns: k:1!null
   322   ├── cardinality: [0 - 1]
   323   ├── key: ()
   324   ├── fd: ()-->(1)
   325   └── select
   326        ├── columns: k:1!null u:2
   327        ├── cardinality: [0 - 1]
   328        ├── key: ()
   329        ├── fd: ()-->(1,2)
   330        ├── scan a@u
   331        │    ├── columns: k:1!null u:2
   332        │    ├── limit: 1
   333        │    ├── key: ()
   334        │    └── fd: ()-->(1,2)
   335        └── filters
   336             └── k:1 = 1 [outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
   337  
   338  # Constraint + index-join, with no remainder filter.
   339  opt
   340  SELECT * FROM b WHERE v >= 1 AND v <= 10
   341  ----
   342  index-join b
   343   ├── columns: k:1!null u:2 v:3!null j:4
   344   ├── cardinality: [0 - 10]
   345   ├── key: (1)
   346   ├── fd: (1)-->(2-4), (3)-->(1,2,4)
   347   └── scan b@v
   348        ├── columns: k:1!null v:3!null
   349        ├── constraint: /3: [/1 - /10]
   350        ├── cardinality: [0 - 10]
   351        ├── key: (1)
   352        └── fd: (1)-->(3), (3)-->(1)
   353  
   354  memo
   355  SELECT * FROM b WHERE v >= 1 AND v <= 10
   356  ----
   357  memo (optimized, ~3KB, required=[presentation: k:1,u:2,v:3,j:4])
   358   ├── G1: (select G2 G3) (index-join G4 b,cols=(1-4))
   359   │    └── [presentation: k:1,u:2,v:3,j:4]
   360   │         ├── best: (index-join G4 b,cols=(1-4))
   361   │         └── cost: 51.32
   362   ├── G2: (scan b)
   363   │    └── []
   364   │         ├── best: (scan b)
   365   │         └── cost: 1080.02
   366   ├── G3: (filters G5)
   367   ├── G4: (scan b@v,cols=(1,3),constrained)
   368   │    └── []
   369   │         ├── best: (scan b@v,cols=(1,3),constrained)
   370   │         └── cost: 10.41
   371   ├── G5: (range G6)
   372   ├── G6: (and G7 G8)
   373   ├── G7: (ge G9 G10)
   374   ├── G8: (le G9 G11)
   375   ├── G9: (variable v)
   376   ├── G10: (const 1)
   377   └── G11: (const 10)
   378  
   379  # Don't choose lookup join if it's not beneficial.
   380  opt
   381  SELECT * FROM b WHERE v > 1
   382  ----
   383  select
   384   ├── columns: k:1!null u:2 v:3!null j:4
   385   ├── key: (1)
   386   ├── fd: (1)-->(2-4), (3)-->(1,2,4)
   387   ├── scan b
   388   │    ├── columns: k:1!null u:2 v:3 j:4
   389   │    ├── key: (1)
   390   │    └── fd: (1)-->(2-4), (3)~~>(1,2,4)
   391   └── filters
   392        └── v:3 > 1 [outer=(3), constraints=(/3: [/2 - ]; tight)]
   393  
   394  opt
   395  SELECT * FROM b WHERE v >= 1 AND v <= 10 AND k > 5
   396  ----
   397  index-join b
   398   ├── columns: k:1!null u:2 v:3!null j:4
   399   ├── cardinality: [0 - 10]
   400   ├── key: (1)
   401   ├── fd: (1)-->(2-4), (3)-->(1,2,4)
   402   └── select
   403        ├── columns: k:1!null v:3!null
   404        ├── cardinality: [0 - 10]
   405        ├── key: (1)
   406        ├── fd: (1)-->(3), (3)-->(1)
   407        ├── scan b@v
   408        │    ├── columns: k:1!null v:3!null
   409        │    ├── constraint: /3: [/1 - /10]
   410        │    ├── cardinality: [0 - 10]
   411        │    ├── key: (1)
   412        │    └── fd: (1)-->(3), (3)-->(1)
   413        └── filters
   414             └── k:1 > 5 [outer=(1), constraints=(/1: [/6 - ]; tight)]
   415  
   416  memo
   417  SELECT * FROM b WHERE v >= 1 AND v <= 10 AND k > 5
   418  ----
   419  memo (optimized, ~6KB, required=[presentation: k:1,u:2,v:3,j:4])
   420   ├── G1: (select G2 G3) (select G4 G5) (index-join G6 b,cols=(1-4))
   421   │    └── [presentation: k:1,u:2,v:3,j:4]
   422   │         ├── best: (index-join G6 b,cols=(1-4))
   423   │         └── cost: 24.16
   424   ├── G2: (scan b)
   425   │    └── []
   426   │         ├── best: (scan b)
   427   │         └── cost: 1080.02
   428   ├── G3: (filters G7 G8)
   429   ├── G4: (scan b,constrained)
   430   │    └── []
   431   │         ├── best: (scan b,constrained)
   432   │         └── cost: 360.01
   433   ├── G5: (filters G7)
   434   ├── G6: (select G9 G10)
   435   │    └── []
   436   │         ├── best: (select G9 G10)
   437   │         └── cost: 10.52
   438   ├── G7: (range G11)
   439   ├── G8: (gt G12 G13)
   440   ├── G9: (scan b@v,cols=(1,3),constrained)
   441   │    └── []
   442   │         ├── best: (scan b@v,cols=(1,3),constrained)
   443   │         └── cost: 10.41
   444   ├── G10: (filters G8)
   445   ├── G11: (and G14 G15)
   446   ├── G12: (variable k)
   447   ├── G13: (const 5)
   448   ├── G14: (ge G16 G17)
   449   ├── G15: (le G16 G18)
   450   ├── G16: (variable v)
   451   ├── G17: (const 1)
   452   └── G18: (const 10)
   453  
   454  # Ensure the rule doesn't match at all when the first column of the index is
   455  # not in the filter (i.e. the @v index is not matched by ConstrainScans).
   456  exploretrace rule=GenerateConstrainedScans
   457  SELECT k FROM a WHERE u = 1
   458  ----
   459  ----
   460  ================================================================================
   461  GenerateConstrainedScans
   462  ================================================================================
   463  Source expression:
   464    project
   465     ├── columns: k:1!null
   466     ├── key: (1)
   467     └── select
   468          ├── columns: k:1!null u:2!null
   469          ├── key: (1)
   470          ├── fd: ()-->(2)
   471          ├── scan a
   472          │    ├── columns: k:1!null u:2
   473          │    ├── key: (1)
   474          │    └── fd: (1)-->(2)
   475          └── filters
   476               └── u:2 = 1 [outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
   477  
   478  New expression 1 of 1:
   479    project
   480     ├── columns: k:1!null
   481     ├── key: (1)
   482     └── scan a@u
   483          ├── columns: k:1!null u:2!null
   484          ├── constraint: /2/1: [/1 - /1]
   485          ├── key: (1)
   486          └── fd: ()-->(2)
   487  ----
   488  ----
   489  
   490  # Constraint + index join + remaining filter.
   491  opt
   492  SELECT * FROM b WHERE v >= 1 AND v <= 10 AND k+u = 1
   493  ----
   494  select
   495   ├── columns: k:1!null u:2 v:3!null j:4
   496   ├── cardinality: [0 - 10]
   497   ├── key: (1)
   498   ├── fd: (1)-->(2-4), (3)-->(1,2,4)
   499   ├── index-join b
   500   │    ├── columns: k:1!null u:2 v:3 j:4
   501   │    ├── cardinality: [0 - 10]
   502   │    ├── key: (1)
   503   │    ├── fd: (1)-->(2-4), (3)-->(1), (3)~~>(1,2,4)
   504   │    └── scan b@v
   505   │         ├── columns: k:1!null v:3!null
   506   │         ├── constraint: /3: [/1 - /10]
   507   │         ├── cardinality: [0 - 10]
   508   │         ├── key: (1)
   509   │         └── fd: (1)-->(3), (3)-->(1)
   510   └── filters
   511        └── (k:1 + u:2) = 1 [outer=(1,2)]
   512  
   513  memo
   514  SELECT * FROM b WHERE v >= 1 AND v <= 10 AND k+u = 1
   515  ----
   516  memo (optimized, ~5KB, required=[presentation: k:1,u:2,v:3,j:4])
   517   ├── G1: (select G2 G3) (select G4 G5)
   518   │    └── [presentation: k:1,u:2,v:3,j:4]
   519   │         ├── best: (select G4 G5)
   520   │         └── cost: 51.43
   521   ├── G2: (scan b)
   522   │    └── []
   523   │         ├── best: (scan b)
   524   │         └── cost: 1080.02
   525   ├── G3: (filters G6 G7)
   526   ├── G4: (index-join G8 b,cols=(1-4))
   527   │    └── []
   528   │         ├── best: (index-join G8 b,cols=(1-4))
   529   │         └── cost: 51.32
   530   ├── G5: (filters G7)
   531   ├── G6: (range G9)
   532   ├── G7: (eq G10 G11)
   533   ├── G8: (scan b@v,cols=(1,3),constrained)
   534   │    └── []
   535   │         ├── best: (scan b@v,cols=(1,3),constrained)
   536   │         └── cost: 10.41
   537   ├── G9: (and G12 G13)
   538   ├── G10: (plus G14 G15)
   539   ├── G11: (const 1)
   540   ├── G12: (ge G16 G11)
   541   ├── G13: (le G16 G17)
   542   ├── G14: (variable k)
   543   ├── G15: (variable u)
   544   ├── G16: (variable v)
   545   └── G17: (const 10)
   546  
   547  opt
   548  SELECT * FROM b WHERE v >= 1 AND v <= 10 AND k+u = 1 AND k > 5
   549  ----
   550  select
   551   ├── columns: k:1!null u:2 v:3!null j:4
   552   ├── cardinality: [0 - 10]
   553   ├── key: (1)
   554   ├── fd: (1)-->(2-4), (3)-->(1,2,4)
   555   ├── index-join b
   556   │    ├── columns: k:1!null u:2 v:3 j:4
   557   │    ├── cardinality: [0 - 10]
   558   │    ├── key: (1)
   559   │    ├── fd: (1)-->(2-4), (3)-->(1), (3)~~>(1,2,4)
   560   │    └── select
   561   │         ├── columns: k:1!null v:3!null
   562   │         ├── cardinality: [0 - 10]
   563   │         ├── key: (1)
   564   │         ├── fd: (1)-->(3), (3)-->(1)
   565   │         ├── scan b@v
   566   │         │    ├── columns: k:1!null v:3!null
   567   │         │    ├── constraint: /3: [/1 - /10]
   568   │         │    ├── cardinality: [0 - 10]
   569   │         │    ├── key: (1)
   570   │         │    └── fd: (1)-->(3), (3)-->(1)
   571   │         └── filters
   572   │              └── k:1 > 5 [outer=(1), constraints=(/1: [/6 - ]; tight)]
   573   └── filters
   574        └── (k:1 + u:2) = 1 [outer=(1,2)]
   575  
   576  memo
   577  SELECT * FROM b WHERE v >= 1 AND v <= 10 AND k+u = 1 AND k > 5
   578  ----
   579  memo (optimized, ~7KB, required=[presentation: k:1,u:2,v:3,j:4])
   580   ├── G1: (select G2 G3) (select G4 G5) (select G6 G7)
   581   │    └── [presentation: k:1,u:2,v:3,j:4]
   582   │         ├── best: (select G6 G7)
   583   │         └── cost: 24.21
   584   ├── G2: (scan b)
   585   │    └── []
   586   │         ├── best: (scan b)
   587   │         └── cost: 1080.02
   588   ├── G3: (filters G8 G9 G10)
   589   ├── G4: (scan b,constrained)
   590   │    └── []
   591   │         ├── best: (scan b,constrained)
   592   │         └── cost: 360.01
   593   ├── G5: (filters G8 G9)
   594   ├── G6: (index-join G11 b,cols=(1-4))
   595   │    └── []
   596   │         ├── best: (index-join G11 b,cols=(1-4))
   597   │         └── cost: 24.16
   598   ├── G7: (filters G9)
   599   ├── G8: (range G12)
   600   ├── G9: (eq G13 G14)
   601   ├── G10: (gt G15 G16)
   602   ├── G11: (select G17 G18)
   603   │    └── []
   604   │         ├── best: (select G17 G18)
   605   │         └── cost: 10.52
   606   ├── G12: (and G19 G20)
   607   ├── G13: (plus G15 G21)
   608   ├── G14: (const 1)
   609   ├── G15: (variable k)
   610   ├── G16: (const 5)
   611   ├── G17: (scan b@v,cols=(1,3),constrained)
   612   │    └── []
   613   │         ├── best: (scan b@v,cols=(1,3),constrained)
   614   │         └── cost: 10.41
   615   ├── G18: (filters G10)
   616   ├── G19: (ge G22 G14)
   617   ├── G20: (le G22 G23)
   618   ├── G21: (variable u)
   619   ├── G22: (variable v)
   620   └── G23: (const 10)
   621  
   622  # Constraint + index-join.
   623  opt
   624  SELECT * FROM b WHERE (u, k, v) > (1, 2, 3) AND (u, k, v) < (8, 9, 10)
   625  ----
   626  select
   627   ├── columns: k:1!null u:2!null v:3 j:4
   628   ├── key: (1)
   629   ├── fd: (1)-->(2-4), (3)~~>(1,2,4)
   630   ├── index-join b
   631   │    ├── columns: k:1!null u:2 v:3 j:4
   632   │    ├── key: (1)
   633   │    ├── fd: (1)-->(2-4), (3)~~>(1,2,4)
   634   │    └── scan b@u
   635   │         ├── columns: k:1!null u:2!null
   636   │         ├── constraint: /2/1: [/1/2 - /8/9]
   637   │         ├── key: (1)
   638   │         └── fd: (1)-->(2)
   639   └── filters
   640        ├── (u:2, k:1, v:3) > (1, 2, 3) [outer=(1-3), constraints=(/2/1/3: [/1/2/4 - ]; tight)]
   641        └── (u:2, k:1, v:3) < (8, 9, 10) [outer=(1-3), constraints=(/2/1/3: (/NULL - /8/9/9]; tight)]
   642  
   643  memo
   644  SELECT * FROM b WHERE (u, k, v) > (1, 2, 3) AND (u, k, v) < (8, 9, 10)
   645  ----
   646  memo (optimized, ~5KB, required=[presentation: k:1,u:2,v:3,j:4])
   647   ├── G1: (select G2 G3) (select G4 G3)
   648   │    └── [presentation: k:1,u:2,v:3,j:4]
   649   │         ├── best: (select G4 G3)
   650   │         └── cost: 411.23
   651   ├── G2: (scan b)
   652   │    └── []
   653   │         ├── best: (scan b)
   654   │         └── cost: 1080.02
   655   ├── G3: (filters G5 G6)
   656   ├── G4: (index-join G7 b,cols=(1-4))
   657   │    └── []
   658   │         ├── best: (index-join G7 b,cols=(1-4))
   659   │         └── cost: 410.42
   660   ├── G5: (gt G8 G9)
   661   ├── G6: (lt G8 G10)
   662   ├── G7: (scan b@u,cols=(1,2),constrained)
   663   │    └── []
   664   │         ├── best: (scan b@u,cols=(1,2),constrained)
   665   │         └── cost: 83.21
   666   ├── G8: (tuple G11)
   667   ├── G9: (tuple G12)
   668   ├── G10: (tuple G13)
   669   ├── G11: (scalar-list G14 G15 G16)
   670   ├── G12: (scalar-list G17 G18 G19)
   671   ├── G13: (scalar-list G20 G21 G22)
   672   ├── G14: (variable u)
   673   ├── G15: (variable k)
   674   ├── G16: (variable v)
   675   ├── G17: (const 1)
   676   ├── G18: (const 2)
   677   ├── G19: (const 3)
   678   ├── G20: (const 8)
   679   ├── G21: (const 9)
   680   └── G22: (const 10)
   681  
   682  # GenerateConstrainedScans propagates row-level locking information.
   683  opt
   684  SELECT k FROM a WHERE k = 1 FOR UPDATE
   685  ----
   686  scan a
   687   ├── columns: k:1!null
   688   ├── constraint: /1: [/1 - /1]
   689   ├── locking: for-update
   690   ├── cardinality: [0 - 1]
   691   ├── volatile, side-effects
   692   ├── key: ()
   693   └── fd: ()-->(1)
   694  
   695  opt
   696  SELECT * FROM b WHERE v >= 1 AND v <= 10 FOR UPDATE
   697  ----
   698  index-join b
   699   ├── columns: k:1!null u:2 v:3!null j:4
   700   ├── cardinality: [0 - 10]
   701   ├── volatile, side-effects
   702   ├── key: (1)
   703   ├── fd: (1)-->(2-4), (3)-->(1,2,4)
   704   └── scan b@v
   705        ├── columns: k:1!null v:3!null
   706        ├── constraint: /3: [/1 - /10]
   707        ├── locking: for-update
   708        ├── cardinality: [0 - 10]
   709        ├── volatile, side-effects
   710        ├── key: (1)
   711        └── fd: (1)-->(3), (3)-->(1)
   712  
   713  opt
   714  SELECT * FROM b WHERE v >= 1 AND v <= 10 AND k+u = 1 FOR UPDATE
   715  ----
   716  select
   717   ├── columns: k:1!null u:2 v:3!null j:4
   718   ├── cardinality: [0 - 10]
   719   ├── volatile, side-effects
   720   ├── key: (1)
   721   ├── fd: (1)-->(2-4), (3)-->(1,2,4)
   722   ├── index-join b
   723   │    ├── columns: k:1!null u:2 v:3 j:4
   724   │    ├── cardinality: [0 - 10]
   725   │    ├── volatile, side-effects
   726   │    ├── key: (1)
   727   │    ├── fd: (1)-->(2-4), (3)-->(1), (3)~~>(1,2,4)
   728   │    └── scan b@v
   729   │         ├── columns: k:1!null v:3!null
   730   │         ├── constraint: /3: [/1 - /10]
   731   │         ├── locking: for-update
   732   │         ├── cardinality: [0 - 10]
   733   │         ├── volatile, side-effects
   734   │         ├── key: (1)
   735   │         └── fd: (1)-->(3), (3)-->(1)
   736   └── filters
   737        └── (k:1 + u:2) = 1 [outer=(1,2)]
   738  
   739  # --------------------------------------------------
   740  # GenerateInvertedIndexScans
   741  # --------------------------------------------------
   742  # TODO(justin): these can be serviced without an index join.
   743  # Query only the primary key with no remaining filter.
   744  opt
   745  SELECT k FROM b WHERE j @> '{"a": "b"}'
   746  ----
   747  project
   748   ├── columns: k:1!null
   749   ├── key: (1)
   750   └── index-join b
   751        ├── columns: k:1!null j:4
   752        ├── key: (1)
   753        ├── fd: (1)-->(4)
   754        └── scan b@inv_idx
   755             ├── columns: k:1!null
   756             ├── constraint: /4/1: [/'{"a": "b"}' - /'{"a": "b"}']
   757             └── key: (1)
   758  
   759  memo
   760  SELECT k FROM b WHERE j @> '{"a": "b"}'
   761  ----
   762  memo (optimized, ~7KB, required=[presentation: k:1])
   763   ├── G1: (project G2 G3 k)
   764   │    └── [presentation: k:1]
   765   │         ├── best: (project G2 G3 k)
   766   │         └── cost: 567.81
   767   ├── G2: (select G4 G5) (index-join G6 b,cols=(1,4))
   768   │    └── []
   769   │         ├── best: (index-join G6 b,cols=(1,4))
   770   │         └── cost: 566.69
   771   ├── G3: (projections)
   772   ├── G4: (scan b,cols=(1,4))
   773   │    └── []
   774   │         ├── best: (scan b,cols=(1,4))
   775   │         └── cost: 1060.02
   776   ├── G5: (filters G7)
   777   ├── G6: (scan b@inv_idx,cols=(1),constrained)
   778   │    └── []
   779   │         ├── best: (scan b@inv_idx,cols=(1),constrained)
   780   │         └── cost: 114.45
   781   ├── G7: (contains G8 G9)
   782   ├── G8: (variable j)
   783   └── G9: (const '{"a": "b"}')
   784  
   785  # Query only the primary key with a remaining filter. 2+ paths in containment
   786  # query should favor zigzag joins.
   787  opt
   788  SELECT k FROM b WHERE j @> '{"a": "b", "c": "d"}'
   789  ----
   790  project
   791   ├── columns: k:1!null
   792   ├── key: (1)
   793   └── inner-join (lookup b)
   794        ├── columns: k:1!null j:4
   795        ├── key columns: [1] = [1]
   796        ├── lookup columns are key
   797        ├── key: (1)
   798        ├── fd: (1)-->(4)
   799        ├── inner-join (zigzag b@inv_idx b@inv_idx)
   800        │    ├── columns: k:1!null
   801        │    ├── eq columns: [1] = [1]
   802        │    ├── left fixed columns: [4] = ['{"a": "b"}']
   803        │    ├── right fixed columns: [4] = ['{"c": "d"}']
   804        │    └── filters (true)
   805        └── filters
   806             └── j:4 @> '{"a": "b", "c": "d"}' [outer=(4)]
   807  
   808  # Query requiring an index join with no remaining filter.
   809  opt
   810  SELECT u, k FROM b WHERE j @> '{"a": "b"}'
   811  ----
   812  project
   813   ├── columns: u:2 k:1!null
   814   ├── key: (1)
   815   ├── fd: (1)-->(2)
   816   └── index-join b
   817        ├── columns: k:1!null u:2 j:4
   818        ├── key: (1)
   819        ├── fd: (1)-->(2,4)
   820        └── scan b@inv_idx
   821             ├── columns: k:1!null
   822             ├── constraint: /4/1: [/'{"a": "b"}' - /'{"a": "b"}']
   823             └── key: (1)
   824  
   825  opt
   826  SELECT j, k FROM b WHERE j @> '{"a": "b"}'
   827  ----
   828  index-join b
   829   ├── columns: j:4 k:1!null
   830   ├── key: (1)
   831   ├── fd: (1)-->(4)
   832   └── scan b@inv_idx
   833        ├── columns: k:1!null
   834        ├── constraint: /4/1: [/'{"a": "b"}' - /'{"a": "b"}']
   835        └── key: (1)
   836  
   837  opt
   838  SELECT * FROM b WHERE j @> '{"a": "b"}'
   839  ----
   840  index-join b
   841   ├── columns: k:1!null u:2 v:3 j:4
   842   ├── key: (1)
   843   ├── fd: (1)-->(2-4), (3)~~>(1,2,4)
   844   └── scan b@inv_idx
   845        ├── columns: k:1!null
   846        ├── constraint: /4/1: [/'{"a": "b"}' - /'{"a": "b"}']
   847        └── key: (1)
   848  
   849  # Query requiring a zigzag join with a remaining filter.
   850  # TODO(itsbilal): remove filter from index join if zigzag join covers it.
   851  opt
   852  SELECT j, k FROM b WHERE j @> '{"a": "b", "c": "d"}'
   853  ----
   854  inner-join (lookup b)
   855   ├── columns: j:4 k:1!null
   856   ├── key columns: [1] = [1]
   857   ├── lookup columns are key
   858   ├── key: (1)
   859   ├── fd: (1)-->(4)
   860   ├── inner-join (zigzag b@inv_idx b@inv_idx)
   861   │    ├── columns: k:1!null
   862   │    ├── eq columns: [1] = [1]
   863   │    ├── left fixed columns: [4] = ['{"a": "b"}']
   864   │    ├── right fixed columns: [4] = ['{"c": "d"}']
   865   │    └── filters (true)
   866   └── filters
   867        └── j:4 @> '{"a": "b", "c": "d"}' [outer=(4)]
   868  
   869  opt
   870  SELECT * FROM b WHERE j @> '{"a": {"b": "c", "d": "e"}, "f": "g"}'
   871  ----
   872  inner-join (lookup b)
   873   ├── columns: k:1!null u:2 v:3 j:4
   874   ├── key columns: [1] = [1]
   875   ├── lookup columns are key
   876   ├── key: (1)
   877   ├── fd: (1)-->(2-4), (3)~~>(1,2,4)
   878   ├── inner-join (zigzag b@inv_idx b@inv_idx)
   879   │    ├── columns: k:1!null
   880   │    ├── eq columns: [1] = [1]
   881   │    ├── left fixed columns: [4] = ['{"a": {"b": "c"}}']
   882   │    ├── right fixed columns: [4] = ['{"a": {"d": "e"}}']
   883   │    └── filters (true)
   884   └── filters
   885        └── j:4 @> '{"a": {"b": "c", "d": "e"}, "f": "g"}' [outer=(4)]
   886  
   887  opt
   888  SELECT * FROM b WHERE j @> '{}'
   889  ----
   890  select
   891   ├── columns: k:1!null u:2 v:3 j:4
   892   ├── key: (1)
   893   ├── fd: (1)-->(2-4), (3)~~>(1,2,4)
   894   ├── scan b
   895   │    ├── columns: k:1!null u:2 v:3 j:4
   896   │    ├── key: (1)
   897   │    └── fd: (1)-->(2-4), (3)~~>(1,2,4)
   898   └── filters
   899        └── j:4 @> '{}' [outer=(4)]
   900  
   901  opt
   902  SELECT * FROM b WHERE j @> '[]'
   903  ----
   904  select
   905   ├── columns: k:1!null u:2 v:3 j:4
   906   ├── key: (1)
   907   ├── fd: (1)-->(2-4), (3)~~>(1,2,4)
   908   ├── scan b
   909   │    ├── columns: k:1!null u:2 v:3 j:4
   910   │    ├── key: (1)
   911   │    └── fd: (1)-->(2-4), (3)~~>(1,2,4)
   912   └── filters
   913        └── j:4 @> '[]' [outer=(4)]
   914  
   915  opt
   916  SELECT * FROM b WHERE j @> '2'
   917  ----
   918  index-join b
   919   ├── columns: k:1!null u:2 v:3 j:4
   920   ├── key: (1)
   921   ├── fd: (1)-->(2-4), (3)~~>(1,2,4)
   922   └── scan b@inv_idx
   923        ├── columns: k:1!null
   924        ├── constraint: /4/1
   925        │    ├── [/'2' - /'2']
   926        │    └── [/'[2]' - /'[2]']
   927        └── key: (1)
   928  
   929  opt
   930  SELECT * FROM b WHERE j @> '[{}]'
   931  ----
   932  select
   933   ├── columns: k:1!null u:2 v:3 j:4
   934   ├── key: (1)
   935   ├── fd: (1)-->(2-4), (3)~~>(1,2,4)
   936   ├── scan b
   937   │    ├── columns: k:1!null u:2 v:3 j:4
   938   │    ├── key: (1)
   939   │    └── fd: (1)-->(2-4), (3)~~>(1,2,4)
   940   └── filters
   941        └── j:4 @> '[{}]' [outer=(4)]
   942  
   943  opt
   944  SELECT * FROM b WHERE j @> '{"a": {}}'
   945  ----
   946  select
   947   ├── columns: k:1!null u:2 v:3 j:4
   948   ├── key: (1)
   949   ├── fd: (1)-->(2-4), (3)~~>(1,2,4)
   950   ├── scan b
   951   │    ├── columns: k:1!null u:2 v:3 j:4
   952   │    ├── key: (1)
   953   │    └── fd: (1)-->(2-4), (3)~~>(1,2,4)
   954   └── filters
   955        └── j:4 @> '{"a": {}}' [outer=(4)]
   956  
   957  opt
   958  SELECT * FROM b WHERE j @> '{"a": []}'
   959  ----
   960  select
   961   ├── columns: k:1!null u:2 v:3 j:4
   962   ├── key: (1)
   963   ├── fd: (1)-->(2-4), (3)~~>(1,2,4)
   964   ├── scan b
   965   │    ├── columns: k:1!null u:2 v:3 j:4
   966   │    ├── key: (1)
   967   │    └── fd: (1)-->(2-4), (3)~~>(1,2,4)
   968   └── filters
   969        └── j:4 @> '{"a": []}' [outer=(4)]
   970  
   971  # GenerateInvertedIndexScans propagates row-level locking information.
   972  opt
   973  SELECT k FROM b WHERE j @> '{"a": "b"}' FOR UPDATE
   974  ----
   975  project
   976   ├── columns: k:1!null
   977   ├── volatile, side-effects
   978   ├── key: (1)
   979   └── index-join b
   980        ├── columns: k:1!null j:4
   981        ├── volatile, side-effects
   982        ├── key: (1)
   983        ├── fd: (1)-->(4)
   984        └── scan b@inv_idx
   985             ├── columns: k:1!null
   986             ├── constraint: /4/1: [/'{"a": "b"}' - /'{"a": "b"}']
   987             ├── locking: for-update
   988             ├── volatile, side-effects
   989             └── key: (1)
   990  
   991  # Tests for array inverted indexes.
   992  opt
   993  SELECT k FROM c WHERE a @> ARRAY[1]
   994  ----
   995  project
   996   ├── columns: k:1!null
   997   ├── key: (1)
   998   └── index-join c
   999        ├── columns: k:1!null a:2
  1000        ├── key: (1)
  1001        ├── fd: (1)-->(2)
  1002        └── scan c@inv_idx
  1003             ├── columns: k:1!null
  1004             ├── constraint: /2/1: [/ARRAY[1] - /ARRAY[1]]
  1005             └── key: (1)
  1006  
  1007  opt
  1008  SELECT k FROM c WHERE a @> ARRAY[1,3,1,5]
  1009  ----
  1010  project
  1011   ├── columns: k:1!null
  1012   ├── key: (1)
  1013   └── inner-join (lookup c)
  1014        ├── columns: k:1!null a:2
  1015        ├── key columns: [1] = [1]
  1016        ├── lookup columns are key
  1017        ├── key: (1)
  1018        ├── fd: (1)-->(2)
  1019        ├── inner-join (zigzag c@inv_idx c@inv_idx)
  1020        │    ├── columns: k:1!null
  1021        │    ├── eq columns: [1] = [1]
  1022        │    ├── left fixed columns: [2] = [ARRAY[1]]
  1023        │    ├── right fixed columns: [2] = [ARRAY[3]]
  1024        │    └── filters (true)
  1025        └── filters
  1026             └── a:2 @> ARRAY[1,3,1,5] [outer=(2)]
  1027  
  1028  opt
  1029  SELECT k FROM c WHERE a @> ARRAY[]::INT[]
  1030  ----
  1031  project
  1032   ├── columns: k:1!null
  1033   ├── key: (1)
  1034   └── select
  1035        ├── columns: k:1!null a:2
  1036        ├── key: (1)
  1037        ├── fd: (1)-->(2)
  1038        ├── scan c
  1039        │    ├── columns: k:1!null a:2
  1040        │    ├── key: (1)
  1041        │    └── fd: (1)-->(2)
  1042        └── filters
  1043             └── a:2 @> ARRAY[] [outer=(2)]
  1044  
  1045  opt
  1046  SELECT k FROM c WHERE a IS NULL
  1047  ----
  1048  project
  1049   ├── columns: k:1!null
  1050   ├── key: (1)
  1051   └── select
  1052        ├── columns: k:1!null a:2
  1053        ├── key: (1)
  1054        ├── fd: ()-->(2)
  1055        ├── scan c
  1056        │    ├── columns: k:1!null a:2
  1057        │    ├── key: (1)
  1058        │    └── fd: (1)-->(2)
  1059        └── filters
  1060             └── a:2 IS NULL [outer=(2), constraints=(/2: [/NULL - /NULL]; tight), fd=()-->(2)]
  1061  
  1062  
  1063  # --------------------------------------------------
  1064  # SplitDisjunction
  1065  # --------------------------------------------------
  1066  
  1067  # TODO(mgartner): PruneAggCols should be pruning columns from the DistinctOn
  1068  # and further down the expression tree, ultimatly eliminating the index-joins.
  1069  # PruneAggCols does not run in this case because normalization rules do not run
  1070  # at the root tree generated by an exploration rule.
  1071  opt expect=SplitDisjunction
  1072  SELECT k FROM d WHERE u = 1 OR v = 1
  1073  ----
  1074  project
  1075   ├── columns: k:1!null
  1076   ├── key: (1)
  1077   └── distinct-on
  1078        ├── columns: k:1!null u:2 v:3
  1079        ├── grouping columns: k:1!null
  1080        ├── key: (1)
  1081        ├── fd: (1)-->(2,3)
  1082        ├── union-all
  1083        │    ├── columns: k:1!null u:2 v:3
  1084        │    ├── left columns: k:1!null u:2 v:3
  1085        │    ├── right columns: k:5 u:6 v:7
  1086        │    ├── index-join d
  1087        │    │    ├── columns: k:1!null u:2!null v:3
  1088        │    │    ├── key: (1)
  1089        │    │    ├── fd: ()-->(2), (1)-->(3)
  1090        │    │    └── scan d@u
  1091        │    │         ├── columns: k:1!null u:2!null
  1092        │    │         ├── constraint: /2/1: [/1 - /1]
  1093        │    │         ├── key: (1)
  1094        │    │         └── fd: ()-->(2)
  1095        │    └── index-join d
  1096        │         ├── columns: k:5!null u:6 v:7!null
  1097        │         ├── key: (5)
  1098        │         ├── fd: ()-->(7), (5)-->(6)
  1099        │         └── scan d@v
  1100        │              ├── columns: k:5!null v:7!null
  1101        │              ├── constraint: /7/5: [/1 - /1]
  1102        │              ├── key: (5)
  1103        │              └── fd: ()-->(7)
  1104        └── aggregations
  1105             ├── const-agg [as=u:2, outer=(2)]
  1106             │    └── u:2
  1107             └── const-agg [as=v:3, outer=(3)]
  1108                  └── v:3
  1109  
  1110  opt expect=SplitDisjunction
  1111  SELECT * FROM d WHERE w = 1 AND (u = 1 OR v = 1)
  1112  ----
  1113  distinct-on
  1114   ├── columns: k:1!null u:2 v:3 w:4!null
  1115   ├── grouping columns: k:1!null
  1116   ├── key: (1)
  1117   ├── fd: ()-->(4), (1)-->(2,3)
  1118   ├── union-all
  1119   │    ├── columns: k:1!null u:2 v:3 w:4!null
  1120   │    ├── left columns: k:1!null u:2 v:3 w:4!null
  1121   │    ├── right columns: k:5 u:6 v:7 w:8
  1122   │    ├── select
  1123   │    │    ├── columns: k:1!null u:2!null v:3 w:4!null
  1124   │    │    ├── key: (1)
  1125   │    │    ├── fd: ()-->(2,4), (1)-->(3)
  1126   │    │    ├── index-join d
  1127   │    │    │    ├── columns: k:1!null u:2 v:3 w:4
  1128   │    │    │    ├── key: (1)
  1129   │    │    │    ├── fd: ()-->(2), (1)-->(3,4)
  1130   │    │    │    └── scan d@u
  1131   │    │    │         ├── columns: k:1!null u:2!null
  1132   │    │    │         ├── constraint: /2/1: [/1 - /1]
  1133   │    │    │         ├── key: (1)
  1134   │    │    │         └── fd: ()-->(2)
  1135   │    │    └── filters
  1136   │    │         └── w:4 = 1 [outer=(4), constraints=(/4: [/1 - /1]; tight), fd=()-->(4)]
  1137   │    └── select
  1138   │         ├── columns: k:5!null u:6 v:7!null w:8!null
  1139   │         ├── key: (5)
  1140   │         ├── fd: ()-->(7,8), (5)-->(6)
  1141   │         ├── index-join d
  1142   │         │    ├── columns: k:5!null u:6 v:7 w:8
  1143   │         │    ├── key: (5)
  1144   │         │    ├── fd: ()-->(7), (5)-->(6,8)
  1145   │         │    └── scan d@v
  1146   │         │         ├── columns: k:5!null v:7!null
  1147   │         │         ├── constraint: /7/5: [/1 - /1]
  1148   │         │         ├── key: (5)
  1149   │         │         └── fd: ()-->(7)
  1150   │         └── filters
  1151   │              └── w:8 = 1 [outer=(8), constraints=(/8: [/1 - /1]; tight), fd=()-->(8)]
  1152   └── aggregations
  1153        ├── const-agg [as=u:2, outer=(2)]
  1154        │    └── u:2
  1155        ├── const-agg [as=v:3, outer=(3)]
  1156        │    └── v:3
  1157        └── const-agg [as=w:4, outer=(4)]
  1158             └── w:4
  1159  
  1160  opt expect=SplitDisjunction
  1161  SELECT k FROM d WHERE (u = 1 OR v = 2) AND (u = 10 OR v = 20)
  1162  ----
  1163  project
  1164   ├── columns: k:1!null
  1165   ├── key: (1)
  1166   └── distinct-on
  1167        ├── columns: k:1!null u:2 v:3
  1168        ├── grouping columns: k:1!null
  1169        ├── key: (1)
  1170        ├── fd: (1)-->(2,3)
  1171        ├── union-all
  1172        │    ├── columns: k:1!null u:2!null v:3!null
  1173        │    ├── left columns: k:1!null u:2!null v:3!null
  1174        │    ├── right columns: k:5 u:6 v:7
  1175        │    ├── inner-join (zigzag d@u d@v)
  1176        │    │    ├── columns: k:1!null u:2!null v:3!null
  1177        │    │    ├── eq columns: [1] = [1]
  1178        │    │    ├── left fixed columns: [2] = [1]
  1179        │    │    ├── right fixed columns: [3] = [20]
  1180        │    │    ├── key: (1)
  1181        │    │    ├── fd: ()-->(2,3)
  1182        │    │    └── filters
  1183        │    │         ├── u:2 = 1 [outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
  1184        │    │         └── v:3 = 20 [outer=(3), constraints=(/3: [/20 - /20]; tight), fd=()-->(3)]
  1185        │    └── inner-join (zigzag d@u d@v)
  1186        │         ├── columns: k:5!null u:6!null v:7!null
  1187        │         ├── eq columns: [5] = [5]
  1188        │         ├── left fixed columns: [6] = [10]
  1189        │         ├── right fixed columns: [7] = [2]
  1190        │         ├── key: (5)
  1191        │         ├── fd: ()-->(6,7)
  1192        │         └── filters
  1193        │              ├── v:7 = 2 [outer=(7), constraints=(/7: [/2 - /2]; tight), fd=()-->(7)]
  1194        │              └── u:6 = 10 [outer=(6), constraints=(/6: [/10 - /10]; tight), fd=()-->(6)]
  1195        └── aggregations
  1196             ├── const-agg [as=u:2, outer=(2)]
  1197             │    └── u:2
  1198             └── const-agg [as=v:3, outer=(3)]
  1199                  └── v:3
  1200  
  1201  opt expect=SplitDisjunction
  1202  SELECT sum(k) FROM d WHERE u = 1 OR v = 1
  1203  ----
  1204  scalar-group-by
  1205   ├── columns: sum:5
  1206   ├── cardinality: [1 - 1]
  1207   ├── key: ()
  1208   ├── fd: ()-->(5)
  1209   ├── distinct-on
  1210   │    ├── columns: k:1!null u:2 v:3
  1211   │    ├── grouping columns: k:1!null
  1212   │    ├── key: (1)
  1213   │    ├── fd: (1)-->(2,3)
  1214   │    ├── union-all
  1215   │    │    ├── columns: k:1!null u:2 v:3
  1216   │    │    ├── left columns: k:1!null u:2 v:3
  1217   │    │    ├── right columns: k:6 u:7 v:8
  1218   │    │    ├── index-join d
  1219   │    │    │    ├── columns: k:1!null u:2!null v:3
  1220   │    │    │    ├── key: (1)
  1221   │    │    │    ├── fd: ()-->(2), (1)-->(3)
  1222   │    │    │    └── scan d@u
  1223   │    │    │         ├── columns: k:1!null u:2!null
  1224   │    │    │         ├── constraint: /2/1: [/1 - /1]
  1225   │    │    │         ├── key: (1)
  1226   │    │    │         └── fd: ()-->(2)
  1227   │    │    └── index-join d
  1228   │    │         ├── columns: k:6!null u:7 v:8!null
  1229   │    │         ├── key: (6)
  1230   │    │         ├── fd: ()-->(8), (6)-->(7)
  1231   │    │         └── scan d@v
  1232   │    │              ├── columns: k:6!null v:8!null
  1233   │    │              ├── constraint: /8/6: [/1 - /1]
  1234   │    │              ├── key: (6)
  1235   │    │              └── fd: ()-->(8)
  1236   │    └── aggregations
  1237   │         ├── const-agg [as=u:2, outer=(2)]
  1238   │         │    └── u:2
  1239   │         └── const-agg [as=v:3, outer=(3)]
  1240   │              └── v:3
  1241   └── aggregations
  1242        └── sum [as=sum:5, outer=(1)]
  1243             └── k:1
  1244  
  1245  # Multi-column primary key.
  1246  opt expect=SplitDisjunction
  1247  SELECT k, j FROM f WHERE u = 1 OR v = 2
  1248  ----
  1249  project
  1250   ├── columns: k:1!null j:2!null
  1251   ├── key: (1,2)
  1252   └── distinct-on
  1253        ├── columns: k:1!null j:2!null u:3 v:4
  1254        ├── grouping columns: k:1!null j:2!null
  1255        ├── key: (1,2)
  1256        ├── fd: (1,2)-->(3,4)
  1257        ├── union-all
  1258        │    ├── columns: k:1!null j:2!null u:3 v:4
  1259        │    ├── left columns: k:1!null j:2!null u:3 v:4
  1260        │    ├── right columns: k:5 j:6 u:7 v:8
  1261        │    ├── index-join f
  1262        │    │    ├── columns: k:1!null j:2!null u:3!null v:4
  1263        │    │    ├── key: (1,2)
  1264        │    │    ├── fd: ()-->(3), (1,2)-->(4)
  1265        │    │    └── scan f@u
  1266        │    │         ├── columns: k:1!null j:2!null u:3!null
  1267        │    │         ├── constraint: /3/1/2: [/1 - /1]
  1268        │    │         ├── key: (1,2)
  1269        │    │         └── fd: ()-->(3)
  1270        │    └── index-join f
  1271        │         ├── columns: k:5!null j:6!null u:7 v:8!null
  1272        │         ├── key: (5,6)
  1273        │         ├── fd: ()-->(8), (5,6)-->(7)
  1274        │         └── scan f@v
  1275        │              ├── columns: k:5!null j:6!null v:8!null
  1276        │              ├── constraint: /8/5/6: [/2 - /2]
  1277        │              ├── key: (5,6)
  1278        │              └── fd: ()-->(8)
  1279        └── aggregations
  1280             ├── const-agg [as=u:3, outer=(3)]
  1281             │    └── u:3
  1282             └── const-agg [as=v:4, outer=(4)]
  1283                  └── v:4
  1284  
  1285  # Don't expand INs to many ORs.
  1286  opt expect=SplitDisjunction
  1287  SELECT k FROM d WHERE u IN (1, 2, 3, 4) OR v IN (5, 6, 7, 8)
  1288  ----
  1289  project
  1290   ├── columns: k:1!null
  1291   ├── key: (1)
  1292   └── distinct-on
  1293        ├── columns: k:1!null u:2 v:3
  1294        ├── grouping columns: k:1!null
  1295        ├── key: (1)
  1296        ├── fd: (1)-->(2,3)
  1297        ├── union-all
  1298        │    ├── columns: k:1!null u:2 v:3
  1299        │    ├── left columns: k:1!null u:2 v:3
  1300        │    ├── right columns: k:5 u:6 v:7
  1301        │    ├── index-join d
  1302        │    │    ├── columns: k:1!null u:2!null v:3
  1303        │    │    ├── key: (1)
  1304        │    │    ├── fd: (1)-->(2,3)
  1305        │    │    └── scan d@u
  1306        │    │         ├── columns: k:1!null u:2!null
  1307        │    │         ├── constraint: /2/1: [/1 - /4]
  1308        │    │         ├── key: (1)
  1309        │    │         └── fd: (1)-->(2)
  1310        │    └── index-join d
  1311        │         ├── columns: k:5!null u:6 v:7!null
  1312        │         ├── key: (5)
  1313        │         ├── fd: (5)-->(6,7)
  1314        │         └── scan d@v
  1315        │              ├── columns: k:5!null v:7!null
  1316        │              ├── constraint: /7/5: [/5 - /8]
  1317        │              ├── key: (5)
  1318        │              └── fd: (5)-->(7)
  1319        └── aggregations
  1320             ├── const-agg [as=u:2, outer=(2)]
  1321             │    └── u:2
  1322             └── const-agg [as=v:3, outer=(3)]
  1323                  └── v:3
  1324  
  1325  # Split and constrain with an inverted index on JSONB on one side.
  1326  opt expect=SplitDisjunction
  1327  SELECT k FROM b WHERE k = 1 OR j @> '{"foo": "bar"}'
  1328  ----
  1329  project
  1330   ├── columns: k:1!null
  1331   ├── key: (1)
  1332   └── distinct-on
  1333        ├── columns: k:1!null j:4
  1334        ├── grouping columns: k:1!null
  1335        ├── key: (1)
  1336        ├── fd: (1)-->(4)
  1337        ├── union-all
  1338        │    ├── columns: k:1!null j:4
  1339        │    ├── left columns: k:1!null j:4
  1340        │    ├── right columns: k:5 j:8
  1341        │    ├── scan b
  1342        │    │    ├── columns: k:1!null j:4
  1343        │    │    ├── constraint: /1: [/1 - /1]
  1344        │    │    ├── cardinality: [0 - 1]
  1345        │    │    ├── key: ()
  1346        │    │    └── fd: ()-->(1,4)
  1347        │    └── index-join b
  1348        │         ├── columns: k:5!null j:8
  1349        │         ├── key: (5)
  1350        │         ├── fd: (5)-->(8)
  1351        │         └── scan b@inv_idx
  1352        │              ├── columns: k:5!null
  1353        │              ├── constraint: /8/5: [/'{"foo": "bar"}' - /'{"foo": "bar"}']
  1354        │              └── key: (5)
  1355        └── aggregations
  1356             └── const-agg [as=j:4, outer=(4)]
  1357                  └── j:4
  1358  
  1359  # Split and constrain with an inverted index on an array on one side.
  1360  opt expect=SplitDisjunction
  1361  SELECT k FROM c WHERE k = 1 OR a @> ARRAY[2]
  1362  ----
  1363  project
  1364   ├── columns: k:1!null
  1365   ├── key: (1)
  1366   └── distinct-on
  1367        ├── columns: k:1!null a:2
  1368        ├── grouping columns: k:1!null
  1369        ├── key: (1)
  1370        ├── fd: (1)-->(2)
  1371        ├── union-all
  1372        │    ├── columns: k:1!null a:2
  1373        │    ├── left columns: k:1!null a:2
  1374        │    ├── right columns: k:4 a:5
  1375        │    ├── scan c
  1376        │    │    ├── columns: k:1!null a:2
  1377        │    │    ├── constraint: /1: [/1 - /1]
  1378        │    │    ├── cardinality: [0 - 1]
  1379        │    │    ├── key: ()
  1380        │    │    └── fd: ()-->(1,2)
  1381        │    └── index-join c
  1382        │         ├── columns: k:4!null a:5
  1383        │         ├── key: (4)
  1384        │         ├── fd: (4)-->(5)
  1385        │         └── scan c@inv_idx
  1386        │              ├── columns: k:4!null
  1387        │              ├── constraint: /5/4: [/ARRAY[2] - /ARRAY[2]]
  1388        │              └── key: (4)
  1389        └── aggregations
  1390             └── const-agg [as=a:2, outer=(2)]
  1391                  └── a:2
  1392  
  1393  # Uncorrelated subquery.
  1394  opt expect=SplitDisjunction
  1395  SELECT k FROM d WHERE (u = 1 OR v = 1) AND EXISTS (SELECT u, v FROM a)
  1396  ----
  1397  project
  1398   ├── columns: k:1!null
  1399   ├── key: (1)
  1400   └── distinct-on
  1401        ├── columns: d.k:1!null d.u:2 d.v:3
  1402        ├── grouping columns: d.k:1!null
  1403        ├── key: (1)
  1404        ├── fd: (1)-->(2,3)
  1405        ├── union-all
  1406        │    ├── columns: d.k:1!null d.u:2 d.v:3
  1407        │    ├── left columns: d.k:1!null d.u:2 d.v:3
  1408        │    ├── right columns: d.k:8 d.u:9 d.v:10
  1409        │    ├── index-join d
  1410        │    │    ├── columns: d.k:1!null d.u:2!null d.v:3
  1411        │    │    ├── key: (1)
  1412        │    │    ├── fd: ()-->(2), (1)-->(3)
  1413        │    │    └── select
  1414        │    │         ├── columns: d.k:1!null d.u:2!null
  1415        │    │         ├── key: (1)
  1416        │    │         ├── fd: ()-->(2)
  1417        │    │         ├── scan d@u
  1418        │    │         │    ├── columns: d.k:1!null d.u:2!null
  1419        │    │         │    ├── constraint: /2/1: [/1 - /1]
  1420        │    │         │    ├── key: (1)
  1421        │    │         │    └── fd: ()-->(2)
  1422        │    │         └── filters
  1423        │    │              └── exists [subquery]
  1424        │    │                   └── scan a
  1425        │    │                        ├── columns: a.u:6 a.v:7
  1426        │    │                        ├── limit: 1
  1427        │    │                        ├── key: ()
  1428        │    │                        └── fd: ()-->(6,7)
  1429        │    └── index-join d
  1430        │         ├── columns: d.k:8!null d.u:9 d.v:10!null
  1431        │         ├── key: (8)
  1432        │         ├── fd: ()-->(10), (8)-->(9)
  1433        │         └── select
  1434        │              ├── columns: d.k:8!null d.v:10!null
  1435        │              ├── key: (8)
  1436        │              ├── fd: ()-->(10)
  1437        │              ├── scan d@v
  1438        │              │    ├── columns: d.k:8!null d.v:10!null
  1439        │              │    ├── constraint: /10/8: [/1 - /1]
  1440        │              │    ├── key: (8)
  1441        │              │    └── fd: ()-->(10)
  1442        │              └── filters
  1443        │                   └── exists [subquery]
  1444        │                        └── scan a
  1445        │                             ├── columns: a.u:6 a.v:7
  1446        │                             ├── limit: 1
  1447        │                             ├── key: ()
  1448        │                             └── fd: ()-->(6,7)
  1449        └── aggregations
  1450             ├── const-agg [as=d.u:2, outer=(2)]
  1451             │    └── d.u:2
  1452             └── const-agg [as=d.v:3, outer=(3)]
  1453                  └── d.v:3
  1454  
  1455  # Correlated subquery.
  1456  opt expect=SplitDisjunction
  1457  SELECT k FROM d WHERE (u = 1 OR v = 1) AND EXISTS (SELECT * FROM a WHERE a.u = d.u)
  1458  ----
  1459  project
  1460   ├── columns: k:1!null
  1461   ├── key: (1)
  1462   └── project
  1463        ├── columns: d.k:1!null d.u:2 d.v:3
  1464        ├── key: (1)
  1465        ├── fd: (1)-->(2,3)
  1466        └── inner-join (hash)
  1467             ├── columns: d.k:1!null d.u:2!null d.v:3 a.u:6!null
  1468             ├── key: (1)
  1469             ├── fd: (1)-->(2,3), (2)==(6), (6)==(2)
  1470             ├── distinct-on
  1471             │    ├── columns: d.k:1!null d.u:2 d.v:3
  1472             │    ├── grouping columns: d.k:1!null
  1473             │    ├── key: (1)
  1474             │    ├── fd: (1)-->(2,3)
  1475             │    ├── union-all
  1476             │    │    ├── columns: d.k:1!null d.u:2 d.v:3
  1477             │    │    ├── left columns: d.k:1!null d.u:2 d.v:3
  1478             │    │    ├── right columns: d.k:8 d.u:9 d.v:10
  1479             │    │    ├── index-join d
  1480             │    │    │    ├── columns: d.k:1!null d.u:2!null d.v:3
  1481             │    │    │    ├── key: (1)
  1482             │    │    │    ├── fd: ()-->(2), (1)-->(3)
  1483             │    │    │    └── scan d@u
  1484             │    │    │         ├── columns: d.k:1!null d.u:2!null
  1485             │    │    │         ├── constraint: /2/1: [/1 - /1]
  1486             │    │    │         ├── key: (1)
  1487             │    │    │         └── fd: ()-->(2)
  1488             │    │    └── index-join d
  1489             │    │         ├── columns: d.k:8!null d.u:9 d.v:10!null
  1490             │    │         ├── key: (8)
  1491             │    │         ├── fd: ()-->(10), (8)-->(9)
  1492             │    │         └── scan d@v
  1493             │    │              ├── columns: d.k:8!null d.v:10!null
  1494             │    │              ├── constraint: /10/8: [/1 - /1]
  1495             │    │              ├── key: (8)
  1496             │    │              └── fd: ()-->(10)
  1497             │    └── aggregations
  1498             │         ├── const-agg [as=d.u:2, outer=(2)]
  1499             │         │    └── d.u:2
  1500             │         └── const-agg [as=d.v:3, outer=(3)]
  1501             │              └── d.v:3
  1502             ├── distinct-on
  1503             │    ├── columns: a.u:6
  1504             │    ├── grouping columns: a.u:6
  1505             │    ├── internal-ordering: +6
  1506             │    ├── key: (6)
  1507             │    └── scan a@u
  1508             │         ├── columns: a.u:6
  1509             │         └── ordering: +6
  1510             └── filters
  1511                  └── a.u:6 = d.u:2 [outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
  1512  
  1513  # Correlated subquery with references to outer columns not in the scan columns.
  1514  opt expect=SplitDisjunction
  1515  SELECT k FROM d WHERE (u = 1 OR v = 1) AND EXISTS (SELECT * FROM a WHERE a.u = d.w)
  1516  ----
  1517  project
  1518   ├── columns: k:1!null
  1519   ├── key: (1)
  1520   └── project
  1521        ├── columns: d.k:1!null d.u:2 d.v:3 w:4
  1522        ├── key: (1)
  1523        ├── fd: (1)-->(2-4)
  1524        └── inner-join (hash)
  1525             ├── columns: d.k:1!null d.u:2 d.v:3 w:4!null a.u:6!null
  1526             ├── key: (1)
  1527             ├── fd: (1)-->(2-4), (4)==(6), (6)==(4)
  1528             ├── distinct-on
  1529             │    ├── columns: d.k:1!null d.u:2 d.v:3 w:4
  1530             │    ├── grouping columns: d.k:1!null
  1531             │    ├── key: (1)
  1532             │    ├── fd: (1)-->(2-4)
  1533             │    ├── union-all
  1534             │    │    ├── columns: d.k:1!null d.u:2 d.v:3 w:4
  1535             │    │    ├── left columns: d.k:1!null d.u:2 d.v:3 w:4
  1536             │    │    ├── right columns: d.k:8 d.u:9 d.v:10 w:11
  1537             │    │    ├── index-join d
  1538             │    │    │    ├── columns: d.k:1!null d.u:2!null d.v:3 w:4
  1539             │    │    │    ├── key: (1)
  1540             │    │    │    ├── fd: ()-->(2), (1)-->(3,4)
  1541             │    │    │    └── scan d@u
  1542             │    │    │         ├── columns: d.k:1!null d.u:2!null
  1543             │    │    │         ├── constraint: /2/1: [/1 - /1]
  1544             │    │    │         ├── key: (1)
  1545             │    │    │         └── fd: ()-->(2)
  1546             │    │    └── index-join d
  1547             │    │         ├── columns: d.k:8!null d.u:9 d.v:10!null w:11
  1548             │    │         ├── key: (8)
  1549             │    │         ├── fd: ()-->(10), (8)-->(9,11)
  1550             │    │         └── scan d@v
  1551             │    │              ├── columns: d.k:8!null d.v:10!null
  1552             │    │              ├── constraint: /10/8: [/1 - /1]
  1553             │    │              ├── key: (8)
  1554             │    │              └── fd: ()-->(10)
  1555             │    └── aggregations
  1556             │         ├── const-agg [as=d.u:2, outer=(2)]
  1557             │         │    └── d.u:2
  1558             │         ├── const-agg [as=d.v:3, outer=(3)]
  1559             │         │    └── d.v:3
  1560             │         └── const-agg [as=w:4, outer=(4)]
  1561             │              └── w:4
  1562             ├── distinct-on
  1563             │    ├── columns: a.u:6
  1564             │    ├── grouping columns: a.u:6
  1565             │    ├── internal-ordering: +6
  1566             │    ├── key: (6)
  1567             │    └── scan a@u
  1568             │         ├── columns: a.u:6
  1569             │         └── ordering: +6
  1570             └── filters
  1571                  └── a.u:6 = w:4 [outer=(4,6), constraints=(/4: (/NULL - ]; /6: (/NULL - ]), fd=(4)==(6), (6)==(4)]
  1572  
  1573  # Apply when outer columns of both sides of OR are a subset of index columns.
  1574  opt expect=SplitDisjunction
  1575  SELECT k, u, v FROM e WHERE u = 1 OR v = 1
  1576  ----
  1577  distinct-on
  1578   ├── columns: k:1!null u:2 v:3
  1579   ├── grouping columns: k:1!null
  1580   ├── key: (1)
  1581   ├── fd: (1)-->(2,3)
  1582   ├── union-all
  1583   │    ├── columns: k:1!null u:2 v:3
  1584   │    ├── left columns: k:1!null u:2 v:3
  1585   │    ├── right columns: k:5 u:6 v:7
  1586   │    ├── index-join e
  1587   │    │    ├── columns: k:1!null u:2!null v:3
  1588   │    │    ├── key: (1)
  1589   │    │    ├── fd: ()-->(2), (1)-->(3)
  1590   │    │    └── scan e@uw
  1591   │    │         ├── columns: k:1!null u:2!null
  1592   │    │         ├── constraint: /2/4/1: [/1 - /1]
  1593   │    │         ├── key: (1)
  1594   │    │         └── fd: ()-->(2)
  1595   │    └── index-join e
  1596   │         ├── columns: k:5!null u:6 v:7!null
  1597   │         ├── key: (5)
  1598   │         ├── fd: ()-->(7), (5)-->(6)
  1599   │         └── scan e@vw
  1600   │              ├── columns: k:5!null v:7!null
  1601   │              ├── constraint: /7/8/5: [/1 - /1]
  1602   │              ├── key: (5)
  1603   │              └── fd: ()-->(7)
  1604   └── aggregations
  1605        ├── const-agg [as=u:2, outer=(2)]
  1606        │    └── u:2
  1607        └── const-agg [as=v:3, outer=(3)]
  1608             └── v:3
  1609  
  1610  # Apply when outer columns of both sides of OR are a superset of index columns.
  1611  opt expect=SplitDisjunction
  1612  SELECT k, u, v FROM d WHERE (u = 1 AND w = 2) OR (v = 1 AND w = 3)
  1613  ----
  1614  project
  1615   ├── columns: k:1!null u:2 v:3
  1616   ├── key: (1)
  1617   ├── fd: (1)-->(2,3)
  1618   └── distinct-on
  1619        ├── columns: k:1!null u:2 v:3 w:4!null
  1620        ├── grouping columns: k:1!null
  1621        ├── key: (1)
  1622        ├── fd: (1)-->(2-4)
  1623        ├── union-all
  1624        │    ├── columns: k:1!null u:2 v:3 w:4!null
  1625        │    ├── left columns: k:1!null u:2 v:3 w:4!null
  1626        │    ├── right columns: k:5 u:6 v:7 w:8
  1627        │    ├── select
  1628        │    │    ├── columns: k:1!null u:2!null v:3 w:4!null
  1629        │    │    ├── key: (1)
  1630        │    │    ├── fd: ()-->(2,4), (1)-->(3)
  1631        │    │    ├── index-join d
  1632        │    │    │    ├── columns: k:1!null u:2 v:3 w:4
  1633        │    │    │    ├── key: (1)
  1634        │    │    │    ├── fd: ()-->(2), (1)-->(3,4)
  1635        │    │    │    └── scan d@u
  1636        │    │    │         ├── columns: k:1!null u:2!null
  1637        │    │    │         ├── constraint: /2/1: [/1 - /1]
  1638        │    │    │         ├── key: (1)
  1639        │    │    │         └── fd: ()-->(2)
  1640        │    │    └── filters
  1641        │    │         └── w:4 = 2 [outer=(4), constraints=(/4: [/2 - /2]; tight), fd=()-->(4)]
  1642        │    └── select
  1643        │         ├── columns: k:5!null u:6 v:7!null w:8!null
  1644        │         ├── key: (5)
  1645        │         ├── fd: ()-->(7,8), (5)-->(6)
  1646        │         ├── index-join d
  1647        │         │    ├── columns: k:5!null u:6 v:7 w:8
  1648        │         │    ├── key: (5)
  1649        │         │    ├── fd: ()-->(7), (5)-->(6,8)
  1650        │         │    └── scan d@v
  1651        │         │         ├── columns: k:5!null v:7!null
  1652        │         │         ├── constraint: /7/5: [/1 - /1]
  1653        │         │         ├── key: (5)
  1654        │         │         └── fd: ()-->(7)
  1655        │         └── filters
  1656        │              └── w:8 = 3 [outer=(8), constraints=(/8: [/3 - /3]; tight), fd=()-->(8)]
  1657        └── aggregations
  1658             ├── const-agg [as=u:2, outer=(2)]
  1659             │    └── u:2
  1660             ├── const-agg [as=v:3, outer=(3)]
  1661             │    └── v:3
  1662             └── const-agg [as=w:4, outer=(4)]
  1663                  └── w:4
  1664  
  1665  # Group sub-expr with the same columns together.
  1666  opt expect=SplitDisjunction
  1667  SELECT k, u, v FROM d WHERE (u = 1 OR v = 2) OR (u = 3 OR v = 4)
  1668  ----
  1669  distinct-on
  1670   ├── columns: k:1!null u:2 v:3
  1671   ├── grouping columns: k:1!null
  1672   ├── key: (1)
  1673   ├── fd: (1)-->(2,3)
  1674   ├── union-all
  1675   │    ├── columns: k:1!null u:2 v:3
  1676   │    ├── left columns: k:1!null u:2 v:3
  1677   │    ├── right columns: k:5 u:6 v:7
  1678   │    ├── index-join d
  1679   │    │    ├── columns: k:1!null u:2!null v:3
  1680   │    │    ├── key: (1)
  1681   │    │    ├── fd: (1)-->(2,3)
  1682   │    │    └── scan d@u
  1683   │    │         ├── columns: k:1!null u:2!null
  1684   │    │         ├── constraint: /2/1
  1685   │    │         │    ├── [/1 - /1]
  1686   │    │         │    └── [/3 - /3]
  1687   │    │         ├── key: (1)
  1688   │    │         └── fd: (1)-->(2)
  1689   │    └── index-join d
  1690   │         ├── columns: k:5!null u:6 v:7!null
  1691   │         ├── key: (5)
  1692   │         ├── fd: (5)-->(6,7)
  1693   │         └── scan d@v
  1694   │              ├── columns: k:5!null v:7!null
  1695   │              ├── constraint: /7/5
  1696   │              │    ├── [/2 - /2]
  1697   │              │    └── [/4 - /4]
  1698   │              ├── key: (5)
  1699   │              └── fd: (5)-->(7)
  1700   └── aggregations
  1701        ├── const-agg [as=u:2, outer=(2)]
  1702        │    └── u:2
  1703        └── const-agg [as=v:3, outer=(3)]
  1704             └── v:3
  1705  
  1706  # Group sub-expr with the same columns together. Output should have a single union expr.
  1707  opt expect=SplitDisjunction
  1708  SELECT k, u, v FROM d WHERE u = 1 OR u = 3 OR v = 2 OR v = 4 OR u = 5 OR v = 6
  1709  ----
  1710  distinct-on
  1711   ├── columns: k:1!null u:2 v:3
  1712   ├── grouping columns: k:1!null
  1713   ├── key: (1)
  1714   ├── fd: (1)-->(2,3)
  1715   ├── union-all
  1716   │    ├── columns: k:1!null u:2 v:3
  1717   │    ├── left columns: k:1!null u:2 v:3
  1718   │    ├── right columns: k:5 u:6 v:7
  1719   │    ├── index-join d
  1720   │    │    ├── columns: k:1!null u:2!null v:3
  1721   │    │    ├── key: (1)
  1722   │    │    ├── fd: (1)-->(2,3)
  1723   │    │    └── scan d@u
  1724   │    │         ├── columns: k:1!null u:2!null
  1725   │    │         ├── constraint: /2/1
  1726   │    │         │    ├── [/1 - /1]
  1727   │    │         │    ├── [/3 - /3]
  1728   │    │         │    └── [/5 - /5]
  1729   │    │         ├── key: (1)
  1730   │    │         └── fd: (1)-->(2)
  1731   │    └── index-join d
  1732   │         ├── columns: k:5!null u:6 v:7!null
  1733   │         ├── key: (5)
  1734   │         ├── fd: (5)-->(6,7)
  1735   │         └── scan d@v
  1736   │              ├── columns: k:5!null v:7!null
  1737   │              ├── constraint: /7/5
  1738   │              │    ├── [/2 - /2]
  1739   │              │    ├── [/4 - /4]
  1740   │              │    └── [/6 - /6]
  1741   │              ├── key: (5)
  1742   │              └── fd: (5)-->(7)
  1743   └── aggregations
  1744        ├── const-agg [as=u:2, outer=(2)]
  1745        │    └── u:2
  1746        └── const-agg [as=v:3, outer=(3)]
  1747             └── v:3
  1748  
  1749  # Group sub-expr with the same columns together. Output should have a single union expr.
  1750  opt expect=SplitDisjunction
  1751  SELECT k, u, v FROM d WHERE (u = 3 OR v = 2) OR (u = 5 OR v = 4) OR v = 6
  1752  ----
  1753  distinct-on
  1754   ├── columns: k:1!null u:2 v:3
  1755   ├── grouping columns: k:1!null
  1756   ├── key: (1)
  1757   ├── fd: (1)-->(2,3)
  1758   ├── union-all
  1759   │    ├── columns: k:1!null u:2 v:3
  1760   │    ├── left columns: k:1!null u:2 v:3
  1761   │    ├── right columns: k:5 u:6 v:7
  1762   │    ├── index-join d
  1763   │    │    ├── columns: k:1!null u:2!null v:3
  1764   │    │    ├── key: (1)
  1765   │    │    ├── fd: (1)-->(2,3)
  1766   │    │    └── scan d@u
  1767   │    │         ├── columns: k:1!null u:2!null
  1768   │    │         ├── constraint: /2/1
  1769   │    │         │    ├── [/3 - /3]
  1770   │    │         │    └── [/5 - /5]
  1771   │    │         ├── key: (1)
  1772   │    │         └── fd: (1)-->(2)
  1773   │    └── index-join d
  1774   │         ├── columns: k:5!null u:6 v:7!null
  1775   │         ├── key: (5)
  1776   │         ├── fd: (5)-->(6,7)
  1777   │         └── scan d@v
  1778   │              ├── columns: k:5!null v:7!null
  1779   │              ├── constraint: /7/5
  1780   │              │    ├── [/2 - /2]
  1781   │              │    ├── [/4 - /4]
  1782   │              │    └── [/6 - /6]
  1783   │              ├── key: (5)
  1784   │              └── fd: (5)-->(7)
  1785   └── aggregations
  1786        ├── const-agg [as=u:2, outer=(2)]
  1787        │    └── u:2
  1788        └── const-agg [as=v:3, outer=(3)]
  1789             └── v:3
  1790  
  1791  # Find the first disjunction in the filters that have different column sets on
  1792  # the left and right.
  1793  opt expect=SplitDisjunction
  1794  SELECT k FROM d WHERE (w = 1 OR w = 2) AND (u = 3 OR v = 4)
  1795  ----
  1796  project
  1797   ├── columns: k:1!null
  1798   ├── key: (1)
  1799   └── distinct-on
  1800        ├── columns: k:1!null u:2 v:3 w:4!null
  1801        ├── grouping columns: k:1!null
  1802        ├── key: (1)
  1803        ├── fd: (1)-->(2-4)
  1804        ├── union-all
  1805        │    ├── columns: k:1!null u:2 v:3 w:4!null
  1806        │    ├── left columns: k:1!null u:2 v:3 w:4!null
  1807        │    ├── right columns: k:5 u:6 v:7 w:8
  1808        │    ├── select
  1809        │    │    ├── columns: k:1!null u:2!null v:3 w:4!null
  1810        │    │    ├── key: (1)
  1811        │    │    ├── fd: ()-->(2), (1)-->(3,4)
  1812        │    │    ├── index-join d
  1813        │    │    │    ├── columns: k:1!null u:2 v:3 w:4
  1814        │    │    │    ├── key: (1)
  1815        │    │    │    ├── fd: ()-->(2), (1)-->(3,4)
  1816        │    │    │    └── scan d@u
  1817        │    │    │         ├── columns: k:1!null u:2!null
  1818        │    │    │         ├── constraint: /2/1: [/3 - /3]
  1819        │    │    │         ├── key: (1)
  1820        │    │    │         └── fd: ()-->(2)
  1821        │    │    └── filters
  1822        │    │         └── (w:4 = 1) OR (w:4 = 2) [outer=(4), constraints=(/4: [/1 - /1] [/2 - /2]; tight)]
  1823        │    └── select
  1824        │         ├── columns: k:5!null u:6 v:7!null w:8!null
  1825        │         ├── key: (5)
  1826        │         ├── fd: ()-->(7), (5)-->(6,8)
  1827        │         ├── index-join d
  1828        │         │    ├── columns: k:5!null u:6 v:7 w:8
  1829        │         │    ├── key: (5)
  1830        │         │    ├── fd: ()-->(7), (5)-->(6,8)
  1831        │         │    └── scan d@v
  1832        │         │         ├── columns: k:5!null v:7!null
  1833        │         │         ├── constraint: /7/5: [/4 - /4]
  1834        │         │         ├── key: (5)
  1835        │         │         └── fd: ()-->(7)
  1836        │         └── filters
  1837        │              └── (w:8 = 1) OR (w:8 = 2) [outer=(8), constraints=(/8: [/1 - /1] [/2 - /2]; tight)]
  1838        └── aggregations
  1839             ├── const-agg [as=u:2, outer=(2)]
  1840             │    └── u:2
  1841             ├── const-agg [as=v:3, outer=(3)]
  1842             │    └── v:3
  1843             └── const-agg [as=w:4, outer=(4)]
  1844                  └── w:4
  1845  
  1846  # Find the first disjunction in the filters that have columns on the left and
  1847  # right that constrain a scan.
  1848  opt expect=SplitDisjunction
  1849  SELECT k FROM d WHERE (u = 1 OR w = 2) AND (u = 3 OR v = 4)
  1850  ----
  1851  project
  1852   ├── columns: k:1!null
  1853   ├── key: (1)
  1854   └── distinct-on
  1855        ├── columns: k:1!null u:2 v:3 w:4
  1856        ├── grouping columns: k:1!null
  1857        ├── key: (1)
  1858        ├── fd: (1)-->(2-4)
  1859        ├── union-all
  1860        │    ├── columns: k:1!null u:2 v:3 w:4
  1861        │    ├── left columns: k:1!null u:2 v:3 w:4
  1862        │    ├── right columns: k:5 u:6 v:7 w:8
  1863        │    ├── select
  1864        │    │    ├── columns: k:1!null u:2!null v:3 w:4!null
  1865        │    │    ├── key: (1)
  1866        │    │    ├── fd: ()-->(2,4), (1)-->(3)
  1867        │    │    ├── index-join d
  1868        │    │    │    ├── columns: k:1!null u:2 v:3 w:4
  1869        │    │    │    ├── key: (1)
  1870        │    │    │    ├── fd: ()-->(2), (1)-->(3,4)
  1871        │    │    │    └── scan d@u
  1872        │    │    │         ├── columns: k:1!null u:2!null
  1873        │    │    │         ├── constraint: /2/1: [/3 - /3]
  1874        │    │    │         ├── key: (1)
  1875        │    │    │         └── fd: ()-->(2)
  1876        │    │    └── filters
  1877        │    │         └── w:4 = 2 [outer=(4), constraints=(/4: [/2 - /2]; tight), fd=()-->(4)]
  1878        │    └── select
  1879        │         ├── columns: k:5!null u:6 v:7!null w:8
  1880        │         ├── key: (5)
  1881        │         ├── fd: ()-->(7), (5)-->(6,8)
  1882        │         ├── index-join d
  1883        │         │    ├── columns: k:5!null u:6 v:7 w:8
  1884        │         │    ├── key: (5)
  1885        │         │    ├── fd: ()-->(7), (5)-->(6,8)
  1886        │         │    └── scan d@v
  1887        │         │         ├── columns: k:5!null v:7!null
  1888        │         │         ├── constraint: /7/5: [/4 - /4]
  1889        │         │         ├── key: (5)
  1890        │         │         └── fd: ()-->(7)
  1891        │         └── filters
  1892        │              └── (u:6 = 1) OR (w:8 = 2) [outer=(6,8)]
  1893        └── aggregations
  1894             ├── const-agg [as=u:2, outer=(2)]
  1895             │    └── u:2
  1896             ├── const-agg [as=v:3, outer=(3)]
  1897             │    └── v:3
  1898             └── const-agg [as=w:4, outer=(4)]
  1899                  └── w:4
  1900  
  1901  # Don't apply when outer columns of both sides of OR do not intersect with index columns.
  1902  opt expect-not=SplitDisjunction
  1903  SELECT k, u, w FROM d WHERE u = 1 OR w = 1
  1904  ----
  1905  select
  1906   ├── columns: k:1!null u:2 w:4
  1907   ├── key: (1)
  1908   ├── fd: (1)-->(2,4)
  1909   ├── scan d
  1910   │    ├── columns: k:1!null u:2 w:4
  1911   │    ├── key: (1)
  1912   │    └── fd: (1)-->(2,4)
  1913   └── filters
  1914        └── (u:2 = 1) OR (w:4 = 1) [outer=(2,4)]
  1915  
  1916  # Don't apply to queries without strict keys.
  1917  opt expect-not=SplitDisjunction
  1918  SELECT u, v FROM d WHERE u = 1 OR v = 1
  1919  ----
  1920  project
  1921   ├── columns: u:2 v:3
  1922   └── distinct-on
  1923        ├── columns: k:1!null u:2 v:3
  1924        ├── grouping columns: k:1!null
  1925        ├── key: (1)
  1926        ├── fd: (1)-->(2,3)
  1927        ├── union-all
  1928        │    ├── columns: k:1!null u:2 v:3
  1929        │    ├── left columns: k:1!null u:2 v:3
  1930        │    ├── right columns: k:5 u:6 v:7
  1931        │    ├── index-join d
  1932        │    │    ├── columns: k:1!null u:2!null v:3
  1933        │    │    ├── key: (1)
  1934        │    │    ├── fd: ()-->(2), (1)-->(3)
  1935        │    │    └── scan d@u
  1936        │    │         ├── columns: k:1!null u:2!null
  1937        │    │         ├── constraint: /2/1: [/1 - /1]
  1938        │    │         ├── key: (1)
  1939        │    │         └── fd: ()-->(2)
  1940        │    └── index-join d
  1941        │         ├── columns: k:5!null u:6 v:7!null
  1942        │         ├── key: (5)
  1943        │         ├── fd: ()-->(7), (5)-->(6)
  1944        │         └── scan d@v
  1945        │              ├── columns: k:5!null v:7!null
  1946        │              ├── constraint: /7/5: [/1 - /1]
  1947        │              ├── key: (5)
  1948        │              └── fd: ()-->(7)
  1949        └── aggregations
  1950             ├── const-agg [as=u:2, outer=(2)]
  1951             │    └── u:2
  1952             └── const-agg [as=v:3, outer=(3)]
  1953                  └── v:3
  1954  
  1955  # Don't apply to disjunctions with identical colsets on the left and right.
  1956  opt expect-not=SplitDisjunction
  1957  SELECT k FROM d WHERE u = 1 OR u = 5
  1958  ----
  1959  project
  1960   ├── columns: k:1!null
  1961   ├── key: (1)
  1962   └── scan d@u
  1963        ├── columns: k:1!null u:2!null
  1964        ├── constraint: /2/1
  1965        │    ├── [/1 - /1]
  1966        │    └── [/5 - /5]
  1967        ├── key: (1)
  1968        └── fd: (1)-->(2)
  1969  
  1970  # Verifies that flags are copied to the duplicated scan.
  1971  opt expect=SplitDisjunction
  1972  SELECT k FROM a@{NO_INDEX_JOIN} WHERE u = 1 OR v = 1
  1973  ----
  1974  project
  1975   ├── columns: k:1!null
  1976   ├── key: (1)
  1977   └── distinct-on
  1978        ├── columns: k:1!null u:2 v:3
  1979        ├── grouping columns: k:1!null
  1980        ├── key: (1)
  1981        ├── fd: (1)-->(2,3), (3)~~>(1,2)
  1982        ├── union-all
  1983        │    ├── columns: k:1!null u:2 v:3
  1984        │    ├── left columns: k:1!null u:2 v:3
  1985        │    ├── right columns: k:4 u:5 v:6
  1986        │    ├── scan a@u
  1987        │    │    ├── columns: k:1!null u:2!null v:3
  1988        │    │    ├── constraint: /2/1: [/1 - /1]
  1989        │    │    ├── flags: no-index-join
  1990        │    │    ├── key: (1)
  1991        │    │    └── fd: ()-->(2), (1)-->(3), (3)~~>(1)
  1992        │    └── scan a@v
  1993        │         ├── columns: k:4!null u:5 v:6!null
  1994        │         ├── constraint: /6: [/1 - /1]
  1995        │         ├── flags: no-index-join
  1996        │         ├── cardinality: [0 - 1]
  1997        │         ├── key: ()
  1998        │         └── fd: ()-->(4-6)
  1999        └── aggregations
  2000             ├── const-agg [as=u:2, outer=(2)]
  2001             │    └── u:2
  2002             └── const-agg [as=v:3, outer=(3)]
  2003                  └── v:3
  2004  
  2005  # Columns are passed-through correctly when EliminateUnionAllLeft is applied.
  2006  opt expect=SplitDisjunction
  2007  SELECT k FROM d WHERE u = 2 OR (v = 1 AND v = 3)
  2008  ----
  2009  project
  2010   ├── columns: k:1!null
  2011   ├── key: (1)
  2012   └── distinct-on
  2013        ├── columns: k:1!null u:2!null v:3
  2014        ├── grouping columns: k:1!null
  2015        ├── internal-ordering: +1 opt(2)
  2016        ├── key: (1)
  2017        ├── fd: ()-->(2), (1)-->(3)
  2018        ├── index-join d
  2019        │    ├── columns: k:1!null u:2!null v:3
  2020        │    ├── key: (1)
  2021        │    ├── fd: ()-->(2), (1)-->(3)
  2022        │    ├── ordering: +1 opt(2) [actual: +1]
  2023        │    └── scan d@u
  2024        │         ├── columns: k:1!null u:2!null
  2025        │         ├── constraint: /2/1: [/2 - /2]
  2026        │         ├── key: (1)
  2027        │         ├── fd: ()-->(2)
  2028        │         └── ordering: +1 opt(2) [actual: +1]
  2029        └── aggregations
  2030             ├── const-agg [as=u:2, outer=(2)]
  2031             │    └── u:2
  2032             └── const-agg [as=v:3, outer=(3)]
  2033                  └── v:3
  2034  
  2035  # --------------------------------------------------
  2036  # SplitDisjunctionAddKey
  2037  # --------------------------------------------------
  2038  
  2039  opt expect=SplitDisjunctionAddKey
  2040  SELECT u, v FROM d WHERE u = 1 OR v = 1
  2041  ----
  2042  project
  2043   ├── columns: u:2 v:3
  2044   └── distinct-on
  2045        ├── columns: k:1!null u:2 v:3
  2046        ├── grouping columns: k:1!null
  2047        ├── key: (1)
  2048        ├── fd: (1)-->(2,3)
  2049        ├── union-all
  2050        │    ├── columns: k:1!null u:2 v:3
  2051        │    ├── left columns: k:1!null u:2 v:3
  2052        │    ├── right columns: k:5 u:6 v:7
  2053        │    ├── index-join d
  2054        │    │    ├── columns: k:1!null u:2!null v:3
  2055        │    │    ├── key: (1)
  2056        │    │    ├── fd: ()-->(2), (1)-->(3)
  2057        │    │    └── scan d@u
  2058        │    │         ├── columns: k:1!null u:2!null
  2059        │    │         ├── constraint: /2/1: [/1 - /1]
  2060        │    │         ├── key: (1)
  2061        │    │         └── fd: ()-->(2)
  2062        │    └── index-join d
  2063        │         ├── columns: k:5!null u:6 v:7!null
  2064        │         ├── key: (5)
  2065        │         ├── fd: ()-->(7), (5)-->(6)
  2066        │         └── scan d@v
  2067        │              ├── columns: k:5!null v:7!null
  2068        │              ├── constraint: /7/5: [/1 - /1]
  2069        │              ├── key: (5)
  2070        │              └── fd: ()-->(7)
  2071        └── aggregations
  2072             ├── const-agg [as=u:2, outer=(2)]
  2073             │    └── u:2
  2074             └── const-agg [as=v:3, outer=(3)]
  2075                  └── v:3
  2076  
  2077  opt expect=SplitDisjunctionAddKey
  2078  SELECT u, v, w FROM d WHERE w = 1 AND (u = 1 OR v = 1)
  2079  ----
  2080  project
  2081   ├── columns: u:2 v:3 w:4!null
  2082   ├── fd: ()-->(4)
  2083   └── distinct-on
  2084        ├── columns: k:1!null u:2 v:3 w:4!null
  2085        ├── grouping columns: k:1!null
  2086        ├── key: (1)
  2087        ├── fd: (1)-->(2-4)
  2088        ├── union-all
  2089        │    ├── columns: k:1!null u:2 v:3 w:4!null
  2090        │    ├── left columns: k:1!null u:2 v:3 w:4!null
  2091        │    ├── right columns: k:5 u:6 v:7 w:8
  2092        │    ├── select
  2093        │    │    ├── columns: k:1!null u:2!null v:3 w:4!null
  2094        │    │    ├── key: (1)
  2095        │    │    ├── fd: ()-->(2,4), (1)-->(3)
  2096        │    │    ├── index-join d
  2097        │    │    │    ├── columns: k:1!null u:2 v:3 w:4
  2098        │    │    │    ├── key: (1)
  2099        │    │    │    ├── fd: ()-->(2), (1)-->(3,4)
  2100        │    │    │    └── scan d@u
  2101        │    │    │         ├── columns: k:1!null u:2!null
  2102        │    │    │         ├── constraint: /2/1: [/1 - /1]
  2103        │    │    │         ├── key: (1)
  2104        │    │    │         └── fd: ()-->(2)
  2105        │    │    └── filters
  2106        │    │         └── w:4 = 1 [outer=(4), constraints=(/4: [/1 - /1]; tight), fd=()-->(4)]
  2107        │    └── select
  2108        │         ├── columns: k:5!null u:6 v:7!null w:8!null
  2109        │         ├── key: (5)
  2110        │         ├── fd: ()-->(7,8), (5)-->(6)
  2111        │         ├── index-join d
  2112        │         │    ├── columns: k:5!null u:6 v:7 w:8
  2113        │         │    ├── key: (5)
  2114        │         │    ├── fd: ()-->(7), (5)-->(6,8)
  2115        │         │    └── scan d@v
  2116        │         │         ├── columns: k:5!null v:7!null
  2117        │         │         ├── constraint: /7/5: [/1 - /1]
  2118        │         │         ├── key: (5)
  2119        │         │         └── fd: ()-->(7)
  2120        │         └── filters
  2121        │              └── w:8 = 1 [outer=(8), constraints=(/8: [/1 - /1]; tight), fd=()-->(8)]
  2122        └── aggregations
  2123             ├── const-agg [as=u:2, outer=(2)]
  2124             │    └── u:2
  2125             ├── const-agg [as=v:3, outer=(3)]
  2126             │    └── v:3
  2127             └── const-agg [as=w:4, outer=(4)]
  2128                  └── w:4
  2129  
  2130  opt expect=SplitDisjunctionAddKey
  2131  SELECT u, v FROM d WHERE (u = 1 OR v = 2) AND (u = 10 OR v = 20)
  2132  ----
  2133  project
  2134   ├── columns: u:2 v:3
  2135   └── distinct-on
  2136        ├── columns: k:1!null u:2!null v:3!null
  2137        ├── grouping columns: k:1!null
  2138        ├── key: (1)
  2139        ├── fd: (1)-->(2,3)
  2140        ├── union-all
  2141        │    ├── columns: k:1!null u:2!null v:3!null
  2142        │    ├── left columns: k:1!null u:2!null v:3!null
  2143        │    ├── right columns: k:5 u:6 v:7
  2144        │    ├── inner-join (zigzag d@u d@v)
  2145        │    │    ├── columns: k:1!null u:2!null v:3!null
  2146        │    │    ├── eq columns: [1] = [1]
  2147        │    │    ├── left fixed columns: [2] = [1]
  2148        │    │    ├── right fixed columns: [3] = [20]
  2149        │    │    ├── key: (1)
  2150        │    │    ├── fd: ()-->(2,3)
  2151        │    │    └── filters
  2152        │    │         ├── u:2 = 1 [outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)]
  2153        │    │         └── v:3 = 20 [outer=(3), constraints=(/3: [/20 - /20]; tight), fd=()-->(3)]
  2154        │    └── inner-join (zigzag d@u d@v)
  2155        │         ├── columns: k:5!null u:6!null v:7!null
  2156        │         ├── eq columns: [5] = [5]
  2157        │         ├── left fixed columns: [6] = [10]
  2158        │         ├── right fixed columns: [7] = [2]
  2159        │         ├── key: (5)
  2160        │         ├── fd: ()-->(6,7)
  2161        │         └── filters
  2162        │              ├── v:7 = 2 [outer=(7), constraints=(/7: [/2 - /2]; tight), fd=()-->(7)]
  2163        │              └── u:6 = 10 [outer=(6), constraints=(/6: [/10 - /10]; tight), fd=()-->(6)]
  2164        └── aggregations
  2165             ├── const-agg [as=u:2, outer=(2)]
  2166             │    └── u:2
  2167             └── const-agg [as=v:3, outer=(3)]
  2168                  └── v:3
  2169  
  2170  opt expect=SplitDisjunctionAddKey
  2171  SELECT count(*) FROM d WHERE u = 1 OR v = 1
  2172  ----
  2173  scalar-group-by
  2174   ├── columns: count:5!null
  2175   ├── cardinality: [1 - 1]
  2176   ├── key: ()
  2177   ├── fd: ()-->(5)
  2178   ├── project
  2179   │    ├── columns: u:2 v:3
  2180   │    └── distinct-on
  2181   │         ├── columns: k:1!null u:2 v:3
  2182   │         ├── grouping columns: k:1!null
  2183   │         ├── key: (1)
  2184   │         ├── fd: (1)-->(2,3)
  2185   │         ├── union-all
  2186   │         │    ├── columns: k:1!null u:2 v:3
  2187   │         │    ├── left columns: k:1!null u:2 v:3
  2188   │         │    ├── right columns: k:6 u:7 v:8
  2189   │         │    ├── index-join d
  2190   │         │    │    ├── columns: k:1!null u:2!null v:3
  2191   │         │    │    ├── key: (1)
  2192   │         │    │    ├── fd: ()-->(2), (1)-->(3)
  2193   │         │    │    └── scan d@u
  2194   │         │    │         ├── columns: k:1!null u:2!null
  2195   │         │    │         ├── constraint: /2/1: [/1 - /1]
  2196   │         │    │         ├── key: (1)
  2197   │         │    │         └── fd: ()-->(2)
  2198   │         │    └── index-join d
  2199   │         │         ├── columns: k:6!null u:7 v:8!null
  2200   │         │         ├── key: (6)
  2201   │         │         ├── fd: ()-->(8), (6)-->(7)
  2202   │         │         └── scan d@v
  2203   │         │              ├── columns: k:6!null v:8!null
  2204   │         │              ├── constraint: /8/6: [/1 - /1]
  2205   │         │              ├── key: (6)
  2206   │         │              └── fd: ()-->(8)
  2207   │         └── aggregations
  2208   │              ├── const-agg [as=u:2, outer=(2)]
  2209   │              │    └── u:2
  2210   │              └── const-agg [as=v:3, outer=(3)]
  2211   │                   └── v:3
  2212   └── aggregations
  2213        └── count-rows [as=count_rows:5]
  2214  
  2215  
  2216  # Multi-column primary key.
  2217  opt expect=SplitDisjunctionAddKey
  2218  SELECT u, v FROM f WHERE u = 1 OR v = 2
  2219  ----
  2220  project
  2221   ├── columns: u:3 v:4
  2222   └── distinct-on
  2223        ├── columns: k:1!null j:2!null u:3 v:4
  2224        ├── grouping columns: k:1!null j:2!null
  2225        ├── key: (1,2)
  2226        ├── fd: (1,2)-->(3,4)
  2227        ├── union-all
  2228        │    ├── columns: k:1!null j:2!null u:3 v:4
  2229        │    ├── left columns: k:1!null j:2!null u:3 v:4
  2230        │    ├── right columns: k:5 j:6 u:7 v:8
  2231        │    ├── index-join f
  2232        │    │    ├── columns: k:1!null j:2!null u:3!null v:4
  2233        │    │    ├── key: (1,2)
  2234        │    │    ├── fd: ()-->(3), (1,2)-->(4)
  2235        │    │    └── scan f@u
  2236        │    │         ├── columns: k:1!null j:2!null u:3!null
  2237        │    │         ├── constraint: /3/1/2: [/1 - /1]
  2238        │    │         ├── key: (1,2)
  2239        │    │         └── fd: ()-->(3)
  2240        │    └── index-join f
  2241        │         ├── columns: k:5!null j:6!null u:7 v:8!null
  2242        │         ├── key: (5,6)
  2243        │         ├── fd: ()-->(8), (5,6)-->(7)
  2244        │         └── scan f@v
  2245        │              ├── columns: k:5!null j:6!null v:8!null
  2246        │              ├── constraint: /8/5/6: [/2 - /2]
  2247        │              ├── key: (5,6)
  2248        │              └── fd: ()-->(8)
  2249        └── aggregations
  2250             ├── const-agg [as=u:3, outer=(3)]
  2251             │    └── u:3
  2252             └── const-agg [as=v:4, outer=(4)]
  2253                  └── v:4
  2254  
  2255  # Don't expand INs to many ORs.
  2256  opt expect=SplitDisjunctionAddKey
  2257  SELECT u, v FROM d WHERE u IN (1, 2, 3, 4) OR v IN (5, 6, 7, 8)
  2258  ----
  2259  project
  2260   ├── columns: u:2 v:3
  2261   └── distinct-on
  2262        ├── columns: k:1!null u:2 v:3
  2263        ├── grouping columns: k:1!null
  2264        ├── key: (1)
  2265        ├── fd: (1)-->(2,3)
  2266        ├── union-all
  2267        │    ├── columns: k:1!null u:2 v:3
  2268        │    ├── left columns: k:1!null u:2 v:3
  2269        │    ├── right columns: k:5 u:6 v:7
  2270        │    ├── index-join d
  2271        │    │    ├── columns: k:1!null u:2!null v:3
  2272        │    │    ├── key: (1)
  2273        │    │    ├── fd: (1)-->(2,3)
  2274        │    │    └── scan d@u
  2275        │    │         ├── columns: k:1!null u:2!null
  2276        │    │         ├── constraint: /2/1: [/1 - /4]
  2277        │    │         ├── key: (1)
  2278        │    │         └── fd: (1)-->(2)
  2279        │    └── index-join d
  2280        │         ├── columns: k:5!null u:6 v:7!null
  2281        │         ├── key: (5)
  2282        │         ├── fd: (5)-->(6,7)
  2283        │         └── scan d@v
  2284        │              ├── columns: k:5!null v:7!null
  2285        │              ├── constraint: /7/5: [/5 - /8]
  2286        │              ├── key: (5)
  2287        │              └── fd: (5)-->(7)
  2288        └── aggregations
  2289             ├── const-agg [as=u:2, outer=(2)]
  2290             │    └── u:2
  2291             └── const-agg [as=v:3, outer=(3)]
  2292                  └── v:3
  2293  
  2294  # Split and constrain with an inverted index on JSONB on one side.
  2295  opt expect=SplitDisjunctionAddKey
  2296  SELECT u, j FROM b WHERE u = 1 OR j @> '{"foo": "bar"}'
  2297  ----
  2298  project
  2299   ├── columns: u:2 j:4
  2300   └── distinct-on
  2301        ├── columns: k:1!null u:2 j:4
  2302        ├── grouping columns: k:1!null
  2303        ├── key: (1)
  2304        ├── fd: (1)-->(2,4)
  2305        ├── union-all
  2306        │    ├── columns: k:1!null u:2 j:4
  2307        │    ├── left columns: k:1!null u:2 j:4
  2308        │    ├── right columns: k:5 u:6 j:8
  2309        │    ├── index-join b
  2310        │    │    ├── columns: k:1!null u:2!null j:4
  2311        │    │    ├── key: (1)
  2312        │    │    ├── fd: ()-->(2), (1)-->(4)
  2313        │    │    └── scan b@u
  2314        │    │         ├── columns: k:1!null u:2!null
  2315        │    │         ├── constraint: /2/1: [/1 - /1]
  2316        │    │         ├── key: (1)
  2317        │    │         └── fd: ()-->(2)
  2318        │    └── index-join b
  2319        │         ├── columns: k:5!null u:6 j:8
  2320        │         ├── key: (5)
  2321        │         ├── fd: (5)-->(6,8)
  2322        │         └── scan b@inv_idx
  2323        │              ├── columns: k:5!null
  2324        │              ├── constraint: /8/5: [/'{"foo": "bar"}' - /'{"foo": "bar"}']
  2325        │              └── key: (5)
  2326        └── aggregations
  2327             ├── const-agg [as=u:2, outer=(2)]
  2328             │    └── u:2
  2329             └── const-agg [as=j:4, outer=(4)]
  2330                  └── j:4
  2331  
  2332  # Split and constrain with an inverted index on an array on one side.
  2333  opt expect=SplitDisjunctionAddKey
  2334  SELECT u, a FROM c WHERE u = 1 OR a @> ARRAY[2]
  2335  ----
  2336  project
  2337   ├── columns: u:3 a:2
  2338   └── distinct-on
  2339        ├── columns: k:1!null a:2 u:3
  2340        ├── grouping columns: k:1!null
  2341        ├── key: (1)
  2342        ├── fd: (1)-->(2,3)
  2343        ├── union-all
  2344        │    ├── columns: k:1!null a:2 u:3
  2345        │    ├── left columns: k:1!null a:2 u:3
  2346        │    ├── right columns: k:4 a:5 u:6
  2347        │    ├── index-join c
  2348        │    │    ├── columns: k:1!null a:2 u:3!null
  2349        │    │    ├── key: (1)
  2350        │    │    ├── fd: ()-->(3), (1)-->(2)
  2351        │    │    └── scan c@u
  2352        │    │         ├── columns: k:1!null u:3!null
  2353        │    │         ├── constraint: /3/1: [/1 - /1]
  2354        │    │         ├── key: (1)
  2355        │    │         └── fd: ()-->(3)
  2356        │    └── index-join c
  2357        │         ├── columns: k:4!null a:5 u:6
  2358        │         ├── key: (4)
  2359        │         ├── fd: (4)-->(5,6)
  2360        │         └── scan c@inv_idx
  2361        │              ├── columns: k:4!null
  2362        │              ├── constraint: /5/4: [/ARRAY[2] - /ARRAY[2]]
  2363        │              └── key: (4)
  2364        └── aggregations
  2365             ├── const-agg [as=a:2, outer=(2)]
  2366             │    └── a:2
  2367             └── const-agg [as=u:3, outer=(3)]
  2368                  └── u:3
  2369  
  2370  # Uncorrelated subquery.
  2371  opt expect=SplitDisjunctionAddKey
  2372  SELECT u, v FROM d WHERE (u = 1 OR v = 1) AND EXISTS (SELECT u, v FROM a)
  2373  ----
  2374  project
  2375   ├── columns: u:2 v:3
  2376   └── distinct-on
  2377        ├── columns: d.k:1!null d.u:2 d.v:3
  2378        ├── grouping columns: d.k:1!null
  2379        ├── key: (1)
  2380        ├── fd: (1)-->(2,3)
  2381        ├── union-all
  2382        │    ├── columns: d.k:1!null d.u:2 d.v:3
  2383        │    ├── left columns: d.k:1!null d.u:2 d.v:3
  2384        │    ├── right columns: d.k:8 d.u:9 d.v:10
  2385        │    ├── index-join d
  2386        │    │    ├── columns: d.k:1!null d.u:2!null d.v:3
  2387        │    │    ├── key: (1)
  2388        │    │    ├── fd: ()-->(2), (1)-->(3)
  2389        │    │    └── select
  2390        │    │         ├── columns: d.k:1!null d.u:2!null
  2391        │    │         ├── key: (1)
  2392        │    │         ├── fd: ()-->(2)
  2393        │    │         ├── scan d@u
  2394        │    │         │    ├── columns: d.k:1!null d.u:2!null
  2395        │    │         │    ├── constraint: /2/1: [/1 - /1]
  2396        │    │         │    ├── key: (1)
  2397        │    │         │    └── fd: ()-->(2)
  2398        │    │         └── filters
  2399        │    │              └── exists [subquery]
  2400        │    │                   └── scan a
  2401        │    │                        ├── columns: a.u:6 a.v:7
  2402        │    │                        ├── limit: 1
  2403        │    │                        ├── key: ()
  2404        │    │                        └── fd: ()-->(6,7)
  2405        │    └── index-join d
  2406        │         ├── columns: d.k:8!null d.u:9 d.v:10!null
  2407        │         ├── key: (8)
  2408        │         ├── fd: ()-->(10), (8)-->(9)
  2409        │         └── select
  2410        │              ├── columns: d.k:8!null d.v:10!null
  2411        │              ├── key: (8)
  2412        │              ├── fd: ()-->(10)
  2413        │              ├── scan d@v
  2414        │              │    ├── columns: d.k:8!null d.v:10!null
  2415        │              │    ├── constraint: /10/8: [/1 - /1]
  2416        │              │    ├── key: (8)
  2417        │              │    └── fd: ()-->(10)
  2418        │              └── filters
  2419        │                   └── exists [subquery]
  2420        │                        └── scan a
  2421        │                             ├── columns: a.u:6 a.v:7
  2422        │                             ├── limit: 1
  2423        │                             ├── key: ()
  2424        │                             └── fd: ()-->(6,7)
  2425        └── aggregations
  2426             ├── const-agg [as=d.u:2, outer=(2)]
  2427             │    └── d.u:2
  2428             └── const-agg [as=d.v:3, outer=(3)]
  2429                  └── d.v:3
  2430  
  2431  # Correlated subquery.
  2432  opt expect=SplitDisjunctionAddKey
  2433  SELECT u, v FROM d WHERE (u = 1 OR v = 1) AND EXISTS (SELECT * FROM a WHERE a.u = d.u)
  2434  ----
  2435  project
  2436   ├── columns: u:2 v:3
  2437   └── inner-join (hash)
  2438        ├── columns: d.u:2!null d.v:3 a.u:6!null
  2439        ├── fd: (2)==(6), (6)==(2)
  2440        ├── project
  2441        │    ├── columns: d.u:2 d.v:3
  2442        │    └── distinct-on
  2443        │         ├── columns: d.k:1!null d.u:2 d.v:3
  2444        │         ├── grouping columns: d.k:1!null
  2445        │         ├── key: (1)
  2446        │         ├── fd: (1)-->(2,3)
  2447        │         ├── union-all
  2448        │         │    ├── columns: d.k:1!null d.u:2 d.v:3
  2449        │         │    ├── left columns: d.k:1!null d.u:2 d.v:3
  2450        │         │    ├── right columns: d.k:8 d.u:9 d.v:10
  2451        │         │    ├── index-join d
  2452        │         │    │    ├── columns: d.k:1!null d.u:2!null d.v:3
  2453        │         │    │    ├── key: (1)
  2454        │         │    │    ├── fd: ()-->(2), (1)-->(3)
  2455        │         │    │    └── scan d@u
  2456        │         │    │         ├── columns: d.k:1!null d.u:2!null
  2457        │         │    │         ├── constraint: /2/1: [/1 - /1]
  2458        │         │    │         ├── key: (1)
  2459        │         │    │         └── fd: ()-->(2)
  2460        │         │    └── index-join d
  2461        │         │         ├── columns: d.k:8!null d.u:9 d.v:10!null
  2462        │         │         ├── key: (8)
  2463        │         │         ├── fd: ()-->(10), (8)-->(9)
  2464        │         │         └── scan d@v
  2465        │         │              ├── columns: d.k:8!null d.v:10!null
  2466        │         │              ├── constraint: /10/8: [/1 - /1]
  2467        │         │              ├── key: (8)
  2468        │         │              └── fd: ()-->(10)
  2469        │         └── aggregations
  2470        │              ├── const-agg [as=d.u:2, outer=(2)]
  2471        │              │    └── d.u:2
  2472        │              └── const-agg [as=d.v:3, outer=(3)]
  2473        │                   └── d.v:3
  2474        ├── distinct-on
  2475        │    ├── columns: a.u:6
  2476        │    ├── grouping columns: a.u:6
  2477        │    ├── internal-ordering: +6
  2478        │    ├── key: (6)
  2479        │    └── scan a@u
  2480        │         ├── columns: a.u:6
  2481        │         └── ordering: +6
  2482        └── filters
  2483             └── a.u:6 = d.u:2 [outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
  2484  
  2485  # Correlated subquery with references to outer columns not in the scan columns.
  2486  opt expect=SplitDisjunctionAddKey
  2487  SELECT u, v FROM d WHERE (u = 1 OR v = 1) AND EXISTS (SELECT * FROM a WHERE a.u = d.w)
  2488  ----
  2489  project
  2490   ├── columns: u:2 v:3
  2491   └── project
  2492        ├── columns: d.u:2 d.v:3 w:4
  2493        └── inner-join (hash)
  2494             ├── columns: d.u:2 d.v:3 w:4!null a.u:6!null
  2495             ├── fd: (4)==(6), (6)==(4)
  2496             ├── project
  2497             │    ├── columns: d.u:2 d.v:3 w:4
  2498             │    └── distinct-on
  2499             │         ├── columns: d.k:1!null d.u:2 d.v:3 w:4
  2500             │         ├── grouping columns: d.k:1!null
  2501             │         ├── key: (1)
  2502             │         ├── fd: (1)-->(2-4)
  2503             │         ├── union-all
  2504             │         │    ├── columns: d.k:1!null d.u:2 d.v:3 w:4
  2505             │         │    ├── left columns: d.k:1!null d.u:2 d.v:3 w:4
  2506             │         │    ├── right columns: d.k:8 d.u:9 d.v:10 w:11
  2507             │         │    ├── index-join d
  2508             │         │    │    ├── columns: d.k:1!null d.u:2!null d.v:3 w:4
  2509             │         │    │    ├── key: (1)
  2510             │         │    │    ├── fd: ()-->(2), (1)-->(3,4)
  2511             │         │    │    └── scan d@u
  2512             │         │    │         ├── columns: d.k:1!null d.u:2!null
  2513             │         │    │         ├── constraint: /2/1: [/1 - /1]
  2514             │         │    │         ├── key: (1)
  2515             │         │    │         └── fd: ()-->(2)
  2516             │         │    └── index-join d
  2517             │         │         ├── columns: d.k:8!null d.u:9 d.v:10!null w:11
  2518             │         │         ├── key: (8)
  2519             │         │         ├── fd: ()-->(10), (8)-->(9,11)
  2520             │         │         └── scan d@v
  2521             │         │              ├── columns: d.k:8!null d.v:10!null
  2522             │         │              ├── constraint: /10/8: [/1 - /1]
  2523             │         │              ├── key: (8)
  2524             │         │              └── fd: ()-->(10)
  2525             │         └── aggregations
  2526             │              ├── const-agg [as=d.u:2, outer=(2)]
  2527             │              │    └── d.u:2
  2528             │              ├── const-agg [as=d.v:3, outer=(3)]
  2529             │              │    └── d.v:3
  2530             │              └── const-agg [as=w:4, outer=(4)]
  2531             │                   └── w:4
  2532             ├── distinct-on
  2533             │    ├── columns: a.u:6
  2534             │    ├── grouping columns: a.u:6
  2535             │    ├── internal-ordering: +6
  2536             │    ├── key: (6)
  2537             │    └── scan a@u
  2538             │         ├── columns: a.u:6
  2539             │         └── ordering: +6
  2540             └── filters
  2541                  └── a.u:6 = w:4 [outer=(4,6), constraints=(/4: (/NULL - ]; /6: (/NULL - ]), fd=(4)==(6), (6)==(4)]
  2542  
  2543  # Use rowid when there is no explicit primary key.
  2544  opt expect=SplitDisjunctionAddKey
  2545  SELECT u, v FROM no_explicit_primary_key WHERE u = 1 OR v = 5
  2546  ----
  2547  project
  2548   ├── columns: u:2 v:3
  2549   └── distinct-on
  2550        ├── columns: u:2 v:3 rowid:4!null
  2551        ├── grouping columns: rowid:4!null
  2552        ├── key: (4)
  2553        ├── fd: (4)-->(2,3)
  2554        ├── union-all
  2555        │    ├── columns: u:2 v:3 rowid:4!null
  2556        │    ├── left columns: u:2 v:3 rowid:4!null
  2557        │    ├── right columns: u:6 v:7 rowid:8
  2558        │    ├── index-join no_explicit_primary_key
  2559        │    │    ├── columns: u:2!null v:3 rowid:4!null
  2560        │    │    ├── key: (4)
  2561        │    │    ├── fd: ()-->(2), (4)-->(3)
  2562        │    │    └── scan no_explicit_primary_key@u
  2563        │    │         ├── columns: u:2!null rowid:4!null
  2564        │    │         ├── constraint: /2/4: [/1 - /1]
  2565        │    │         ├── key: (4)
  2566        │    │         └── fd: ()-->(2)
  2567        │    └── index-join no_explicit_primary_key
  2568        │         ├── columns: u:6 v:7!null rowid:8!null
  2569        │         ├── key: (8)
  2570        │         ├── fd: ()-->(7), (8)-->(6)
  2571        │         └── scan no_explicit_primary_key@v
  2572        │              ├── columns: v:7!null rowid:8!null
  2573        │              ├── constraint: /7/8: [/5 - /5]
  2574        │              ├── key: (8)
  2575        │              └── fd: ()-->(7)
  2576        └── aggregations
  2577             ├── const-agg [as=u:2, outer=(2)]
  2578             │    └── u:2
  2579             └── const-agg [as=v:3, outer=(3)]
  2580                  └── v:3
  2581  
  2582  # Apply when outer columns of both sides of OR are a subset of index columns.
  2583  opt expect=SplitDisjunctionAddKey
  2584  SELECT u, v FROM e WHERE u = 1 OR v = 1
  2585  ----
  2586  project
  2587   ├── columns: u:2 v:3
  2588   └── distinct-on
  2589        ├── columns: k:1!null u:2 v:3
  2590        ├── grouping columns: k:1!null
  2591        ├── key: (1)
  2592        ├── fd: (1)-->(2,3)
  2593        ├── union-all
  2594        │    ├── columns: k:1!null u:2 v:3
  2595        │    ├── left columns: k:1!null u:2 v:3
  2596        │    ├── right columns: k:5 u:6 v:7
  2597        │    ├── index-join e
  2598        │    │    ├── columns: k:1!null u:2!null v:3
  2599        │    │    ├── key: (1)
  2600        │    │    ├── fd: ()-->(2), (1)-->(3)
  2601        │    │    └── scan e@uw
  2602        │    │         ├── columns: k:1!null u:2!null
  2603        │    │         ├── constraint: /2/4/1: [/1 - /1]
  2604        │    │         ├── key: (1)
  2605        │    │         └── fd: ()-->(2)
  2606        │    └── index-join e
  2607        │         ├── columns: k:5!null u:6 v:7!null
  2608        │         ├── key: (5)
  2609        │         ├── fd: ()-->(7), (5)-->(6)
  2610        │         └── scan e@vw
  2611        │              ├── columns: k:5!null v:7!null
  2612        │              ├── constraint: /7/8/5: [/1 - /1]
  2613        │              ├── key: (5)
  2614        │              └── fd: ()-->(7)
  2615        └── aggregations
  2616             ├── const-agg [as=u:2, outer=(2)]
  2617             │    └── u:2
  2618             └── const-agg [as=v:3, outer=(3)]
  2619                  └── v:3
  2620  
  2621  # Apply when outer columns of both sides of OR are a superset of index columns.
  2622  opt expect=SplitDisjunctionAddKey
  2623  SELECT u, v FROM d WHERE (u = 1 AND w = 2) OR (v = 1 AND w = 3)
  2624  ----
  2625  project
  2626   ├── columns: u:2 v:3
  2627   └── project
  2628        ├── columns: u:2 v:3 w:4!null
  2629        └── distinct-on
  2630             ├── columns: k:1!null u:2 v:3 w:4!null
  2631             ├── grouping columns: k:1!null
  2632             ├── key: (1)
  2633             ├── fd: (1)-->(2-4)
  2634             ├── union-all
  2635             │    ├── columns: k:1!null u:2 v:3 w:4!null
  2636             │    ├── left columns: k:1!null u:2 v:3 w:4!null
  2637             │    ├── right columns: k:5 u:6 v:7 w:8
  2638             │    ├── select
  2639             │    │    ├── columns: k:1!null u:2!null v:3 w:4!null
  2640             │    │    ├── key: (1)
  2641             │    │    ├── fd: ()-->(2,4), (1)-->(3)
  2642             │    │    ├── index-join d
  2643             │    │    │    ├── columns: k:1!null u:2 v:3 w:4
  2644             │    │    │    ├── key: (1)
  2645             │    │    │    ├── fd: ()-->(2), (1)-->(3,4)
  2646             │    │    │    └── scan d@u
  2647             │    │    │         ├── columns: k:1!null u:2!null
  2648             │    │    │         ├── constraint: /2/1: [/1 - /1]
  2649             │    │    │         ├── key: (1)
  2650             │    │    │         └── fd: ()-->(2)
  2651             │    │    └── filters
  2652             │    │         └── w:4 = 2 [outer=(4), constraints=(/4: [/2 - /2]; tight), fd=()-->(4)]
  2653             │    └── select
  2654             │         ├── columns: k:5!null u:6 v:7!null w:8!null
  2655             │         ├── key: (5)
  2656             │         ├── fd: ()-->(7,8), (5)-->(6)
  2657             │         ├── index-join d
  2658             │         │    ├── columns: k:5!null u:6 v:7 w:8
  2659             │         │    ├── key: (5)
  2660             │         │    ├── fd: ()-->(7), (5)-->(6,8)
  2661             │         │    └── scan d@v
  2662             │         │         ├── columns: k:5!null v:7!null
  2663             │         │         ├── constraint: /7/5: [/1 - /1]
  2664             │         │         ├── key: (5)
  2665             │         │         └── fd: ()-->(7)
  2666             │         └── filters
  2667             │              └── w:8 = 3 [outer=(8), constraints=(/8: [/3 - /3]; tight), fd=()-->(8)]
  2668             └── aggregations
  2669                  ├── const-agg [as=u:2, outer=(2)]
  2670                  │    └── u:2
  2671                  ├── const-agg [as=v:3, outer=(3)]
  2672                  │    └── v:3
  2673                  └── const-agg [as=w:4, outer=(4)]
  2674                       └── w:4
  2675  
  2676  # Group sub-expr with the same columns together.
  2677  opt expect=SplitDisjunctionAddKey
  2678  SELECT u, v FROM d WHERE (u = 1 OR v = 2) OR (u = 3 OR v = 4)
  2679  ----
  2680  project
  2681   ├── columns: u:2 v:3
  2682   └── distinct-on
  2683        ├── columns: k:1!null u:2 v:3
  2684        ├── grouping columns: k:1!null
  2685        ├── key: (1)
  2686        ├── fd: (1)-->(2,3)
  2687        ├── union-all
  2688        │    ├── columns: k:1!null u:2 v:3
  2689        │    ├── left columns: k:1!null u:2 v:3
  2690        │    ├── right columns: k:5 u:6 v:7
  2691        │    ├── index-join d
  2692        │    │    ├── columns: k:1!null u:2!null v:3
  2693        │    │    ├── key: (1)
  2694        │    │    ├── fd: (1)-->(2,3)
  2695        │    │    └── scan d@u
  2696        │    │         ├── columns: k:1!null u:2!null
  2697        │    │         ├── constraint: /2/1
  2698        │    │         │    ├── [/1 - /1]
  2699        │    │         │    └── [/3 - /3]
  2700        │    │         ├── key: (1)
  2701        │    │         └── fd: (1)-->(2)
  2702        │    └── index-join d
  2703        │         ├── columns: k:5!null u:6 v:7!null
  2704        │         ├── key: (5)
  2705        │         ├── fd: (5)-->(6,7)
  2706        │         └── scan d@v
  2707        │              ├── columns: k:5!null v:7!null
  2708        │              ├── constraint: /7/5
  2709        │              │    ├── [/2 - /2]
  2710        │              │    └── [/4 - /4]
  2711        │              ├── key: (5)
  2712        │              └── fd: (5)-->(7)
  2713        └── aggregations
  2714             ├── const-agg [as=u:2, outer=(2)]
  2715             │    └── u:2
  2716             └── const-agg [as=v:3, outer=(3)]
  2717                  └── v:3
  2718  
  2719  # Group sub-expr with the same columns together. Output should have a single union expr.
  2720  opt expect=SplitDisjunctionAddKey
  2721  SELECT u, v FROM d WHERE u = 1 OR u = 3 OR v = 2 OR v = 4 OR u = 5 OR v = 6
  2722  ----
  2723  project
  2724   ├── columns: u:2 v:3
  2725   └── distinct-on
  2726        ├── columns: k:1!null u:2 v:3
  2727        ├── grouping columns: k:1!null
  2728        ├── key: (1)
  2729        ├── fd: (1)-->(2,3)
  2730        ├── union-all
  2731        │    ├── columns: k:1!null u:2 v:3
  2732        │    ├── left columns: k:1!null u:2 v:3
  2733        │    ├── right columns: k:5 u:6 v:7
  2734        │    ├── index-join d
  2735        │    │    ├── columns: k:1!null u:2!null v:3
  2736        │    │    ├── key: (1)
  2737        │    │    ├── fd: (1)-->(2,3)
  2738        │    │    └── scan d@u
  2739        │    │         ├── columns: k:1!null u:2!null
  2740        │    │         ├── constraint: /2/1
  2741        │    │         │    ├── [/1 - /1]
  2742        │    │         │    ├── [/3 - /3]
  2743        │    │         │    └── [/5 - /5]
  2744        │    │         ├── key: (1)
  2745        │    │         └── fd: (1)-->(2)
  2746        │    └── index-join d
  2747        │         ├── columns: k:5!null u:6 v:7!null
  2748        │         ├── key: (5)
  2749        │         ├── fd: (5)-->(6,7)
  2750        │         └── scan d@v
  2751        │              ├── columns: k:5!null v:7!null
  2752        │              ├── constraint: /7/5
  2753        │              │    ├── [/2 - /2]
  2754        │              │    ├── [/4 - /4]
  2755        │              │    └── [/6 - /6]
  2756        │              ├── key: (5)
  2757        │              └── fd: (5)-->(7)
  2758        └── aggregations
  2759             ├── const-agg [as=u:2, outer=(2)]
  2760             │    └── u:2
  2761             └── const-agg [as=v:3, outer=(3)]
  2762                  └── v:3
  2763  
  2764  # Group sub-expr with the same columns together. Output should have a single union expr.
  2765  opt expect=SplitDisjunctionAddKey
  2766  SELECT u, v FROM d WHERE (u = 3 OR v = 2) OR (u = 5 OR v = 4) OR v = 6
  2767  ----
  2768  project
  2769   ├── columns: u:2 v:3
  2770   └── distinct-on
  2771        ├── columns: k:1!null u:2 v:3
  2772        ├── grouping columns: k:1!null
  2773        ├── key: (1)
  2774        ├── fd: (1)-->(2,3)
  2775        ├── union-all
  2776        │    ├── columns: k:1!null u:2 v:3
  2777        │    ├── left columns: k:1!null u:2 v:3
  2778        │    ├── right columns: k:5 u:6 v:7
  2779        │    ├── index-join d
  2780        │    │    ├── columns: k:1!null u:2!null v:3
  2781        │    │    ├── key: (1)
  2782        │    │    ├── fd: (1)-->(2,3)
  2783        │    │    └── scan d@u
  2784        │    │         ├── columns: k:1!null u:2!null
  2785        │    │         ├── constraint: /2/1
  2786        │    │         │    ├── [/3 - /3]
  2787        │    │         │    └── [/5 - /5]
  2788        │    │         ├── key: (1)
  2789        │    │         └── fd: (1)-->(2)
  2790        │    └── index-join d
  2791        │         ├── columns: k:5!null u:6 v:7!null
  2792        │         ├── key: (5)
  2793        │         ├── fd: (5)-->(6,7)
  2794        │         └── scan d@v
  2795        │              ├── columns: k:5!null v:7!null
  2796        │              ├── constraint: /7/5
  2797        │              │    ├── [/2 - /2]
  2798        │              │    ├── [/4 - /4]
  2799        │              │    └── [/6 - /6]
  2800        │              ├── key: (5)
  2801        │              └── fd: (5)-->(7)
  2802        └── aggregations
  2803             ├── const-agg [as=u:2, outer=(2)]
  2804             │    └── u:2
  2805             └── const-agg [as=v:3, outer=(3)]
  2806                  └── v:3
  2807  
  2808  # Find the first disjunction in the filters that have different column sets on
  2809  # the left and right.
  2810  opt expect=SplitDisjunctionAddKey
  2811  SELECT u, v FROM d WHERE (w = 1 OR w = 2) AND (u = 3 OR v = 4)
  2812  ----
  2813  project
  2814   ├── columns: u:2 v:3
  2815   └── project
  2816        ├── columns: u:2 v:3 w:4!null
  2817        └── distinct-on
  2818             ├── columns: k:1!null u:2 v:3 w:4!null
  2819             ├── grouping columns: k:1!null
  2820             ├── key: (1)
  2821             ├── fd: (1)-->(2-4)
  2822             ├── union-all
  2823             │    ├── columns: k:1!null u:2 v:3 w:4!null
  2824             │    ├── left columns: k:1!null u:2 v:3 w:4!null
  2825             │    ├── right columns: k:5 u:6 v:7 w:8
  2826             │    ├── select
  2827             │    │    ├── columns: k:1!null u:2!null v:3 w:4!null
  2828             │    │    ├── key: (1)
  2829             │    │    ├── fd: ()-->(2), (1)-->(3,4)
  2830             │    │    ├── index-join d
  2831             │    │    │    ├── columns: k:1!null u:2 v:3 w:4
  2832             │    │    │    ├── key: (1)
  2833             │    │    │    ├── fd: ()-->(2), (1)-->(3,4)
  2834             │    │    │    └── scan d@u
  2835             │    │    │         ├── columns: k:1!null u:2!null
  2836             │    │    │         ├── constraint: /2/1: [/3 - /3]
  2837             │    │    │         ├── key: (1)
  2838             │    │    │         └── fd: ()-->(2)
  2839             │    │    └── filters
  2840             │    │         └── (w:4 = 1) OR (w:4 = 2) [outer=(4), constraints=(/4: [/1 - /1] [/2 - /2]; tight)]
  2841             │    └── select
  2842             │         ├── columns: k:5!null u:6 v:7!null w:8!null
  2843             │         ├── key: (5)
  2844             │         ├── fd: ()-->(7), (5)-->(6,8)
  2845             │         ├── index-join d
  2846             │         │    ├── columns: k:5!null u:6 v:7 w:8
  2847             │         │    ├── key: (5)
  2848             │         │    ├── fd: ()-->(7), (5)-->(6,8)
  2849             │         │    └── scan d@v
  2850             │         │         ├── columns: k:5!null v:7!null
  2851             │         │         ├── constraint: /7/5: [/4 - /4]
  2852             │         │         ├── key: (5)
  2853             │         │         └── fd: ()-->(7)
  2854             │         └── filters
  2855             │              └── (w:8 = 1) OR (w:8 = 2) [outer=(8), constraints=(/8: [/1 - /1] [/2 - /2]; tight)]
  2856             └── aggregations
  2857                  ├── const-agg [as=u:2, outer=(2)]
  2858                  │    └── u:2
  2859                  ├── const-agg [as=v:3, outer=(3)]
  2860                  │    └── v:3
  2861                  └── const-agg [as=w:4, outer=(4)]
  2862                       └── w:4
  2863  
  2864  # Find the first disjunction in the filters that have columns on the left and
  2865  # right that constrain a scan.
  2866  opt expect=SplitDisjunctionAddKey
  2867  SELECT u, v FROM d WHERE (u = 1 OR w = 2) AND (u = 3 OR v = 4)
  2868  ----
  2869  project
  2870   ├── columns: u:2 v:3
  2871   └── project
  2872        ├── columns: u:2 v:3 w:4
  2873        └── distinct-on
  2874             ├── columns: k:1!null u:2 v:3 w:4
  2875             ├── grouping columns: k:1!null
  2876             ├── key: (1)
  2877             ├── fd: (1)-->(2-4)
  2878             ├── union-all
  2879             │    ├── columns: k:1!null u:2 v:3 w:4
  2880             │    ├── left columns: k:1!null u:2 v:3 w:4
  2881             │    ├── right columns: k:5 u:6 v:7 w:8
  2882             │    ├── select
  2883             │    │    ├── columns: k:1!null u:2!null v:3 w:4!null
  2884             │    │    ├── key: (1)
  2885             │    │    ├── fd: ()-->(2,4), (1)-->(3)
  2886             │    │    ├── index-join d
  2887             │    │    │    ├── columns: k:1!null u:2 v:3 w:4
  2888             │    │    │    ├── key: (1)
  2889             │    │    │    ├── fd: ()-->(2), (1)-->(3,4)
  2890             │    │    │    └── scan d@u
  2891             │    │    │         ├── columns: k:1!null u:2!null
  2892             │    │    │         ├── constraint: /2/1: [/3 - /3]
  2893             │    │    │         ├── key: (1)
  2894             │    │    │         └── fd: ()-->(2)
  2895             │    │    └── filters
  2896             │    │         └── w:4 = 2 [outer=(4), constraints=(/4: [/2 - /2]; tight), fd=()-->(4)]
  2897             │    └── select
  2898             │         ├── columns: k:5!null u:6 v:7!null w:8
  2899             │         ├── key: (5)
  2900             │         ├── fd: ()-->(7), (5)-->(6,8)
  2901             │         ├── index-join d
  2902             │         │    ├── columns: k:5!null u:6 v:7 w:8
  2903             │         │    ├── key: (5)
  2904             │         │    ├── fd: ()-->(7), (5)-->(6,8)
  2905             │         │    └── scan d@v
  2906             │         │         ├── columns: k:5!null v:7!null
  2907             │         │         ├── constraint: /7/5: [/4 - /4]
  2908             │         │         ├── key: (5)
  2909             │         │         └── fd: ()-->(7)
  2910             │         └── filters
  2911             │              └── (u:6 = 1) OR (w:8 = 2) [outer=(6,8)]
  2912             └── aggregations
  2913                  ├── const-agg [as=u:2, outer=(2)]
  2914                  │    └── u:2
  2915                  ├── const-agg [as=v:3, outer=(3)]
  2916                  │    └── v:3
  2917                  └── const-agg [as=w:4, outer=(4)]
  2918                       └── w:4
  2919  
  2920  # Don't apply when outer columns of both sides of OR do not intersect with index columns.
  2921  opt expect-not=SplitDisjunctionAddKey
  2922  SELECT u, w FROM d WHERE u = 1 OR w = 1
  2923  ----
  2924  select
  2925   ├── columns: u:2 w:4
  2926   ├── scan d
  2927   │    └── columns: u:2 w:4
  2928   └── filters
  2929        └── (u:2 = 1) OR (w:4 = 1) [outer=(2,4)]
  2930  
  2931  # Don't apply to queries with strict keys.
  2932  opt expect-not=SplitDisjunctionAddKey
  2933  SELECT k, u, v FROM d WHERE u = 1 OR v = 1
  2934  ----
  2935  distinct-on
  2936   ├── columns: k:1!null u:2 v:3
  2937   ├── grouping columns: k:1!null
  2938   ├── key: (1)
  2939   ├── fd: (1)-->(2,3)
  2940   ├── union-all
  2941   │    ├── columns: k:1!null u:2 v:3
  2942   │    ├── left columns: k:1!null u:2 v:3
  2943   │    ├── right columns: k:5 u:6 v:7
  2944   │    ├── index-join d
  2945   │    │    ├── columns: k:1!null u:2!null v:3
  2946   │    │    ├── key: (1)
  2947   │    │    ├── fd: ()-->(2), (1)-->(3)
  2948   │    │    └── scan d@u
  2949   │    │         ├── columns: k:1!null u:2!null
  2950   │    │         ├── constraint: /2/1: [/1 - /1]
  2951   │    │         ├── key: (1)
  2952   │    │         └── fd: ()-->(2)
  2953   │    └── index-join d
  2954   │         ├── columns: k:5!null u:6 v:7!null
  2955   │         ├── key: (5)
  2956   │         ├── fd: ()-->(7), (5)-->(6)
  2957   │         └── scan d@v
  2958   │              ├── columns: k:5!null v:7!null
  2959   │              ├── constraint: /7/5: [/1 - /1]
  2960   │              ├── key: (5)
  2961   │              └── fd: ()-->(7)
  2962   └── aggregations
  2963        ├── const-agg [as=u:2, outer=(2)]
  2964        │    └── u:2
  2965        └── const-agg [as=v:3, outer=(3)]
  2966             └── v:3
  2967  
  2968  # Don't apply to disjunctions with identical colsets on the left and right.
  2969  opt expect-not=SplitDisjunctionAddKey
  2970  SELECT u FROM d WHERE u = 1 OR u = 5
  2971  ----
  2972  scan d@u
  2973   ├── columns: u:2!null
  2974   └── constraint: /2/1
  2975        ├── [/1 - /1]
  2976        └── [/5 - /5]
  2977  
  2978  # Verifies that flags are copied to the duplicated scan.
  2979  opt expect=SplitDisjunctionAddKey
  2980  SELECT u, v FROM a@{NO_INDEX_JOIN} WHERE u = 1 OR v = 1
  2981  ----
  2982  project
  2983   ├── columns: u:2 v:3
  2984   ├── lax-key: (2,3)
  2985   ├── fd: (3)~~>(2)
  2986   └── distinct-on
  2987        ├── columns: k:1!null u:2 v:3
  2988        ├── grouping columns: k:1!null
  2989        ├── key: (1)
  2990        ├── fd: (1)-->(2,3)
  2991        ├── union-all
  2992        │    ├── columns: k:1!null u:2 v:3
  2993        │    ├── left columns: k:1!null u:2 v:3
  2994        │    ├── right columns: k:4 u:5 v:6
  2995        │    ├── scan a@u
  2996        │    │    ├── columns: k:1!null u:2!null v:3
  2997        │    │    ├── constraint: /2/1: [/1 - /1]
  2998        │    │    ├── flags: no-index-join
  2999        │    │    ├── key: (1)
  3000        │    │    └── fd: ()-->(2), (1)-->(3), (3)~~>(1)
  3001        │    └── scan a@v
  3002        │         ├── columns: k:4!null u:5 v:6!null
  3003        │         ├── constraint: /6: [/1 - /1]
  3004        │         ├── flags: no-index-join
  3005        │         ├── cardinality: [0 - 1]
  3006        │         ├── key: ()
  3007        │         └── fd: ()-->(4-6)
  3008        └── aggregations
  3009             ├── const-agg [as=u:2, outer=(2)]
  3010             │    └── u:2
  3011             └── const-agg [as=v:3, outer=(3)]
  3012                  └── v:3
  3013  
  3014  # Columns are passed-through correctly when EliminateUnionAllLeft is applied.
  3015  opt expect=SplitDisjunctionAddKey
  3016  SELECT u, v FROM d WHERE u = 2 OR (v = 1 AND v = 3)
  3017  ----
  3018  project
  3019   ├── columns: u:2!null v:3
  3020   ├── fd: ()-->(2)
  3021   └── index-join d
  3022        ├── columns: k:1!null u:2!null v:3
  3023        ├── key: (1)
  3024        ├── fd: ()-->(2), (1)-->(3)
  3025        └── scan d@u
  3026             ├── columns: k:1!null u:2!null
  3027             ├── constraint: /2/1: [/2 - /2]
  3028             ├── key: (1)
  3029             └── fd: ()-->(2)