exec-ddl
CREATE TABLE bx (
  b INT PRIMARY KEY,
  x INT
)
----

exec-ddl
CREATE TABLE cy (
  c INT PRIMARY KEY,
  y INT
)
----

exec-ddl
CREATE TABLE dz (
  d INT PRIMARY KEY,
  z INT
)
----

exec-ddl
CREATE TABLE abc (
  a INT PRIMARY KEY,
  b INT,
  c INT,
  d INT
)
----

opt set=reorder_joins_limit=2 expect=ReorderJoins
SELECT * FROM abc, bx, cy WHERE a = 1 AND abc.b = bx.b AND abc.c = cy.c
----
inner-join (lookup cy)
 ├── columns: a:1!null b:2!null c:3!null d:4 b:7!null x:8 c:11!null y:12
 ├── key columns: [3] = [11]
 ├── lookup columns are key
 ├── cardinality: [0 - 1]
 ├── key: ()
 ├── fd: ()-->(1-4,7,8,11,12), (2)==(7), (7)==(2), (3)==(11), (11)==(3)
 ├── inner-join (lookup bx)
 │    ├── columns: a:1!null abc.b:2!null abc.c:3 d:4 bx.b:7!null x:8
 │    ├── key columns: [2] = [7]
 │    ├── lookup columns are key
 │    ├── cardinality: [0 - 1]
 │    ├── key: ()
 │    ├── fd: ()-->(1-4,7,8), (2)==(7), (7)==(2)
 │    ├── scan abc
 │    │    ├── columns: a:1!null abc.b:2 abc.c:3 d:4
 │    │    ├── constraint: /1: [/1 - /1]
 │    │    ├── cardinality: [0 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(1-4)
 │    └── filters (true)
 └── filters (true)

opt set=reorder_joins_limit=2 expect=ReorderJoins
SELECT * FROM bx, abc, cy WHERE a = 1 AND abc.b = bx.b AND abc.c = cy.c
----
inner-join (lookup bx)
 ├── columns: b:1!null x:2 a:5!null b:6!null c:7!null d:8 c:11!null y:12
 ├── key columns: [6] = [1]
 ├── lookup columns are key
 ├── cardinality: [0 - 1]
 ├── key: ()
 ├── fd: ()-->(1,2,5-8,11,12), (7)==(11), (11)==(7), (1)==(6), (6)==(1)
 ├── inner-join (lookup cy)
 │    ├── columns: a:5!null abc.b:6 abc.c:7!null d:8 cy.c:11!null y:12
 │    ├── key columns: [7] = [11]
 │    ├── lookup columns are key
 │    ├── cardinality: [0 - 1]
 │    ├── key: ()
 │    ├── fd: ()-->(5-8,11,12), (7)==(11), (11)==(7)
 │    ├── scan abc
 │    │    ├── columns: a:5!null abc.b:6 abc.c:7 d:8
 │    │    ├── constraint: /5: [/1 - /1]
 │    │    ├── cardinality: [0 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(5-8)
 │    └── filters (true)
 └── filters (true)

opt set=reorder_joins_limit=2 expect=ReorderJoins
SELECT * FROM bx, cy, abc WHERE a = 1 AND abc.b = bx.b AND abc.c = cy.c
----
inner-join (lookup bx)
 ├── columns: b:1!null x:2 c:5!null y:6 a:9!null b:10!null c:11!null d:12
 ├── key columns: [10] = [1]
 ├── lookup columns are key
 ├── cardinality: [0 - 1]
 ├── key: ()
 ├── fd: ()-->(1,2,5,6,9-12), (5)==(11), (11)==(5), (1)==(10), (10)==(1)
 ├── inner-join (lookup cy)
 │    ├── columns: cy.c:5!null y:6 a:9!null abc.b:10 abc.c:11!null d:12
 │    ├── key columns: [11] = [5]
 │    ├── lookup columns are key
 │    ├── cardinality: [0 - 1]
 │    ├── key: ()
 │    ├── fd: ()-->(5,6,9-12), (5)==(11), (11)==(5)
 │    ├── scan abc
 │    │    ├── columns: a:9!null abc.b:10 abc.c:11 d:12
 │    │    ├── constraint: /9: [/1 - /1]
 │    │    ├── cardinality: [0 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(9-12)
 │    └── filters (true)
 └── filters (true)

# Reorder subtrees of size 2.
exploretrace set=reorder_joins_limit=2 rule=ReorderJoins format=hide-all
SELECT *
FROM bx, cy, dz, abc
WHERE a = 1 AND abc.b = bx.b AND abc.c = cy.c AND cy.c = dz.d
----
----
================================================================================
ReorderJoins
================================================================================
Source expression:
  inner-join (hash)
   ├── scan bx
   ├── inner-join (hash)
   │    ├── scan cy
   │    ├── inner-join (hash)
   │    │    ├── scan dz
   │    │    ├── scan abc
   │    │    │    └── constraint: /13: [/1 - /1]
   │    │    └── filters
   │    │         └── dz.d = abc.c
   │    └── filters
   │         └── cy.c = dz.d
   └── filters
        └── abc.b = bx.b

New expression 1 of 1:
  inner-join (hash)
   ├── scan bx
   ├── inner-join (hash)
   │    ├── scan cy
   │    ├── inner-join (hash)
   │    │    ├── scan abc
   │    │    │    └── constraint: /13: [/1 - /1]
   │    │    ├── scan dz
   │    │    └── filters
   │    │         └── dz.d = abc.c
   │    └── filters
   │         └── cy.c = dz.d
   └── filters
        └── abc.b = bx.b

================================================================================
ReorderJoins
================================================================================
Source expression:
  inner-join (hash)
   ├── scan bx
   ├── inner-join (hash)
   │    ├── scan cy
   │    ├── inner-join (lookup dz)
   │    │    ├── lookup columns are key
   │    │    ├── scan abc
   │    │    │    └── constraint: /13: [/1 - /1]
   │    │    └── filters (true)
   │    └── filters
   │         └── cy.c = dz.d
   └── filters
        └── abc.b = bx.b

New expression 1 of 5:
  inner-join (hash)
   ├── scan bx
   ├── inner-join (hash)
   │    ├── inner-join (lookup dz)
   │    │    ├── lookup columns are key
   │    │    ├── scan abc
   │    │    │    └── constraint: /13: [/1 - /1]
   │    │    └── filters (true)
   │    ├── scan cy
   │    └── filters
   │         └── cy.c = dz.d
   └── filters
        └── abc.b = bx.b

New expression 2 of 5:
  inner-join (hash)
   ├── scan bx
   ├── inner-join (hash)
   │    ├── scan dz
   │    ├── inner-join (hash)
   │    │    ├── scan cy
   │    │    ├── scan abc
   │    │    │    └── constraint: /13: [/1 - /1]
   │    │    └── filters
   │    │         └── cy.c = abc.c
   │    └── filters
   │         └── dz.d = abc.c
   └── filters
        └── abc.b = bx.b

New expression 3 of 5:
  inner-join (hash)
   ├── scan bx
   ├── inner-join (hash)
   │    ├── inner-join (hash)
   │    │    ├── scan cy
   │    │    ├── scan abc
   │    │    │    └── constraint: /13: [/1 - /1]
   │    │    └── filters
   │    │         └── cy.c = abc.c
   │    ├── scan dz
   │    └── filters
   │         └── dz.d = abc.c
   └── filters
        └── abc.b = bx.b

New expression 4 of 5:
  inner-join (hash)
   ├── scan bx
   ├── inner-join (hash)
   │    ├── inner-join (hash)
   │    │    ├── scan cy
   │    │    ├── scan dz
   │    │    └── filters
   │    │         └── cy.c = dz.d
   │    ├── scan abc
   │    │    └── constraint: /13: [/1 - /1]
   │    └── filters
   │         └── dz.d = abc.c
   └── filters
        └── abc.b = bx.b

New expression 5 of 5:
  inner-join (hash)
   ├── scan bx
   ├── inner-join (hash)
   │    ├── scan abc
   │    │    └── constraint: /13: [/1 - /1]
   │    ├── inner-join (hash)
   │    │    ├── scan cy
   │    │    ├── scan dz
   │    │    └── filters
   │    │         └── cy.c = dz.d
   │    └── filters
   │         └── dz.d = abc.c
   └── filters
        └── abc.b = bx.b

================================================================================
ReorderJoins
================================================================================
Source expression:
  inner-join (hash)
   ├── scan bx
   ├── inner-join (lookup cy)
   │    ├── lookup columns are key
   │    ├── inner-join (lookup dz)
   │    │    ├── lookup columns are key
   │    │    ├── scan abc
   │    │    │    └── constraint: /13: [/1 - /1]
   │    │    └── filters (true)
   │    └── filters (true)
   └── filters
        └── abc.b = bx.b

New expression 1 of 3:
  inner-join (hash)
   ├── inner-join (lookup cy)
   │    ├── lookup columns are key
   │    ├── inner-join (lookup dz)
   │    │    ├── lookup columns are key
   │    │    ├── scan abc
   │    │    │    └── constraint: /13: [/1 - /1]
   │    │    └── filters (true)
   │    └── filters (true)
   ├── scan bx
   └── filters
        └── abc.b = bx.b

New expression 2 of 3:
  inner-join (hash)
   ├── scan cy
   ├── inner-join (hash)
   │    ├── scan bx
   │    ├── inner-join (lookup dz)
   │    │    ├── lookup columns are key
   │    │    ├── scan abc
   │    │    │    └── constraint: /13: [/1 - /1]
   │    │    └── filters (true)
   │    └── filters
   │         └── abc.b = bx.b
   └── filters
        └── cy.c = dz.d

New expression 3 of 3:
  inner-join (hash)
   ├── inner-join (hash)
   │    ├── scan bx
   │    ├── inner-join (lookup dz)
   │    │    ├── lookup columns are key
   │    │    ├── scan abc
   │    │    │    └── constraint: /13: [/1 - /1]
   │    │    └── filters (true)
   │    └── filters
   │         └── abc.b = bx.b
   ├── scan cy
   └── filters
        └── cy.c = dz.d
----
----

# No joins should be reordered besides commutation.
memo set=reorder_joins_limit=0 expect-not=ReorderJoins
SELECT * FROM bx, cy, abc WHERE a = 1 AND abc.b = bx.b AND abc.c = cy.c
----
memo (optimized, ~25KB, required=[presentation: b:1,x:2,c:5,y:6,a:9,b:10,c:11,d:12])
 ├── G1: (inner-join G2 G3 G4) (merge-join G2 G3 G5 inner-join,+1,+10)
 │    └── [presentation: b:1,x:2,c:5,y:6,a:9,b:10,c:11,d:12]
 │         ├── best: (merge-join G2="[ordering: +1]" G3 G5 inner-join,+1,+10)
 │         └── cost: 2163.99
 ├── G2: (scan bx,cols=(1,2))
 │    ├── [ordering: +1]
 │    │    ├── best: (scan bx,cols=(1,2))
 │    │    └── cost: 1068.42
 │    └── []
 │         ├── best: (scan bx,cols=(1,2))
 │         └── cost: 1068.42
 ├── G3: (inner-join G6 G7 G8) (merge-join G6 G7 G5 inner-join,+5,+11)
 │    └── []
 │         ├── best: (merge-join G6="[ordering: +5]" G7 G5 inner-join,+5,+11)
 │         └── cost: 1086.54
 ├── G4: (filters G9)
 ├── G5: (filters)
 ├── G6: (scan cy,cols=(5,6))
 │    ├── [ordering: +5]
 │    │    ├── best: (scan cy,cols=(5,6))
 │    │    └── cost: 1068.42
 │    └── []
 │         ├── best: (scan cy,cols=(5,6))
 │         └── cost: 1068.42
 ├── G7: (select G10 G11) (scan abc,cols=(9-12),constrained)
 │    └── []
 │         ├── best: (scan abc,cols=(9-12),constrained)
 │         └── cost: 9.09
 ├── G8: (filters G12)
 ├── G9: (eq G13 G14)
 ├── G10: (scan abc,cols=(9-12))
 │    └── []
 │         ├── best: (scan abc,cols=(9-12))
 │         └── cost: 1108.82
 ├── G11: (filters G15)
 ├── G12: (eq G16 G17)
 ├── G13: (variable abc.b)
 ├── G14: (variable bx.b)
 ├── G15: (eq G18 G19)
 ├── G16: (variable abc.c)
 ├── G17: (variable cy.c)
 ├── G18: (variable a)
 └── G19: (const 1)

memo set=reorder_joins_limit=2
SELECT * FROM bx, cy, abc WHERE a = 1 AND abc.b = bx.b AND abc.c = cy.c
----
memo (optimized, ~36KB, required=[presentation: b:1,x:2,c:5,y:6,a:9,b:10,c:11,d:12])
 ├── G1: (inner-join G2 G3 G4) (inner-join G3 G2 G4) (inner-join G5 G6 G7) (inner-join G6 G5 G7) (merge-join G2 G3 G8 inner-join,+1,+10) (merge-join G3 G2 G8 inner-join,+10,+1) (lookup-join G3 G8 bx,keyCols=[10],outCols=(1,2,5,6,9-12)) (merge-join G5 G6 G8 inner-join,+5,+11) (merge-join G6 G5 G8 inner-join,+11,+5) (lookup-join G6 G8 cy,keyCols=[11],outCols=(1,2,5,6,9-12))
 │    └── [presentation: b:1,x:2,c:5,y:6,a:9,b:10,c:11,d:12]
 │         ├── best: (lookup-join G3 G8 bx,keyCols=[10],outCols=(1,2,5,6,9-12))
 │         └── cost: 29.13
 ├── G2: (scan bx,cols=(1,2))
 │    ├── [ordering: +1]
 │    │    ├── best: (scan bx,cols=(1,2))
 │    │    └── cost: 1068.42
 │    └── []
 │         ├── best: (scan bx,cols=(1,2))
 │         └── cost: 1068.42
 ├── G3: (inner-join G5 G9 G7) (inner-join G9 G5 G7) (merge-join G5 G9 G8 inner-join,+5,+11) (merge-join G9 G5 G8 inner-join,+11,+5) (lookup-join G9 G8 cy,keyCols=[11],outCols=(5,6,9-12))
 │    └── []
 │         ├── best: (lookup-join G9 G8 cy,keyCols=[11],outCols=(5,6,9-12))
 │         └── cost: 19.13
 ├── G4: (filters G10)
 ├── G5: (scan cy,cols=(5,6))
 │    ├── [ordering: +5]
 │    │    ├── best: (scan cy,cols=(5,6))
 │    │    └── cost: 1068.42
 │    └── []
 │         ├── best: (scan cy,cols=(5,6))
 │         └── cost: 1068.42
 ├── G6: (inner-join G2 G9 G4) (inner-join G9 G2 G4) (merge-join G2 G9 G8 inner-join,+1,+10) (merge-join G9 G2 G8 inner-join,+10,+1) (lookup-join G9 G8 bx,keyCols=[10],outCols=(1,2,9-12))
 │    └── []
 │         ├── best: (lookup-join G9 G8 bx,keyCols=[10],outCols=(1,2,9-12))
 │         └── cost: 19.13
 ├── G7: (filters G11)
 ├── G8: (filters)
 ├── G9: (select G12 G13) (scan abc,cols=(9-12),constrained)
 │    └── []
 │         ├── best: (scan abc,cols=(9-12),constrained)
 │         └── cost: 9.09
 ├── G10: (eq G14 G15)
 ├── G11: (eq G16 G17)
 ├── G12: (scan abc,cols=(9-12))
 │    └── []
 │         ├── best: (scan abc,cols=(9-12))
 │         └── cost: 1108.82
 ├── G13: (filters G18)
 ├── G14: (variable abc.b)
 ├── G15: (variable bx.b)
 ├── G16: (variable abc.c)
 ├── G17: (variable cy.c)
 ├── G18: (eq G19 G20)
 ├── G19: (variable a)
 └── G20: (const 1)

opt set=reorder_joins_limit=3 expect=ReorderJoins
SELECT * FROM bx, cy, dz, abc WHERE a = 1
----
inner-join (cross)
 ├── columns: b:1!null x:2 c:5!null y:6 d:9!null z:10 a:13!null b:14 c:15 d:16
 ├── key: (1,5,9)
 ├── fd: ()-->(13-16), (1)-->(2), (5)-->(6), (9)-->(10)
 ├── inner-join (cross)
 │    ├── columns: cy.c:5!null y:6 dz.d:9!null z:10 a:13!null abc.b:14 abc.c:15 abc.d:16
 │    ├── key: (5,9)
 │    ├── fd: ()-->(13-16), (5)-->(6), (9)-->(10)
 │    ├── scan cy
 │    │    ├── columns: cy.c:5!null y:6
 │    │    ├── key: (5)
 │    │    └── fd: (5)-->(6)
 │    ├── inner-join (cross)
 │    │    ├── columns: dz.d:9!null z:10 a:13!null abc.b:14 abc.c:15 abc.d:16
 │    │    ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │    │    ├── key: (9)
 │    │    ├── fd: ()-->(13-16), (9)-->(10)
 │    │    ├── scan dz
 │    │    │    ├── columns: dz.d:9!null z:10
 │    │    │    ├── key: (9)
 │    │    │    └── fd: (9)-->(10)
 │    │    ├── scan abc
 │    │    │    ├── columns: a:13!null abc.b:14 abc.c:15 abc.d:16
 │    │    │    ├── constraint: /13: [/1 - /1]
 │    │    │    ├── cardinality: [0 - 1]
 │    │    │    ├── key: ()
 │    │    │    └── fd: ()-->(13-16)
 │    │    └── filters (true)
 │    └── filters (true)
 ├── scan bx
 │    ├── columns: bx.b:1!null x:2
 │    ├── key: (1)
 │    └── fd: (1)-->(2)
 └── filters (true)

opt set=reorder_joins_limit=2 format=show-all
SELECT * FROM abc, bx, cy, dz WHERE a = 1
----
inner-join (cross)
 ├── columns: a:1(int!null) b:2(int) c:3(int) d:4(int) b:7(int!null) x:8(int) c:11(int!null) y:12(int) d:15(int!null) z:16(int)
 ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 ├── stats: [rows=1e+09]
 ├── cost: 32525762.2
 ├── key: (7,11,15)
 ├── fd: ()-->(1-4), (7)-->(8), (11)-->(12), (15)-->(16)
 ├── prune: (2-4,7,8,11,12,15,16)
 ├── interesting orderings: (+7) (+11) (+15)
 ├── inner-join (cross)
 │    ├── columns: t.public.bx.b:7(int!null) t.public.bx.x:8(int) t.public.cy.c:11(int!null) t.public.cy.y:12(int) t.public.dz.d:15(int!null) t.public.dz.z:16(int)
 │    ├── stats: [rows=1e+09]
 │    ├── cost: 10025753.1
 │    ├── key: (7,11,15)
 │    ├── fd: (7)-->(8), (11)-->(12), (15)-->(16)
 │    ├── prune: (7,8,11,12,15,16)
 │    ├── interesting orderings: (+7) (+11) (+15)
 │    ├── inner-join (cross)
 │    │    ├── columns: t.public.cy.c:11(int!null) t.public.cy.y:12(int) t.public.dz.d:15(int!null) t.public.dz.z:16(int)
 │    │    ├── stats: [rows=1000000]
 │    │    ├── cost: 12167.0063
 │    │    ├── key: (11,15)
 │    │    ├── fd: (11)-->(12), (15)-->(16)
 │    │    ├── prune: (11,12,15,16)
 │    │    ├── interesting orderings: (+11) (+15)
 │    │    ├── scan t.public.cy
 │    │    │    ├── columns: t.public.cy.c:11(int!null) t.public.cy.y:12(int)
 │    │    │    ├── stats: [rows=1000]
 │    │    │    ├── cost: 1068.42
 │    │    │    ├── key: (11)
 │    │    │    ├── fd: (11)-->(12)
 │    │    │    ├── prune: (11,12)
 │    │    │    ├── interesting orderings: (+11)
 │    │    │    └── unfiltered-cols: (11-14)
 │    │    ├── scan t.public.dz
 │    │    │    ├── columns: t.public.dz.d:15(int!null) t.public.dz.z:16(int)
 │    │    │    ├── stats: [rows=1000]
 │    │    │    ├── cost: 1068.42
 │    │    │    ├── key: (15)
 │    │    │    ├── fd: (15)-->(16)
 │    │    │    ├── prune: (15,16)
 │    │    │    ├── interesting orderings: (+15)
 │    │    │    └── unfiltered-cols: (15-18)
 │    │    └── filters (true)
 │    ├── scan t.public.bx
 │    │    ├── columns: t.public.bx.b:7(int!null) t.public.bx.x:8(int)
 │    │    ├── stats: [rows=1000]
 │    │    ├── cost: 1068.42
 │    │    ├── key: (7)
 │    │    ├── fd: (7)-->(8)
 │    │    ├── prune: (7,8)
 │    │    ├── interesting orderings: (+7)
 │    │    └── unfiltered-cols: (7-10)
 │    └── filters (true)
 ├── scan t.public.abc
 │    ├── columns: t.public.abc.a:1(int!null) t.public.abc.b:2(int) t.public.abc.c:3(int) t.public.abc.d:4(int)
 │    ├── constraint: /1: [/1 - /1]
 │    ├── cardinality: [0 - 1]
 │    ├── stats: [rows=1, distinct(1)=1, null(1)=0]
 │    ├── cost: 9.09
 │    ├── key: ()
 │    ├── fd: ()-->(1-4)
 │    └── prune: (2-4)
 └── filters (true)

# Note the difference in memo size for with and without reorder-joins, for only four tables.
# TODO(justin): Find a way to reduce this.

memo set=reorder_joins_limit=0
SELECT * FROM bx, cy, dz, abc WHERE x = y AND y = z AND z = a
----
memo (optimized, ~32KB, required=[presentation: b:1,x:2,c:5,y:6,d:9,z:10,a:13,b:14,c:15,d:16])
 ├── G1: (inner-join G2 G3 G4) (merge-join G2 G3 G5 inner-join,+2,+6)
 │    └── [presentation: b:1,x:2,c:5,y:6,d:9,z:10,a:13,b:14,c:15,d:16]
 │         ├── best: (inner-join G2 G3 G4)
 │         └── cost: 5651.39
 ├── G2: (scan bx,cols=(1,2))
 │    ├── [ordering: +2]
 │    │    ├── best: (sort G2)
 │    │    └── cost: 1307.90
 │    └── []
 │         ├── best: (scan bx,cols=(1,2))
 │         └── cost: 1068.42
 ├── G3: (inner-join G6 G7 G8) (merge-join G6 G7 G5 inner-join,+6,+10)
 │    ├── [ordering: +(6|10|13)]
 │    │    ├── best: (merge-join G6="[ordering: +6]" G7="[ordering: +(10|13)]" G5 inner-join,+6,+10)
 │    │    └── cost: 3872.46
 │    └── []
 │         ├── best: (inner-join G6 G7 G8)
 │         └── cost: 3413.72
 ├── G4: (filters G9)
 ├── G5: (filters)
 ├── G6: (scan cy,cols=(5,6))
 │    ├── [ordering: +6]
 │    │    ├── best: (sort G6)
 │    │    └── cost: 1307.90
 │    └── []
 │         ├── best: (scan cy,cols=(5,6))
 │         └── cost: 1068.42
 ├── G7: (inner-join G10 G11 G12) (merge-join G10 G11 G5 inner-join,+10,+13) (lookup-join G10 G5 abc,keyCols=[10],outCols=(9,10,13-16))
 │    ├── [ordering: +(10|13)]
 │    │    ├── best: (merge-join G10="[ordering: +10]" G11="[ordering: +13]" G5 inner-join,+10,+13)
 │    │    └── cost: 2446.64
 │    └── []
 │         ├── best: (inner-join G10 G11 G12)
 │         └── cost: 2217.31
 ├── G8: (filters G13)
 ├── G9: (eq G14 G15)
 ├── G10: (scan dz,cols=(9,10))
 │    ├── [ordering: +10]
 │    │    ├── best: (sort G10)
 │    │    └── cost: 1307.90
 │    └── []
 │         ├── best: (scan dz,cols=(9,10))
 │         └── cost: 1068.42
 ├── G11: (scan abc,cols=(13-16))
 │    ├── [ordering: +13]
 │    │    ├── best: (scan abc,cols=(13-16))
 │    │    └── cost: 1108.82
 │    └── []
 │         ├── best: (scan abc,cols=(13-16))
 │         └── cost: 1108.82
 ├── G12: (filters G16)
 ├── G13: (eq G15 G17)
 ├── G14: (variable x)
 ├── G15: (variable y)
 ├── G16: (eq G17 G18)
 ├── G17: (variable z)
 └── G18: (variable a)

# This query is the worst-case scenario for join ordering because all relations
# are connected, which allows every join order to be added to the memo.
# TODO(drewk): implement branch pruning and/or a max operator limit for the
# memo.
memo set=reorder_joins_limit=3
SELECT * FROM bx, cy, dz, abc WHERE x = y AND y = z AND z = a
----
memo (optimized, ~68KB, required=[presentation: b:1,x:2,c:5,y:6,d:9,z:10,a:13,b:14,c:15,d:16])
 ├── G1: (inner-join G2 G3 G4) (inner-join G3 G2 G4) (inner-join G5 G6 G7) (inner-join G6 G5 G7) (inner-join G8 G9 G7) (inner-join G9 G8 G7) (inner-join G10 G11 G12) (inner-join G11 G10 G12) (inner-join G13 G14 G12) (inner-join G14 G13 G12) (inner-join G15 G16 G12) (inner-join G16 G15 G12) (inner-join G17 G18 G12) (inner-join G18 G17 G12) (merge-join G3 G2 G19 inner-join,+6,+2) (merge-join G6 G5 G19 inner-join,+10,+6) (merge-join G9 G8 G19 inner-join,+10,+6) (merge-join G11 G10 G19 inner-join,+13,+10) (merge-join G14 G13 G19 inner-join,+13,+10) (merge-join G16 G15 G19 inner-join,+13,+10) (lookup-join G17 G19 abc,keyCols=[10],outCols=(1,2,5,6,9,10,13-16)) (merge-join G18 G17 G19 inner-join,+13,+10)
 │    └── [presentation: b:1,x:2,c:5,y:6,d:9,z:10,a:13,b:14,c:15,d:16]
 │         ├── best: (inner-join G3 G2 G4)
 │         └── cost: 5592.62
 ├── G2: (scan bx,cols=(1,2))
 │    ├── [ordering: +2]
 │    │    ├── best: (sort G2)
 │    │    └── cost: 1307.90
 │    └── []
 │         ├── best: (scan bx,cols=(1,2))
 │         └── cost: 1068.42
 ├── G3: (inner-join G5 G9 G7) (inner-join G9 G5 G7) (inner-join G10 G14 G12) (inner-join G14 G10 G12) (inner-join G15 G18 G12) (inner-join G18 G15 G12) (merge-join G9 G5 G19 inner-join,+10,+6) (merge-join G14 G10 G19 inner-join,+13,+10) (lookup-join G15 G19 abc,keyCols=[10],outCols=(5,6,9,10,13-16)) (merge-join G18 G15 G19 inner-join,+13,+10)
 │    ├── [ordering: +(6|10|13)]
 │    │    ├── best: (merge-join G9="[ordering: +(10|13)]" G5="[ordering: +6]" G19 inner-join,+10,+6)
 │    │    └── cost: 3872.48
 │    └── []
 │         ├── best: (inner-join G5 G9 G7)
 │         └── cost: 3413.72
 ├── G4: (filters G20)
 ├── G5: (scan cy,cols=(5,6))
 │    ├── [ordering: +6]
 │    │    ├── best: (sort G5)
 │    │    └── cost: 1307.90
 │    └── []
 │         ├── best: (scan cy,cols=(5,6))
 │         └── cost: 1068.42
 ├── G6: (inner-join G2 G9 G21) (inner-join G9 G2 G21) (inner-join G10 G16 G12) (inner-join G16 G10 G12) (inner-join G13 G18 G12) (inner-join G18 G13 G12) (merge-join G9 G2 G19 inner-join,+10,+2) (merge-join G16 G10 G19 inner-join,+13,+10) (lookup-join G13 G19 abc,keyCols=[10],outCols=(1,2,9,10,13-16)) (merge-join G18 G13 G19 inner-join,+13,+10)
 │    ├── [ordering: +(2|10|13)]
 │    │    ├── best: (merge-join G9="[ordering: +(10|13)]" G2="[ordering: +2]" G19 inner-join,+10,+2)
 │    │    └── cost: 3872.48
 │    └── []
 │         ├── best: (inner-join G2 G9 G21)
 │         └── cost: 3413.72
 ├── G7: (filters G22)
 ├── G8: (inner-join G2 G5 G4) (inner-join G5 G2 G4)
 │    ├── [ordering: +(2|6)]
 │    │    ├── best: (sort G8)
 │    │    └── cost: 5466.98
 │    └── []
 │         ├── best: (inner-join G2 G5 G4)
 │         └── cost: 2265.02
 ├── G9: (inner-join G10 G18 G12) (inner-join G18 G10 G12) (lookup-join G10 G19 abc,keyCols=[10],outCols=(9,10,13-16)) (merge-join G18 G10 G19 inner-join,+13,+10)
 │    ├── [ordering: +(10|13)]
 │    │    ├── best: (merge-join G18="[ordering: +13]" G10="[ordering: +10]" G19 inner-join,+13,+10)
 │    │    └── cost: 2446.64
 │    └── []
 │         ├── best: (inner-join G10 G18 G12)
 │         └── cost: 2217.31
 ├── G10: (scan dz,cols=(9,10))
 │    ├── [ordering: +10]
 │    │    ├── best: (sort G10)
 │    │    └── cost: 1307.90
 │    └── []
 │         ├── best: (scan dz,cols=(9,10))
 │         └── cost: 1068.42
 ├── G11: (inner-join G2 G14 G4) (inner-join G14 G2 G4) (inner-join G5 G16 G4) (inner-join G16 G5 G4) (inner-join G8 G18 G23) (inner-join G18 G8 G23) (merge-join G14 G2 G19 inner-join,+6,+2) (merge-join G16 G5 G19 inner-join,+2,+6) (lookup-join G8 G19 abc,keyCols=[2],outCols=(1,2,5,6,13-16)) (merge-join G18 G8 G19 inner-join,+13,+2)
 │    ├── [ordering: +(2|6|13)]
 │    │    ├── best: (merge-join G14="[ordering: +(6|13)]" G2="[ordering: +2]" G19 inner-join,+6,+2)
 │    │    └── cost: 3872.48
 │    └── []
 │         ├── best: (inner-join G2 G14 G4)
 │         └── cost: 3413.72
 ├── G12: (filters G24)
 ├── G13: (inner-join G2 G10 G21) (inner-join G10 G2 G21)
 │    ├── [ordering: +(2|10)]
 │    │    ├── best: (sort G13)
 │    │    └── cost: 5466.98
 │    └── []
 │         ├── best: (inner-join G2 G10 G21)
 │         └── cost: 2265.02
 ├── G14: (inner-join G5 G18 G25) (inner-join G18 G5 G25) (lookup-join G5 G19 abc,keyCols=[6],outCols=(5,6,13-16)) (merge-join G18 G5 G19 inner-join,+13,+6)
 │    ├── [ordering: +(6|13)]
 │    │    ├── best: (merge-join G18="[ordering: +13]" G5="[ordering: +6]" G19 inner-join,+13,+6)
 │    │    └── cost: 2446.64
 │    └── []
 │         ├── best: (inner-join G5 G18 G25)
 │         └── cost: 2217.31
 ├── G15: (inner-join G5 G10 G7) (inner-join G10 G5 G7)
 │    ├── [ordering: +(6|10)]
 │    │    ├── best: (sort G15)
 │    │    └── cost: 5466.98
 │    └── []
 │         ├── best: (inner-join G5 G10 G7)
 │         └── cost: 2265.02
 ├── G16: (inner-join G2 G18 G23) (inner-join G18 G2 G23) (lookup-join G2 G19 abc,keyCols=[2],outCols=(1,2,13-16)) (merge-join G18 G2 G19 inner-join,+13,+2)
 │    ├── [ordering: +(2|13)]
 │    │    ├── best: (merge-join G18="[ordering: +13]" G2="[ordering: +2]" G19 inner-join,+13,+2)
 │    │    └── cost: 2446.64
 │    └── []
 │         ├── best: (inner-join G2 G18 G23)
 │         └── cost: 2217.31
 ├── G17: (inner-join G2 G15 G4) (inner-join G15 G2 G4) (inner-join G5 G13 G7) (inner-join G13 G5 G7) (inner-join G8 G10 G7) (inner-join G10 G8 G7)
 │    ├── [ordering: +(2|6|10)]
 │    │    ├── best: (sort G17)
 │    │    └── cost: 45824.65
 │    └── []
 │         ├── best: (inner-join G15 G2 G4)
 │         └── cost: 4443.91
 ├── G18: (scan abc,cols=(13-16))
 │    ├── [ordering: +13]
 │    │    ├── best: (scan abc,cols=(13-16))
 │    │    └── cost: 1108.82
 │    └── []
 │         ├── best: (scan abc,cols=(13-16))
 │         └── cost: 1108.82
 ├── G19: (filters)
 ├── G20: (eq G26 G27)
 ├── G21: (filters G28)
 ├── G22: (eq G27 G29)
 ├── G23: (filters G30)
 ├── G24: (eq G29 G31)
 ├── G25: (filters G32)
 ├── G26: (variable x)
 ├── G27: (variable y)
 ├── G28: (eq G26 G29)
 ├── G29: (variable z)
 ├── G30: (eq G26 G31)
 ├── G31: (variable a)
 └── G32: (eq G27 G31)

opt
SELECT * FROM bx, cy, dz, abc WHERE x = y AND y = z AND z = a
----
inner-join (hash)
 ├── columns: b:1!null x:2!null c:5!null y:6!null d:9!null z:10!null a:13!null b:14 c:15 d:16
 ├── key: (1,5,9)
 ├── fd: (1)-->(2), (5)-->(6), (9)-->(10), (13)-->(14-16), (2)==(6,10,13), (6)==(2,10,13), (10)==(2,6,13), (13)==(2,6,10)
 ├── inner-join (hash)
 │    ├── columns: cy.c:5!null y:6!null dz.d:9!null z:10!null a:13!null abc.b:14 abc.c:15 abc.d:16
 │    ├── key: (5,9)
 │    ├── fd: (5)-->(6), (9)-->(10), (13)-->(14-16), (6)==(10,13), (10)==(6,13), (13)==(6,10)
 │    ├── scan cy
 │    │    ├── columns: cy.c:5!null y:6
 │    │    ├── key: (5)
 │    │    └── fd: (5)-->(6)
 │    ├── inner-join (hash)
 │    │    ├── columns: dz.d:9!null z:10!null a:13!null abc.b:14 abc.c:15 abc.d:16
 │    │    ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │    │    ├── key: (9)
 │    │    ├── fd: (9)-->(10), (13)-->(14-16), (10)==(13), (13)==(10)
 │    │    ├── scan dz
 │    │    │    ├── columns: dz.d:9!null z:10
 │    │    │    ├── key: (9)
 │    │    │    └── fd: (9)-->(10)
 │    │    ├── scan abc
 │    │    │    ├── columns: a:13!null abc.b:14 abc.c:15 abc.d:16
 │    │    │    ├── key: (13)
 │    │    │    └── fd: (13)-->(14-16)
 │    │    └── filters
 │    │         └── z:10 = a:13 [outer=(10,13), constraints=(/10: (/NULL - ]; /13: (/NULL - ]), fd=(10)==(13), (13)==(10)]
 │    └── filters
 │         └── y:6 = z:10 [outer=(6,10), constraints=(/6: (/NULL - ]; /10: (/NULL - ]), fd=(6)==(10), (10)==(6)]
 ├── scan bx
 │    ├── columns: bx.b:1!null x:2
 │    ├── key: (1)
 │    └── fd: (1)-->(2)
 └── filters
      └── x:2 = y:6 [outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]

# Regression test for #34795.
exec-ddl
CREATE TABLE a (id INT8 PRIMARY KEY)
----

opt set=reorder_joins_limit=3 disable=(EliminateJoinUnderProjectLeft,EliminateJoinUnderProjectRight)
SELECT
    1
FROM
    a as a1
    INNER JOIN a as a2 ON 1 = a2.id
    INNER JOIN a AS a3 ON a1.id = a3.id
    CROSS JOIN a as a4
WHERE
    a4.id = 1 AND (SELECT true FROM a WHERE a1.id = 1)
----
project
 ├── columns: "?column?":17!null
 ├── fd: ()-->(17)
 ├── inner-join (cross)
 │    ├── columns: a1.id:1!null a2.id:4!null a3.id:7!null a4.id:10!null bool:16!null
 │    ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │    ├── key: (7)
 │    ├── fd: ()-->(4,10,16), (1)==(7), (7)==(1)
 │    ├── inner-join (cross)
 │    │    ├── columns: a1.id:1!null a2.id:4!null a3.id:7!null bool:16!null
 │    │    ├── multiplicity: left-rows(zero-or-more), right-rows(zero-or-one)
 │    │    ├── key: (7)
 │    │    ├── fd: ()-->(4,16), (1)==(7), (7)==(1)
 │    │    ├── scan a [as=a2]
 │    │    │    ├── columns: a2.id:4!null
 │    │    │    ├── constraint: /4: [/1 - /1]
 │    │    │    ├── cardinality: [0 - 1]
 │    │    │    ├── key: ()
 │    │    │    └── fd: ()-->(4)
 │    │    ├── inner-join (lookup a [as=a3])
 │    │    │    ├── columns: a1.id:1!null a3.id:7!null bool:16!null
 │    │    │    ├── key columns: [1] = [7]
 │    │    │    ├── lookup columns are key
 │    │    │    ├── key: (7)
 │    │    │    ├── fd: ()-->(16), (1)==(7), (7)==(1)
 │    │    │    ├── select
 │    │    │    │    ├── columns: a1.id:1!null bool:16!null
 │    │    │    │    ├── key: (1)
 │    │    │    │    ├── fd: ()-->(16)
 │    │    │    │    ├── ensure-distinct-on
 │    │    │    │    │    ├── columns: a1.id:1!null bool:16
 │    │    │    │    │    ├── grouping columns: a1.id:1!null
 │    │    │    │    │    ├── error: "more than one row returned by a subquery used as an expression"
 │    │    │    │    │    ├── key: (1)
 │    │    │    │    │    ├── fd: (1)-->(16)
 │    │    │    │    │    ├── left-join (cross)
 │    │    │    │    │    │    ├── columns: a1.id:1!null bool:16
 │    │    │    │    │    │    ├── scan a [as=a1]
 │    │    │    │    │    │    │    ├── columns: a1.id:1!null
 │    │    │    │    │    │    │    └── key: (1)
 │    │    │    │    │    │    ├── project
 │    │    │    │    │    │    │    ├── columns: bool:16!null
 │    │    │    │    │    │    │    ├── fd: ()-->(16)
 │    │    │    │    │    │    │    ├── scan a
 │    │    │    │    │    │    │    └── projections
 │    │    │    │    │    │    │         └── true [as=bool:16]
 │    │    │    │    │    │    └── filters
 │    │    │    │    │    │         └── a1.id:1 = 1 [outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
 │    │    │    │    │    └── aggregations
 │    │    │    │    │         └── const-agg [as=bool:16, outer=(16)]
 │    │    │    │    │              └── bool:16
 │    │    │    │    └── filters
 │    │    │    │         └── bool:16 [outer=(16), constraints=(/16: [/true - /true]; tight), fd=()-->(16)]
 │    │    │    └── filters (true)
 │    │    └── filters (true)
 │    ├── scan a [as=a4]
 │    │    ├── columns: a4.id:10!null
 │    │    ├── constraint: /10: [/1 - /1]
 │    │    ├── cardinality: [0 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(10)
 │    └── filters (true)
 └── projections
      └── 1 [as="?column?":17]

# An edge for b = d should be added to the graph.
reorderjoins format=hide-all
SELECT * FROM bx
INNER JOIN cy ON b = c
INNER JOIN (SELECT * FROM dz WHERE z > 0) ON c = d
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── scan bx
   ├── scan cy
   └── filters
        └── b = c
Vertexes
  A:
    scan bx
  B:
    scan cy
Edges
  b = c [inner, ses=AB, tes=AB, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joins Considered: 2
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── inner-join (hash)
   │    ├── scan bx
   │    ├── scan cy
   │    └── filters
   │         └── b = c
   ├── select
   │    ├── scan dz
   │    └── filters
   │         └── z > 0
   └── filters
        └── c = d
Vertexes
  A:
    scan bx
  B:
    scan cy
  C:
    select
     ├── scan dz
     └── filters
          └── z > 0
Edges
  b = c [inner, ses=AB, tes=AB, rules=()]
  c = d [inner, ses=BC, tes=BC, rules=()]
  b = d [inner, ses=AC, tes=AC, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joining AC
  A C [inner, refs=AC]
  C A [inner, refs=AC]
Joining BC
  B C [inner, refs=BC]
  C B [inner, refs=BC]
Joining ABC
  A BC [inner, refs=AB]
  BC A [inner, refs=AB]
  B AC [inner, refs=AB]
  AC B [inner, refs=AB]
  AB C [inner, refs=BC]
  C AB [inner, refs=BC]
Joins Considered: 12
================================================================================
Final Plan
================================================================================
inner-join (merge)
 ├── scan bx
 ├── inner-join (merge)
 │    ├── scan cy
 │    ├── select
 │    │    ├── scan dz
 │    │    └── filters
 │    │         └── z > 0
 │    └── filters (true)
 └── filters (true)

reorderjoins format=hide-all
SELECT * FROM bx
INNER JOIN cy ON b = c
INNER JOIN (SELECT max(z) AS m FROM dz) ON y = m
INNER JOIN abc ON m = a
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── scan bx
   ├── scan cy
   └── filters
        └── bx.b = cy.c
Vertexes
  A:
    scan bx
  B:
    scan cy
Edges
  bx.b = cy.c [inner, ses=AB, tes=AB, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joins Considered: 2
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── inner-join (hash)
   │    ├── scan bx
   │    ├── scan cy
   │    └── filters
   │         └── bx.b = cy.c
   ├── scalar-group-by
   │    ├── scan dz
   │    └── aggregations
   │         └── max
   │              └── z
   └── filters
        └── y = max
Vertexes
  A:
    scan bx
  B:
    scan cy
  C:
    scalar-group-by
     ├── scan dz
     └── aggregations
          └── max
               └── z
Edges
  bx.b = cy.c [inner, ses=AB, tes=AB, rules=()]
  y = max [inner, ses=BC, tes=BC, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joining BC
  B C [inner, refs=BC]
  C B [inner, refs=BC]
Joining ABC
  A BC [inner, refs=AB]
  BC A [inner, refs=AB]
  AB C [inner, refs=BC]
  C AB [inner, refs=BC]
Joins Considered: 8
--------------------------------------------------------------------------------
Join Tree #3
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── inner-join (hash)
   │    ├── inner-join (hash)
   │    │    ├── scan bx
   │    │    ├── scan cy
   │    │    └── filters
   │    │         └── bx.b = cy.c
   │    ├── scalar-group-by
   │    │    ├── scan dz
   │    │    └── aggregations
   │    │         └── max
   │    │              └── z
   │    └── filters
   │         └── y = max
   ├── scan abc
   └── filters
        └── max = a
Vertexes
  A:
    scan bx
  B:
    scan cy
  C:
    scalar-group-by
     ├── scan dz
     └── aggregations
          └── max
               └── z
  D:
    scan abc
Edges
  bx.b = cy.c [inner, ses=AB, tes=AB, rules=()]
  y = max [inner, ses=BC, tes=BC, rules=()]
  max = a [inner, ses=CD, tes=CD, rules=()]
  y = a [inner, ses=BD, tes=BD, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joining BC
  B C [inner, refs=BC]
  C B [inner, refs=BC]
Joining ABC
  A BC [inner, refs=AB]
  BC A [inner, refs=AB]
  AB C [inner, refs=BC]
  C AB [inner, refs=BC]
Joining BD
  B D [inner, refs=BD]
  D B [inner, refs=BD]
Joining ABD
  A BD [inner, refs=AB]
  BD A [inner, refs=AB]
  AB D [inner, refs=BD]
  D AB [inner, refs=BD]
Joining CD
  C D [inner, refs=CD]
  D C [inner, refs=CD]
Joining BCD
  B CD [inner, refs=BC]
  CD B [inner, refs=BC]
  C BD [inner, refs=BC]
  BD C [inner, refs=BC]
  BC D [inner, refs=CD]
  D BC [inner, refs=CD]
Joining ABCD
  A BCD [inner, refs=AB]
  BCD A [inner, refs=AB]
  AB CD [inner, refs=BC]
  CD AB [inner, refs=BC]
  C ABD [inner, refs=BC]
  ABD C [inner, refs=BC]
  ABC D [inner, refs=CD]
  D ABC [inner, refs=CD]
Joins Considered: 30
================================================================================
Final Plan
================================================================================
inner-join (lookup bx)
 ├── lookup columns are key
 ├── inner-join (hash)
 │    ├── scan cy
 │    ├── inner-join (lookup abc)
 │    │    ├── lookup columns are key
 │    │    ├── scalar-group-by
 │    │    │    ├── scan dz
 │    │    │    └── aggregations
 │    │    │         └── max
 │    │    │              └── z
 │    │    └── filters (true)
 │    └── filters
 │         └── y = max
 └── filters (true)

# Treat the join with hints as a base relation. Note that the implicit edges
# x = a and y = a are added to the join graph. The x = y filter is not an edge
# because it comes from the join with hints. However, it can still be used in
# calculating transitive closure.
reorderjoins format=hide-all
SELECT * FROM bx
INNER HASH JOIN cy ON x = y
INNER JOIN dz ON y = z
INNER JOIN abc ON z = a
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── inner-join (hash)
   │    ├── flags: force hash join (store right side)
   │    ├── scan bx
   │    ├── scan cy
   │    └── filters
   │         └── x = y
   ├── scan dz
   └── filters
        └── y = z
Vertexes
  A:
    inner-join (hash)
     ├── flags: force hash join (store right side)
     ├── scan bx
     ├── scan cy
     └── filters
          └── x = y
  B:
    scan dz
Edges
  y = z [inner, ses=AB, tes=AB, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joins Considered: 2
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── inner-join (hash)
   │    ├── inner-join (hash)
   │    │    ├── flags: force hash join (store right side)
   │    │    ├── scan bx
   │    │    ├── scan cy
   │    │    └── filters
   │    │         └── x = y
   │    ├── scan dz
   │    └── filters
   │         └── y = z
   ├── scan abc
   └── filters
        └── z = a
Vertexes
  A:
    inner-join (hash)
     ├── flags: force hash join (store right side)
     ├── scan bx
     ├── scan cy
     └── filters
          └── x = y
  B:
    scan dz
  C:
    scan abc
Edges
  y = z [inner, ses=AB, tes=AB, rules=()]
  z = a [inner, ses=BC, tes=BC, rules=()]
  x = z [inner, ses=AB, tes=AB, rules=()]
  x = a [inner, ses=AC, tes=AC, rules=()]
  y = a [inner, ses=AC, tes=AC, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joining AC
  A C [inner, refs=AC]
  C A [inner, refs=AC]
Joining BC
  B C [inner, refs=BC]
  C B [inner, refs=BC]
Joining ABC
  A BC [inner, refs=AB]
  BC A [inner, refs=AB]
  B AC [inner, refs=AB]
  AC B [inner, refs=AB]
  AB C [inner, refs=BC]
  C AB [inner, refs=BC]
Joins Considered: 12
================================================================================
Final Plan
================================================================================
inner-join (hash)
 ├── inner-join (hash)
 │    ├── flags: force hash join (store right side)
 │    ├── scan bx
 │    ├── scan cy
 │    └── filters
 │         └── x = y
 ├── inner-join (hash)
 │    ├── scan dz
 │    ├── scan abc
 │    └── filters
 │         └── z = a
 └── filters
      └── y = z

# Ignore the apply join. However, all other joins can be reordered despite the
# presence of outer columns.
reorderjoins format=hide-all
SELECT * FROM bx
INNER JOIN LATERAL
(
  SELECT * FROM (VALUES (x))
  INNER JOIN cy ON True
  INNER JOIN dz ON y = z
  INNER JOIN abc ON z = a
)
ON x = y
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── scan cy
   ├── scan dz
   └── filters
        └── y = z
Vertexes
  A:
    scan cy
  B:
    scan dz
Edges
  y = z [inner, ses=AB, tes=AB, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joins Considered: 2
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── inner-join (hash)
   │    ├── scan cy
   │    ├── scan dz
   │    └── filters
   │         └── y = z
   ├── scan abc
   └── filters
        └── z = a
Vertexes
  A:
    scan cy
  B:
    scan dz
  C:
    scan abc
Edges
  y = z [inner, ses=AB, tes=AB, rules=()]
  z = a [inner, ses=BC, tes=BC, rules=()]
  y = a [inner, ses=AC, tes=AC, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joining AC
  A C [inner, refs=AC]
  C A [inner, refs=AC]
Joining BC
  B C [inner, refs=BC]
  C B [inner, refs=BC]
Joining ABC
  A BC [inner, refs=AB]
  BC A [inner, refs=AB]
  B AC [inner, refs=AB]
  AC B [inner, refs=AB]
  AB C [inner, refs=BC]
  C AB [inner, refs=BC]
Joins Considered: 12
--------------------------------------------------------------------------------
Join Tree #3
--------------------------------------------------------------------------------
  inner-join (cross)
   ├── values
   │    └── (x,)
   ├── inner-join (hash)
   │    ├── inner-join (hash)
   │    │    ├── scan cy
   │    │    ├── scan dz
   │    │    └── filters
   │    │         └── y = z
   │    ├── scan abc
   │    └── filters
   │         └── z = a
   └── filters (true)
Vertexes
  D:
    values
     └── (x,)
  A:
    scan cy
  B:
    scan dz
  C:
    scan abc
Edges
  y = z [inner, ses=AB, tes=AB, rules=()]
  z = a [inner, ses=BC, tes=BC, rules=()]
  cross [inner, ses=, tes=DABC, rules=()]
  y = a [inner, ses=AC, tes=AC, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joining AC
  A C [inner, refs=AC]
  C A [inner, refs=AC]
Joining BC
  B C [inner, refs=BC]
  C B [inner, refs=BC]
Joining ABC
  A BC [inner, refs=AB]
  BC A [inner, refs=AB]
  B AC [inner, refs=AB]
  AC B [inner, refs=AB]
  AB C [inner, refs=BC]
  C AB [inner, refs=BC]
Joining DABC
  D ABC [inner, refs=]
  ABC D [inner, refs=]
Joins Considered: 14
================================================================================
Final Plan
================================================================================
inner-join-apply
 ├── scan bx
 ├── inner-join (cross)
 │    ├── inner-join (hash)
 │    │    ├── scan cy
 │    │    ├── inner-join (hash)
 │    │    │    ├── scan dz
 │    │    │    ├── scan abc
 │    │    │    └── filters
 │    │    │         └── z = a
 │    │    └── filters
 │    │         └── y = z
 │    ├── values
 │    │    └── (x,)
 │    └── filters (true)
 └── filters
      └── x = y

reorderjoins format=hide-all
SELECT * FROM
(
  SELECT b AS bx_b, c AS cy_c
  FROM bx
  INNER JOIN cy ON x = y
)
INNER JOIN
(
  SELECT b AS abc_b, c AS abc_c
  FROM abc
  INNER JOIN dz ON a = z
)
ON abc_b = bx_b AND abc_c = cy_c
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── scan bx
   ├── scan cy
   └── filters
        └── x = y
Vertexes
  A:
    scan bx
  B:
    scan cy
Edges
  x = y [inner, ses=AB, tes=AB, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joins Considered: 2
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── scan abc
   ├── scan dz
   └── filters
        └── a = z
Vertexes
  C:
    scan abc
  D:
    scan dz
Edges
  a = z [inner, ses=CD, tes=CD, rules=()]
Joining CD
  C D [inner, refs=CD]
  D C [inner, refs=CD]
Joins Considered: 2
--------------------------------------------------------------------------------
Join Tree #3
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── inner-join (hash)
   │    ├── scan bx
   │    ├── scan cy
   │    └── filters
   │         └── x = y
   ├── inner-join (hash)
   │    ├── scan abc
   │    ├── scan dz
   │    └── filters
   │         └── a = z
   └── filters
        ├── abc.b = bx.b
        └── abc.c = cy.c
Vertexes
  A:
    scan bx
  B:
    scan cy
  C:
    scan abc
  D:
    scan dz
Edges
  x = y [inner, ses=AB, tes=AB, rules=()]
  a = z [inner, ses=CD, tes=CD, rules=()]
  abc.b = bx.b [inner, ses=AC, tes=AC, rules=()]
  abc.c = cy.c [inner, ses=BC, tes=BC, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joining AC
  A C [inner, refs=AC]
  C A [inner, refs=AC]
Joining BC
  B C [inner, refs=BC]
  C B [inner, refs=BC]
Joining ABC
  A BC [inner, refs=ABC]
  BC A [inner, refs=ABC]
  B AC [inner, refs=ABC]
  AC B [inner, refs=ABC]
  AB C [inner, refs=ABC]
  C AB [inner, refs=ABC]
Joining CD
  C D [inner, refs=CD]
  D C [inner, refs=CD]
Joining ACD
  A CD [inner, refs=AC]
  CD A [inner, refs=AC]
  AC D [inner, refs=CD]
  D AC [inner, refs=CD]
Joining BCD
  B CD [inner, refs=BC]
  CD B [inner, refs=BC]
  BC D [inner, refs=CD]
  D BC [inner, refs=CD]
Joining ABCD
  A BCD [inner, refs=ABC]
  BCD A [inner, refs=ABC]
  B ACD [inner, refs=ABC]
  ACD B [inner, refs=ABC]
  AB CD [inner, refs=ABC]
  CD AB [inner, refs=ABC]
  ABC D [inner, refs=CD]
  D ABC [inner, refs=CD]
Joins Considered: 30
================================================================================
Final Plan
================================================================================
project
 └── inner-join (hash)
      ├── scan dz
      ├── inner-join (hash)
      │    ├── scan bx
      │    ├── inner-join (hash)
      │    │    ├── scan cy
      │    │    ├── scan abc
      │    │    └── filters
      │    │         └── abc.c = cy.c
      │    └── filters
      │         ├── x = y
      │         └── abc.b = bx.b
      └── filters
           └── a = z

reorderjoins format=hide-all
SELECT *
FROM bx
INNER JOIN cy ON b = c
LEFT JOIN dz ON x = z
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── scan bx
   ├── scan cy
   └── filters
        └── b = c
Vertexes
  A:
    scan bx
  B:
    scan cy
Edges
  b = c [inner, ses=AB, tes=AB, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joins Considered: 2
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  left-join (hash)
   ├── inner-join (hash)
   │    ├── scan bx
   │    ├── scan cy
   │    └── filters
   │         └── b = c
   ├── scan dz
   └── filters
        └── x = z
Vertexes
  A:
    scan bx
  B:
    scan cy
  C:
    scan dz
Edges
  b = c [inner, ses=AB, tes=AB, rules=()]
  x = z [left, ses=AC, tes=AC, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joining AC
  A C [left, refs=AC]
Joining ABC
  B AC [inner, refs=AB]
  AC B [inner, refs=AB]
  AB C [left, refs=AC]
Joins Considered: 6
================================================================================
Final Plan
================================================================================
left-join (hash)
 ├── inner-join (merge)
 │    ├── scan bx
 │    ├── scan cy
 │    └── filters (true)
 ├── scan dz
 └── filters
      └── x = z

# Left-asscom property for left joins.
reorderjoins format=hide-all
SELECT *
FROM bx
LEFT JOIN cy ON b = c
LEFT JOIN dz ON x = z
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  left-join (hash)
   ├── scan bx
   ├── scan cy
   └── filters
        └── b = c
Vertexes
  A:
    scan bx
  B:
    scan cy
Edges
  b = c [left, ses=AB, tes=AB, rules=()]
Joining AB
  A B [left, refs=AB]
Joins Considered: 1
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  left-join (hash)
   ├── left-join (hash)
   │    ├── scan bx
   │    ├── scan cy
   │    └── filters
   │         └── b = c
   ├── scan dz
   └── filters
        └── x = z
Vertexes
  A:
    scan bx
  B:
    scan cy
  C:
    scan dz
Edges
  b = c [left, ses=AB, tes=AB, rules=()]
  x = z [left, ses=AC, tes=AC, rules=()]
Joining AB
  A B [left, refs=AB]
Joining AC
  A C [left, refs=AC]
Joining ABC
  AC B [left, refs=AB]
  AB C [left, refs=AC]
Joins Considered: 4
================================================================================
Final Plan
================================================================================
left-join (hash)
 ├── left-join (merge)
 │    ├── scan bx
 │    ├── scan cy
 │    └── filters (true)
 ├── scan dz
 └── filters
      └── x = z

# Left-asscom property does not apply when the upper left join references the
# right side of the lower. However, associative property does apply when the
# predicate of the upper join rejects nulls on the right input of the lower
# join.
reorderjoins format=hide-all
SELECT *
FROM bx
LEFT JOIN cy ON b = c
LEFT JOIN dz ON y = z
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  left-join (hash)
   ├── scan bx
   ├── scan cy
   └── filters
        └── b = c
Vertexes
  A:
    scan bx
  B:
    scan cy
Edges
  b = c [left, ses=AB, tes=AB, rules=()]
Joining AB
  A B [left, refs=AB]
Joins Considered: 1
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  left-join (hash)
   ├── left-join (hash)
   │    ├── scan bx
   │    ├── scan cy
   │    └── filters
   │         └── b = c
   ├── scan dz
   └── filters
        └── y = z
Vertexes
  A:
    scan bx
  B:
    scan cy
  C:
    scan dz
Edges
  b = c [left, ses=AB, tes=AB, rules=()]
  y = z [left, ses=BC, tes=BC, rules=()]
Joining AB
  A B [left, refs=AB]
Joining BC
  B C [left, refs=BC]
Joining ABC
  A BC [left, refs=AB]
  AB C [left, refs=BC]
Joins Considered: 4
================================================================================
Final Plan
================================================================================
left-join (hash)
 ├── left-join (merge)
 │    ├── scan bx
 │    ├── scan cy
 │    └── filters (true)
 ├── scan dz
 └── filters
      └── y = z

# Left, semi and anti join tree. Multiple applications of left-asscom are
# possible. Note that the inner join results from commuting the semi join.
reorderjoins format=hide-all
SELECT *
FROM bx
LEFT JOIN cy ON bx.b = cy.c
WHERE
EXISTS (SELECT * FROM dz WHERE bx.b = dz.d) AND
NOT EXISTS (SELECT * FROM abc WHERE bx.b = abc.a)
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  anti-join (hash)
   ├── scan bx
   ├── scan abc
   └── filters
        └── bx.b = a
Vertexes
  A:
    scan bx
  B:
    scan abc
Edges
  bx.b = a [anti, ses=AB, tes=AB, rules=()]
Joining AB
  A B [anti, refs=AB]
Joins Considered: 1
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  semi-join (hash)
   ├── anti-join (hash)
   │    ├── scan bx
   │    ├── scan abc
   │    └── filters
   │         └── bx.b = a
   ├── scan dz
   └── filters
        └── bx.b = dz.d
Vertexes
  A:
    scan bx
  B:
    scan abc
  C:
    scan dz
Edges
  bx.b = a [anti, ses=AB, tes=AB, rules=()]
  bx.b = dz.d [semi, ses=AC, tes=AC, rules=()]
Joining AB
  A B [anti, refs=AB]
Joining AC
  A C [semi, refs=AC]
Joining ABC
  AC B [anti, refs=AB]
  AB C [semi, refs=AC]
Joins Considered: 4
--------------------------------------------------------------------------------
Join Tree #3
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── anti-join (hash)
   │    ├── scan bx
   │    ├── scan abc
   │    └── filters
   │         └── bx.b = a
   ├── scan dz
   └── filters
        └── bx.b = dz.d
Vertexes
  A:
    scan bx
  B:
    scan abc
  C:
    scan dz
Edges
  bx.b = a [anti, ses=AB, tes=AB, rules=()]
  bx.b = dz.d [inner, ses=AC, tes=AC, rules=()]
Joining AB
  A B [anti, refs=AB]
Joining AC
  A C [inner, refs=AC]
  C A [inner, refs=AC]
Joining ABC
  AC B [anti, refs=AB]
  AB C [inner, refs=AC]
  C AB [inner, refs=AC]
Joins Considered: 6
--------------------------------------------------------------------------------
Join Tree #4
--------------------------------------------------------------------------------
  left-join (hash)
   ├── semi-join (hash)
   │    ├── anti-join (hash)
   │    │    ├── scan bx
   │    │    ├── scan abc
   │    │    └── filters
   │    │         └── bx.b = a
   │    ├── scan dz
   │    └── filters
   │         └── bx.b = dz.d
   ├── scan cy
   └── filters
        └── bx.b = cy.c
Vertexes
  A:
    scan bx
  B:
    scan abc
  C:
    scan dz
  D:
    scan cy
Edges
  bx.b = a [anti, ses=AB, tes=AB, rules=()]
  bx.b = dz.d [semi, ses=AC, tes=AC, rules=()]
  bx.b = cy.c [left, ses=AD, tes=AD, rules=()]
Joining AB
  A B [anti, refs=AB]
Joining AC
  A C [semi, refs=AC]
Joining ABC
  AC B [anti, refs=AB]
  AB C [semi, refs=AC]
Joining AD
  A D [left, refs=AD]
Joining ABD
  AD B [anti, refs=AB]
  AB D [left, refs=AD]
Joining ACD
  AD C [semi, refs=AC]
  AC D [left, refs=AD]
Joining ABCD
  ACD B [anti, refs=AB]
  ABD C [semi, refs=AC]
  ABC D [left, refs=AD]
Joins Considered: 12
================================================================================
Final Plan
================================================================================
semi-join (lookup dz)
 ├── lookup columns are key
 ├── left-join (lookup cy)
 │    ├── lookup columns are key
 │    ├── anti-join (merge)
 │    │    ├── scan bx
 │    │    ├── scan abc
 │    │    └── filters (true)
 │    └── filters (true)
 └── filters (true)

# Join tree with only cross joins.
reorderjoins format=hide-all
SELECT * FROM bx
INNER JOIN cy ON True
INNER JOIN dz ON True
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  inner-join (cross)
   ├── scan bx
   ├── scan cy
   └── filters (true)
Vertexes
  A:
    scan bx
  B:
    scan cy
Edges
  cross [inner, ses=, tes=AB, rules=()]
Joining AB
  A B [inner, refs=]
  B A [inner, refs=]
Joins Considered: 2
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  inner-join (cross)
   ├── inner-join (cross)
   │    ├── scan bx
   │    ├── scan cy
   │    └── filters (true)
   ├── scan dz
   └── filters (true)
Vertexes
  A:
    scan bx
  B:
    scan cy
  C:
    scan dz
Edges
  cross [inner, ses=, tes=AB, rules=()]
  cross [inner, ses=, tes=ABC, rules=()]
Joining AB
  A B [inner, refs=]
  B A [inner, refs=]
Joining ABC
  AB C [inner, refs=]
  C AB [inner, refs=]
Joins Considered: 4
================================================================================
Final Plan
================================================================================
inner-join (cross)
 ├── inner-join (cross)
 │    ├── scan bx
 │    ├── scan cy
 │    └── filters (true)
 ├── scan dz
 └── filters (true)

reorderjoins format=hide-all
SELECT *
FROM bx
FULL JOIN cy ON b = c
FULL JOIN dz ON y = z
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  full-join (hash)
   ├── scan bx
   ├── scan cy
   └── filters
        └── b = c
Vertexes
  A:
    scan bx
  B:
    scan cy
Edges
  b = c [full, ses=AB, tes=AB, rules=()]
Joining AB
  A B [full, refs=AB]
  B A [full, refs=AB]
Joins Considered: 2
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  full-join (hash)
   ├── full-join (hash)
   │    ├── scan bx
   │    ├── scan cy
   │    └── filters
   │         └── b = c
   ├── scan dz
   └── filters
        └── y = z
Vertexes
  A:
    scan bx
  B:
    scan cy
  C:
    scan dz
Edges
  b = c [full, ses=AB, tes=AB, rules=()]
  y = z [full, ses=BC, tes=BC, rules=()]
Joining AB
  A B [full, refs=AB]
  B A [full, refs=AB]
Joining BC
  B C [full, refs=BC]
  C B [full, refs=BC]
Joining ABC
  A BC [full, refs=AB]
  BC A [full, refs=AB]
  AB C [full, refs=BC]
  C AB [full, refs=BC]
Joins Considered: 8
================================================================================
Final Plan
================================================================================
full-join (hash)
 ├── full-join (merge)
 │    ├── scan bx
 │    ├── scan cy
 │    └── filters (true)
 ├── scan dz
 └── filters
      └── y = z

exec-ddl
CREATE TABLE abc2 (
  a INT PRIMARY KEY,
  b INT,
  c INT,
  d INT
)
----

exec-ddl
CREATE TABLE abc3 (
  a INT PRIMARY KEY,
  b INT,
  c INT,
  d INT
)
----

exec-ddl
CREATE TABLE abc4 (
  a INT PRIMARY KEY,
  b INT,
  c INT,
  d INT
)
----

exec-ddl
CREATE TABLE abc5 (
  a INT PRIMARY KEY,
  b INT,
  c INT,
  d INT
)
----

# Iteratively reorder subtrees of up to size 2.
reorderjoins set=reorder_joins_limit=2 format=hide-all
SELECT * FROM abc AS a1
INNER JOIN abc2 AS a2 ON a1.a = a2.a
LEFT JOIN abc3 AS a3 ON a2.b = a3.b
INNER JOIN abc4 AS a4 ON a3.a = a4.a
WHERE EXISTS (SELECT * FROM abc5 AS a5 WHERE a2.c = a5.c)
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  semi-join (hash)
   ├── scan abc2 [as=a2]
   ├── scan abc5 [as=a5]
   └── filters
        └── a2.c = a5.c
Vertexes
  A:
    scan abc2 [as=a2]
  B:
    scan abc5 [as=a5]
Edges
  a2.c = a5.c [semi, ses=AB, tes=AB, rules=()]
Joining AB
  A B [semi, refs=AB]
Joins Considered: 1
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── scan abc2 [as=a2]
   ├── distinct-on
   │    └── scan abc5 [as=a5]
   └── filters
        └── a2.c = a5.c
Vertexes
  A:
    scan abc2 [as=a2]
  C:
    distinct-on
     └── scan abc5 [as=a5]
Edges
  a2.c = a5.c [inner, ses=AC, tes=AC, rules=()]
Joining AC
  A C [inner, refs=AC]
  C A [inner, refs=AC]
Joins Considered: 2
--------------------------------------------------------------------------------
Join Tree #3
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── scan abc [as=a1]
   ├── semi-join (hash)
   │    ├── scan abc2 [as=a2]
   │    ├── scan abc5 [as=a5]
   │    └── filters
   │         └── a2.c = a5.c
   └── filters
        └── a1.a = a2.a
Vertexes
  D:
    scan abc [as=a1]
  A:
    scan abc2 [as=a2]
  B:
    scan abc5 [as=a5]
Edges
  a2.c = a5.c [semi, ses=AB, tes=AB, rules=()]
  a1.a = a2.a [inner, ses=DA, tes=DA, rules=()]
Joining DA
  D A [inner, refs=DA]
  A D [inner, refs=DA]
Joining AB
  A B [semi, refs=AB]
Joining DAB
  D AB [inner, refs=DA]
  AB D [inner, refs=DA]
  DA B [semi, refs=AB]
Joins Considered: 6
--------------------------------------------------------------------------------
Join Tree #4
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── inner-join (hash)
   │    ├── scan abc [as=a1]
   │    ├── semi-join (hash)
   │    │    ├── scan abc2 [as=a2]
   │    │    ├── scan abc5 [as=a5]
   │    │    └── filters
   │    │         └── a2.c = a5.c
   │    └── filters
   │         └── a1.a = a2.a
   ├── scan abc3 [as=a3]
   └── filters
        └── a2.b = a3.b
Vertexes
  D:
    scan abc [as=a1]
  E:
    semi-join (hash)
     ├── scan abc2 [as=a2]
     ├── scan abc5 [as=a5]
     └── filters
          └── a2.c = a5.c
  F:
    scan abc3 [as=a3]
Edges
  a1.a = a2.a [inner, ses=DE, tes=DE, rules=()]
  a2.b = a3.b [inner, ses=EF, tes=EF, rules=()]
Joining DE
  D E [inner, refs=DE]
  E D [inner, refs=DE]
Joining EF
  E F [inner, refs=EF]
  F E [inner, refs=EF]
Joining DEF
  D EF [inner, refs=DE]
  EF D [inner, refs=DE]
  DE F [inner, refs=EF]
  F DE [inner, refs=EF]
Joins Considered: 8
--------------------------------------------------------------------------------
Join Tree #5
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── inner-join (hash)
   │    ├── inner-join (hash)
   │    │    ├── scan abc [as=a1]
   │    │    ├── semi-join (hash)
   │    │    │    ├── scan abc2 [as=a2]
   │    │    │    ├── scan abc5 [as=a5]
   │    │    │    └── filters
   │    │    │         └── a2.c = a5.c
   │    │    └── filters
   │    │         └── a1.a = a2.a
   │    ├── scan abc3 [as=a3]
   │    └── filters
   │         └── a2.b = a3.b
   ├── scan abc4 [as=a4]
   └── filters
        └── a3.a = a4.a
Vertexes
  G:
    inner-join (hash)
     ├── scan abc [as=a1]
     ├── semi-join (hash)
     │    ├── scan abc2 [as=a2]
     │    ├── scan abc5 [as=a5]
     │    └── filters
     │         └── a2.c = a5.c
     └── filters
          └── a1.a = a2.a
  F:
    scan abc3 [as=a3]
  H:
    scan abc4 [as=a4]
Edges
  a2.b = a3.b [inner, ses=GF, tes=GF, rules=()]
  a3.a = a4.a [inner, ses=FH, tes=FH, rules=()]
Joining GF
  G F [inner, refs=GF]
  F G [inner, refs=GF]
Joining FH
  F H [inner, refs=FH]
  H F [inner, refs=FH]
Joining GFH
  G FH [inner, refs=GF]
  FH G [inner, refs=GF]
  GF H [inner, refs=FH]
  H GF [inner, refs=FH]
Joins Considered: 8
================================================================================
Final Plan
================================================================================
inner-join (hash)
 ├── project
 │    └── inner-join (hash)
 │         ├── inner-join (merge)
 │         │    ├── scan abc [as=a1]
 │         │    ├── scan abc2 [as=a2]
 │         │    └── filters (true)
 │         ├── distinct-on
 │         │    └── scan abc5 [as=a5]
 │         └── filters
 │              └── a2.c = a5.c
 ├── inner-join (merge)
 │    ├── scan abc3 [as=a3]
 │    ├── scan abc4 [as=a4]
 │    └── filters (true)
 └── filters
      └── a2.b = a3.b

# Reorder with a conflict rule.
reorderjoins
SELECT * FROM
(
   SELECT * FROM abc
   LEFT JOIN bx ON a = x
   WHERE EXISTS (SELECT * FROM cy WHERE x = y)
)
INNER JOIN dz ON a = z
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── scan abc
   ├── select
   │    ├── scan bx
   │    └── filters
   │         └── x IS NOT NULL
   └── filters
        └── a = x
Vertexes
  A:
    scan abc
  B:
    select
     ├── scan bx
     └── filters
          └── x IS NOT NULL
Edges
  a = x [inner, ses=AB, tes=AB, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joins Considered: 2
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  semi-join (hash)
   ├── inner-join (hash)
   │    ├── scan abc
   │    ├── select
   │    │    ├── scan bx
   │    │    └── filters
   │    │         └── x IS NOT NULL
   │    └── filters
   │         └── a = x
   ├── scan cy
   └── filters
        └── x = y
Vertexes
  A:
    scan abc
  B:
    select
     ├── scan bx
     └── filters
          └── x IS NOT NULL
  C:
    scan cy
Edges
  a = x [inner, ses=AB, tes=AB, rules=()]
  x = y [semi, ses=BC, tes=BC, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joining BC
  B C [semi, refs=BC]
Joining ABC
  A BC [inner, refs=AB]
  BC A [inner, refs=AB]
  AB C [semi, refs=BC]
Joins Considered: 6
--------------------------------------------------------------------------------
Join Tree #3
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── inner-join (hash)
   │    ├── scan abc
   │    ├── select
   │    │    ├── scan bx
   │    │    └── filters
   │    │         └── x IS NOT NULL
   │    └── filters
   │         └── a = x
   ├── distinct-on
   │    └── scan cy
   └── filters
        └── x = y
Vertexes
  A:
    scan abc
  B:
    select
     ├── scan bx
     └── filters
          └── x IS NOT NULL
  D:
    distinct-on
     └── scan cy
Edges
  a = x [inner, ses=AB, tes=AB, rules=()]
  x = y [inner, ses=BD, tes=BD, rules=()]
  a = y [inner, ses=AD, tes=AD, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joining AD
  A D [inner, refs=AD]
  D A [inner, refs=AD]
Joining BD
  B D [inner, refs=BD]
  D B [inner, refs=BD]
Joining ABD
  A BD [inner, refs=AB]
  BD A [inner, refs=AB]
  B AD [inner, refs=AB]
  AD B [inner, refs=AB]
  AB D [inner, refs=BD]
  D AB [inner, refs=BD]
Joins Considered: 12
--------------------------------------------------------------------------------
Join Tree #4
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── semi-join (hash)
   │    ├── inner-join (hash)
   │    │    ├── scan abc
   │    │    ├── select
   │    │    │    ├── scan bx
   │    │    │    └── filters
   │    │    │         └── x IS NOT NULL
   │    │    └── filters
   │    │         └── a = x
   │    ├── scan cy
   │    └── filters
   │         └── x = y
   ├── scan dz
   └── filters
        └── a = z
Vertexes
  A:
    scan abc
  B:
    select
     ├── scan bx
     └── filters
          └── x IS NOT NULL
  C:
    scan cy
  E:
    scan dz
Edges
  a = x [inner, ses=AB, tes=AB, rules=()]
  x = y [semi, ses=BC, tes=BC, rules=()]
  a = z [inner, ses=AE, tes=AE, rules=(C->B)]
  x = z [inner, ses=BE, tes=BE, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joining BC
  B C [semi, refs=BC]
Joining ABC
  A BC [inner, refs=AB]
  BC A [inner, refs=AB]
  AB C [semi, refs=BC]
Joining AE
  A E [inner, refs=AE]
  E A [inner, refs=AE]
Joining BE
  B E [inner, refs=BE]
  E B [inner, refs=BE]
Joining ABE
  A BE [inner, refs=AB]
  BE A [inner, refs=AB]
  B AE [inner, refs=AB]
  AE B [inner, refs=AB]
  AB E [inner, refs=AE]
  E AB [inner, refs=AE]
Joining BCE
  BE C [semi, refs=BC]
  BC E [inner, refs=BE]
  E BC [inner, refs=BE]
Joining ABCE
  A BCE [inner, refs=AB]
  BCE A [inner, refs=AB]
  ABE C [semi, refs=BC]
  BC AE [inner, refs=AB]
  AE BC [inner, refs=AB]
  ABC E [inner, refs=AE]
  E ABC [inner, refs=AE]
Joins Considered: 26
================================================================================
Final Plan
================================================================================
inner-join (hash)
 ├── columns: a:1!null b:2 c:3 d:4 b:7!null x:8!null d:16!null z:17!null
 ├── key: (7,16)
 ├── fd: (1)-->(2-4), (7)-->(8), (16)-->(17), (1)==(8,17), (8)==(1,17), (17)==(1,8)
 ├── scan dz
 │    ├── columns: dz.d:16!null z:17
 │    ├── key: (16)
 │    └── fd: (16)-->(17)
 ├── project
 │    ├── columns: a:1!null abc.b:2 abc.c:3 abc.d:4 bx.b:7!null x:8!null
 │    ├── key: (7)
 │    ├── fd: (1)-->(2-4), (7)-->(8), (1)==(8), (8)==(1)
 │    └── inner-join (hash)
 │         ├── columns: a:1!null abc.b:2 abc.c:3 abc.d:4 bx.b:7!null x:8!null y:12!null
 │         ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more)
 │         ├── key: (7)
 │         ├── fd: (1)-->(2-4), (7)-->(8), (1)==(8,12), (8)==(1,12), (12)==(1,8)
 │         ├── select
 │         │    ├── columns: bx.b:7!null x:8!null
 │         │    ├── key: (7)
 │         │    ├── fd: (7)-->(8)
 │         │    ├── scan bx
 │         │    │    ├── columns: bx.b:7!null x:8
 │         │    │    ├── key: (7)
 │         │    │    └── fd: (7)-->(8)
 │         │    └── filters
 │         │         └── x:8 IS NOT NULL [outer=(8), constraints=(/8: (/NULL - ]; tight)]
 │         ├── inner-join (lookup abc)
 │         │    ├── columns: a:1!null abc.b:2 abc.c:3 abc.d:4 y:12!null
 │         │    ├── key columns: [12] = [1]
 │         │    ├── lookup columns are key
 │         │    ├── key: (12)
 │         │    ├── fd: (1)-->(2-4), (1)==(12), (12)==(1)
 │         │    ├── distinct-on
 │         │    │    ├── columns: y:12
 │         │    │    ├── grouping columns: y:12
 │         │    │    ├── key: (12)
 │         │    │    └── scan cy
 │         │    │         └── columns: y:12
 │         │    └── filters (true)
 │         └── filters
 │              └── a:1 = x:8 [outer=(1,8), constraints=(/1: (/NULL - ]; /8: (/NULL - ]), fd=(1)==(8), (8)==(1)]
 └── filters
      └── a:1 = z:17 [outer=(1,17), constraints=(/1: (/NULL - ]; /17: (/NULL - ]), fd=(1)==(17), (17)==(1)]


# Special case for reordering inner-joins and left-joins: One inner-join
# conjunct can be pushed into the left side of the left-join, and the other
# remains above the left-join on a select operator. The 'OR z IS NULL' expression
# prevents the left join from being null-rejected.
reorderjoins
SELECT * FROM bx
INNER JOIN
(
  SELECT * FROM cy
  LEFT JOIN dz
  ON c = d
)
ON b = c AND (x = z OR z IS NULL)
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  left-join (hash)
   ├── scan cy
   ├── scan dz
   └── filters
        └── c = d
Vertexes
  A:
    scan cy
  B:
    scan dz
Edges
  c = d [left, ses=AB, tes=AB, rules=()]
Joining AB
  A B [left, refs=AB]
Joins Considered: 1
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── scan bx
   ├── left-join (hash)
   │    ├── scan cy
   │    ├── scan dz
   │    └── filters
   │         └── c = d
   └── filters
        ├── b = c
        └── (x = z) OR (z IS NULL)
Vertexes
  C:
    scan bx
  A:
    scan cy
  B:
    scan dz
Edges
  c = d [left, ses=AB, tes=AB, rules=()]
  b = c [inner, ses=CA, tes=CA, rules=()]
  (x = z) OR (z IS NULL) [inner, ses=CB, tes=CAB, rules=()]
Joining CA
  C A [inner, refs=CA]
  A C [inner, refs=CA]
Joining AB
  A B [left, refs=AB]
Joining CAB
  C AB [inner, refs=CAB]
  AB C [inner, refs=CAB]
  CA B [left, refs=AB] [select, refs=CB]
Joins Considered: 6
================================================================================
Final Plan
================================================================================
inner-join (merge)
 ├── columns: b:1!null x:2 c:5!null y:6 d:9 z:10
 ├── left ordering: +1
 ├── right ordering: +5
 ├── key: (5)
 ├── fd: (1)-->(2), (5)-->(6,9,10), (9)-->(10), (1)==(5), (5)==(1)
 ├── scan bx
 │    ├── columns: b:1!null x:2
 │    ├── key: (1)
 │    ├── fd: (1)-->(2)
 │    └── ordering: +1
 ├── left-join (merge)
 │    ├── columns: c:5!null y:6 d:9 z:10
 │    ├── left ordering: +5
 │    ├── right ordering: +9
 │    ├── key: (5)
 │    ├── fd: (5)-->(6,9,10), (9)-->(10)
 │    ├── ordering: +5
 │    ├── scan cy
 │    │    ├── columns: c:5!null y:6
 │    │    ├── key: (5)
 │    │    ├── fd: (5)-->(6)
 │    │    └── ordering: +5
 │    ├── scan dz
 │    │    ├── columns: d:9!null z:10
 │    │    ├── key: (9)
 │    │    ├── fd: (9)-->(10)
 │    │    └── ordering: +9
 │    └── filters (true)
 └── filters
      └── (x:2 = z:10) OR (z:10 IS NULL) [outer=(2,10)]

# Regression test for #59076. Do not reorder on the inner join produced by
# CommuteSemiJoin when it matches on an already-reordered semi join because
# doing so can lead to an exponential blowup in the size of the memo.
check-size rule-limit=200 group-limit=100
SELECT * FROM cy
WHERE EXISTS (SELECT 1 FROM dz WHERE z = y)
AND EXISTS (SELECT 1 FROM bx WHERE x = y)
AND EXISTS (SELECT 1 FROM abc WHERE a = y)
----
Rules Applied: 134
Groups Added: 73


# Regression test for #76522. Do not produce query plans where some of the
# original filters have been omitted.

exec-ddl
CREATE TABLE t76522_1 (
  a INT NOT NULL,
  b INT NOT NULL,
  PRIMARY KEY (a ASC, b ASC)
)
----

exec-ddl
CREATE TABLE t76522_2 (
  a INT NOT NULL,
  c INT,
  should_not_be_eliminated INT,
  PRIMARY KEY (a ASC)
)
----

exec-ddl
CREATE TABLE t76522_3 (
  a INT NOT NULL,
  d INT NOT NULL,
  f INT,
  g INT,
  PRIMARY KEY (a ASC, d ASC)
)
----

exec-ddl
CREATE TABLE t76522_4 (
  e INT NOT NULL,
  f INT,
  g INT,
  PRIMARY KEY (e ASC)
)
----

exec-ddl
CREATE TABLE t76522_5 (
  h INT NOT NULL,
  f INT NOT NULL,
  g INT NOT NULL,
  b INT,
  should_not_be_eliminated INT,
  c INT,
  PRIMARY KEY (h ASC, f ASC, g ASC)
)
----

# Give t76522_1 many rows where a has many distincts.
exec-ddl
ALTER TABLE t76522_1 INJECT STATISTICS '[
    {
        "columns": [
            "a"
        ],
        "created_at": "2022-01-17 12:51:38.433911",
        "distinct_count": 9161427,
        "null_count": 0,
        "row_count": 44484238
    }
]'
----

# Give t76522_2 many rows where a has many distincts.
exec-ddl
ALTER TABLE t76522_2 INJECT STATISTICS '[
    {
        "columns": [
            "a"
        ],
        "created_at": "2022-01-17 12:51:38.433911",
        "distinct_count": 17014025,
        "null_count": 0,
        "row_count": 17024553
    }
]'
----

# Give t76522_3 many rows where a has many distincts.
exec-ddl
ALTER TABLE t76522_3 INJECT STATISTICS '[
    {
        "columns": [
            "a"
        ],
        "created_at": "2022-01-17 12:51:38.433911",
        "distinct_count": 17187349,
        "null_count": 0,
        "row_count": 18138540
    }
]'
----

# Give t76522_4 many rows where e has many distincts.
exec-ddl
ALTER TABLE t76522_4 INJECT STATISTICS '[
    {
        "columns": [
            "e"
        ],
        "created_at": "2022-01-17 12:51:38.433911",
        "distinct_count": 346919,
        "null_count": 0,
        "row_count": 346109
    }
]';
----

# Give t5 few rows.
exec-ddl
ALTER TABLE t76522_5 INJECT STATISTICS '[
    {
        "columns": [
            "h"
        ],
        "created_at": "2022-01-17 12:51:38.433911",
        "distinct_count": 119,
        "null_count": 0,
        "row_count": 119
    }
]'
----

# Prior to the fix, these filters were missing from the query plan:
#
#   t5.c = t2.c
#   t2.should_not_be_eliminated = t5.should_not_be_eliminated
#
opt
SELECT
  t2.a
FROM
  t76522_1 AS t1
  INNER JOIN t76522_2 AS t2 ON t1.a = t2.a
  INNER JOIN t76522_3 AS t3 ON t1.a = t3.a
  INNER JOIN t76522_4 AS t4 ON t3.d = t4.e
  INNER JOIN t76522_5 AS t5 ON
      t4.f = t5.f
      AND t4.g = t5.g
      AND t5.b = t1.b
      AND t5.c = t2.c
WHERE
  t1.a = 123456 AND t2.should_not_be_eliminated = t5.should_not_be_eliminated;
----
project
 ├── columns: a:5!null
 ├── fd: ()-->(5)
 └── inner-join (lookup t76522_1 [as=t1])
      ├── columns: t1.a:1!null t1.b:2!null t2.a:5!null t2.c:6!null t2.should_not_be_eliminated:7!null t3.a:10!null d:11!null e:16!null t4.f:17!null t4.g:18!null t5.f:22!null t5.g:23!null t5.b:24!null t5.should_not_be_eliminated:25!null t5.c:26!null
      ├── key columns: [5 24] = [1 2]
      ├── lookup columns are key
      ├── fd: ()-->(1,5-7,10,25,26), (16)-->(17,18), (1)==(5,10), (5)==(1,10), (10)==(1,5), (11)==(16), (16)==(11), (17)==(22), (22)==(17), (18)==(23), (23)==(18), (2)==(24), (24)==(2), (6)==(26), (26)==(6), (7)==(25), (25)==(7)
      ├── inner-join (lookup t76522_2 [as=t2])
      │    ├── columns: t2.a:5!null t2.c:6!null t2.should_not_be_eliminated:7!null t3.a:10!null d:11!null e:16!null t4.f:17!null t4.g:18!null t5.f:22!null t5.g:23!null t5.b:24 t5.should_not_be_eliminated:25!null t5.c:26!null
      │    ├── key columns: [10] = [5]
      │    ├── lookup columns are key
      │    ├── fd: ()-->(5-7,10,25,26), (16)-->(17,18), (17)==(22), (22)==(17), (18)==(23), (23)==(18), (11)==(16), (16)==(11), (6)==(26), (26)==(6), (7)==(25), (25)==(7), (5)==(10), (10)==(5)
      │    ├── inner-join (hash)
      │    │    ├── columns: t3.a:10!null d:11!null e:16!null t4.f:17!null t4.g:18!null t5.f:22!null t5.g:23!null t5.b:24 t5.should_not_be_eliminated:25 t5.c:26
      │    │    ├── fd: ()-->(10), (16)-->(17,18), (17)==(22), (22)==(17), (18)==(23), (23)==(18), (11)==(16), (16)==(11)
      │    │    ├── scan t76522_5 [as=t5]
      │    │    │    └── columns: t5.f:22!null t5.g:23!null t5.b:24 t5.should_not_be_eliminated:25 t5.c:26
      │    │    ├── inner-join (lookup t76522_4 [as=t4])
      │    │    │    ├── columns: t3.a:10!null d:11!null e:16!null t4.f:17 t4.g:18
      │    │    │    ├── key columns: [11] = [16]
      │    │    │    ├── lookup columns are key
      │    │    │    ├── key: (16)
      │    │    │    ├── fd: ()-->(10), (16)-->(17,18), (11)==(16), (16)==(11)
      │    │    │    ├── scan t76522_3 [as=t3]
      │    │    │    │    ├── columns: t3.a:10!null d:11!null
      │    │    │    │    ├── constraint: /10/11: [/123456 - /123456]
      │    │    │    │    ├── key: (11)
      │    │    │    │    └── fd: ()-->(10)
      │    │    │    └── filters (true)
      │    │    └── filters
      │    │         ├── t4.f:17 = t5.f:22 [outer=(17,22), constraints=(/17: (/NULL - ]; /22: (/NULL - ]), fd=(17)==(22), (22)==(17)]
      │    │         └── t4.g:18 = t5.g:23 [outer=(18,23), constraints=(/18: (/NULL - ]; /23: (/NULL - ]), fd=(18)==(23), (23)==(18)]
      │    └── filters
      │         ├── t5.c:26 = t2.c:6 [outer=(6,26), constraints=(/6: (/NULL - ]; /26: (/NULL - ]), fd=(6)==(26), (26)==(6)]
      │         ├── t2.should_not_be_eliminated:7 = t5.should_not_be_eliminated:25 [outer=(7,25), constraints=(/7: (/NULL - ]; /25: (/NULL - ]), fd=(7)==(25), (25)==(7)]
      │         └── t2.a:5 = 123456 [outer=(5), constraints=(/5: [/123456 - /123456]; tight), fd=()-->(5)]
      └── filters
           └── t1.a:1 = 123456 [outer=(1), constraints=(/1: [/123456 - /123456]; tight), fd=()-->(1)]

# Regression test for #80901. Do not cause a memo cycle when redundant inner
# join filters can be inferred that reference the right side of a left join, and
# the inner join is pushed into the left side of the left join.

exec-ddl
CREATE TABLE table80901_1 (
  col1_5  DECIMAL,
  col1_7  BOOL,
  col1_9  OID,
  col1_11 STRING,
  col1_12 STRING,
  col1_14 STRING,
  col1_15 STRING,
  col1_16 STRING,
  col1_17 STRING
);
----

exec-ddl
CREATE TABLE table80901_3 (col3_2 OID, col3_4 FLOAT8, col3_9 STRING);
----

memo memo-cycles
SELECT (
  SELECT NULL
  FROM table80901_1 AS tab_42924
  JOIN table80901_1 AS tab_42927
    LEFT JOIN table80901_3 AS tab_42928 ON tab_42921.col1_7
  JOIN table80901_3 AS tab_42929
  ON tab_42927.col1_9 = tab_42929.col3_2
  ON tab_42924.col1_17 = tab_42927.col1_15
    AND tab_42924.col1_12 = tab_42929.col3_9
    AND tab_42924.col1_14 = tab_42928.col3_9
    AND tab_42924.col1_14 = tab_42929.col3_9
    AND tab_42924.col1_11 = tab_42927.col1_17
    AND tab_42924.col1_11 = tab_42927.col1_16
    AND tab_42924.col1_15 = tab_42929.col3_9
    AND tab_42924.col1_5 = tab_42929.crdb_internal_mvcc_timestamp
)
  FROM table80901_1 AS tab_42921;
----
memo (optimized, ~73KB, required=[presentation: ?column?:50])
 ├── G1: (project G2 G3)
 │    └── [presentation: ?column?:50]
 │         ├── best: (project G2 G3)
 │         └── cost: 13020.58
 ├── G2: (ensure-distinct-on G4 G5 cols=(10)) (ensure-distinct-on G4 G5 cols=(10),ordering=+10)
 │    └── []
 │         ├── best: (ensure-distinct-on G4 G5 cols=(10))
 │         └── cost: 11283.07
 ├── G3: (projections G6)
 ├── G4: (left-join-apply G7 G8 G9)
 │    ├── [ordering: +10]
 │    │    ├── best: (sort G4)
 │    │    └── cost: 40658.22
 │    └── []
 │         ├── best: (left-join-apply G7 G8 G9)
 │         └── cost: 6629.67
 ├── G5: (aggregations G10)
 ├── G6: (variable "?column?")
 ├── G7: (scan table80901_1 [as=tab_42921],cols=(2,10))
 │    └── []
 │         ├── best: (scan table80901_1 [as=tab_42921],cols=(2,10))
 │         └── cost: 1149.22
 ├── G8: (project G11 G12)
 │    └── []
 │         ├── best: (project G11 G12)
 │         └── cost: 4597.66
 ├── G9: (filters)
 ├── G10: (const-agg G6)
 ├── G11: (inner-join G13 G14 G15) (inner-join G13 G16 G17) (inner-join G16 G13 G17) (select G18 G19) (inner-join G20 G21 G22) (inner-join G21 G20 G22) (inner-join G23 G24 G25) (inner-join G24 G23 G25)
 │    └── []
 │         ├── best: (select G18 G19)
 │         └── cost: 4595.90
 ├── G12: (projections G26)
 ├── G13: (scan table80901_1 [as=tab_42924],cols=(13,16-19,21))
 │    └── []
 │         ├── best: (scan table80901_1 [as=tab_42924],cols=(13,16-19,21))
 │         └── cost: 1189.62
 ├── G14: (inner-join G27 G24 G28) (left-join G29 G30 G31) (inner-join G24 G27 G28) (right-join G30 G29 G31)
 │    └── []
 │         ├── best: (left-join G29 G30 G31)
 │         └── cost: 101625.03
 ├── G15: (filters G32 G33 G34 G35 G36 G37 G38 G39)
 ├── G16: (select G14 G40) (inner-join G20 G24 G41) (inner-join G24 G20 G41)
 │    └── []
 │         ├── best: (inner-join G20 G24 G41)
 │         └── cost: 26855.10
 ├── G17: (filters G32 G33 G34 G36 G37 G38 G39)
 ├── G18: (left-join G42 G30 G31) (right-join G30 G42 G31)
 │    └── []
 │         ├── best: (right-join G30 G42 G31)
 │         └── cost: 4595.00
 ├── G19: (filters G34)
 ├── G20: (left-join G43 G30 G31) (right-join G30 G43 G31)
 │    └── []
 │         ├── best: (left-join G43 G30 G31)
 │         └── cost: 12278.12
 ├── G21: (inner-join G13 G24 G44) (inner-join G24 G13 G44)
 │    └── []
 │         ├── best: (inner-join G13 G24 G44)
 │         └── cost: 2319.47
 ├── G22: (filters G45 G32 G34 G36 G37)
 ├── G23: (inner-join G13 G20 G46) (inner-join G20 G13 G46) (select G47 G48)
 │    └── []
 │         ├── best: (select G47 G48)
 │         └── cost: 5384.91
 ├── G24: (scan table80901_3 [as=tab_42929],cols=(43,45,47))
 │    └── []
 │         ├── best: (scan table80901_3 [as=tab_42929],cols=(43,45,47))
 │         └── cost: 1098.72
 ├── G25: (filters G45 G33 G39)
 ├── G26: (null)
 ├── G27: (left-join G43 G30 G31) (right-join G30 G43 G31)
 │    └── []
 │         ├── best: (left-join G43 G30 G31)
 │         └── cost: 12278.12
 ├── G28: (filters G45)
 ├── G29: (inner-join G43 G24 G28) (inner-join G24 G43 G28)
 │    └── []
 │         ├── best: (inner-join G43 G24 G28)
 │         └── cost: 2396.32
 ├── G30: (scan table80901_3 [as=tab_42928],cols=(39))
 │    └── []
 │         ├── best: (scan table80901_3 [as=tab_42928],cols=(39))
 │         └── cost: 1078.52
 ├── G31: (filters G49)
 ├── G32: (eq G50 G51)
 ├── G33: (eq G52 G53)
 ├── G34: (eq G54 G55)
 ├── G35: (eq G54 G53)
 ├── G36: (eq G56 G57)
 ├── G37: (eq G56 G58)
 ├── G38: (eq G59 G53)
 ├── G39: (eq G60 G61)
 ├── G40: (filters G62)
 ├── G41: (filters G45 G62)
 ├── G42: (inner-join G13 G29 G63) (inner-join G29 G13 G63) (inner-join G43 G21 G64) (inner-join G21 G43 G64) (inner-join G65 G24 G66) (inner-join G24 G65 G66)
 │    └── []
 │         ├── best: (inner-join G43 G21 G64)
 │         └── cost: 3503.07
 ├── G43: (scan table80901_1 [as=tab_42927],cols=(27,31-33))
 │    └── []
 │         ├── best: (scan table80901_1 [as=tab_42927],cols=(27,31-33))
 │         └── cost: 1169.42
 ├── G44: (filters G33 G35 G38 G39)
 ├── G45: (eq G67 G68)
 ├── G46: (filters G32 G34 G36 G37 G69 G70)
 ├── G47: (left-join G65 G30 G31) (right-join G30 G65 G31)
 │    └── []
 │         ├── best: (right-join G30 G65 G31)
 │         └── cost: 4433.87
 ├── G48: (filters G34 G69 G70)
 ├── G49: (variable tab_42921.col1_7)
 ├── G50: (variable tab_42924.col1_17)
 ├── G51: (variable tab_42927.col1_15)
 ├── G52: (variable tab_42924.col1_12)
 ├── G53: (variable tab_42929.col3_9)
 ├── G54: (variable tab_42924.col1_14)
 ├── G55: (variable tab_42928.col3_9)
 ├── G56: (variable tab_42924.col1_11)
 ├── G57: (variable tab_42927.col1_17)
 ├── G58: (variable tab_42927.col1_16)
 ├── G59: (variable tab_42924.col1_15)
 ├── G60: (variable tab_42924.col1_5)
 ├── G61: (variable tab_42929.crdb_internal_mvcc_timestamp)
 ├── G62: (eq G55 G53)
 ├── G63: (filters G32 G33 G35 G36 G37 G38 G39)
 ├── G64: (filters G45 G32 G36 G37)
 ├── G65: (inner-join G13 G43 G71) (inner-join G43 G13 G71)
 │    └── []
 │         ├── best: (inner-join G13 G43 G71)
 │         └── cost: 2390.17
 ├── G66: (filters G45 G33 G35 G38 G39)
 ├── G67: (variable tab_42927.col1_9)
 ├── G68: (variable tab_42929.col3_2)
 ├── G69: (eq G52 G55)
 ├── G70: (eq G59 G55)
 └── G71: (filters G32 G36 G37)

# Regression test for #88659 - don't add reordered joins to existing groups when
# filters haven't been pushed down. The c:3 = c:9 filter shouldn't be dropped.
exec-ddl
CREATE TABLE t88659 (
  a INT PRIMARY KEY,
  b INT NOT NULL,
  c DECIMAL,
  INDEX idx (b DESC),
  UNIQUE INDEX uniq ((b + a) ASC) STORING (b),
  FAMILY (a, b)
);
----

exec-ddl
ALTER TABLE t88659 INJECT STATISTICS '[
  {
    "columns": ["b"],
    "created_at": "2000-01-01 00:00:00+00:00",
    "distinct_count": 999999999,
    "name": "__auto__",
    "null_count": 0,
    "row_count": 999999999999}
]':::JSONB;
----

opt set=unconstrained_non_covering_index_scan_enabled=true set=testing_optimizer_random_seed=2758112374651167630 set=testing_optimizer_cost_perturbation=1.0
SELECT *
FROM t88659 AS t0
JOIN t88659 AS t2 ON (t0.b) = (t2.a)
JOIN t88659 AS t3 ON (t2.c) = (t3.c) AND (t0.c) = (t3.c)
JOIN t88659 AS t4 ON (t3.a) = (t4.b) AND (t2.b) = (t4.a) AND (t2.a) = (t4.a);
----
inner-join (lookup t88659)
 ├── columns: a:1!null b:2!null c:3!null a:7!null b:8!null c:9!null a:13!null b:14!null c:15!null a:19!null b:20!null c:21
 ├── key columns: [7] = [19]
 ├── lookup columns are key
 ├── immutable
 ├── key: (1)
 ├── fd: (1)-->(2,3), (7)-->(9), (13)-->(15), (19)-->(21), (2)==(7,8,13,14,19,20), (7)==(2,8,13,14,19,20), (8)==(2,7,13,14,19,20), (13)==(2,7,8,14,19,20), (14)==(2,7,8,13,19,20), (19)==(2,7,8,13,14,20), (20)==(2,7,8,13,14,19), (3)==(9,15,21), (9)==(3,15,21), (15)==(3,9,21), (21)==(3,9,15)
 ├── inner-join (lookup t88659)
 │    ├── columns: a:1!null b:2!null c:3!null a:7!null b:8!null c:9!null a:13!null b:14!null c:15!null
 │    ├── key columns: [2] = [13]
 │    ├── lookup columns are key
 │    ├── immutable
 │    ├── key: (1)
 │    ├── fd: (1)-->(2,3), (7)-->(9), (13)-->(15), (2)==(7,8,13,14), (7)==(2,8,13,14), (8)==(2,7,13,14), (13)==(2,7,8,14), (14)==(2,7,8,13), (3)==(9,15), (9)==(3,15), (15)==(3,9)
 │    ├── inner-join (lookup t88659)
 │    │    ├── columns: a:1!null b:2!null c:3!null a:7!null b:8!null c:9!null
 │    │    ├── key columns: [1] = [1]
 │    │    ├── lookup columns are key
 │    │    ├── immutable
 │    │    ├── key: (1)
 │    │    ├── fd: (1)-->(2,3), (7)-->(9), (2)==(7,8), (7)==(2,8), (8)==(2,7), (3)==(9), (9)==(3)
 │    │    ├── inner-join (lookup t88659@idx)
 │    │    │    ├── columns: a:1!null b:2!null a:7!null b:8!null c:9
 │    │    │    ├── key columns: [7] = [2]
 │    │    │    ├── key: (1)
 │    │    │    ├── fd: (7)-->(9), (1)-->(2), (2)==(7,8), (7)==(2,8), (8)==(2,7)
 │    │    │    ├── select
 │    │    │    │    ├── columns: a:7!null b:8!null c:9
 │    │    │    │    ├── key: (7)
 │    │    │    │    ├── fd: (7)-->(9), (7)==(8), (8)==(7)
 │    │    │    │    ├── scan t88659
 │    │    │    │    │    ├── columns: a:7!null b:8!null c:9
 │    │    │    │    │    ├── computed column expressions
 │    │    │    │    │    │    └── crdb_internal_idx_expr:12
 │    │    │    │    │    │         └── b:8 + a:7
 │    │    │    │    │    ├── key: (7)
 │    │    │    │    │    └── fd: (7)-->(8,9)
 │    │    │    │    └── filters
 │    │    │    │         └── a:7 = b:8 [outer=(7,8), constraints=(/7: (/NULL - ]; /8: (/NULL - ]), fd=(7)==(8), (8)==(7)]
 │    │    │    └── filters (true)
 │    │    └── filters
 │    │         └── c:3 = c:9 [outer=(3,9), immutable, constraints=(/3: (/NULL - ]; /9: (/NULL - ]), fd=(3)==(9), (9)==(3)]
 │    └── filters
 │         ├── c:9 = c:15 [outer=(9,15), immutable, constraints=(/9: (/NULL - ]; /15: (/NULL - ]), fd=(9)==(15), (15)==(9)]
 │         └── b:2 = b:14 [outer=(2,14), constraints=(/2: (/NULL - ]; /14: (/NULL - ]), fd=(2)==(14), (14)==(2)]
 └── filters
      ├── a:13 = b:20 [outer=(13,20), constraints=(/13: (/NULL - ]; /20: (/NULL - ]), fd=(13)==(20), (20)==(13)]
      └── c:3 = c:21 [outer=(3,21), immutable, constraints=(/3: (/NULL - ]; /21: (/NULL - ]), fd=(3)==(21), (21)==(3)]

# Regression test for #90761 - don't drop LeftJoin filter when there are enough
# InnerJoin edges to "link" all relations and the LeftJoin doesn't get
# simplified.
exec-ddl
CREATE TABLE t90761 (a INT, b INT, c INT);
----

# The 't2.b > t4.b' filter should not be dropped.
reorderjoins disable=RejectNullsLeftJoin
SELECT 1
FROM t90761 AS t1
JOIN t90761 AS t2
  LEFT JOIN t90761 AS t3
    JOIN t90761 AS t4 ON true
  ON t2.b > t4.b
ON t1.a = t4.a AND t1.c = t2.c;
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  inner-join (cross)
   ├── scan t90761 [as=t3]
   ├── scan t90761 [as=t4]
   └── filters (true)
Vertexes
  A:
    scan t90761 [as=t3]
  B:
    scan t90761 [as=t4]
Edges
  cross [inner, ses=, tes=AB, rules=()]
Joining AB
  A B [inner, refs=]
  B A [inner, refs=]
Joins Considered: 2
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  left-join (cross)
   ├── scan t90761 [as=t2]
   ├── inner-join (cross)
   │    ├── scan t90761 [as=t3]
   │    ├── scan t90761 [as=t4]
   │    └── filters (true)
   └── filters
        └── t2.b > t4.b
Vertexes
  C:
    scan t90761 [as=t2]
  A:
    scan t90761 [as=t3]
  B:
    scan t90761 [as=t4]
Edges
  cross [inner, ses=, tes=AB, rules=()]
  t2.b > t4.b [left, ses=CB, tes=CAB, rules=()]
Joining AB
  A B [inner, refs=]
  B A [inner, refs=]
Joining CAB
  C AB [left, refs=CB]
Joins Considered: 3
--------------------------------------------------------------------------------
Join Tree #3
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── scan t90761 [as=t1]
   ├── left-join (cross)
   │    ├── scan t90761 [as=t2]
   │    ├── inner-join (cross)
   │    │    ├── scan t90761 [as=t3]
   │    │    ├── scan t90761 [as=t4]
   │    │    └── filters (true)
   │    └── filters
   │         └── t2.b > t4.b
   └── filters
        ├── t1.a = t4.a
        └── t1.c = t2.c
Vertexes
  D:
    scan t90761 [as=t1]
  C:
    scan t90761 [as=t2]
  A:
    scan t90761 [as=t3]
  B:
    scan t90761 [as=t4]
Edges
  cross [inner, ses=, tes=AB, rules=()]
  t2.b > t4.b [left, ses=CB, tes=CAB, rules=()]
  t1.a = t4.a [inner, ses=DB, tes=DCB, rules=()]
  t1.c = t2.c [inner, ses=DC, tes=DC, rules=()]
Joining DC
  D C [inner, refs=DC]
  C D [inner, refs=DC]
Joining DCB
  DC B [inner, refs=DB]
  B DC [inner, refs=DB]
Joining AB
  A B [inner, refs=]
  B A [inner, refs=]
Joining CAB
  C AB [left, refs=CB]
Joining DCAB
  D CAB [inner, refs=DCB]
  CAB D [inner, refs=DCB]
  DC AB [left, refs=CB] [select, refs=DB]
Joins Considered: 10
================================================================================
Final Plan
================================================================================
project
 ├── columns: "?column?":25!null
 ├── fd: ()-->(25)
 ├── inner-join (hash)
 │    ├── columns: t1.a:1!null t1.c:3!null t2.b:8 t2.c:9!null t4.a:19!null t4.b:20
 │    ├── fd: (1)==(19), (19)==(1), (3)==(9), (9)==(3)
 │    ├── right-join (cross)
 │    │    ├── columns: t2.b:8 t2.c:9 t4.a:19 t4.b:20
 │    │    ├── inner-join (cross)
 │    │    │    ├── columns: t4.a:19 t4.b:20
 │    │    │    ├── scan t90761 [as=t3]
 │    │    │    ├── scan t90761 [as=t4]
 │    │    │    │    └── columns: t4.a:19 t4.b:20
 │    │    │    └── filters (true)
 │    │    ├── scan t90761 [as=t2]
 │    │    │    └── columns: t2.b:8 t2.c:9
 │    │    └── filters
 │    │         └── t2.b:8 > t4.b:20 [outer=(8,20), constraints=(/8: (/NULL - ]; /20: (/NULL - ])]
 │    ├── scan t90761 [as=t1]
 │    │    └── columns: t1.a:1 t1.c:3
 │    └── filters
 │         ├── t1.a:1 = t4.a:19 [outer=(1,19), constraints=(/1: (/NULL - ]; /19: (/NULL - ]), fd=(1)==(19), (19)==(1)]
 │         └── t1.c:3 = t2.c:9 [outer=(3,9), constraints=(/3: (/NULL - ]; /9: (/NULL - ]), fd=(3)==(9), (9)==(3)]
 └── projections
      └── 1 [as="?column?":25]

# The 't2.b > t4.b' filter should not be dropped. Case with 'IS NOT NULL'
# instead of a disabled rule.
opt format=hide-all
SELECT 1
FROM t90761 AS t1
JOIN t90761 AS t2
  LEFT JOIN t90761 AS t3
    JOIN t90761 AS t4 ON true
  ON t2.b > t4.b
ON (t1.a = t4.a OR t4.a IS NULL) AND t1.c = t2.c;
----
project
 ├── inner-join (hash)
 │    ├── right-join (cross)
 │    │    ├── inner-join (cross)
 │    │    │    ├── scan t90761 [as=t3]
 │    │    │    ├── scan t90761 [as=t4]
 │    │    │    └── filters (true)
 │    │    ├── scan t90761 [as=t2]
 │    │    └── filters
 │    │         └── t2.b > t4.b
 │    ├── scan t90761 [as=t1]
 │    └── filters
 │         ├── (t1.a = t4.a) OR (t4.a IS NULL)
 │         └── t1.c = t2.c
 └── projections
      └── 1

# Regression test for #105608 - don't infer an equality on nullable columns from
# the top-level join's functional dependencies.
exec-ddl
CREATE TABLE patient (id INT8 NOT NULL, site_id INT8, PRIMARY KEY (id));
----

exec-ddl
CREATE TABLE site (id INT8 NOT NULL, PRIMARY KEY (id));
----

exec-ddl
CREATE TABLE task (id INT8 NOT NULL, description VARCHAR(255), patient_id INT8, PRIMARY KEY (id));
----

exec-ddl
ALTER TABLE IF EXISTS patient ADD CONSTRAINT fkhty4ykfvf29xscswwd63mgdoc FOREIGN KEY (site_id) REFERENCES site;
----

exec-ddl
ALTER TABLE IF EXISTS task ADD CONSTRAINT fkkk2ow88d08vqxqyvvvmys1j2m FOREIGN KEY (patient_id) REFERENCES patient;
----

# The edge "task0_.patient_id = task1_.patient_id" should not be created.
reorderjoins
SELECT task0_.id AS id1_2_, task0_.description AS descript2_2_, task0_.patient_id AS patient_3_2_
FROM task AS task0_ WHERE EXISTS (
	SELECT task1_.id FROM task AS task1_
	LEFT JOIN patient AS patient2_
	ON task1_.patient_id = patient2_.id
	LEFT JOIN site AS site3_
	ON patient2_.site_id = site3_.id
	WHERE (task1_.id = task0_.id) AND ((patient2_.id IS NULL) OR (site3_.id IN (2, 1)))
);
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  left-join (hash)
   ├── scan task [as=task1_]
   ├── scan patient [as=patient2_]
   └── filters
        └── task1_.patient_id = patient2_.id
Vertexes
  A:
    scan task [as=task1_]
  B:
    scan patient [as=patient2_]
Edges
  task1_.patient_id = patient2_.id [left, ses=AB, tes=AB, rules=()]
Joining AB
  A B [left, refs=AB]
Joins Considered: 1
--------------------------------------------------------------------------------
Join Tree #2
--------------------------------------------------------------------------------
  left-join (hash)
   ├── left-join (hash)
   │    ├── scan task [as=task1_]
   │    ├── scan patient [as=patient2_]
   │    └── filters
   │         └── task1_.patient_id = patient2_.id
   ├── scan site [as=site3_]
   └── filters
        └── site_id = site3_.id
Vertexes
  A:
    scan task [as=task1_]
  B:
    scan patient [as=patient2_]
  C:
    scan site [as=site3_]
Edges
  task1_.patient_id = patient2_.id [left, ses=AB, tes=AB, rules=()]
  site_id = site3_.id [left, ses=BC, tes=BC, rules=()]
Joining AB
  A B [left, refs=AB]
Joining BC
  B C [left, refs=BC]
Joining ABC
  A BC [left, refs=AB]
  AB C [left, refs=BC]
Joins Considered: 4
--------------------------------------------------------------------------------
Join Tree #3
--------------------------------------------------------------------------------
  semi-join (hash)
   ├── scan task [as=task0_]
   ├── select
   │    ├── left-join (hash)
   │    │    ├── left-join (hash)
   │    │    │    ├── scan task [as=task1_]
   │    │    │    ├── scan patient [as=patient2_]
   │    │    │    └── filters
   │    │    │         └── task1_.patient_id = patient2_.id
   │    │    ├── scan site [as=site3_]
   │    │    └── filters
   │    │         └── site_id = site3_.id
   │    └── filters
   │         └── (patient2_.id IS NULL) OR (site3_.id IN (1, 2))
   └── filters
        └── task1_.id = task0_.id
Vertexes
  D:
    scan task [as=task0_]
  E:
    select
     ├── left-join (hash)
     │    ├── left-join (hash)
     │    │    ├── scan task [as=task1_]
     │    │    ├── scan patient [as=patient2_]
     │    │    └── filters
     │    │         └── task1_.patient_id = patient2_.id
     │    ├── scan site [as=site3_]
     │    └── filters
     │         └── site_id = site3_.id
     └── filters
          └── (patient2_.id IS NULL) OR (site3_.id IN (1, 2))
Edges
  task1_.id = task0_.id [semi, ses=DE, tes=DE, rules=()]
Joining DE
  D E [semi, refs=DE]
Joins Considered: 1
--------------------------------------------------------------------------------
Join Tree #4
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── scan task [as=task0_]
   ├── select
   │    ├── left-join (hash)
   │    │    ├── left-join (hash)
   │    │    │    ├── scan task [as=task1_]
   │    │    │    ├── scan patient [as=patient2_]
   │    │    │    └── filters
   │    │    │         └── task1_.patient_id = patient2_.id
   │    │    ├── scan site [as=site3_]
   │    │    └── filters
   │    │         └── site_id = site3_.id
   │    └── filters
   │         └── (patient2_.id IS NULL) OR (site3_.id IN (1, 2))
   └── filters
        └── task1_.id = task0_.id
Vertexes
  D:
    scan task [as=task0_]
  E:
    select
     ├── left-join (hash)
     │    ├── left-join (hash)
     │    │    ├── scan task [as=task1_]
     │    │    ├── scan patient [as=patient2_]
     │    │    └── filters
     │    │         └── task1_.patient_id = patient2_.id
     │    ├── scan site [as=site3_]
     │    └── filters
     │         └── site_id = site3_.id
     └── filters
          └── (patient2_.id IS NULL) OR (site3_.id IN (1, 2))
Edges
  task1_.id = task0_.id [inner, ses=DE, tes=DE, rules=()]
Joining DE
  D E [inner, refs=DE]
  E D [inner, refs=DE]
Joins Considered: 2
================================================================================
Final Plan
================================================================================
project
 ├── columns: id1_2_:1!null descript2_2_:2 patient_3_2_:3
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 └── project
      ├── columns: task0_.id:1!null task0_.description:2 task0_.patient_id:3 task1_.id:6!null
      ├── key: (6)
      ├── fd: (1)-->(2,3), (6)-->(3), (1)==(6), (6)==(1)
      └── inner-join (lookup task [as=task0_])
           ├── columns: task0_.id:1!null task0_.description:2 task0_.patient_id:3 task1_.id:6!null task1_.patient_id:8 patient2_.id:11 site_id:12 site3_.id:15
           ├── key columns: [6] = [1]
           ├── lookup columns are key
           ├── key: (6)
           ├── fd: (1)-->(2,3), (6)-->(8,11,12,15), (11)-->(12), (1)==(6), (6)==(1), (3)==(8), (8)==(3)
           ├── select
           │    ├── columns: task1_.id:6!null task1_.patient_id:8 patient2_.id:11 site_id:12 site3_.id:15
           │    ├── key: (6)
           │    ├── fd: (6)-->(8,11,12,15), (11)-->(12)
           │    ├── left-join (hash)
           │    │    ├── columns: task1_.id:6!null task1_.patient_id:8 patient2_.id:11 site_id:12 site3_.id:15
           │    │    ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-more)
           │    │    ├── key: (6)
           │    │    ├── fd: (6)-->(8,11,12,15), (11)-->(12)
           │    │    ├── left-join (hash)
           │    │    │    ├── columns: task1_.id:6!null task1_.patient_id:8 patient2_.id:11 site_id:12
           │    │    │    ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-more)
           │    │    │    ├── key: (6)
           │    │    │    ├── fd: (6)-->(8,11,12), (11)-->(12)
           │    │    │    ├── scan task [as=task1_]
           │    │    │    │    ├── columns: task1_.id:6!null task1_.patient_id:8
           │    │    │    │    ├── key: (6)
           │    │    │    │    └── fd: (6)-->(8)
           │    │    │    ├── scan patient [as=patient2_]
           │    │    │    │    ├── columns: patient2_.id:11!null site_id:12
           │    │    │    │    ├── key: (11)
           │    │    │    │    └── fd: (11)-->(12)
           │    │    │    └── filters
           │    │    │         └── task1_.patient_id:8 = patient2_.id:11 [outer=(8,11), constraints=(/8: (/NULL - ]; /11: (/NULL - ]), fd=(8)==(11), (11)==(8)]
           │    │    ├── scan site [as=site3_]
           │    │    │    ├── columns: site3_.id:15!null
           │    │    │    └── key: (15)
           │    │    └── filters
           │    │         └── site_id:12 = site3_.id:15 [outer=(12,15), constraints=(/12: (/NULL - ]; /15: (/NULL - ]), fd=(12)==(15), (15)==(12)]
           │    └── filters
           │         └── (patient2_.id:11 IS NULL) OR (site3_.id:15 IN (1, 2)) [outer=(11,15)]
           └── filters (true)

# Regression test for #106371 - only infer self-join equalities if the original
# equality columns form a key over the *base* table, not just the join inputs.
exec-ddl
CREATE TABLE t106371 (x INT NOT NULL, y INT NOT NULL);
----

# Don't add an "a.y = b.y" filter.
reorderjoins
SELECT * FROM (SELECT * FROM t106371 ORDER BY y LIMIT 1) a
JOIN (SELECT DISTINCT ON (x) * FROM (SELECT * FROM t106371 WHERE y = 2)) b ON a.x = b.x;
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── limit
   │    ├── scan t106371
   │    └── 1
   ├── distinct-on
   │    ├── select
   │    │    ├── scan t106371
   │    │    └── filters
   │    │         └── y = 2
   │    └── aggregations
   │         └── first-agg
   │              └── y
   └── filters
        └── x = x
Vertexes
  A:
    limit
     ├── scan t106371
     └── 1
  B:
    distinct-on
     ├── select
     │    ├── scan t106371
     │    └── filters
     │         └── y = 2
     └── aggregations
          └── first-agg
               └── y
Edges
  x = x [inner, ses=AB, tes=AB, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joins Considered: 2
================================================================================
Final Plan
================================================================================
inner-join (hash)
 ├── columns: x:1!null y:2!null x:6!null y:7!null
 ├── cardinality: [0 - 1]
 ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-one)
 ├── key: ()
 ├── fd: ()-->(1,2,6,7), (1)==(6), (6)==(1)
 ├── distinct-on
 │    ├── columns: x:6!null y:7!null
 │    ├── grouping columns: x:6!null
 │    ├── key: (6)
 │    ├── fd: ()-->(7)
 │    ├── select
 │    │    ├── columns: x:6!null y:7!null
 │    │    ├── fd: ()-->(7)
 │    │    ├── scan t106371
 │    │    │    └── columns: x:6!null y:7!null
 │    │    └── filters
 │    │         └── y:7 = 2 [outer=(7), constraints=(/7: [/2 - /2]; tight), fd=()-->(7)]
 │    └── aggregations
 │         └── first-agg [as=y:7, outer=(7)]
 │              └── y:7
 ├── top-k
 │    ├── columns: x:1!null y:2!null
 │    ├── internal-ordering: +2
 │    ├── k: 1
 │    ├── cardinality: [0 - 1]
 │    ├── key: ()
 │    ├── fd: ()-->(1,2)
 │    └── scan t106371
 │         └── columns: x:1!null y:2!null
 └── filters
      └── x:1 = x:6 [outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)]

# Regression test for #113632 - inferring an equality for a REFCURSOR[] column
# should not cause an internal error.
exec-ddl
CREATE TABLE t113632 (k INT PRIMARY KEY, v REFCURSOR[]);
----

opt disable=(EliminateJoinUnderProjectLeft,EliminateJoinUnderProjectRight)
SELECT * FROM t113632 t1 INNER JOIN t113632 t2 ON t1.k = t2.k;
----
inner-join (merge)
 ├── columns: k:1!null v:2 k:5!null v:6
 ├── left ordering: +1
 ├── right ordering: +5
 ├── key: (5)
 ├── fd: (1)-->(2), (5)-->(6), (1)==(5), (5)==(1), (2)==(6), (6)==(2)
 ├── scan t113632 [as=t1]
 │    ├── columns: t1.k:1!null t1.v:2
 │    ├── key: (1)
 │    ├── fd: (1)-->(2)
 │    └── ordering: +1
 ├── scan t113632 [as=t2]
 │    ├── columns: t2.k:5!null t2.v:6
 │    ├── key: (5)
 │    ├── fd: (5)-->(6)
 │    └── ordering: +5
 └── filters (true)

# Regression test for #116131 - don't match ReorderJoins when the join order
# limit is zero.
exploretrace rule=ReorderJoins set=reorder_joins_limit=0
SELECT * FROM bx INNER JOIN cy ON b = c;
----

# Tests for straight join - join reordering should not be
# explored when the STRAIGHT keyword is present.
exec-ddl
CREATE TABLE straight_join1 (x INT, PRIMARY KEY (x));
----

exec-ddl
CREATE TABLE straight_join2 (x INT, y INT, PRIMARY KEY (x));
----

exec-ddl
CREATE TABLE straight_join3 (x INT, z INT, PRIMARY KEY (x));
----

reorderjoins format=hide-all
SELECT * FROM straight_join1 INNER STRAIGHT JOIN straight_join2 ON straight_join1.x = straight_join2.y
----
Joins Considered: 0
================================================================================
Final Plan
================================================================================
inner-join (hash)
 ├── flags: disallow hash join (store left side) and lookup join (into left side) and inverted join (into left side)
 ├── scan straight_join1
 ├── scan straight_join2
 └── filters
      └── straight_join1.x = y

reorderjoins format=hide-all
SELECT * FROM straight_join1 LEFT STRAIGHT JOIN straight_join2 ON straight_join1.x = straight_join2.y
----
Joins Considered: 0
================================================================================
Final Plan
================================================================================
left-join (hash)
 ├── flags: disallow hash join (store left side) and lookup join (into left side) and inverted join (into left side)
 ├── scan straight_join1
 ├── scan straight_join2
 └── filters
      └── straight_join1.x = y

reorderjoins format=hide-all
SELECT * FROM straight_join1 RIGHT STRAIGHT JOIN straight_join2 ON straight_join1.x = straight_join2.y
----
Joins Considered: 0
================================================================================
Final Plan
================================================================================
right-join (hash)
 ├── flags: disallow hash join (store left side) and lookup join (into left side) and inverted join (into left side)
 ├── scan straight_join1
 ├── scan straight_join2
 └── filters
      └── straight_join1.x = y

# Only 2 joins are considered (instead of 8) when the STRAIGHT hint is present in one join.
reorderjoins format=hide-all
SELECT * 
FROM straight_join1
INNER STRAIGHT JOIN straight_join2 ON straight_join1.x = straight_join2.y
INNER JOIN straight_join3 ON straight_join1.x = straight_join3.z
----
--------------------------------------------------------------------------------
Join Tree #1
--------------------------------------------------------------------------------
  inner-join (hash)
   ├── inner-join (hash)
   │    ├── flags: disallow hash join (store left side) and lookup join (into left side) and inverted join (into left side)
   │    ├── scan straight_join1
   │    ├── scan straight_join2
   │    └── filters
   │         └── straight_join1.x = y
   ├── scan straight_join3
   └── filters
        └── straight_join1.x = z
Vertexes
  A:
    inner-join (hash)
     ├── flags: disallow hash join (store left side) and lookup join (into left side) and inverted join (into left side)
     ├── scan straight_join1
     ├── scan straight_join2
     └── filters
          └── straight_join1.x = y
  B:
    scan straight_join3
Edges
  straight_join1.x = z [inner, ses=AB, tes=AB, rules=()]
Joining AB
  A B [inner, refs=AB]
  B A [inner, refs=AB]
Joins Considered: 2
================================================================================
Final Plan
================================================================================
inner-join (hash)
 ├── scan straight_join3
 ├── inner-join (hash)
 │    ├── flags: disallow hash join (store left side) and lookup join (into left side) and inverted join (into left side)
 │    ├── scan straight_join1
 │    ├── scan straight_join2
 │    └── filters
 │         └── straight_join1.x = y
 └── filters
      └── straight_join1.x = z

# No joins are considered when the STRAIGHT hint is present in both joins.
reorderjoins format=hide-all
SELECT * 
FROM straight_join1
INNER STRAIGHT JOIN straight_join2 ON straight_join1.x = straight_join2.y
INNER STRAIGHT JOIN straight_join3 ON straight_join1.x = straight_join3.z
----
Joins Considered: 0
================================================================================
Final Plan
================================================================================
inner-join (hash)
 ├── flags: disallow hash join (store left side) and lookup join (into left side) and inverted join (into left side)
 ├── inner-join (hash)
 │    ├── flags: disallow hash join (store left side) and lookup join (into left side) and inverted join (into left side)
 │    ├── scan straight_join1
 │    ├── scan straight_join2
 │    └── filters
 │         └── straight_join1.x = y
 ├── scan straight_join3
 └── filters
      └── straight_join1.x = z
