exec-ddl
CREATE TABLE ltable(
  k int primary key,
  geom geometry
)
----

exec-ddl
CREATE TABLE rtable(
  k int primary key,
  geom geometry,
  INVERTED INDEX geom_index(geom)
)
----

opt
SELECT ltable.k, rtable.k FROM ltable JOIN rtable ON ST_Intersects(ltable.geom, rtable.geom)
----
project
 ├── columns: k:1(int!null) k:5(int!null)
 ├── immutable
 ├── stats: [rows=9801]
 ├── key: (1,5)
 └── inner-join (lookup rtable)
      ├── columns: ltable.k:1(int!null) ltable.geom:2(geometry!null) rtable.k:5(int!null) rtable.geom:6(geometry!null)
      ├── key columns: [10] = [5]
      ├── lookup columns are key
      ├── immutable
      ├── stats: [rows=9801]
      ├── key: (1,5)
      ├── fd: (1)-->(2), (5)-->(6)
      ├── inner-join (inverted rtable@geom_index,inverted)
      │    ├── columns: ltable.k:1(int!null) ltable.geom:2(geometry) rtable.k:10(int!null)
      │    ├── inverted-expr
      │    │    └── st_intersects(ltable.geom:2, rtable.geom:11) [type=bool]
      │    ├── stats: [rows=10000, distinct(10)=999.957, null(10)=0]
      │    ├── key: (1,10)
      │    ├── fd: (1)-->(2)
      │    ├── scan ltable
      │    │    ├── columns: ltable.k:1(int!null) ltable.geom:2(geometry)
      │    │    ├── stats: [rows=1000, distinct(2)=100, null(2)=10]
      │    │    ├── key: (1)
      │    │    └── fd: (1)-->(2)
      │    └── filters (true)
      └── filters
           └── st_intersects(ltable.geom:2, rtable.geom:6) [type=bool, outer=(2,6), immutable, constraints=(/2: (/NULL - ]; /6: (/NULL - ])]

opt
SELECT ltable.k, rtable.k FROM ltable JOIN rtable@geom_index ON ST_Intersects(ltable.geom, rtable.geom)
----
project
 ├── columns: k:1(int!null) k:5(int!null)
 ├── immutable
 ├── stats: [rows=9801]
 ├── key: (1,5)
 └── inner-join (lookup rtable)
      ├── columns: ltable.k:1(int!null) ltable.geom:2(geometry!null) rtable.k:5(int!null) rtable.geom:6(geometry!null)
      ├── key columns: [10] = [5]
      ├── lookup columns are key
      ├── immutable
      ├── stats: [rows=9801]
      ├── key: (1,5)
      ├── fd: (1)-->(2), (5)-->(6)
      ├── inner-join (inverted rtable@geom_index,inverted)
      │    ├── columns: ltable.k:1(int!null) ltable.geom:2(geometry) rtable.k:10(int!null)
      │    ├── inverted-expr
      │    │    └── st_intersects(ltable.geom:2, rtable.geom:11) [type=bool]
      │    ├── stats: [rows=10000, distinct(10)=999.957, null(10)=0]
      │    ├── key: (1,10)
      │    ├── fd: (1)-->(2)
      │    ├── scan ltable
      │    │    ├── columns: ltable.k:1(int!null) ltable.geom:2(geometry)
      │    │    ├── stats: [rows=1000, distinct(2)=100, null(2)=10]
      │    │    ├── key: (1)
      │    │    └── fd: (1)-->(2)
      │    └── filters (true)
      └── filters
           └── st_intersects(ltable.geom:2, rtable.geom:6) [type=bool, outer=(2,6), immutable, constraints=(/2: (/NULL - ]; /6: (/NULL - ])]

exec-ddl
CREATE TABLE json_arr1 (
  k INT PRIMARY KEY,
  j JSONB,
  a STRING[],
  INVERTED INDEX j_idx (j),
  INVERTED INDEX a_idx (a)
)
----

exec-ddl
CREATE TABLE json_arr2 (
  k INT PRIMARY KEY,
  j JSONB,
  a STRING[]
)
----

exec-ddl
ALTER TABLE json_arr1 INJECT STATISTICS '[
  {
    "columns": ["j"],
    "created_at": "2018-01-01 1:00:00.00000+00:00",
    "row_count": 1000,
    "distinct_count": 1000,
    "avg_size": 25
  }
]'
----

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

opt
SELECT t1.k
FROM json_arr1 AS t1
JOIN json_arr2 AS t2
ON t1.j @> t2.j
----
project
 ├── columns: k:1(int!null)
 ├── immutable
 ├── stats: [rows=100]
 └── inner-join (lookup json_arr1 [as=t1])
      ├── columns: t1.k:1(int!null) t1.j:2(jsonb) t2.j:9(jsonb)
      ├── key columns: [13] = [1]
      ├── lookup columns are key
      ├── immutable
      ├── stats: [rows=100]
      ├── fd: (1)-->(2)
      ├── inner-join (inverted json_arr1@j_idx,inverted [as=t1])
      │    ├── columns: t2.j:9(jsonb) t1.k:13(int!null)
      │    ├── inverted-expr
      │    │    └── t1.j:14 @> t2.j:9 [type=bool]
      │    ├── stats: [rows=100, distinct(13)=95.6179, null(13)=0]
      │    ├── scan json_arr2 [as=t2]
      │    │    ├── columns: t2.j:9(jsonb)
      │    │    └── stats: [rows=10]
      │    └── filters (true)
      └── filters
           └── t1.j:2 @> t2.j:9 [type=bool, outer=(2,9), immutable]

# TODO(rytaft): The following two inverted joins have the same estimated row
# count even though the first one has an extra conjunct in the inverted
# expression. The first one should have a lower estimated row count.
opt
SELECT *
FROM json_arr1 AS t1
JOIN json_arr2 AS t2
ON t1.a @> t2.a AND t1.a @> '{"foo"}'::string[] AND t2.k > 5
----
inner-join (lookup json_arr1 [as=t1])
 ├── columns: k:1(int!null) j:2(jsonb) a:3(string[]!null) k:8(int!null) j:9(jsonb) a:10(string[])
 ├── key columns: [13] = [1]
 ├── lookup columns are key
 ├── immutable
 ├── stats: [rows=3.666667]
 ├── key: (1,8)
 ├── fd: (1)-->(2,3), (8)-->(9,10)
 ├── inner-join (inverted json_arr1@a_idx,inverted [as=t1])
 │    ├── columns: t2.k:8(int!null) t2.j:9(jsonb) t2.a:10(string[]) t1.k:13(int!null)
 │    ├── inverted-expr
 │    │    └── (t1.a:15 @> t2.a:10) AND (t1.a:15 @> ARRAY['foo']) [type=bool]
 │    ├── stats: [rows=33.33333, distinct(13)=32.9462, null(13)=0]
 │    ├── key: (8,13)
 │    ├── fd: (8)-->(9,10)
 │    ├── scan json_arr2 [as=t2]
 │    │    ├── columns: t2.k:8(int!null) t2.j:9(jsonb) t2.a:10(string[])
 │    │    ├── constraint: /8: [/6 - ]
 │    │    ├── stats: [rows=3.333333, distinct(8)=3.33333, null(8)=0]
 │    │    ├── key: (8)
 │    │    └── fd: (8)-->(9,10)
 │    └── filters (true)
 └── filters
      ├── t1.a:3 @> t2.a:10 [type=bool, outer=(3,10), immutable]
      └── t1.a:3 @> ARRAY['foo'] [type=bool, outer=(3), immutable, constraints=(/3: (/NULL - ])]

opt
SELECT t2.k
FROM json_arr1 AS t1
JOIN json_arr2 AS t2
ON t1.a @> t2.a AND t1.j @> t2.j AND t2.k > 5
----
project
 ├── columns: k:8(int!null)
 ├── immutable
 ├── stats: [rows=33.33333]
 └── inner-join (lookup json_arr1 [as=t1])
      ├── columns: t1.j:2(jsonb) t1.a:3(string[]) t2.k:8(int!null) t2.j:9(jsonb) t2.a:10(string[])
      ├── key columns: [13] = [1]
      ├── lookup columns are key
      ├── immutable
      ├── stats: [rows=33.33333]
      ├── fd: (8)-->(9,10)
      ├── inner-join (inverted json_arr1@j_idx,inverted [as=t1])
      │    ├── columns: t2.k:8(int!null) t2.j:9(jsonb) t2.a:10(string[]) t1.k:13(int!null)
      │    ├── inverted-expr
      │    │    └── t1.j:14 @> t2.j:9 [type=bool]
      │    ├── stats: [rows=33.33333, distinct(13)=32.9462, null(13)=0]
      │    ├── key: (8,13)
      │    ├── fd: (8)-->(9,10)
      │    ├── scan json_arr2 [as=t2]
      │    │    ├── columns: t2.k:8(int!null) t2.j:9(jsonb) t2.a:10(string[])
      │    │    ├── constraint: /8: [/6 - ]
      │    │    ├── stats: [rows=3.333333, distinct(8)=3.33333, null(8)=0]
      │    │    ├── key: (8)
      │    │    └── fd: (8)-->(9,10)
      │    └── filters (true)
      └── filters
           ├── t1.a:3 @> t2.a:10 [type=bool, outer=(3,10), immutable]
           └── t1.j:2 @> t2.j:9 [type=bool, outer=(2,9), immutable]

opt
SELECT t1.k
FROM json_arr1 AS t1
JOIN json_arr2 AS t2
ON t1.j <@ t2.j
----
project
 ├── columns: k:1(int!null)
 ├── immutable
 ├── stats: [rows=3333.333]
 └── inner-join (lookup json_arr1 [as=t1])
      ├── columns: t1.k:1(int!null) t1.j:2(jsonb) t2.j:9(jsonb)
      ├── key columns: [13] = [1]
      ├── lookup columns are key
      ├── immutable
      ├── stats: [rows=3333.333]
      ├── fd: (1)-->(2)
      ├── inner-join (inverted json_arr1@j_idx,inverted [as=t1])
      │    ├── columns: t2.j:9(jsonb) t1.k:13(int!null)
      │    ├── inverted-expr
      │    │    └── t1.j:14 <@ t2.j:9 [type=bool]
      │    ├── stats: [rows=100, distinct(13)=95.6179, null(13)=0]
      │    ├── scan json_arr2 [as=t2]
      │    │    ├── columns: t2.j:9(jsonb)
      │    │    └── stats: [rows=10]
      │    └── filters (true)
      └── filters
           └── t1.j:2 <@ t2.j:9 [type=bool, outer=(2,9), immutable]

opt
SELECT *
FROM json_arr1 AS t1
JOIN json_arr2 AS t2
ON t1.a <@ t2.a AND t1.a <@ '{"foo"}'::string[] AND t2.k > 5
----
inner-join (lookup json_arr1 [as=t1])
 ├── columns: k:1(int!null) j:2(jsonb) a:3(string[]) k:8(int!null) j:9(jsonb) a:10(string[])
 ├── key columns: [13] = [1]
 ├── lookup columns are key
 ├── immutable
 ├── stats: [rows=370.3704]
 ├── key: (1,8)
 ├── fd: (1)-->(2,3), (8)-->(9,10)
 ├── inner-join (inverted json_arr1@a_idx,inverted [as=t1])
 │    ├── columns: t2.k:8(int!null) t2.j:9(jsonb) t2.a:10(string[]) t1.k:13(int!null)
 │    ├── inverted-expr
 │    │    └── (t1.a:15 <@ t2.a:10) AND (t1.a:15 <@ ARRAY['foo']) [type=bool]
 │    ├── stats: [rows=33.33333, distinct(13)=32.9462, null(13)=0]
 │    ├── key: (8,13)
 │    ├── fd: (8)-->(9,10)
 │    ├── scan json_arr2 [as=t2]
 │    │    ├── columns: t2.k:8(int!null) t2.j:9(jsonb) t2.a:10(string[])
 │    │    ├── constraint: /8: [/6 - ]
 │    │    ├── stats: [rows=3.333333, distinct(8)=3.33333, null(8)=0]
 │    │    ├── key: (8)
 │    │    └── fd: (8)-->(9,10)
 │    └── filters (true)
 └── filters
      ├── t1.a:3 <@ t2.a:10 [type=bool, outer=(3,10), immutable]
      └── t1.a:3 <@ ARRAY['foo'] [type=bool, outer=(3), immutable]

opt
SELECT t2.k
FROM json_arr1 AS t1
JOIN json_arr2 AS t2
ON t1.a <@ t2.a AND t1.j <@ t2.j AND t2.k > 5
----
project
 ├── columns: k:8(int!null)
 ├── immutable
 ├── stats: [rows=370.3704]
 └── inner-join (lookup json_arr1 [as=t1])
      ├── columns: t1.j:2(jsonb) t1.a:3(string[]) t2.k:8(int!null) t2.j:9(jsonb) t2.a:10(string[])
      ├── key columns: [13] = [1]
      ├── lookup columns are key
      ├── immutable
      ├── stats: [rows=370.3704]
      ├── fd: (8)-->(9,10)
      ├── inner-join (inverted json_arr1@j_idx,inverted [as=t1])
      │    ├── columns: t2.k:8(int!null) t2.j:9(jsonb) t2.a:10(string[]) t1.k:13(int!null)
      │    ├── inverted-expr
      │    │    └── t1.j:14 <@ t2.j:9 [type=bool]
      │    ├── stats: [rows=33.33333, distinct(13)=32.9462, null(13)=0]
      │    ├── key: (8,13)
      │    ├── fd: (8)-->(9,10)
      │    ├── scan json_arr2 [as=t2]
      │    │    ├── columns: t2.k:8(int!null) t2.j:9(jsonb) t2.a:10(string[])
      │    │    ├── constraint: /8: [/6 - ]
      │    │    ├── stats: [rows=3.333333, distinct(8)=3.33333, null(8)=0]
      │    │    ├── key: (8)
      │    │    └── fd: (8)-->(9,10)
      │    └── filters (true)
      └── filters
           ├── t1.a:3 <@ t2.a:10 [type=bool, outer=(3,10), immutable]
           └── t1.j:2 <@ t2.j:9 [type=bool, outer=(2,9), immutable]

exec-ddl
CREATE TABLE json_multi_col (
  k INT PRIMARY KEY,
  j JSONB,
  s STRING,
  i INT,
  INVERTED INDEX sj_idx (s, j)
)
----

exec-ddl
ALTER TABLE json_multi_col INJECT STATISTICS '[
  {
    "columns": ["j"],
    "created_at": "2018-01-01 1:00:00.00000+00:00",
    "row_count": 1000,
    "distinct_count": 1000,
    "null_count": 0,
    "avg_size": 34
  },
  {
    "columns": ["s"],
    "created_at": "2018-01-01 1:00:00.00000+00:00",
    "row_count": 1000,
    "distinct_count": 100,
    "null_count": 0,
    "avg_size": 12
  },
  {
    "columns": ["i"],
    "created_at": "2018-01-01 1:00:00.00000+00:00",
    "row_count": 1000,
    "distinct_count": 10,
    "null_count": 0,
    "avg_size": 2
  }
]'
----

opt
SELECT t1.k
FROM json_multi_col AS t1
JOIN json_arr2 AS t2
ON t1.s IN ('foo', 'bar', 'baz') AND t1.j @> t2.j
----
project
 ├── columns: k:1(int!null)
 ├── immutable
 ├── stats: [rows=3]
 └── inner-join (lookup json_multi_col [as=t1])
      ├── columns: t1.k:1(int!null) t1.j:2(jsonb) s:3(string!null) t2.j:9(jsonb)
      ├── key columns: [14] = [1]
      ├── lookup columns are key
      ├── immutable
      ├── stats: [rows=3]
      ├── fd: (1)-->(2,3)
      ├── inner-join (inverted json_multi_col@sj_idx,inverted [as=t1])
      │    ├── columns: t2.j:9(jsonb) t1.k:14(int!null) s:16(string!null)
      │    ├── prefix key columns: [13] = [16]
      │    ├── inverted-expr
      │    │    └── t1.j:15 @> t2.j:9 [type=bool]
      │    ├── stats: [rows=3, distinct(13)=3, null(13)=0, distinct(14)=2.99565, null(14)=0, distinct(16)=3, null(16)=0]
      │    ├── fd: (14)-->(16)
      │    ├── inner-join (cross)
      │    │    ├── columns: t2.j:9(jsonb) "inverted_join_const_col_@3":13(string!null)
      │    │    ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-more)
      │    │    ├── stats: [rows=30, distinct(13)=3, null(13)=0]
      │    │    ├── scan json_arr2 [as=t2]
      │    │    │    ├── columns: t2.j:9(jsonb)
      │    │    │    └── stats: [rows=10]
      │    │    ├── values
      │    │    │    ├── columns: "inverted_join_const_col_@3":13(string!null)
      │    │    │    ├── cardinality: [3 - 3]
      │    │    │    ├── stats: [rows=3, distinct(13)=3, null(13)=0]
      │    │    │    ├── ('bar',) [type=tuple{string}]
      │    │    │    ├── ('baz',) [type=tuple{string}]
      │    │    │    └── ('foo',) [type=tuple{string}]
      │    │    └── filters (true)
      │    └── filters (true)
      └── filters
           └── t1.j:2 @> t2.j:9 [type=bool, outer=(2,9), immutable]

exec-ddl
DROP INDEX sj_idx
----

exec-ddl
CREATE INVERTED INDEX sij_idx ON json_multi_col (s, i, j)
----

opt
SELECT t1.k
FROM json_multi_col AS t1
JOIN json_arr2 AS t2
ON t1.s IN ('foo', 'bar', 'baz') AND i = 10 AND t1.j @> t2.j
----
project
 ├── columns: k:1(int!null)
 ├── immutable
 ├── stats: [rows=0.3]
 └── inner-join (lookup json_multi_col [as=t1])
      ├── columns: t1.k:1(int!null) t1.j:2(jsonb) s:3(string!null) i:4(int!null) t2.j:10(jsonb)
      ├── key columns: [16] = [1]
      ├── lookup columns are key
      ├── immutable
      ├── stats: [rows=0.3]
      ├── fd: ()-->(4), (1)-->(2,3)
      ├── inner-join (inverted json_multi_col@sij_idx,inverted [as=t1])
      │    ├── columns: t2.j:10(jsonb) t1.k:16(int!null) s:18(string!null) i:19(int!null)
      │    ├── prefix key columns: [14 15] = [18 19]
      │    ├── inverted-expr
      │    │    └── t1.j:17 @> t2.j:10 [type=bool]
      │    ├── stats: [rows=0.3, distinct(14)=0.3, null(14)=0, distinct(15)=0.3, null(15)=0, distinct(16)=0.299957, null(16)=0, distinct(18)=0.3, null(18)=0, distinct(19)=0.3, null(19)=0, distinct(18,19)=0.3, null(18,19)=0]
      │    ├── fd: ()-->(19), (16)-->(18)
      │    ├── project
      │    │    ├── columns: "inverted_join_const_col_@4":15(int!null) t2.j:10(jsonb) "inverted_join_const_col_@3":14(string!null)
      │    │    ├── stats: [rows=30, distinct(14)=3, null(14)=0, distinct(15)=1, null(15)=0]
      │    │    ├── fd: ()-->(15)
      │    │    ├── inner-join (cross)
      │    │    │    ├── columns: t2.j:10(jsonb) "inverted_join_const_col_@3":14(string!null)
      │    │    │    ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-more)
      │    │    │    ├── stats: [rows=30, distinct(14)=3, null(14)=0]
      │    │    │    ├── scan json_arr2 [as=t2]
      │    │    │    │    ├── columns: t2.j:10(jsonb)
      │    │    │    │    └── stats: [rows=10]
      │    │    │    ├── values
      │    │    │    │    ├── columns: "inverted_join_const_col_@3":14(string!null)
      │    │    │    │    ├── cardinality: [3 - 3]
      │    │    │    │    ├── stats: [rows=3, distinct(14)=3, null(14)=0]
      │    │    │    │    ├── ('bar',) [type=tuple{string}]
      │    │    │    │    ├── ('baz',) [type=tuple{string}]
      │    │    │    │    └── ('foo',) [type=tuple{string}]
      │    │    │    └── filters (true)
      │    │    └── projections
      │    │         └── 10 [as="inverted_join_const_col_@4":15, type=int]
      │    └── filters (true)
      └── filters
           └── t1.j:2 @> t2.j:10 [type=bool, outer=(2,10), immutable]

exec-ddl
DROP INDEX sij_idx
----

exec-ddl
CREATE INVERTED INDEX ij_idx ON json_multi_col (i, j)
----

opt
SELECT t1.k
FROM json_multi_col AS t1
JOIN json_arr2 AS t2
ON t1.i = t2.k AND t1.j @> t2.j
----
project
 ├── columns: k:1(int!null)
 ├── immutable
 ├── stats: [rows=10]
 ├── key: (1)
 └── inner-join (lookup json_multi_col [as=t1])
      ├── columns: t1.k:1(int!null) t1.j:2(jsonb) i:4(int!null) t2.k:10(int!null) t2.j:11(jsonb)
      ├── key columns: [15] = [1]
      ├── lookup columns are key
      ├── immutable
      ├── stats: [rows=10, distinct(4)=10, null(4)=0, distinct(10)=10, null(10)=0]
      ├── key: (1)
      ├── fd: (1)-->(2,4), (10)-->(11), (4)==(10), (10)==(4)
      ├── inner-join (inverted json_multi_col@ij_idx,inverted [as=t1])
      │    ├── columns: t2.k:10(int!null) t2.j:11(jsonb) t1.k:15(int!null) i:18(int!null)
      │    ├── prefix key columns: [10] = [18]
      │    ├── inverted-expr
      │    │    └── t1.j:16 @> t2.j:11 [type=bool]
      │    ├── stats: [rows=10, distinct(10)=10, null(10)=0, distinct(15)=9.95512, null(15)=0, distinct(18)=10, null(18)=0]
      │    ├── key: (15)
      │    ├── fd: (10)-->(11), (15)-->(18), (10)==(18), (18)==(10)
      │    ├── scan json_arr2 [as=t2]
      │    │    ├── columns: t2.k:10(int!null) t2.j:11(jsonb)
      │    │    ├── stats: [rows=10, distinct(10)=10, null(10)=0]
      │    │    ├── key: (10)
      │    │    └── fd: (10)-->(11)
      │    └── filters (true)
      └── filters
           └── t1.j:2 @> t2.j:11 [type=bool, outer=(2,11), immutable]
