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

exec-ddl
CREATE TABLE small (m INT, n INT)
----
TABLE small
 ├── m int
 ├── n int
 ├── rowid int not null (hidden)
 └── INDEX primary
      └── rowid int not null (hidden)

exec-ddl
ALTER TABLE small INJECT STATISTICS '[
  {
    "columns": ["m"],
    "created_at": "2018-01-01 1:00:00.00000+00:00",
    "row_count": 10,
    "distinct_count": 10
  }
]'
----

# We can only test lookup stat generation when using non-covering indexes
# (that's when we create a group with LookupJoin). We can compare the
# logical properties against those of the top-level join.

opt
SELECT * FROM small JOIN abcd ON a=m
----
inner-join (lookup abcd)
 ├── columns: m:1(int!null) n:2(int) a:4(int!null) b:5(int) c:6(int)
 ├── key columns: [7] = [7]
 ├── fd: (1)==(4), (4)==(1)
 ├── prune: (2,5,6)
 ├── inner-join (lookup abcd@secondary)
 │    ├── columns: m:1(int!null) n:2(int) a:4(int!null) b:5(int) abcd.rowid:7(int!null)
 │    ├── key columns: [1] = [4]
 │    ├── fd: (7)-->(4,5), (1)==(4), (4)==(1)
 │    ├── scan small
 │    │    ├── columns: m:1(int) n:2(int)
 │    │    └── prune: (1,2)
 │    └── true [type=bool]
 └── true [type=bool]

# Filter that gets pushed down on both sides, but comes back into the ON
# condition for the lookup side.
opt
SELECT * FROM small JOIN abcd ON a=m WHERE n > 2
----
inner-join (lookup abcd)
 ├── columns: m:1(int!null) n:2(int!null) a:4(int!null) b:5(int) c:6(int)
 ├── key columns: [7] = [7]
 ├── fd: (1)==(4), (4)==(1)
 ├── prune: (5,6)
 ├── inner-join (lookup abcd@secondary)
 │    ├── columns: m:1(int!null) n:2(int!null) a:4(int!null) b:5(int) abcd.rowid:7(int!null)
 │    ├── key columns: [1] = [4]
 │    ├── fd: (7)-->(4,5), (1)==(4), (4)==(1)
 │    ├── select
 │    │    ├── columns: m:1(int) n:2(int!null)
 │    │    ├── prune: (1)
 │    │    ├── scan small
 │    │    │    ├── columns: m:1(int) n:2(int)
 │    │    │    └── prune: (1,2)
 │    │    └── filters [type=bool, outer=(2), constraints=(/2: [/3 - ]; tight)]
 │    │         └── gt [type=bool, outer=(2), constraints=(/2: [/3 - ]; tight)]
 │    │              ├── variable: n [type=int, outer=(2)]
 │    │              └── const: 2 [type=int]
 │    └── true [type=bool]
 └── true [type=bool]

# Filter that applies to the right side and gets pulled back into the ON
# condition.
opt
SELECT * FROM small JOIN abcd ON a=m WHERE b > 2
----
inner-join (lookup abcd)
 ├── columns: m:1(int!null) n:2(int) a:4(int!null) b:5(int!null) c:6(int)
 ├── key columns: [7] = [7]
 ├── fd: (1)==(4), (4)==(1)
 ├── prune: (2,6)
 ├── inner-join (lookup abcd@secondary)
 │    ├── columns: m:1(int!null) n:2(int) a:4(int!null) b:5(int!null) abcd.rowid:7(int!null)
 │    ├── key columns: [1] = [4]
 │    ├── fd: (7)-->(4,5), (1)==(4), (4)==(1)
 │    ├── scan small
 │    │    ├── columns: m:1(int) n:2(int)
 │    │    └── prune: (1,2)
 │    └── filters [type=bool, outer=(5), constraints=(/5: [/3 - ]; tight)]
 │         └── gt [type=bool, outer=(5), constraints=(/5: [/3 - ]; tight)]
 │              ├── variable: b [type=int, outer=(5)]
 │              └── const: 2 [type=int]
 └── true [type=bool]

# Filter that can only be applied after the primary index join.
opt
SELECT * FROM small JOIN abcd ON a=m WHERE c>2
----
inner-join (lookup abcd)
 ├── columns: m:1(int!null) n:2(int) a:4(int!null) b:5(int) c:6(int!null)
 ├── key columns: [7] = [7]
 ├── fd: (1)==(4), (4)==(1)
 ├── prune: (2,5)
 ├── inner-join (lookup abcd@secondary)
 │    ├── columns: m:1(int!null) n:2(int) a:4(int!null) b:5(int) abcd.rowid:7(int!null)
 │    ├── key columns: [1] = [4]
 │    ├── fd: (7)-->(4,5), (1)==(4), (4)==(1)
 │    ├── scan small
 │    │    ├── columns: m:1(int) n:2(int)
 │    │    └── prune: (1,2)
 │    └── true [type=bool]
 └── filters [type=bool, outer=(6), constraints=(/6: [/3 - ]; tight)]
      └── gt [type=bool, outer=(6), constraints=(/6: [/3 - ]; tight)]
           ├── variable: c [type=int, outer=(6)]
           └── const: 2 [type=int]

# Multiple equalities.
opt
SELECT * FROM small JOIN abcd ON a=m AND b=n WHERE c>2
----
inner-join (lookup abcd)
 ├── columns: m:1(int!null) n:2(int!null) a:4(int!null) b:5(int!null) c:6(int!null)
 ├── key columns: [7] = [7]
 ├── fd: (1)==(4), (4)==(1), (2)==(5), (5)==(2)
 ├── inner-join (lookup abcd@secondary)
 │    ├── columns: m:1(int!null) n:2(int!null) a:4(int!null) b:5(int!null) abcd.rowid:7(int!null)
 │    ├── key columns: [1 2] = [4 5]
 │    ├── fd: (7)-->(4,5), (1)==(4), (4)==(1), (2)==(5), (5)==(2)
 │    ├── scan small
 │    │    ├── columns: m:1(int) n:2(int)
 │    │    └── prune: (1,2)
 │    └── true [type=bool]
 └── filters [type=bool, outer=(6), constraints=(/6: [/3 - ]; tight)]
      └── gt [type=bool, outer=(6), constraints=(/6: [/3 - ]; tight)]
           ├── variable: c [type=int, outer=(6)]
           └── const: 2 [type=int]
