exec-ddl
CREATE TABLE a (k INT PRIMARY KEY, i INT, s STRING, d DECIMAL NOT NULL)
----
TABLE a
 ├── k int not null
 ├── i int
 ├── s string
 ├── d decimal not null
 └── INDEX primary
      └── k int not null

exec-ddl
CREATE TABLE b (x INT, z INT NOT NULL)
----
TABLE b
 ├── x int
 ├── z int not null
 ├── rowid int not null (hidden)
 └── INDEX primary
      └── rowid int not null (hidden)

opt
SELECT k, x FROM a INNER JOIN b ON k=x WHERE d=1.0
----
project
 ├── columns: k:1(int!null) x:5(int!null)
 ├── stats: [rows=100]
 ├── cost: 2124.745
 ├── fd: (1)==(5), (5)==(1)
 └── inner-join
      ├── columns: k:1(int!null) d:4(decimal!null) x:5(int!null)
      ├── stats: [rows=100, distinct(1)=10, null(1)=0, distinct(4)=1, null(4)=0, distinct(5)=10, null(5)=0]
      ├── cost: 2123.735
      ├── fd: ()-->(4), (1)==(5), (5)==(1)
      ├── scan b
      │    ├── columns: x:5(int)
      │    ├── stats: [rows=1000, distinct(5)=100, null(5)=10]
      │    └── cost: 1040.02
      ├── select
      │    ├── columns: k:1(int!null) d:4(decimal!null)
      │    ├── stats: [rows=10, distinct(1)=10, null(1)=0, distinct(4)=1, null(4)=0]
      │    ├── cost: 1070.03
      │    ├── key: (1)
      │    ├── fd: ()-->(4)
      │    ├── scan a
      │    │    ├── columns: k:1(int!null) d:4(decimal!null)
      │    │    ├── stats: [rows=1000, distinct(1)=1000, null(1)=0, distinct(4)=100, null(4)=0]
      │    │    ├── cost: 1060.02
      │    │    ├── key: (1)
      │    │    └── fd: (1)-->(4)
      │    └── filters
      │         └── d = 1.0 [type=bool, outer=(4), constraints=(/4: [/1.0 - /1.0]; tight), fd=()-->(4)]
      └── filters
           └── k = x [type=bool, outer=(1,5), constraints=(/1: (/NULL - ]; /5: (/NULL - ]), fd=(1)==(5), (5)==(1)]

# Verify that we pick merge join if we force it.
opt
SELECT k, x FROM a INNER MERGE JOIN b ON k=x
----
inner-join (merge)
 ├── columns: k:1(int!null) x:5(int!null)
 ├── flags: no-lookup-join;no-hash-join
 ├── left ordering: +1
 ├── right ordering: +5
 ├── stats: [rows=1000, distinct(1)=100, null(1)=0, distinct(5)=100, null(5)=0]
 ├── cost: 2339.37569
 ├── fd: (1)==(5), (5)==(1)
 ├── scan a
 │    ├── columns: k:1(int!null)
 │    ├── stats: [rows=1000, distinct(1)=1000, null(1)=0]
 │    ├── cost: 1050.02
 │    ├── key: (1)
 │    └── ordering: +1
 ├── sort
 │    ├── columns: x:5(int)
 │    ├── stats: [rows=1000, distinct(5)=100, null(5)=10]
 │    ├── cost: 1259.34569
 │    ├── ordering: +5
 │    └── scan b
 │         ├── columns: x:5(int)
 │         ├── stats: [rows=1000, distinct(5)=100, null(5)=10]
 │         └── cost: 1040.02
 └── filters (true)

# Verify that we pick lookup join if we force it. Note that lookup join is only
# possible if b is the left table.
opt
SELECT k, x FROM b INNER LOOKUP JOIN a ON k=x
----
inner-join (lookup a)
 ├── columns: k:4(int!null) x:1(int!null)
 ├── flags: no-merge-join;no-hash-join
 ├── key columns: [1] = [4]
 ├── stats: [rows=1000, distinct(1)=100, null(1)=0, distinct(4)=100, null(4)=0]
 ├── cost: 7090.03
 ├── fd: (1)==(4), (4)==(1)
 ├── scan b
 │    ├── columns: x:1(int)
 │    ├── stats: [rows=1000, distinct(1)=100, null(1)=10]
 │    └── cost: 1040.02
 └── filters (true)


# Verify that if we force lookup join but one isn't possible, the hash join has
# huge cost (this will result in an error if we try to execbuild the result).
opt
SELECT k, x FROM a INNER LOOKUP JOIN b ON k=x
----
inner-join
 ├── columns: k:1(int!null) x:5(int!null)
 ├── flags: no-merge-join;no-hash-join
 ├── stats: [rows=1000, distinct(1)=100, null(1)=0, distinct(5)=100, null(5)=0]
 ├── cost: 1e+100
 ├── fd: (1)==(5), (5)==(1)
 ├── scan a
 │    ├── columns: k:1(int!null)
 │    ├── stats: [rows=1000, distinct(1)=1000, null(1)=0]
 │    ├── cost: 1050.02
 │    └── key: (1)
 ├── scan b
 │    ├── columns: x:5(int)
 │    ├── stats: [rows=1000, distinct(5)=100, null(5)=10]
 │    └── cost: 1040.02
 └── filters
      └── k = x [type=bool, outer=(1,5), constraints=(/1: (/NULL - ]; /5: (/NULL - ]), fd=(1)==(5), (5)==(1)]


exec-ddl
CREATE TABLE abc (a INT PRIMARY KEY, b INT, c INT, INDEX c_idx (c))
----
TABLE abc
 ├── a int not null
 ├── b int
 ├── c int
 ├── INDEX primary
 │    └── a int not null
 └── INDEX c_idx
      ├── c int
      └── a int not null

exec-ddl
ALTER TABLE abc INJECT STATISTICS '[
  {
    "columns": ["a"],
    "created_at": "2018-05-01 1:00:00.00000+00:00",
    "row_count": 500000000,
    "distinct_count": 500000000
  }
]'
----

# Check that we choose the index join when it makes sense.
opt
SELECT * FROM abc WHERE c = 1
----
index-join abc
 ├── columns: a:1(int!null) b:2(int) c:3(int!null)
 ├── stats: [rows=9.9, distinct(1)=9.9, null(1)=0, distinct(3)=1, null(3)=0]
 ├── cost: 50.609
 ├── key: (1)
 ├── fd: ()-->(3), (1)-->(2)
 └── scan abc@c_idx
      ├── columns: a:1(int!null) c:3(int!null)
      ├── constraint: /3/1: [/1 - /1]
      ├── stats: [rows=9.9, distinct(1)=9.9, null(1)=0, distinct(3)=1, null(3)=0]
      ├── cost: 10.306
      ├── key: (1)
      └── fd: ()-->(3)

# Regression test for #34810: make sure we pick the lookup join that uses
# all equality columns.

exec-ddl
CREATE TABLE abcde (
  a TEXT NOT NULL,
  b UUID NOT NULL,
  c UUID NOT NULL,
  d VARCHAR(255) NOT NULL,
  e TEXT NOT NULL,
  CONSTRAINT "primary" PRIMARY KEY (a, b, c),
  UNIQUE INDEX idx_abd (a, b, d),
  UNIQUE INDEX idx_abcd (a, b, c, d)
)
----
TABLE abcde
 ├── a string not null
 ├── b uuid not null
 ├── c uuid not null
 ├── d string not null
 ├── e string not null
 ├── INDEX primary
 │    ├── a string not null
 │    ├── b uuid not null
 │    └── c uuid not null
 ├── INDEX idx_abd
 │    ├── a string not null
 │    ├── b uuid not null
 │    ├── d string not null
 │    └── c uuid not null (storing)
 └── INDEX idx_abcd
      ├── a string not null
      ├── b uuid not null
      ├── c uuid not null
      └── d string not null

exec-ddl
ALTER TABLE abcde INJECT STATISTICS '[
  {
    "columns": ["a"],
    "created_at": "2019-02-08 04:10:40.001179+00:00",
    "row_count": 250000,
    "distinct_count": 1
  },
  {
    "columns": ["b"],
    "created_at": "2019-02-08 04:10:40.119954+00:00",
    "row_count": 250000,
    "distinct_count": 2
  },
  {
    "columns": ["d"],
    "created_at": "2019-02-08 04:10:40.119954+00:00",
    "row_count": 250000,
    "distinct_count": 125000
  }
]'
----

exec-ddl
CREATE TABLE wxyz (
  w TEXT NOT NULL,
  x UUID NOT NULL,
  y UUID NOT NULL,
  z TEXT NOT NULL,
  CONSTRAINT "primary" PRIMARY KEY (w, x, y),
  CONSTRAINT "foreign" FOREIGN KEY (w, x, y) REFERENCES abcde (a, b, c)
)
----
TABLE wxyz
 ├── w string not null
 ├── x uuid not null
 ├── y uuid not null
 ├── z string not null
 ├── INDEX primary
 │    ├── w string not null
 │    ├── x uuid not null
 │    └── y uuid not null
 └── FOREIGN KEY (w, x, y) REFERENCES t.public.abcde (a, b, c)

exec-ddl
ALTER TABLE wxyz INJECT STATISTICS '[
  {
    "columns": ["w"],
    "created_at": "2019-02-08 04:10:40.001179+00:00",
    "row_count": 10000,
    "distinct_count": 1
  },
  {
    "columns": ["x"],
    "created_at": "2019-02-08 04:10:40.119954+00:00",
    "row_count": 10000,
    "distinct_count": 1
  },
  {
    "columns": ["y"],
    "created_at": "2019-02-08 04:10:40.119954+00:00",
    "row_count": 10000,
    "distinct_count": 2500
  }
]'
----

opt
SELECT w, x, y, z
FROM wxyz
INNER JOIN abcde
ON w = a AND x = b AND y = c
WHERE w = 'foo' AND x = '2AB23800-06B1-4E19-A3BB-DF3768B808D2'
ORDER BY d
LIMIT 10
----
project
 ├── columns: w:1(string!null) x:2(uuid!null) y:3(uuid!null) z:4(string!null)  [hidden: d:8(string!null)]
 ├── cardinality: [0 - 10]
 ├── stats: [rows=10]
 ├── cost: 164278.036
 ├── key: (8)
 ├── fd: ()-->(1,2), (3)-->(4,8), (8)-->(3,4)
 ├── ordering: +8 opt(1,2) [actual: +8]
 └── limit
      ├── columns: w:1(string!null) x:2(uuid!null) y:3(uuid!null) z:4(string!null) a:5(string!null) b:6(uuid!null) c:7(uuid!null) d:8(string!null)
      ├── internal-ordering: +8 opt(1,2,5,6)
      ├── cardinality: [0 - 10]
      ├── stats: [rows=10]
      ├── cost: 164277.926
      ├── key: (7)
      ├── fd: ()-->(1,2,5,6), (3)-->(4), (7)-->(8), (8)-->(7), (1)==(5), (5)==(1), (2)==(6), (6)==(2), (3)==(7), (7)==(3)
      ├── ordering: +8 opt(1,2,5,6) [actual: +8]
      ├── sort
      │    ├── columns: w:1(string!null) x:2(uuid!null) y:3(uuid!null) z:4(string!null) a:5(string!null) b:6(uuid!null) c:7(uuid!null) d:8(string!null)
      │    ├── stats: [rows=50048.8759, distinct(1)=1, null(1)=0, distinct(2)=1, null(2)=0, distinct(3)=2500, null(3)=0, distinct(4)=1000, null(4)=0, distinct(5)=1, null(5)=0, distinct(6)=1, null(6)=0, distinct(7)=2500, null(7)=0, distinct(8)=38781.1698, null(8)=0]
      │    ├── cost: 164277.816
      │    ├── key: (7)
      │    ├── fd: ()-->(1,2,5,6), (3)-->(4), (7)-->(8), (8)-->(7), (1)==(5), (5)==(1), (2)==(6), (6)==(2), (3)==(7), (7)==(3)
      │    ├── ordering: +8 opt(1,2,5,6) [actual: +8]
      │    └── inner-join (merge)
      │         ├── columns: w:1(string!null) x:2(uuid!null) y:3(uuid!null) z:4(string!null) a:5(string!null) b:6(uuid!null) c:7(uuid!null) d:8(string!null)
      │         ├── left ordering: +1,+2,+3
      │         ├── right ordering: +5,+6,+7
      │         ├── stats: [rows=50048.8759, distinct(1)=1, null(1)=0, distinct(2)=1, null(2)=0, distinct(3)=2500, null(3)=0, distinct(4)=1000, null(4)=0, distinct(5)=1, null(5)=0, distinct(6)=1, null(6)=0, distinct(7)=2500, null(7)=0, distinct(8)=38781.1698, null(8)=0]
      │         ├── cost: 147650.519
      │         ├── key: (7)
      │         ├── fd: ()-->(1,2,5,6), (3)-->(4), (7)-->(8), (8)-->(7), (1)==(5), (5)==(1), (2)==(6), (6)==(2), (3)==(7), (7)==(3)
      │         ├── scan wxyz
      │         │    ├── columns: w:1(string!null) x:2(uuid!null) y:3(uuid!null) z:4(string!null)
      │         │    ├── constraint: /1/2/3: [/'foo'/'2ab23800-06b1-4e19-a3bb-df3768b808d2' - /'foo'/'2ab23800-06b1-4e19-a3bb-df3768b808d2']
      │         │    ├── stats: [rows=10000, distinct(1)=1, null(1)=0, distinct(2)=1, null(2)=0, distinct(3)=2500, null(3)=0, distinct(4)=1000, null(4)=0]
      │         │    ├── cost: 10800.01
      │         │    ├── key: (3)
      │         │    ├── fd: ()-->(1,2), (3)-->(4)
      │         │    └── ordering: +3 opt(1,2) [actual: +3]
      │         ├── scan abcde@idx_abcd
      │         │    ├── columns: a:5(string!null) b:6(uuid!null) c:7(uuid!null) d:8(string!null)
      │         │    ├── constraint: /5/6/7/8: [/'foo'/'2ab23800-06b1-4e19-a3bb-df3768b808d2' - /'foo'/'2ab23800-06b1-4e19-a3bb-df3768b808d2']
      │         │    ├── stats: [rows=125000, distinct(5)=1, null(5)=0, distinct(6)=1, null(6)=0, distinct(7)=24975.5859, null(7)=0, distinct(8)=93750, null(8)=0]
      │         │    ├── cost: 135000.01
      │         │    ├── key: (7)
      │         │    ├── fd: ()-->(5,6), (7)-->(8), (8)-->(7)
      │         │    └── ordering: +7 opt(5,6) [actual: +7]
      │         └── filters (true)
      └── const: 10 [type=int]
