exec-ddl
CREATE TABLE abc (a INT, b INT, c INT, INDEX ab(a, b))
----
TABLE abc
 ├── a int
 ├── b int
 ├── c int
 ├── rowid int not null (hidden)
 ├── INDEX primary
 │    └── rowid int not null (hidden)
 └── INDEX ab
      ├── a int
      ├── b int
      └── rowid int not null (hidden)

exec-ddl
CREATE TABLE def (d INT, e INT, f INT)
----
TABLE def
 ├── d int
 ├── e int
 ├── f int
 ├── rowid int not null (hidden)
 └── INDEX primary
      └── rowid int not null (hidden)

expr
(InnerJoin
  (Scan [ (Table "abc") (Cols "a,b,c") ])
  (Scan [ (Table "def") (Cols "d,e,f") ])
  [ (Eq (Var "a") (Var "d")) ]
  [ ]
)
----
inner-join
 ├── columns: t.public.abc.a:1(int!null) t.public.abc.b:2(int) t.public.abc.c:3(int) t.public.def.d:5(int!null) t.public.def.e:6(int) t.public.def.f:7(int)
 ├── stats: [rows=10000, distinct(1)=100, null(1)=0, distinct(5)=100, null(5)=0]
 ├── cost: 2270.05
 ├── fd: (1)==(5), (5)==(1)
 ├── prune: (2,3,6,7)
 ├── interesting orderings: (+1,+2)
 ├── scan t.public.abc
 │    ├── columns: t.public.abc.a:1(int) t.public.abc.b:2(int) t.public.abc.c:3(int)
 │    ├── stats: [rows=1000, distinct(1)=100, null(1)=10]
 │    ├── cost: 1070.02
 │    ├── prune: (1-3)
 │    └── interesting orderings: (+1,+2)
 ├── scan t.public.def
 │    ├── columns: t.public.def.d:5(int) t.public.def.e:6(int) t.public.def.f:7(int)
 │    ├── stats: [rows=1000, distinct(5)=100, null(5)=10]
 │    ├── cost: 1070.02
 │    └── prune: (5-7)
 └── filters
      └── eq [type=bool, outer=(1,5), constraints=(/1: (/NULL - ]; /5: (/NULL - ]), fd=(1)==(5), (5)==(1)]
           ├── variable: t.public.abc.a [type=int]
           └── variable: t.public.def.d [type=int]

expr
(MakeLookupJoin
  (Scan [ (Table "def") (Cols "d,e") ])
  [ (JoinType "left-join") (Table "abc") (Index "abc@ab") (KeyCols "a") (Cols "a,b") ]
  [ (Gt (Var "a") (Var "e")) ]
)
----
left-join (lookup abc@ab)
 ├── columns: t.public.abc.a:5(int) t.public.abc.b:6(int)
 ├── key columns: [5] = [5]
 ├── stats: [rows=333333.333]
 ├── cost: 691726.697
 ├── scan t.public.def
 │    ├── columns: t.public.def.d:1(int) t.public.def.e:2(int)
 │    ├── stats: [rows=1000]
 │    ├── cost: 1060.02
 │    └── prune: (1,2)
 └── filters
      └── gt [type=bool, outer=(2,5), constraints=(/2: (/NULL - ]; /5: (/NULL - ])]
           ├── variable: t.public.abc.a [type=int]
           └── variable: t.public.def.e [type=int]

expr
(MergeJoin
  (Sort (Scan [ (Table "abc") (Cols "a,b,c") ]))
  (Sort (Scan [ (Table "def") (Cols "d,e,f") ]))
  [ ]
  [
    (JoinType "inner-join")
    (LeftEq "+a")
    (RightEq "+d")
    (LeftOrdering "+a")
    (RightOrdering "+d")
  ]
)
----
inner-join (merge)
 ├── columns: t.public.abc.a:1(int!null) t.public.abc.b:2(int) t.public.abc.c:3(int) t.public.def.d:5(int!null) t.public.def.e:6(int) t.public.def.f:7(int)
 ├── left ordering: +1
 ├── right ordering: +5
 ├── stats: [rows=10000, distinct(1)=100, null(1)=0, distinct(5)=100, null(5)=0]
 ├── cost: 2698.70137
 ├── fd: (1)==(5), (5)==(1)
 ├── sort
 │    ├── columns: t.public.abc.a:1(int) t.public.abc.b:2(int) t.public.abc.c:3(int)
 │    ├── stats: [rows=1000, distinct(1)=100, null(1)=10]
 │    ├── cost: 1289.34569
 │    ├── ordering: +1
 │    └── scan t.public.abc
 │         ├── columns: t.public.abc.a:1(int) t.public.abc.b:2(int) t.public.abc.c:3(int)
 │         ├── stats: [rows=1000, distinct(1)=100, null(1)=10]
 │         └── cost: 1070.02
 ├── sort
 │    ├── columns: t.public.def.d:5(int) t.public.def.e:6(int) t.public.def.f:7(int)
 │    ├── stats: [rows=1000, distinct(5)=100, null(5)=10]
 │    ├── cost: 1289.34569
 │    ├── ordering: +5
 │    └── scan t.public.def
 │         ├── columns: t.public.def.d:5(int) t.public.def.e:6(int) t.public.def.f:7(int)
 │         ├── stats: [rows=1000, distinct(5)=100, null(5)=10]
 │         └── cost: 1070.02
 └── filters (true)

expr
(InnerJoinApply
  (Sort (Scan [ (Table "abc") (Cols "a,b,c") ]))
  (Select
    (Scan [ (Table "def") (Cols "d,e,f") ])
    [ (Eq (Var "a") (Plus (Var "d") (Var "e"))) ]
  )
  [ ]
  [ ]
)
----
inner-join-apply
 ├── columns: t.public.abc.a:1(int) t.public.abc.b:2(int) t.public.abc.c:3(int) t.public.def.d:5(int) t.public.def.e:6(int) t.public.def.f:7(int)
 ├── stats: [rows=333333.333]
 ├── cost: 5611.39451
 ├── prune: (7)
 ├── sort
 │    ├── columns: t.public.abc.a:1(int) t.public.abc.b:2(int) t.public.abc.c:3(int)
 │    ├── stats: [rows=1000]
 │    ├── cost: 1179.68784
 │    └── scan t.public.abc
 │         ├── columns: t.public.abc.a:1(int) t.public.abc.b:2(int) t.public.abc.c:3(int)
 │         ├── stats: [rows=1000]
 │         └── cost: 1070.02
 ├── select
 │    ├── columns: t.public.def.d:5(int) t.public.def.e:6(int) t.public.def.f:7(int)
 │    ├── outer: (1)
 │    ├── stats: [rows=333.333333]
 │    ├── cost: 1080.03
 │    ├── prune: (7)
 │    ├── scan t.public.def
 │    │    ├── columns: t.public.def.d:5(int) t.public.def.e:6(int) t.public.def.f:7(int)
 │    │    ├── stats: [rows=1000]
 │    │    ├── cost: 1070.02
 │    │    └── prune: (5-7)
 │    └── filters
 │         └── eq [type=bool, outer=(1,5,6), constraints=(/1: (/NULL - ])]
 │              ├── variable: t.public.abc.a [type=int]
 │              └── plus [type=int]
 │                   ├── variable: t.public.def.d [type=int]
 │                   └── variable: t.public.def.e [type=int]
 └── filters (true)

expr
(IndexJoin
  (Scan
    [
      (Table "abc")
      (Index "abc@ab")
      (Cols "a")
      (HardLimit 10)
    ]
  )
  [
    (Table (FindTable "abc"))
    (Cols "c")
  ]
)
----
index-join abc
 ├── columns: t.public.abc.c:3(int)
 ├── cardinality: [0 - 10]
 ├── stats: [rows=10]
 ├── cost: 51.03
 ├── interesting orderings: (+1)
 └── scan t.public.abc@ab
      ├── columns: t.public.abc.a:1(int)
      ├── limit: 10
      ├── stats: [rows=10]
      ├── cost: 10.42
      ├── prune: (1)
      └── interesting orderings: (+1)
