diff --git a/pkg/ccl/logictestccl/testdata/logic_test/regional_by_row_query_behavior b/pkg/ccl/logictestccl/testdata/logic_test/regional_by_row_query_behavior index 3e362de0b236..7eccaff61e07 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/regional_by_row_query_behavior +++ b/pkg/ccl/logictestccl/testdata/logic_test/regional_by_row_query_behavior @@ -2080,43 +2080,25 @@ SELECT * FROM [EXPLAIN UPSERT INTO regional_by_row_table_virt (pk, a, b) VALUES │ │ │ └── • error if rows │ │ -│ └── • limit -│ │ count: 1 +│ └── • lookup join (semi) +│ │ table: regional_by_row_table_virt@regional_by_row_table_virt_v_key +│ │ lookup condition: (v_comp = v) AND (crdb_region IN ('ap-southeast-2', 'ca-central-1', 'us-east-1')) +│ │ pred: (upsert_pk != pk) OR (upsert_crdb_region != crdb_region) │ │ -│ └── • lookup join -│ │ table: regional_by_row_table_virt@regional_by_row_table_virt_v_key -│ │ equality: (lookup_join_const_col_@30, v_comp) = (crdb_region,v) -│ │ equality cols are key -│ │ pred: (upsert_pk != pk) OR (upsert_crdb_region != crdb_region) -│ │ -│ └── • cross join -│ │ -│ ├── • values -│ │ size: 1 column, 3 rows -│ │ -│ └── • scan buffer -│ label: buffer 1 +│ └── • scan buffer +│ label: buffer 1 │ └── • constraint-check │ └── • error if rows │ - └── • limit - │ count: 1 + └── • lookup join (semi) + │ table: regional_by_row_table_virt@regional_by_row_table_virt_expr_key + │ lookup condition: (crdb_internal_idx_expr_comp = crdb_internal_idx_expr) AND (crdb_region IN ('ap-southeast-2', 'ca-central-1', 'us-east-1')) + │ pred: (upsert_pk != pk) OR (upsert_crdb_region != crdb_region) │ - └── • lookup join - │ table: regional_by_row_table_virt@regional_by_row_table_virt_expr_key - │ equality: (lookup_join_const_col_@44, crdb_internal_idx_expr_comp) = (crdb_region,crdb_internal_idx_expr) - │ equality cols are key - │ pred: (upsert_pk != pk) OR (upsert_crdb_region != crdb_region) - │ - └── • cross join - │ - ├── • values - │ size: 1 column, 3 rows - │ - └── • scan buffer - label: buffer 1 + └── • scan buffer + label: buffer 1 statement ok INSERT INTO regional_by_row_table_virt (pk, a, b) VALUES (1, 1, 1) diff --git a/pkg/sql/logictest/testdata/logic_test/lookup_join_spans b/pkg/sql/logictest/testdata/logic_test/lookup_join_spans index a519abd07d8b..c9201e6b40d2 100644 --- a/pkg/sql/logictest/testdata/logic_test/lookup_join_spans +++ b/pkg/sql/logictest/testdata/logic_test/lookup_join_spans @@ -462,7 +462,7 @@ FROM metric_values as v INNER JOIN metrics as m ON metric_id=id WHERE - time < '2020-01-01 00:00:10+00:00' AND + time < '2020-01-01 00:00:10+00:00' AND name='cpu' AND v.nullable = m.nullable ORDER BY value diff --git a/pkg/sql/opt/exec/execbuilder/testdata/lookup_join_spans b/pkg/sql/opt/exec/execbuilder/testdata/lookup_join_spans index a2dd57f22e9e..31f28e40e2a4 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/lookup_join_spans +++ b/pkg/sql/opt/exec/execbuilder/testdata/lookup_join_spans @@ -915,27 +915,15 @@ vectorized: true │ columns: (s_i_id, ol_o_id) │ estimated row count: 196 (missing stats) │ -└── • project +└── • lookup join (inner) │ columns: (s_i_id, ol_o_id, ol_i_id) │ estimated row count: 196 (missing stats) + │ table: order_line@ol_io + │ lookup condition: (s_i_id = ol_i_id) AND (ol_o_id IN (20, 21)) + │ pred: (ol_o_id IN (19, 20, 21)) AND (ol_o_id >= 20) │ - └── • lookup join (inner) - │ columns: (s_i_id, "lookup_join_const_col_@5", ol_o_id, ol_i_id) - │ table: order_line@ol_io - │ equality: (s_i_id, lookup_join_const_col_@5) = (ol_i_id,ol_o_id) - │ - └── • cross join (inner) - │ columns: (s_i_id, "lookup_join_const_col_@5") - │ estimated row count: 2,000 (missing stats) - │ - ├── • scan - │ columns: (s_i_id) - │ estimated row count: 1,000 (missing stats) - │ table: stock@stock_pkey - │ spans: FULL SCAN - │ - └── • values - columns: ("lookup_join_const_col_@5") - size: 1 column, 2 rows - row 0, expr 0: 20 - row 1, expr 0: 21 + └── • scan + columns: (s_i_id) + estimated row count: 1,000 (missing stats) + table: stock@stock_pkey + spans: FULL SCAN diff --git a/pkg/sql/opt/exec/execbuilder/testdata/unique b/pkg/sql/opt/exec/execbuilder/testdata/unique index eb059fb6ecb2..a6995d303e76 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/unique +++ b/pkg/sql/opt/exec/execbuilder/testdata/unique @@ -747,51 +747,20 @@ vectorized: true │ │ columns: (column3) │ │ estimated row count: 1 (missing stats) │ │ -│ └── • project +│ └── • lookup join (semi) │ │ columns: (column1, column3) │ │ estimated row count: 1 (missing stats) +│ │ table: uniq_enum@uniq_enum_pkey +│ │ lookup condition: (column3 = i) AND (r IN ('us-east', 'us-west', 'eu-west')) +│ │ pred: column1 != r │ │ -│ └── • distinct -│ │ columns: (column1, column3, rownum) -│ │ estimated row count: 2 (missing stats) -│ │ distinct on: rownum +│ └── • project +│ │ columns: (column1, column3) +│ │ estimated row count: 2 │ │ -│ └── • project -│ │ columns: (column1, column3, rownum) -│ │ -│ └── • project -│ │ columns: (r, i, column1, column3, rownum) -│ │ estimated row count: 7 (missing stats) -│ │ -│ └── • lookup join (inner) -│ │ columns: ("lookup_join_const_col_@12", column1, column3, rownum, r, i) -│ │ table: uniq_enum@uniq_enum_pkey -│ │ equality: (lookup_join_const_col_@12, column3) = (r,i) -│ │ equality cols are key -│ │ pred: column1 != r -│ │ -│ └── • cross join (inner) -│ │ columns: ("lookup_join_const_col_@12", column1, column3, rownum) -│ │ estimated row count: 6 -│ │ -│ ├── • values -│ │ columns: ("lookup_join_const_col_@12") -│ │ size: 1 column, 3 rows -│ │ row 0, expr 0: 'us-east' -│ │ row 1, expr 0: 'us-west' -│ │ row 2, expr 0: 'eu-west' -│ │ -│ └── • ordinality -│ │ columns: (column1, column3, rownum) -│ │ estimated row count: 2 -│ │ -│ └── • project -│ │ columns: (column1, column3) -│ │ estimated row count: 2 -│ │ -│ └── • scan buffer -│ columns: (column1, column2, column3, column4, check1) -│ label: buffer 1 +│ └── • scan buffer +│ columns: (column1, column2, column3, column4, check1) +│ label: buffer 1 │ └── • constraint-check │ @@ -802,51 +771,20 @@ vectorized: true │ columns: (column2, column4) │ estimated row count: 1 (missing stats) │ - └── • project + └── • lookup join (semi) │ columns: (column1, column2, column3, column4) │ estimated row count: 1 (missing stats) + │ table: uniq_enum@uniq_enum_r_s_j_key + │ lookup condition: ((column2 = s) AND (column4 = j)) AND (r IN ('us-east', 'us-west', 'eu-west')) + │ pred: (column1 != r) OR (column3 != i) │ - └── • distinct - │ columns: (column1, column2, column3, column4, rownum) - │ estimated row count: 0 (missing stats) - │ distinct on: rownum + └── • project + │ columns: (column1, column2, column3, column4) + │ estimated row count: 2 │ - └── • project - │ columns: (column1, column2, column3, column4, rownum) - │ - └── • project - │ columns: (r, s, i, j, column1, column2, column3, column4, rownum) - │ estimated row count: 0 (missing stats) - │ - └── • lookup join (inner) - │ columns: ("lookup_join_const_col_@22", column1, column2, column3, column4, rownum, r, s, i, j) - │ table: uniq_enum@uniq_enum_r_s_j_key - │ equality: (lookup_join_const_col_@22, column2, column4) = (r,s,j) - │ equality cols are key - │ pred: (column1 != r) OR (column3 != i) - │ - └── • cross join (inner) - │ columns: ("lookup_join_const_col_@22", column1, column2, column3, column4, rownum) - │ estimated row count: 6 - │ - ├── • values - │ columns: ("lookup_join_const_col_@22") - │ size: 1 column, 3 rows - │ row 0, expr 0: 'us-east' - │ row 1, expr 0: 'us-west' - │ row 2, expr 0: 'eu-west' - │ - └── • ordinality - │ columns: (column1, column2, column3, column4, rownum) - │ estimated row count: 2 - │ - └── • project - │ columns: (column1, column2, column3, column4) - │ estimated row count: 2 - │ - └── • scan buffer - columns: (column1, column2, column3, column4, check1) - label: buffer 1 + └── • scan buffer + columns: (column1, column2, column3, column4, check1) + label: buffer 1 # Test that we use the index when available for the insert checks. This uses # the default value for columns r and j. @@ -902,51 +840,20 @@ vectorized: true │ columns: (column2) │ estimated row count: 1 (missing stats) │ - └── • project + └── • lookup join (semi) │ columns: (r_default, column2) │ estimated row count: 1 (missing stats) + │ table: uniq_enum@uniq_enum_pkey + │ lookup condition: (column2 = i) AND (r IN ('us-east', 'us-west', 'eu-west')) + │ pred: r_default != r │ - └── • distinct - │ columns: (r_default, column2, rownum) - │ estimated row count: 2 (missing stats) - │ distinct on: rownum + └── • project + │ columns: (r_default, column2) + │ estimated row count: 2 │ - └── • project - │ columns: (r_default, column2, rownum) - │ - └── • project - │ columns: (r, i, r_default, column2, rownum) - │ estimated row count: 7 (missing stats) - │ - └── • lookup join (inner) - │ columns: ("lookup_join_const_col_@12", r_default, column2, rownum, r, i) - │ table: uniq_enum@uniq_enum_pkey - │ equality: (lookup_join_const_col_@12, column2) = (r,i) - │ equality cols are key - │ pred: r_default != r - │ - └── • cross join (inner) - │ columns: ("lookup_join_const_col_@12", r_default, column2, rownum) - │ estimated row count: 6 - │ - ├── • values - │ columns: ("lookup_join_const_col_@12") - │ size: 1 column, 3 rows - │ row 0, expr 0: 'us-east' - │ row 1, expr 0: 'us-west' - │ row 2, expr 0: 'eu-west' - │ - └── • ordinality - │ columns: (r_default, column2, rownum) - │ estimated row count: 2 - │ - └── • project - │ columns: (r_default, column2) - │ estimated row count: 2 - │ - └── • scan buffer - columns: (r_default, column1, column2, j_default, check1) - label: buffer 1 + └── • scan buffer + columns: (r_default, column1, column2, j_default, check1) + label: buffer 1 # Test that we use the index when available for de-duplicating INSERT ON # CONFLICT DO NOTHING rows before inserting. @@ -2219,51 +2126,20 @@ vectorized: true │ │ columns: (i_new) │ │ estimated row count: 3 (missing stats) │ │ -│ └── • project +│ └── • lookup join (semi) │ │ columns: (r_new, i_new) │ │ estimated row count: 3 (missing stats) +│ │ table: uniq_enum@uniq_enum_pkey +│ │ lookup condition: (i_new = i) AND (r IN ('us-east', 'us-west', 'eu-west')) +│ │ pred: r_new != r │ │ -│ └── • distinct -│ │ columns: (r_new, i_new, rownum) +│ └── • project +│ │ columns: (r_new, i_new) │ │ estimated row count: 9 (missing stats) -│ │ distinct on: rownum │ │ -│ └── • project -│ │ columns: (r_new, i_new, rownum) -│ │ -│ └── • project -│ │ columns: (r, i, r_new, i_new, rownum) -│ │ estimated row count: 31 (missing stats) -│ │ -│ └── • lookup join (inner) -│ │ columns: (r_new, i_new, rownum, "lookup_join_const_col_@17", r, i) -│ │ table: uniq_enum@uniq_enum_pkey -│ │ equality: (lookup_join_const_col_@17, i_new) = (r,i) -│ │ equality cols are key -│ │ pred: r_new != r -│ │ -│ └── • cross join (inner) -│ │ columns: (r_new, i_new, rownum, "lookup_join_const_col_@17") -│ │ estimated row count: 28 (missing stats) -│ │ -│ ├── • ordinality -│ │ │ columns: (r_new, i_new, rownum) -│ │ │ estimated row count: 9 (missing stats) -│ │ │ -│ │ └── • project -│ │ │ columns: (r_new, i_new) -│ │ │ estimated row count: 9 (missing stats) -│ │ │ -│ │ └── • scan buffer -│ │ columns: (r, s, i, j, r_new, s_new, i_new, check1) -│ │ label: buffer 1 -│ │ -│ └── • values -│ columns: ("lookup_join_const_col_@17") -│ size: 1 column, 3 rows -│ row 0, expr 0: 'us-east' -│ row 1, expr 0: 'us-west' -│ row 2, expr 0: 'eu-west' +│ └── • scan buffer +│ columns: (r, s, i, j, r_new, s_new, i_new, check1) +│ label: buffer 1 │ └── • constraint-check │ @@ -2274,51 +2150,20 @@ vectorized: true │ columns: (s_new, j) │ estimated row count: 3 (missing stats) │ - └── • project + └── • lookup join (semi) │ columns: (r_new, s_new, i_new, j) │ estimated row count: 3 (missing stats) + │ table: uniq_enum@uniq_enum_r_s_j_key + │ lookup condition: ((s_new = s) AND (j = j)) AND (r IN ('us-east', 'us-west', 'eu-west')) + │ pred: (r_new != r) OR (i_new != i) │ - └── • distinct - │ columns: (r_new, s_new, i_new, j, rownum) - │ estimated row count: 0 (missing stats) - │ distinct on: rownum + └── • project + │ columns: (r_new, s_new, i_new, j) + │ estimated row count: 9 (missing stats) │ - └── • project - │ columns: (r_new, s_new, i_new, j, rownum) - │ - └── • project - │ columns: (r, s, i, j, r_new, s_new, i_new, j, rownum) - │ estimated row count: 0 (missing stats) - │ - └── • lookup join (inner) - │ columns: (r_new, s_new, i_new, j, rownum, "lookup_join_const_col_@27", r, s, i, j) - │ table: uniq_enum@uniq_enum_r_s_j_key - │ equality: (lookup_join_const_col_@27, s_new, j) = (r,s,j) - │ equality cols are key - │ pred: (r_new != r) OR (i_new != i) - │ - └── • cross join (inner) - │ columns: (r_new, s_new, i_new, j, rownum, "lookup_join_const_col_@27") - │ estimated row count: 28 (missing stats) - │ - ├── • ordinality - │ │ columns: (r_new, s_new, i_new, j, rownum) - │ │ estimated row count: 9 (missing stats) - │ │ - │ └── • project - │ │ columns: (r_new, s_new, i_new, j) - │ │ estimated row count: 9 (missing stats) - │ │ - │ └── • scan buffer - │ columns: (r, s, i, j, r_new, s_new, i_new, check1) - │ label: buffer 1 - │ - └── • values - columns: ("lookup_join_const_col_@27") - size: 1 column, 3 rows - row 0, expr 0: 'us-east' - row 1, expr 0: 'us-west' - row 2, expr 0: 'eu-west' + └── • scan buffer + columns: (r, s, i, j, r_new, s_new, i_new, check1) + label: buffer 1 # None of the updated values have nulls. query T @@ -3455,51 +3300,20 @@ vectorized: true │ │ columns: (upsert_i) │ │ estimated row count: 1 (missing stats) │ │ -│ └── • project +│ └── • lookup join (semi) │ │ columns: (upsert_r, upsert_i) │ │ estimated row count: 1 (missing stats) +│ │ table: uniq_enum@uniq_enum_pkey +│ │ lookup condition: (upsert_i = i) AND (r IN ('us-east', 'us-west', 'eu-west')) +│ │ pred: upsert_r != r │ │ -│ └── • distinct -│ │ columns: (upsert_r, upsert_i, rownum) +│ └── • project +│ │ columns: (upsert_r, upsert_i) │ │ estimated row count: 2 (missing stats) -│ │ distinct on: rownum │ │ -│ └── • project -│ │ columns: (upsert_r, upsert_i, rownum) -│ │ -│ └── • project -│ │ columns: (r, i, upsert_r, upsert_i, rownum) -│ │ estimated row count: 7 (missing stats) -│ │ -│ └── • lookup join (inner) -│ │ columns: ("lookup_join_const_col_@20", upsert_r, upsert_i, rownum, r, i) -│ │ table: uniq_enum@uniq_enum_pkey -│ │ equality: (lookup_join_const_col_@20, upsert_i) = (r,i) -│ │ equality cols are key -│ │ pred: upsert_r != r -│ │ -│ └── • cross join (inner) -│ │ columns: ("lookup_join_const_col_@20", upsert_r, upsert_i, rownum) -│ │ estimated row count: 6 (missing stats) -│ │ -│ ├── • values -│ │ columns: ("lookup_join_const_col_@20") -│ │ size: 1 column, 3 rows -│ │ row 0, expr 0: 'us-east' -│ │ row 1, expr 0: 'us-west' -│ │ row 2, expr 0: 'eu-west' -│ │ -│ └── • ordinality -│ │ columns: (upsert_r, upsert_i, rownum) -│ │ estimated row count: 2 (missing stats) -│ │ -│ └── • project -│ │ columns: (upsert_r, upsert_i) -│ │ estimated row count: 2 (missing stats) -│ │ -│ └── • scan buffer -│ columns: (column1, column2, column3, column4, r, s, i, j, column2, column4, r, check1, upsert_r, upsert_i) -│ label: buffer 1 +│ └── • scan buffer +│ columns: (column1, column2, column3, column4, r, s, i, j, column2, column4, r, check1, upsert_r, upsert_i) +│ label: buffer 1 │ └── • constraint-check │ @@ -3510,51 +3324,20 @@ vectorized: true │ columns: (column2, column4) │ estimated row count: 1 (missing stats) │ - └── • project + └── • lookup join (semi) │ columns: (upsert_r, column2, upsert_i, column4) │ estimated row count: 1 (missing stats) + │ table: uniq_enum@uniq_enum_r_s_j_key + │ lookup condition: ((column2 = s) AND (column4 = j)) AND (r IN ('us-east', 'us-west', 'eu-west')) + │ pred: (upsert_r != r) OR (upsert_i != i) │ - └── • distinct - │ columns: (upsert_r, column2, upsert_i, column4, rownum) - │ estimated row count: 0 (missing stats) - │ distinct on: rownum + └── • project + │ columns: (upsert_r, column2, upsert_i, column4) + │ estimated row count: 2 (missing stats) │ - └── • project - │ columns: (upsert_r, column2, upsert_i, column4, rownum) - │ - └── • project - │ columns: (r, s, i, j, upsert_r, column2, upsert_i, column4, rownum) - │ estimated row count: 0 (missing stats) - │ - └── • lookup join (inner) - │ columns: ("lookup_join_const_col_@30", upsert_r, column2, upsert_i, column4, rownum, r, s, i, j) - │ table: uniq_enum@uniq_enum_r_s_j_key - │ equality: (lookup_join_const_col_@30, column2, column4) = (r,s,j) - │ equality cols are key - │ pred: (upsert_r != r) OR (upsert_i != i) - │ - └── • cross join (inner) - │ columns: ("lookup_join_const_col_@30", upsert_r, column2, upsert_i, column4, rownum) - │ estimated row count: 6 (missing stats) - │ - ├── • values - │ columns: ("lookup_join_const_col_@30") - │ size: 1 column, 3 rows - │ row 0, expr 0: 'us-east' - │ row 1, expr 0: 'us-west' - │ row 2, expr 0: 'eu-west' - │ - └── • ordinality - │ columns: (upsert_r, column2, upsert_i, column4, rownum) - │ estimated row count: 2 (missing stats) - │ - └── • project - │ columns: (upsert_r, column2, upsert_i, column4) - │ estimated row count: 2 (missing stats) - │ - └── • scan buffer - columns: (column1, column2, column3, column4, r, s, i, j, column2, column4, r, check1, upsert_r, upsert_i) - label: buffer 1 + └── • scan buffer + columns: (column1, column2, column3, column4, r, s, i, j, column2, column4, r, check1, upsert_r, upsert_i) + label: buffer 1 # Test that we use the index when available for the ON CONFLICT checks. query T @@ -3638,51 +3421,20 @@ vectorized: true │ columns: (upsert_i) │ estimated row count: 1 (missing stats) │ - └── • project + └── • lookup join (semi) │ columns: (upsert_r, upsert_i) │ estimated row count: 1 (missing stats) + │ table: uniq_enum@uniq_enum_pkey + │ lookup condition: (upsert_i = i) AND (r IN ('us-east', 'us-west', 'eu-west')) + │ pred: upsert_r != r │ - └── • distinct - │ columns: (upsert_r, upsert_i, rownum) + └── • project + │ columns: (upsert_r, upsert_i) │ estimated row count: 2 (missing stats) - │ distinct on: rownum │ - └── • project - │ columns: (upsert_r, upsert_i, rownum) - │ - └── • project - │ columns: (r, i, upsert_r, upsert_i, rownum) - │ estimated row count: 7 (missing stats) - │ - └── • lookup join (inner) - │ columns: ("lookup_join_const_col_@23", upsert_r, upsert_i, rownum, r, i) - │ table: uniq_enum@uniq_enum_pkey - │ equality: (lookup_join_const_col_@23, upsert_i) = (r,i) - │ equality cols are key - │ pred: upsert_r != r - │ - └── • cross join (inner) - │ columns: ("lookup_join_const_col_@23", upsert_r, upsert_i, rownum) - │ estimated row count: 6 (missing stats) - │ - ├── • values - │ columns: ("lookup_join_const_col_@23") - │ size: 1 column, 3 rows - │ row 0, expr 0: 'us-east' - │ row 1, expr 0: 'us-west' - │ row 2, expr 0: 'eu-west' - │ - └── • ordinality - │ columns: (upsert_r, upsert_i, rownum) - │ estimated row count: 2 (missing stats) - │ - └── • project - │ columns: (upsert_r, upsert_i) - │ estimated row count: 2 (missing stats) - │ - └── • scan buffer - columns: (column1, column2, column3, column4, r, s, i, j, upsert_i, r, check1, upsert_r) - label: buffer 1 + └── • scan buffer + columns: (column1, column2, column3, column4, r, s, i, j, upsert_i, r, check1, upsert_r) + label: buffer 1 # None of the upserted values have nulls. query T diff --git a/pkg/sql/opt/exec/explain/testdata/gists_tpce b/pkg/sql/opt/exec/explain/testdata/gists_tpce index 805708d0a5e5..69e8a264e4cc 100644 --- a/pkg/sql/opt/exec/explain/testdata/gists_tpce +++ b/pkg/sql/opt/exec/explain/testdata/gists_tpce @@ -21,8 +21,8 @@ WHERE tr_b_id = b_id GROUP BY b_name ORDER BY 2 DESC ---- -hash: 14732118561700026222 -plan-gist: AgGUAQQAPAAAAAGqAQQAAwIAABQAogEEAgAUAJgBBAIAFACsAQQCAAkAAgIAARQAhgECAgEHBAsCBwQRBgQ= +hash: 13421148216330463175 +plan-gist: AgGUAQQAPAAAAAGqAQQAAwIAABQAogEEAgAUAJgBBAIAFACsAQQCAAkAAgIAARQAhgEEAAEHBAsCBwQRBgQ= explain(shape): • sort │ order: -sum @@ -35,10 +35,9 @@ explain(shape): └── • render │ └── • lookup join - │ table: broker@broker_pkey - │ equality: (tr_b_id) = (b_id) + │ table: broker@broker_b_name_idx │ equality cols are key - │ pred: b_name IN _ + │ lookup condition: (tr_b_id = b_id) AND (b_name IN _) │ └── • hash join │ equality: (tr_s_symb) = (s_symb) @@ -77,8 +76,7 @@ explain(gist): └── • render │ └── • lookup join - │ table: broker@broker_pkey - │ equality: (tr_s_symb) = (b_id) + │ table: broker@broker_b_name_idx │ equality cols are key │ └── • hash join diff --git a/pkg/sql/opt/xform/join_funcs.go b/pkg/sql/opt/xform/join_funcs.go index af4155645a83..5df36f0ef3d8 100644 --- a/pkg/sql/opt/xform/join_funcs.go +++ b/pkg/sql/opt/xform/join_funcs.go @@ -446,81 +446,61 @@ func (c *CustomFuncs) generateLookupJoinsImpl( // join implements logic equivalent to simple equality between // columns (where NULL never equals anything). foundVals, allIdx, ok := c.findJoinFilterConstants(allFilters, idxCol) - var foundRange bool - if !ok { - // Also allow a limited form of range condition filters. - allIdx, foundRange = c.findJoinFilterRange(allFilters, idxCol) - if !foundRange { - break - } + if ok && len(foundVals) == 1 { + // If a single constant value was found, project it in the input + // and use it as an equality column. + idxColType := c.e.f.Metadata().ColumnMeta(idxCol).Type + constColID := c.e.f.Metadata().AddColumn( + fmt.Sprintf("lookup_join_const_col_@%d", idxCol), + idxColType, + ) + inputProjections = append(inputProjections, c.e.f.ConstructProjectionsItem( + c.e.f.ConstructConstVal(foundVals[0], idxColType), + constColID, + )) + constFilters = append(constFilters, allFilters[allIdx]) + lookupJoin.KeyCols = append(lookupJoin.KeyCols, constColID) + rightSideCols = append(rightSideCols, idxCol) + continue } - if len(foundVals) > 1 { - if joinType == opt.LeftJoinOp || joinType == opt.SemiJoinOp || joinType == opt.AntiJoinOp { - // We cannot use the method constructJoinWithConstants to - // create a cross join for left, semi, or anti joins, - // because constructing a cross join with foundVals will - // increase the size of the input. As a result, non-matching - // input rows will show up more than once in the output, - // which is incorrect (see #59615 and #78681). - shouldBuildMultiSpanLookupJoin = true - break - } - if j == 0 && projectedVirtualCols.Empty() && index.PartitionCount() > 1 { - // If this is the first index column and there is more than one - // partition, we may be able to build a locality optimized lookup - // join. This requires a multi-span lookup join as a starting point. - // See GenerateLocalityOptimizedLookupJoin for details. - // - // Note that we do not currently support locality optimized - // lookup joins for indexes on virtual columns. - shouldBuildMultiSpanLookupJoin = true - break - } + var foundRange bool + if !ok { + // If constant values were not found, try to find a filter that + // constrains this index column to a range. + _, foundRange = c.findJoinFilterRange(allFilters, idxCol) } - if foundRange { + // If more than one constant value or a range to constrain the index + // column was found, use a LookupExpr rather than KeyCols. + if len(foundVals) > 1 || foundRange { shouldBuildMultiSpanLookupJoin = true - break - } - - // We will join these constant values with the input to make - // equality columns for the lookup join. - if constFilters == nil { - constFilters = make(memo.FiltersExpr, 0, numIndexKeyCols-j) } - idxColType := c.e.f.Metadata().ColumnMeta(idxCol).Type - constColAlias := fmt.Sprintf("lookup_join_const_col_@%d", idxCol) - join, constColID := c.constructJoinWithConstants( - lookupJoin.Input, - foundVals, - idxColType, - constColAlias, - ) - - lookupJoin.Input = join - lookupJoin.KeyCols = append(lookupJoin.KeyCols, constColID) - rightSideCols = append(rightSideCols, idxCol) - constFilters = append(constFilters, allFilters[allIdx]) + // Either multiple constant values or a range were found, or the + // index column cannot be constrained. In all cases, we cannot + // continue on to the next index column, so we break out of the + // loop. + break } if shouldBuildMultiSpanLookupJoin { - // Some of the index columns were constrained to multiple constant values - // or a range expression, and we did not use the method - // constructJoinWithConstants to create a cross join as the input (either - // because it would have been incorrect or because it would have - // eliminated the opportunity to apply other optimizations such as - // locality optimized search; see above). + // Some of the index columns were constrained to multiple constant + // values or a range expression, so we cannot build a lookup join + // with KeyCols. As an alternative, we store all the filters needed + // for the lookup in LookupExpr, which will be used to construct + // spans at execution time. Each input row will generate multiple + // spans to lookup in the index. + // + // For example, if the index cols are (region, id) and the + // LookupExpr is `region in ('east', 'west') AND id = input.id`, + // each input row will generate two spans to be scanned in the + // lookup: // - // As an alternative, we store all the filters needed for the lookup in - // LookupExpr, which will be used to construct spans at execution time. - // The result is that each input row will generate multiple spans to - // lookup in the index. For example, if the index cols are (region, id) - // and the LookupExpr is `region in ('east', 'west') AND id = input.id`, - // each input row will generate two spans to be scanned in the lookup: - // [/'east'/ - /'east'/] [/'west'/ - /'west'/] - // where is the value of input.id for the current input row. + // [/'east'/ - /'east'/] + // [/'west'/ - /'west'/] + // + // Where is the value of input.id for the current input row. var eqFilters memo.FiltersExpr extractEqualityFilter := func(leftCol, rightCol opt.ColumnID) memo.FiltersItem { return memo.ExtractJoinEqualityFilter( @@ -534,9 +514,9 @@ func (c *CustomFuncs) generateLookupJoinsImpl( // Reset KeyCols since we're not using it anymore. lookupJoin.KeyCols = opt.ColList{} - // Reset input since we don't need any constant values that may have - // been joined on the input above. - lookupJoin.Input = input + // Reset the input projections since we don't need any constant + // values projected. + inputProjections = nil } if len(lookupJoin.KeyCols) == 0 && len(lookupJoin.LookupExpr) == 0 { @@ -805,9 +785,9 @@ func (c *CustomFuncs) findFiltersForIndexLookup( constFilters = make(memo.FiltersExpr, 0, numIndexKeyCols-j) } - // Ensure that the constant filter is an equality, IN or inequality - // expression. These are the only types of expressions currently supported - // by the lookupJoiner for building lookup spans. + // Construct a constant filter as an equality, IN expression, or + // inequality. These are the only types of expressions currently + // supported by the lookupJoiner for building lookup spans. constFilter := filters[allIdx] if !c.isCanonicalLookupJoinFilter(constFilter) { if len(values) > 0 { @@ -835,31 +815,36 @@ func (c *CustomFuncs) findFiltersForIndexLookup( // isCanonicalLookupJoinFilter returns true for the limited set of expr's that are // supported by the lookup joiner at execution time. func (c *CustomFuncs) isCanonicalLookupJoinFilter(filter memo.FiltersItem) bool { - var checkExpr func(expr opt.Expr) bool - checkExpr = func(expr opt.Expr) bool { + isVar := func(expr opt.Expr) bool { + _, ok := expr.(*memo.VariableExpr) + return ok + } + var isCanonicalInequality func(expr opt.Expr) bool + isCanonicalInequality = func(expr opt.Expr) bool { switch t := expr.(type) { case *memo.RangeExpr: - return checkExpr(t.And) + return isCanonicalInequality(t.And) case *memo.AndExpr: - return checkExpr(t.Left) && checkExpr(t.Right) + return isCanonicalInequality(t.Left) && isCanonicalInequality(t.Right) case *memo.GeExpr: - return checkExpr(t.Left) && checkExpr(t.Right) + return isCanonicalInequality(t.Left) && isCanonicalInequality(t.Right) case *memo.GtExpr: - return checkExpr(t.Left) && checkExpr(t.Right) + return isCanonicalInequality(t.Left) && isCanonicalInequality(t.Right) case *memo.LeExpr: - return checkExpr(t.Left) && checkExpr(t.Right) + return isCanonicalInequality(t.Left) && isCanonicalInequality(t.Right) case *memo.LtExpr: - return checkExpr(t.Left) && checkExpr(t.Right) - case *memo.VariableExpr: - return true - case *memo.EqExpr: - return checkExpr(t.Left) && checkExpr(t.Right) - case *memo.InExpr: - return checkExpr(t.Left) && memo.CanExtractConstTuple(t.Right) + return isCanonicalInequality(t.Left) && isCanonicalInequality(t.Right) } - return opt.IsConstValueOp(expr) + return isVar(expr) || opt.IsConstValueOp(expr) + } + switch t := filter.Condition.(type) { + case *memo.EqExpr: + return isVar(t.Left) && opt.IsConstValueOp(t.Right) + case *memo.InExpr: + return isVar(t.Left) && memo.CanExtractConstTuple(t.Right) + default: + return isCanonicalInequality(t) } - return checkExpr(filter.Condition) } // makeConstFilter builds a filter that constrains the given column to the given @@ -1364,9 +1349,7 @@ func (c *CustomFuncs) findJoinFilterRange( constraintCol := constraint.Columns.Get(0).ID() // See comment in findFiltersForIndexLookup for why we check filter here. // We only support 1 span in the execution engine so check that. - if constraintCol != col || - constraint.Spans.Count() != 1 || - !c.isCanonicalLookupJoinFilter(filters[filterIdx]) { + if constraintCol != col || constraint.Spans.Count() != 1 { continue } return filterIdx, true diff --git a/pkg/sql/opt/xform/join_funcs_test.go b/pkg/sql/opt/xform/join_funcs_test.go index fccace1b1d38..5a2ac5d36068 100644 --- a/pkg/sql/opt/xform/join_funcs_test.go +++ b/pkg/sql/opt/xform/join_funcs_test.go @@ -171,12 +171,16 @@ func TestCustomFuncs_isCanonicalFilter(t *testing.T) { }, {name: "and-eq-lt", filter: "i = 10 AND i < 10", - want: true, + want: false, }, {name: "or-eq-lt", filter: "i = 10 OR i < 10", want: false, }, + {name: "and-in-lt", + filter: "i IN (10, 20, 30) AND i > 10", + want: false, + }, } fut := xform.TestingIsCanonicalLookupJoinFilter for _, tt := range tests { diff --git a/pkg/sql/opt/xform/testdata/external/tpce b/pkg/sql/opt/xform/testdata/external/tpce index 7f6622f83044..e590b3b9394f 100644 --- a/pkg/sql/opt/xform/testdata/external/tpce +++ b/pkg/sql/opt/xform/testdata/external/tpce @@ -80,15 +80,17 @@ sort │ │ │ │ │ │ ├── key columns: [41] = [21] │ │ │ │ │ │ ├── lookup columns are key │ │ │ │ │ │ ├── fd: (21)-->(26), (47)-->(49), (44)==(47), (47)==(44), (21)==(41), (41)==(21) - │ │ │ │ │ │ ├── inner-join (lookup broker) + │ │ │ │ │ │ ├── inner-join (lookup broker@broker_b_name_idx) │ │ │ │ │ │ │ ├── columns: tr_s_symb:41!null tr_qty:42!null tr_bid_price:43!null tr_b_id:44!null b_id:47!null b_name:49!null - │ │ │ │ │ │ │ ├── key columns: [44] = [47] + │ │ │ │ │ │ │ ├── lookup expression + │ │ │ │ │ │ │ │ └── filters + │ │ │ │ │ │ │ │ ├── tr_b_id:44 = b_id:47 [outer=(44,47), constraints=(/44: (/NULL - ]; /47: (/NULL - ]), fd=(44)==(47), (47)==(44)] + │ │ │ │ │ │ │ │ └── b_name:49 IN ('Broker1', 'Broker10', 'Broker11', 'Broker12', 'Broker13', 'Broker14', 'Broker15', 'Broker16', 'Broker17', 'Broker18', 'Broker19', 'Broker2', 'Broker20', 'Broker21', 'Broker22', 'Broker23', 'Broker24', 'Broker25', 'Broker26', 'Broker27', 'Broker28', 'Broker29', 'Broker3', 'Broker30', 'Broker4', 'Broker5', 'Broker6', 'Broker7', 'Broker8', 'Broker9') [outer=(49), constraints=(/49: [/'Broker1' - /'Broker1'] [/'Broker10' - /'Broker10'] [/'Broker11' - /'Broker11'] [/'Broker12' - /'Broker12'] [/'Broker13' - /'Broker13'] [/'Broker14' - /'Broker14'] [/'Broker15' - /'Broker15'] [/'Broker16' - /'Broker16'] [/'Broker17' - /'Broker17'] [/'Broker18' - /'Broker18'] [/'Broker19' - /'Broker19'] [/'Broker2' - /'Broker2'] [/'Broker20' - /'Broker20'] [/'Broker21' - /'Broker21'] [/'Broker22' - /'Broker22'] [/'Broker23' - /'Broker23'] [/'Broker24' - /'Broker24'] [/'Broker25' - /'Broker25'] [/'Broker26' - /'Broker26'] [/'Broker27' - /'Broker27'] [/'Broker28' - /'Broker28'] [/'Broker29' - /'Broker29'] [/'Broker3' - /'Broker3'] [/'Broker30' - /'Broker30'] [/'Broker4' - /'Broker4'] [/'Broker5' - /'Broker5'] [/'Broker6' - /'Broker6'] [/'Broker7' - /'Broker7'] [/'Broker8' - /'Broker8'] [/'Broker9' - /'Broker9']; tight)] │ │ │ │ │ │ │ ├── lookup columns are key │ │ │ │ │ │ │ ├── fd: (47)-->(49), (44)==(47), (47)==(44) │ │ │ │ │ │ │ ├── scan trade_request@trade_request_tr_b_id_tr_s_symb_idx │ │ │ │ │ │ │ │ └── columns: tr_s_symb:41!null tr_qty:42!null tr_bid_price:43!null tr_b_id:44!null - │ │ │ │ │ │ │ └── filters - │ │ │ │ │ │ │ └── b_name:49 IN ('Broker1', 'Broker10', 'Broker11', 'Broker12', 'Broker13', 'Broker14', 'Broker15', 'Broker16', 'Broker17', 'Broker18', 'Broker19', 'Broker2', 'Broker20', 'Broker21', 'Broker22', 'Broker23', 'Broker24', 'Broker25', 'Broker26', 'Broker27', 'Broker28', 'Broker29', 'Broker3', 'Broker30', 'Broker4', 'Broker5', 'Broker6', 'Broker7', 'Broker8', 'Broker9') [outer=(49), constraints=(/49: [/'Broker1' - /'Broker1'] [/'Broker10' - /'Broker10'] [/'Broker11' - /'Broker11'] [/'Broker12' - /'Broker12'] [/'Broker13' - /'Broker13'] [/'Broker14' - /'Broker14'] [/'Broker15' - /'Broker15'] [/'Broker16' - /'Broker16'] [/'Broker17' - /'Broker17'] [/'Broker18' - /'Broker18'] [/'Broker19' - /'Broker19'] [/'Broker2' - /'Broker2'] [/'Broker20' - /'Broker20'] [/'Broker21' - /'Broker21'] [/'Broker22' - /'Broker22'] [/'Broker23' - /'Broker23'] [/'Broker24' - /'Broker24'] [/'Broker25' - /'Broker25'] [/'Broker26' - /'Broker26'] [/'Broker27' - /'Broker27'] [/'Broker28' - /'Broker28'] [/'Broker29' - /'Broker29'] [/'Broker3' - /'Broker3'] [/'Broker30' - /'Broker30'] [/'Broker4' - /'Broker4'] [/'Broker5' - /'Broker5'] [/'Broker6' - /'Broker6'] [/'Broker7' - /'Broker7'] [/'Broker8' - /'Broker8'] [/'Broker9' - /'Broker9']; tight)] + │ │ │ │ │ │ │ └── filters (true) │ │ │ │ │ │ └── filters (true) │ │ │ │ │ └── filters (true) │ │ │ │ └── filters (true) diff --git a/pkg/sql/opt/xform/testdata/external/tpce-no-stats b/pkg/sql/opt/xform/testdata/external/tpce-no-stats index e25461f422b6..4de0ede7fa3b 100644 --- a/pkg/sql/opt/xform/testdata/external/tpce-no-stats +++ b/pkg/sql/opt/xform/testdata/external/tpce-no-stats @@ -52,9 +52,12 @@ sort │ ├── project │ │ ├── columns: column54:54!null b_name:49!null │ │ ├── immutable - │ │ ├── inner-join (lookup broker) + │ │ ├── inner-join (lookup broker@broker_b_name_idx) │ │ │ ├── columns: sc_id:1!null sc_name:2!null in_id:5!null in_sc_id:7!null co_id:10!null co_in_id:13!null s_symb:21!null s_co_id:26!null tr_s_symb:41!null tr_qty:42!null tr_bid_price:43!null tr_b_id:44!null b_id:47!null b_name:49!null - │ │ │ ├── key columns: [44] = [47] + │ │ │ ├── lookup expression + │ │ │ │ └── filters + │ │ │ │ ├── tr_b_id:44 = b_id:47 [outer=(44,47), constraints=(/44: (/NULL - ]; /47: (/NULL - ]), fd=(44)==(47), (47)==(44)] + │ │ │ │ └── b_name:49 IN ('Broker1', 'Broker10', 'Broker11', 'Broker12', 'Broker13', 'Broker14', 'Broker15', 'Broker16', 'Broker17', 'Broker18', 'Broker19', 'Broker2', 'Broker20', 'Broker21', 'Broker22', 'Broker23', 'Broker24', 'Broker25', 'Broker26', 'Broker27', 'Broker28', 'Broker29', 'Broker3', 'Broker30', 'Broker4', 'Broker5', 'Broker6', 'Broker7', 'Broker8', 'Broker9') [outer=(49), constraints=(/49: [/'Broker1' - /'Broker1'] [/'Broker10' - /'Broker10'] [/'Broker11' - /'Broker11'] [/'Broker12' - /'Broker12'] [/'Broker13' - /'Broker13'] [/'Broker14' - /'Broker14'] [/'Broker15' - /'Broker15'] [/'Broker16' - /'Broker16'] [/'Broker17' - /'Broker17'] [/'Broker18' - /'Broker18'] [/'Broker19' - /'Broker19'] [/'Broker2' - /'Broker2'] [/'Broker20' - /'Broker20'] [/'Broker21' - /'Broker21'] [/'Broker22' - /'Broker22'] [/'Broker23' - /'Broker23'] [/'Broker24' - /'Broker24'] [/'Broker25' - /'Broker25'] [/'Broker26' - /'Broker26'] [/'Broker27' - /'Broker27'] [/'Broker28' - /'Broker28'] [/'Broker29' - /'Broker29'] [/'Broker3' - /'Broker3'] [/'Broker30' - /'Broker30'] [/'Broker4' - /'Broker4'] [/'Broker5' - /'Broker5'] [/'Broker6' - /'Broker6'] [/'Broker7' - /'Broker7'] [/'Broker8' - /'Broker8'] [/'Broker9' - /'Broker9']; tight)] │ │ │ ├── lookup columns are key │ │ │ ├── fd: ()-->(1,2,7), (10)-->(13), (21)-->(26), (47)-->(49), (44)==(47), (47)==(44), (21)==(41), (41)==(21), (10)==(26), (26)==(10), (5)==(13), (13)==(5), (1)==(7), (7)==(1) │ │ │ ├── inner-join (hash) @@ -89,8 +92,7 @@ sort │ │ │ │ │ └── filters (true) │ │ │ │ └── filters │ │ │ │ └── s_symb:21 = tr_s_symb:41 [outer=(21,41), constraints=(/21: (/NULL - ]; /41: (/NULL - ]), fd=(21)==(41), (41)==(21)] - │ │ │ └── filters - │ │ │ └── b_name:49 IN ('Broker1', 'Broker10', 'Broker11', 'Broker12', 'Broker13', 'Broker14', 'Broker15', 'Broker16', 'Broker17', 'Broker18', 'Broker19', 'Broker2', 'Broker20', 'Broker21', 'Broker22', 'Broker23', 'Broker24', 'Broker25', 'Broker26', 'Broker27', 'Broker28', 'Broker29', 'Broker3', 'Broker30', 'Broker4', 'Broker5', 'Broker6', 'Broker7', 'Broker8', 'Broker9') [outer=(49), constraints=(/49: [/'Broker1' - /'Broker1'] [/'Broker10' - /'Broker10'] [/'Broker11' - /'Broker11'] [/'Broker12' - /'Broker12'] [/'Broker13' - /'Broker13'] [/'Broker14' - /'Broker14'] [/'Broker15' - /'Broker15'] [/'Broker16' - /'Broker16'] [/'Broker17' - /'Broker17'] [/'Broker18' - /'Broker18'] [/'Broker19' - /'Broker19'] [/'Broker2' - /'Broker2'] [/'Broker20' - /'Broker20'] [/'Broker21' - /'Broker21'] [/'Broker22' - /'Broker22'] [/'Broker23' - /'Broker23'] [/'Broker24' - /'Broker24'] [/'Broker25' - /'Broker25'] [/'Broker26' - /'Broker26'] [/'Broker27' - /'Broker27'] [/'Broker28' - /'Broker28'] [/'Broker29' - /'Broker29'] [/'Broker3' - /'Broker3'] [/'Broker30' - /'Broker30'] [/'Broker4' - /'Broker4'] [/'Broker5' - /'Broker5'] [/'Broker6' - /'Broker6'] [/'Broker7' - /'Broker7'] [/'Broker8' - /'Broker8'] [/'Broker9' - /'Broker9']; tight)] + │ │ │ └── filters (true) │ │ └── projections │ │ └── tr_qty:42::INT8 * tr_bid_price:43::DECIMAL [as=column54:54, outer=(42,43), immutable] │ └── aggregations diff --git a/pkg/sql/opt/xform/testdata/external/trading b/pkg/sql/opt/xform/testdata/external/trading index 594b8d32963d..1b8062b72980 100644 --- a/pkg/sql/opt/xform/testdata/external/trading +++ b/pkg/sql/opt/xform/testdata/external/trading @@ -710,7 +710,7 @@ project │ ├── ordering: -(3|13) opt(1,2,4,11,12) [actual: -3] │ ├── limit hint: 20.00 │ ├── project - │ │ ├── columns: "lookup_join_const_col_@12":21!null "lookup_join_const_col_@11":20!null transactiondetails.dealerid:1!null transactiondetails.isbuy:2!null transactiondate:3!null cardid:4!null quantity:5!null sellprice:6!null buyprice:7!null + │ │ ├── columns: "lookup_join_const_col_@11":20!null "lookup_join_const_col_@12":21!null transactiondetails.dealerid:1!null transactiondetails.isbuy:2!null transactiondate:3!null cardid:4!null quantity:5!null sellprice:6!null buyprice:7!null │ │ ├── stats: [rows=478.6466] │ │ ├── key: (3,5) │ │ ├── fd: ()-->(1,2,4,20,21), (3,5)-->(6,7) @@ -732,8 +732,8 @@ project │ │ │ ├── ordering: -3 opt(1,2,4) [actual: -3] │ │ │ └── limit hint: 100.00 │ │ └── projections - │ │ ├── false [as="lookup_join_const_col_@12":21] - │ │ └── 1 [as="lookup_join_const_col_@11":20] + │ │ ├── 1 [as="lookup_join_const_col_@11":20] + │ │ └── false [as="lookup_join_const_col_@12":21] │ └── filters (true) └── 20 @@ -1098,39 +1098,19 @@ top-k │ ├── columns: quantity:15!null accountname:10!null │ ├── inner-join (lookup inventorydetails) │ │ ├── columns: id:6!null quantity:7!null dealerid:8!null cardid:9!null accountname:10!null inventorydetails.quantity:11!null - │ │ ├── key columns: [17 6 18] = [8 9 10] - │ │ ├── lookup columns are key + │ │ ├── lookup expression + │ │ │ └── filters + │ │ │ ├── cardid:9 = id:6 [outer=(6,9), constraints=(/6: (/NULL - ]; /9: (/NULL - ]), fd=(6)==(9), (9)==(6)] + │ │ │ ├── dealerid:8 IN (1, 2, 3, 4, 5) [outer=(8), constraints=(/8: [/1 - /1] [/2 - /2] [/3 - /3] [/4 - /4] [/5 - /5]; tight)] + │ │ │ └── accountname:10 IN ('account-1', 'account-2', 'account-3') [outer=(10), constraints=(/10: [/'account-1' - /'account-1'] [/'account-2' - /'account-2'] [/'account-3' - /'account-3']; tight)] │ │ ├── fd: (8-10)-->(11), (6)==(9), (9)==(6) - │ │ ├── inner-join (cross) - │ │ │ ├── columns: id:6!null quantity:7!null "lookup_join_const_col_@8":17!null "lookup_join_const_col_@10":18!null - │ │ │ ├── cardinality: [30 - 30] - │ │ │ ├── multiplicity: left-rows(one-or-more), right-rows(one-or-more) - │ │ │ ├── inner-join (cross) - │ │ │ │ ├── columns: id:6!null quantity:7!null "lookup_join_const_col_@8":17!null - │ │ │ │ ├── cardinality: [10 - 10] - │ │ │ │ ├── multiplicity: left-rows(one-or-more), right-rows(one-or-more) - │ │ │ │ ├── values - │ │ │ │ │ ├── columns: "lookup_join_const_col_@8":17!null - │ │ │ │ │ ├── cardinality: [5 - 5] - │ │ │ │ │ ├── (1,) - │ │ │ │ │ ├── (2,) - │ │ │ │ │ ├── (3,) - │ │ │ │ │ ├── (4,) - │ │ │ │ │ └── (5,) - │ │ │ │ ├── values - │ │ │ │ │ ├── columns: id:6!null quantity:7!null - │ │ │ │ │ ├── cardinality: [2 - 2] - │ │ │ │ │ ├── (42948, 3) - │ │ │ │ │ └── (24924, 4) - │ │ │ │ └── filters (true) - │ │ │ ├── values - │ │ │ │ ├── columns: "lookup_join_const_col_@10":18!null - │ │ │ │ ├── cardinality: [3 - 3] - │ │ │ │ ├── ('account-1',) - │ │ │ │ ├── ('account-2',) - │ │ │ │ └── ('account-3',) - │ │ │ └── filters (true) - │ │ └── filters (true) + │ │ ├── values + │ │ │ ├── columns: id:6!null quantity:7!null + │ │ │ ├── cardinality: [2 - 2] + │ │ │ ├── (42948, 3) + │ │ │ └── (24924, 4) + │ │ └── filters + │ │ └── ((((dealerid:8 = 1) OR (dealerid:8 = 2)) OR (dealerid:8 = 3)) OR (dealerid:8 = 4)) OR (dealerid:8 = 5) [outer=(8), constraints=(/8: [/1 - /1] [/2 - /2] [/3 - /3] [/4 - /4] [/5 - /5]; tight)] │ └── projections │ └── CASE WHEN quantity:7 < inventorydetails.quantity:11 THEN quantity:7 ELSE inventorydetails.quantity:11 END [as=quantity:15, outer=(7,11)] └── aggregations diff --git a/pkg/sql/opt/xform/testdata/external/trading-mutation b/pkg/sql/opt/xform/testdata/external/trading-mutation index 269ddca5baa9..81f8c7a0a98e 100644 --- a/pkg/sql/opt/xform/testdata/external/trading-mutation +++ b/pkg/sql/opt/xform/testdata/external/trading-mutation @@ -714,7 +714,7 @@ project │ ├── ordering: -(3|15) opt(1,2,4,13,14) [actual: -3] │ ├── limit hint: 20.00 │ ├── project - │ │ ├── columns: "lookup_join_const_col_@14":25!null "lookup_join_const_col_@13":24!null transactiondetails.dealerid:1!null transactiondetails.isbuy:2!null transactiondate:3!null cardid:4!null quantity:5!null sellprice:6!null buyprice:7!null + │ │ ├── columns: "lookup_join_const_col_@13":24!null "lookup_join_const_col_@14":25!null transactiondetails.dealerid:1!null transactiondetails.isbuy:2!null transactiondate:3!null cardid:4!null quantity:5!null sellprice:6!null buyprice:7!null │ │ ├── stats: [rows=478.6466] │ │ ├── key: (3,5) │ │ ├── fd: ()-->(1,2,4,24,25), (3,5)-->(6,7) @@ -736,8 +736,8 @@ project │ │ │ ├── ordering: -3 opt(1,2,4) [actual: -3] │ │ │ └── limit hint: 100.00 │ │ └── projections - │ │ ├── false [as="lookup_join_const_col_@14":25] - │ │ └── 1 [as="lookup_join_const_col_@13":24] + │ │ ├── 1 [as="lookup_join_const_col_@13":24] + │ │ └── false [as="lookup_join_const_col_@14":25] │ └── filters (true) └── 20 @@ -1102,39 +1102,19 @@ top-k │ ├── columns: quantity:17!null accountname:10!null │ ├── inner-join (lookup inventorydetails) │ │ ├── columns: id:6!null quantity:7!null dealerid:8!null cardid:9!null accountname:10!null inventorydetails.quantity:11!null - │ │ ├── key columns: [19 6 20] = [8 9 10] - │ │ ├── lookup columns are key + │ │ ├── lookup expression + │ │ │ └── filters + │ │ │ ├── cardid:9 = id:6 [outer=(6,9), constraints=(/6: (/NULL - ]; /9: (/NULL - ]), fd=(6)==(9), (9)==(6)] + │ │ │ ├── dealerid:8 IN (1, 2, 3, 4, 5) [outer=(8), constraints=(/8: [/1 - /1] [/2 - /2] [/3 - /3] [/4 - /4] [/5 - /5]; tight)] + │ │ │ └── accountname:10 IN ('account-1', 'account-2', 'account-3') [outer=(10), constraints=(/10: [/'account-1' - /'account-1'] [/'account-2' - /'account-2'] [/'account-3' - /'account-3']; tight)] │ │ ├── fd: (8-10)-->(11), (6)==(9), (9)==(6) - │ │ ├── inner-join (cross) - │ │ │ ├── columns: id:6!null quantity:7!null "lookup_join_const_col_@8":19!null "lookup_join_const_col_@10":20!null - │ │ │ ├── cardinality: [30 - 30] - │ │ │ ├── multiplicity: left-rows(one-or-more), right-rows(one-or-more) - │ │ │ ├── inner-join (cross) - │ │ │ │ ├── columns: id:6!null quantity:7!null "lookup_join_const_col_@8":19!null - │ │ │ │ ├── cardinality: [10 - 10] - │ │ │ │ ├── multiplicity: left-rows(one-or-more), right-rows(one-or-more) - │ │ │ │ ├── values - │ │ │ │ │ ├── columns: "lookup_join_const_col_@8":19!null - │ │ │ │ │ ├── cardinality: [5 - 5] - │ │ │ │ │ ├── (1,) - │ │ │ │ │ ├── (2,) - │ │ │ │ │ ├── (3,) - │ │ │ │ │ ├── (4,) - │ │ │ │ │ └── (5,) - │ │ │ │ ├── values - │ │ │ │ │ ├── columns: id:6!null quantity:7!null - │ │ │ │ │ ├── cardinality: [2 - 2] - │ │ │ │ │ ├── (42948, 3) - │ │ │ │ │ └── (24924, 4) - │ │ │ │ └── filters (true) - │ │ │ ├── values - │ │ │ │ ├── columns: "lookup_join_const_col_@10":20!null - │ │ │ │ ├── cardinality: [3 - 3] - │ │ │ │ ├── ('account-1',) - │ │ │ │ ├── ('account-2',) - │ │ │ │ └── ('account-3',) - │ │ │ └── filters (true) - │ │ └── filters (true) + │ │ ├── values + │ │ │ ├── columns: id:6!null quantity:7!null + │ │ │ ├── cardinality: [2 - 2] + │ │ │ ├── (42948, 3) + │ │ │ └── (24924, 4) + │ │ └── filters + │ │ └── ((((dealerid:8 = 1) OR (dealerid:8 = 2)) OR (dealerid:8 = 3)) OR (dealerid:8 = 4)) OR (dealerid:8 = 5) [outer=(8), constraints=(/8: [/1 - /1] [/2 - /2] [/3 - /3] [/4 - /4] [/5 - /5]; tight)] │ └── projections │ └── CASE WHEN quantity:7 < inventorydetails.quantity:11 THEN quantity:7 ELSE inventorydetails.quantity:11 END [as=quantity:17, outer=(7,11)] └── aggregations diff --git a/pkg/sql/opt/xform/testdata/rules/groupby b/pkg/sql/opt/xform/testdata/rules/groupby index b0a1d677f665..edcfb2cbc46e 100644 --- a/pkg/sql/opt/xform/testdata/rules/groupby +++ b/pkg/sql/opt/xform/testdata/rules/groupby @@ -2172,7 +2172,7 @@ memo (optimized, ~5KB, required=[presentation: u:2,v:3,w:4] [ordering: +4]) memo SELECT (SELECT w FROM kuvw WHERE v=1 AND x=u) FROM xyz ORDER BY x+1, x ---- -memo (optimized, ~32KB, required=[presentation: w:12] [ordering: +13,+1]) +memo (optimized, ~29KB, required=[presentation: w:12] [ordering: +13,+1]) ├── G1: (project G2 G3 x) │ ├── [presentation: w:12] [ordering: +13,+1] │ │ ├── best: (sort G1) diff --git a/pkg/sql/opt/xform/testdata/rules/join b/pkg/sql/opt/xform/testdata/rules/join index d56a0cef9997..bf97670c888c 100644 --- a/pkg/sql/opt/xform/testdata/rules/join +++ b/pkg/sql/opt/xform/testdata/rules/join @@ -3425,13 +3425,13 @@ inner-join (lookup abcde) │ ├── key columns: [1 14 15] = [6 7 8] │ ├── fd: ()-->(7,8), (11)-->(6), (1)==(6), (6)==(1) │ ├── project - │ │ ├── columns: "lookup_join_const_col_@8":15!null "lookup_join_const_col_@7":14!null m:1 n:2 + │ │ ├── columns: "lookup_join_const_col_@7":14!null "lookup_join_const_col_@8":15!null m:1 n:2 │ │ ├── fd: ()-->(14,15) │ │ ├── scan small │ │ │ └── columns: m:1 n:2 │ │ └── projections - │ │ ├── 10 [as="lookup_join_const_col_@8":15] - │ │ └── 10 [as="lookup_join_const_col_@7":14] + │ │ ├── 10 [as="lookup_join_const_col_@7":14] + │ │ └── 10 [as="lookup_join_const_col_@8":15] │ └── filters (true) └── filters (true) @@ -3446,85 +3446,60 @@ inner-join (lookup abcde) ├── fd: (1)==(6), (6)==(1) ├── inner-join (lookup abcde@abcde_a_b_c_idx) │ ├── columns: m:1!null n:2 a:6!null b:7!null c:8 abcde.rowid:11!null - │ ├── key columns: [1 14] = [6 7] + │ ├── lookup expression + │ │ └── filters + │ │ ├── a:6 = m:1 [outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)] + │ │ └── b:7 IN (10, 20, 30) [outer=(7), constraints=(/7: [/10 - /10] [/20 - /20] [/30 - /30]; tight)] │ ├── fd: (11)-->(6-8), (1)==(6), (6)==(1) - │ ├── inner-join (cross) - │ │ ├── columns: m:1 n:2 "lookup_join_const_col_@7":14!null - │ │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-more) - │ │ ├── scan small - │ │ │ └── columns: m:1 n:2 - │ │ ├── values - │ │ │ ├── columns: "lookup_join_const_col_@7":14!null - │ │ │ ├── cardinality: [3 - 3] - │ │ │ ├── (10,) - │ │ │ ├── (20,) - │ │ │ └── (30,) - │ │ └── filters (true) + │ ├── scan small + │ │ └── columns: m:1 n:2 │ └── filters (true) └── filters (true) -# Test that a LOOKUP join hint does not propagate to the cross join. If it was, -# the cross join would have an artificially high cost and the lookup join would -# not be selected as the optimal plan. +# One column constrained to multiple constants and another constrained to a +# single constant used by lookup joiner. opt expect=GenerateLookupJoinsWithFilter -SELECT * FROM small INNER LOOKUP JOIN abcde ON a=m AND b IN (10, 20, 30) +SELECT * FROM small INNER JOIN abcde ON a=m AND b IN (10, 20, 30) AND c=10 ---- inner-join (lookup abcde) - ├── columns: m:1!null n:2 a:6!null b:7!null c:8 d:9 e:10 + ├── columns: m:1!null n:2 a:6!null b:7!null c:8!null d:9 e:10 ├── key columns: [11] = [11] ├── lookup columns are key - ├── fd: (1)==(6), (6)==(1) + ├── fd: ()-->(8), (1)==(6), (6)==(1) ├── inner-join (lookup abcde@abcde_a_b_c_idx) - │ ├── columns: m:1!null n:2 a:6!null b:7!null c:8 abcde.rowid:11!null - │ ├── flags: force lookup join (into right side) - │ ├── key columns: [1 14] = [6 7] - │ ├── fd: (11)-->(6-8), (1)==(6), (6)==(1) - │ ├── inner-join (cross) - │ │ ├── columns: m:1 n:2 "lookup_join_const_col_@7":14!null - │ │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-more) - │ │ ├── scan small - │ │ │ └── columns: m:1 n:2 - │ │ ├── values - │ │ │ ├── columns: "lookup_join_const_col_@7":14!null - │ │ │ ├── cardinality: [3 - 3] - │ │ │ ├── (10,) - │ │ │ ├── (20,) - │ │ │ └── (30,) - │ │ └── filters (true) + │ ├── columns: m:1!null n:2 a:6!null b:7!null c:8!null abcde.rowid:11!null + │ ├── lookup expression + │ │ └── filters + │ │ ├── a:6 = m:1 [outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)] + │ │ ├── b:7 IN (10, 20, 30) [outer=(7), constraints=(/7: [/10 - /10] [/20 - /20] [/30 - /30]; tight)] + │ │ └── c:8 = 10 [outer=(8), constraints=(/8: [/10 - /10]; tight), fd=()-->(8)] + │ ├── fd: ()-->(8), (11)-->(6,7), (1)==(6), (6)==(1) + │ ├── scan small + │ │ └── columns: m:1 n:2 │ └── filters (true) └── filters (true) -# One column constrained to multiple constants and another constrained to a -# single constant used by lookup joiner. +# One column constrained to a single constant and another constrained to +# multiple constants. A project should not be added to the input for the single +# constant - the equality should be included in the the lookup expression. opt expect=GenerateLookupJoinsWithFilter -SELECT * FROM small INNER JOIN abcde ON a=m AND b IN (10, 20, 30) AND c=10 +SELECT * FROM small INNER JOIN abcde ON a=m AND b=10 AND c IN (10, 20, 30) ---- inner-join (lookup abcde) ├── columns: m:1!null n:2 a:6!null b:7!null c:8!null d:9 e:10 ├── key columns: [11] = [11] ├── lookup columns are key - ├── fd: ()-->(8), (1)==(6), (6)==(1) + ├── fd: ()-->(7), (1)==(6), (6)==(1) ├── inner-join (lookup abcde@abcde_a_b_c_idx) │ ├── columns: m:1!null n:2 a:6!null b:7!null c:8!null abcde.rowid:11!null - │ ├── key columns: [1 14 15] = [6 7 8] - │ ├── fd: ()-->(8), (11)-->(6,7), (1)==(6), (6)==(1) - │ ├── project - │ │ ├── columns: "lookup_join_const_col_@8":15!null m:1 n:2 "lookup_join_const_col_@7":14!null - │ │ ├── fd: ()-->(15) - │ │ ├── inner-join (cross) - │ │ │ ├── columns: m:1 n:2 "lookup_join_const_col_@7":14!null - │ │ │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-more) - │ │ │ ├── scan small - │ │ │ │ └── columns: m:1 n:2 - │ │ │ ├── values - │ │ │ │ ├── columns: "lookup_join_const_col_@7":14!null - │ │ │ │ ├── cardinality: [3 - 3] - │ │ │ │ ├── (10,) - │ │ │ │ ├── (20,) - │ │ │ │ └── (30,) - │ │ │ └── filters (true) - │ │ └── projections - │ │ └── 10 [as="lookup_join_const_col_@8":15] + │ ├── lookup expression + │ │ └── filters + │ │ ├── a:6 = m:1 [outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)] + │ │ ├── b:7 = 10 [outer=(7), constraints=(/7: [/10 - /10]; tight), fd=()-->(7)] + │ │ └── c:8 IN (10, 20, 30) [outer=(8), constraints=(/8: [/10 - /10] [/20 - /20] [/30 - /30]; tight)] + │ ├── fd: ()-->(7), (11)-->(6,8), (1)==(6), (6)==(1) + │ ├── scan small + │ │ └── columns: m:1 n:2 │ └── filters (true) └── filters (true) @@ -3539,28 +3514,14 @@ inner-join (lookup abcde) ├── fd: (1)==(6), (6)==(1) ├── inner-join (lookup abcde@abcde_a_b_c_idx) │ ├── columns: m:1!null n:2 a:6!null b:7!null c:8!null abcde.rowid:11!null - │ ├── key columns: [1 14 15] = [6 7 8] + │ ├── lookup expression + │ │ └── filters + │ │ ├── a:6 = m:1 [outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)] + │ │ ├── b:7 IN (10, 20) [outer=(7), constraints=(/7: [/10 - /10] [/20 - /20]; tight)] + │ │ └── c:8 IN (30, 40) [outer=(8), constraints=(/8: [/30 - /30] [/40 - /40]; tight)] │ ├── fd: (11)-->(6-8), (1)==(6), (6)==(1) - │ ├── inner-join (cross) - │ │ ├── columns: m:1 n:2 "lookup_join_const_col_@7":14!null "lookup_join_const_col_@8":15!null - │ │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-more) - │ │ ├── inner-join (cross) - │ │ │ ├── columns: m:1 n:2 "lookup_join_const_col_@7":14!null - │ │ │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-more) - │ │ │ ├── scan small - │ │ │ │ └── columns: m:1 n:2 - │ │ │ ├── values - │ │ │ │ ├── columns: "lookup_join_const_col_@7":14!null - │ │ │ │ ├── cardinality: [2 - 2] - │ │ │ │ ├── (10,) - │ │ │ │ └── (20,) - │ │ │ └── filters (true) - │ │ ├── values - │ │ │ ├── columns: "lookup_join_const_col_@8":15!null - │ │ │ ├── cardinality: [2 - 2] - │ │ │ ├── (30,) - │ │ │ └── (40,) - │ │ └── filters (true) + │ ├── scan small + │ │ └── columns: m:1 n:2 │ └── filters (true) └── filters (true) @@ -3578,13 +3539,13 @@ inner-join (lookup abcde) │ ├── key columns: [1 14 15] = [6 7 8] │ ├── fd: ()-->(7,8), (11)-->(6), (1)==(6), (6)==(1) │ ├── project - │ │ ├── columns: "lookup_join_const_col_@8":15!null "lookup_join_const_col_@7":14!null m:1 n:2 + │ │ ├── columns: "lookup_join_const_col_@7":14!null "lookup_join_const_col_@8":15!null m:1 n:2 │ │ ├── fd: ()-->(14,15) │ │ ├── scan small │ │ │ └── columns: m:1 n:2 │ │ └── projections - │ │ ├── 10 [as="lookup_join_const_col_@8":15] - │ │ └── 10 [as="lookup_join_const_col_@7":14] + │ │ ├── 10 [as="lookup_join_const_col_@7":14] + │ │ └── 10 [as="lookup_join_const_col_@8":15] │ └── filters (true) └── filters └── d:9 = 10 [outer=(9), constraints=(/9: [/10 - /10]; tight), fd=()-->(9)] @@ -3624,13 +3585,13 @@ project ├── key columns: [14 1 15] = [6 7 8] ├── fd: ()-->(6,8), (1)==(7), (7)==(1) ├── project - │ ├── columns: "lookup_join_const_col_@8":15!null "lookup_join_const_col_@6":14!null m:1 + │ ├── columns: "lookup_join_const_col_@6":14!null "lookup_join_const_col_@8":15!null m:1 │ ├── fd: ()-->(14,15) │ ├── scan small │ │ └── columns: m:1 │ └── projections - │ ├── 10 [as="lookup_join_const_col_@8":15] - │ └── 10 [as="lookup_join_const_col_@6":14] + │ ├── 10 [as="lookup_join_const_col_@6":14] + │ └── 10 [as="lookup_join_const_col_@8":15] └── filters (true) # Projection of constant columns work with non const expressions as well. @@ -3652,13 +3613,13 @@ inner-join (lookup bool_col) │ ├── key columns: [1 14 15] = [6 7 8] │ ├── fd: ()-->(7,8), (11)-->(6), (1)==(6), (6)==(1) │ ├── project - │ │ ├── columns: "lookup_join_const_col_@8":15!null "lookup_join_const_col_@7":14!null m:1 n:2 + │ │ ├── columns: "lookup_join_const_col_@7":14!null "lookup_join_const_col_@8":15!null m:1 n:2 │ │ ├── fd: ()-->(14,15) │ │ ├── scan small │ │ │ └── columns: m:1 n:2 │ │ └── projections - │ │ ├── true [as="lookup_join_const_col_@8":15] - │ │ └── 10 [as="lookup_join_const_col_@7":14] + │ │ ├── 10 [as="lookup_join_const_col_@7":14] + │ │ └── true [as="lookup_join_const_col_@8":15] │ └── filters (true) └── filters (true) @@ -3675,13 +3636,13 @@ inner-join (lookup bool_col) │ ├── key columns: [1 14 15] = [6 7 8] │ ├── fd: ()-->(7,8), (11)-->(6), (1)==(6), (6)==(1) │ ├── project - │ │ ├── columns: "lookup_join_const_col_@8":15!null "lookup_join_const_col_@7":14!null m:1 n:2 + │ │ ├── columns: "lookup_join_const_col_@7":14!null "lookup_join_const_col_@8":15!null m:1 n:2 │ │ ├── fd: ()-->(14,15) │ │ ├── scan small │ │ │ └── columns: m:1 n:2 │ │ └── projections - │ │ ├── true [as="lookup_join_const_col_@8":15] - │ │ └── 10 [as="lookup_join_const_col_@7":14] + │ │ ├── 10 [as="lookup_join_const_col_@7":14] + │ │ └── true [as="lookup_join_const_col_@8":15] │ └── filters (true) └── filters (true) @@ -3698,13 +3659,13 @@ inner-join (lookup bool_col) │ ├── key columns: [1 14 15] = [6 7 8] │ ├── fd: ()-->(7,8), (11)-->(6), (1)==(6), (6)==(1) │ ├── project - │ │ ├── columns: "lookup_join_const_col_@8":15!null "lookup_join_const_col_@7":14!null m:1 n:2 + │ │ ├── columns: "lookup_join_const_col_@7":14!null "lookup_join_const_col_@8":15!null m:1 n:2 │ │ ├── fd: ()-->(14,15) │ │ ├── scan small │ │ │ └── columns: m:1 n:2 │ │ └── projections - │ │ ├── false [as="lookup_join_const_col_@8":15] - │ │ └── 10 [as="lookup_join_const_col_@7":14] + │ │ ├── 10 [as="lookup_join_const_col_@7":14] + │ │ └── false [as="lookup_join_const_col_@8":15] │ └── filters (true) └── filters (true) @@ -3734,19 +3695,13 @@ inner-join (lookup abcd_check) ├── fd: (1)==(6), (6)==(1) ├── inner-join (lookup abcd_check@abcd_check_a_b_c_idx) │ ├── columns: m:1!null n:2 a:6!null b:7!null c:8 abcd_check.rowid:10!null - │ ├── key columns: [1 13] = [6 7] + │ ├── lookup expression + │ │ └── filters + │ │ ├── a:6 = m:1 [outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)] + │ │ └── b:7 IN (10, 20) [outer=(7), constraints=(/7: [/10 - /10] [/20 - /20]; tight)] │ ├── fd: (10)-->(6-8), (1)==(6), (6)==(1) - │ ├── inner-join (cross) - │ │ ├── columns: m:1 n:2 "lookup_join_const_col_@7":13!null - │ │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-more) - │ │ ├── scan small - │ │ │ └── columns: m:1 n:2 - │ │ ├── values - │ │ │ ├── columns: "lookup_join_const_col_@7":13!null - │ │ │ ├── cardinality: [2 - 2] - │ │ │ ├── (10,) - │ │ │ └── (20,) - │ │ └── filters (true) + │ ├── scan small + │ │ └── columns: m:1 n:2 │ └── filters (true) └── filters (true) @@ -3760,24 +3715,14 @@ inner-join (lookup abcd_check) ├── fd: ()-->(8), (1)==(6), (6)==(1) ├── inner-join (lookup abcd_check@abcd_check_a_b_c_idx) │ ├── columns: m:1!null n:2 a:6!null b:7!null c:8!null abcd_check.rowid:10!null - │ ├── key columns: [1 13 14] = [6 7 8] + │ ├── lookup expression + │ │ └── filters + │ │ ├── a:6 = m:1 [outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)] + │ │ ├── b:7 IN (10, 20) [outer=(7), constraints=(/7: [/10 - /10] [/20 - /20]; tight)] + │ │ └── c:8 = 30 [outer=(8), constraints=(/8: [/30 - /30]; tight), fd=()-->(8)] │ ├── fd: ()-->(8), (10)-->(6,7), (1)==(6), (6)==(1) - │ ├── project - │ │ ├── columns: "lookup_join_const_col_@8":14!null m:1 n:2 "lookup_join_const_col_@7":13!null - │ │ ├── fd: ()-->(14) - │ │ ├── inner-join (cross) - │ │ │ ├── columns: m:1 n:2 "lookup_join_const_col_@7":13!null - │ │ │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-more) - │ │ │ ├── scan small - │ │ │ │ └── columns: m:1 n:2 - │ │ │ ├── values - │ │ │ │ ├── columns: "lookup_join_const_col_@7":13!null - │ │ │ │ ├── cardinality: [2 - 2] - │ │ │ │ ├── (10,) - │ │ │ │ └── (20,) - │ │ │ └── filters (true) - │ │ └── projections - │ │ └── 30 [as="lookup_join_const_col_@8":14] + │ ├── scan small + │ │ └── columns: m:1 n:2 │ └── filters (true) └── filters (true) @@ -3822,13 +3767,13 @@ inner-join (lookup abcd_comp) │ ├── key columns: [1 13 14] = [6 7 8] │ ├── fd: ()-->(7,8), (10)-->(6), (1)==(6), (6)==(1) │ ├── project - │ │ ├── columns: "lookup_join_const_col_@8":14!null "lookup_join_const_col_@7":13!null m:1 n:2 + │ │ ├── columns: "lookup_join_const_col_@7":13!null "lookup_join_const_col_@8":14!null m:1 n:2 │ │ ├── fd: ()-->(13,14) │ │ ├── scan small │ │ │ └── columns: m:1 n:2 │ │ └── projections - │ │ ├── 30 [as="lookup_join_const_col_@8":14] - │ │ └── 1 [as="lookup_join_const_col_@7":13] + │ │ ├── 1 [as="lookup_join_const_col_@7":13] + │ │ └── 30 [as="lookup_join_const_col_@8":14] │ └── filters (true) └── filters └── d:9 = 5 [outer=(9), constraints=(/9: [/5 - /5]; tight), fd=()-->(9)] @@ -5086,25 +5031,21 @@ CREATE INDEX j_v1 ON virt (j, v1) opt SELECT m, virt.k, virt.v1 FROM small INNER LOOKUP JOIN virt ON m = virt.v1 ---- -inner-join (lookup virt@j_v1) +project ├── columns: m:1!null k:6!null v1:9!null - ├── flags: force lookup join (into right side) - ├── key columns: [16 1] = [8 9] ├── immutable ├── fd: (6)-->(9), (1)==(9), (9)==(1) - ├── inner-join (cross) - │ ├── columns: m:1 "lookup_join_const_col_@8":16!null - │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-more) - │ ├── scan small - │ │ └── columns: m:1 - │ ├── values - │ │ ├── columns: "lookup_join_const_col_@8":16!null - │ │ ├── cardinality: [3 - 3] - │ │ ├── (10,) - │ │ ├── (20,) - │ │ └── (30,) - │ └── filters (true) - └── filters (true) + └── inner-join (lookup virt@j_v1) + ├── columns: m:1!null k:6!null j:8!null v1:9!null + ├── flags: force lookup join (into right side) + ├── lookup expression + │ └── filters + │ ├── m:1 = v1:9 [outer=(1,9), constraints=(/1: (/NULL - ]; /9: (/NULL - ]), fd=(1)==(9), (9)==(1)] + │ └── j:8 IN (10, 20, 30) [outer=(8), constraints=(/8: [/10 - /10] [/20 - /20] [/30 - /30]; tight)] + ├── fd: (6)-->(8,9), (1)==(9), (9)==(1) + ├── scan small + │ └── columns: m:1 + └── filters (true) # Non-covering case with a cross join for multiple constant values based on optional # filters. @@ -5118,22 +5059,15 @@ inner-join (lookup virt) ├── immutable ├── fd: (6)-->(7), (7)-->(9), (1)==(9), (9)==(1) ├── inner-join (lookup virt@j_v1) - │ ├── columns: m:1!null k:6!null v1:9!null + │ ├── columns: m:1!null k:6!null j:8!null v1:9!null │ ├── flags: force lookup join (into right side) - │ ├── key columns: [16 1] = [8 9] - │ ├── fd: (6)-->(1,9), (1)==(9), (9)==(1) - │ ├── inner-join (cross) - │ │ ├── columns: m:1 "lookup_join_const_col_@8":16!null - │ │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-more) - │ │ ├── scan small - │ │ │ └── columns: m:1 - │ │ ├── values - │ │ │ ├── columns: "lookup_join_const_col_@8":16!null - │ │ │ ├── cardinality: [3 - 3] - │ │ │ ├── (10,) - │ │ │ ├── (20,) - │ │ │ └── (30,) - │ │ └── filters (true) + │ ├── lookup expression + │ │ └── filters + │ │ ├── m:1 = v1:9 [outer=(1,9), constraints=(/1: (/NULL - ]; /9: (/NULL - ]), fd=(1)==(9), (9)==(1)] + │ │ └── j:8 IN (10, 20, 30) [outer=(8), constraints=(/8: [/10 - /10] [/20 - /20] [/30 - /30]; tight)] + │ ├── fd: (6)-->(8,9), (1)==(9), (9)==(1) + │ ├── scan small + │ │ └── columns: m:1 │ └── filters (true) └── filters (true) @@ -5598,25 +5532,21 @@ project opt expect=GenerateLookupJoinsWithVirtualColsAndFilter SELECT m, virt.k, virt.v1 FROM small INNER LOOKUP JOIN virt ON virt.i IN (1, 2, 3) AND m = virt.v1 ---- -inner-join (lookup virt@i_v1) +project ├── columns: m:1!null k:6!null v1:9!null - ├── flags: force lookup join (into right side) - ├── key columns: [16 1] = [7 9] ├── immutable ├── fd: (6)-->(9), (1)==(9), (9)==(1) - ├── inner-join (cross) - │ ├── columns: m:1 "lookup_join_const_col_@7":16!null - │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-more) - │ ├── scan small - │ │ └── columns: m:1 - │ ├── values - │ │ ├── columns: "lookup_join_const_col_@7":16!null - │ │ ├── cardinality: [3 - 3] - │ │ ├── (1,) - │ │ ├── (2,) - │ │ └── (3,) - │ └── filters (true) - └── filters (true) + └── inner-join (lookup virt@i_v1) + ├── columns: m:1!null k:6!null i:7!null v1:9!null + ├── flags: force lookup join (into right side) + ├── lookup expression + │ └── filters + │ ├── m:1 = v1:9 [outer=(1,9), constraints=(/1: (/NULL - ]; /9: (/NULL - ]), fd=(1)==(9), (9)==(1)] + │ └── i:7 IN (1, 2, 3) [outer=(7), constraints=(/7: [/1 - /1] [/2 - /2] [/3 - /3]; tight)] + ├── fd: (6)-->(7,9), (7)-->(9), (1)==(9), (9)==(1) + ├── scan small + │ └── columns: m:1 + └── filters (true) # Non-covering case with a cross join for multiple constant values for the # leading lookup column. @@ -5630,22 +5560,15 @@ inner-join (lookup virt) ├── immutable ├── fd: (1)==(9), (9)==(1) ├── inner-join (lookup virt@i_v1) - │ ├── columns: m:1!null k:6!null v1:9!null + │ ├── columns: m:1!null k:6!null i:7!null v1:9!null │ ├── flags: force lookup join (into right side) - │ ├── key columns: [16 1] = [7 9] - │ ├── fd: (6)-->(1,9), (1)==(9), (9)==(1) - │ ├── inner-join (cross) - │ │ ├── columns: m:1 "lookup_join_const_col_@7":16!null - │ │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-more) - │ │ ├── scan small - │ │ │ └── columns: m:1 - │ │ ├── values - │ │ │ ├── columns: "lookup_join_const_col_@7":16!null - │ │ │ ├── cardinality: [3 - 3] - │ │ │ ├── (1,) - │ │ │ ├── (2,) - │ │ │ └── (3,) - │ │ └── filters (true) + │ ├── lookup expression + │ │ └── filters + │ │ ├── m:1 = v1:9 [outer=(1,9), constraints=(/1: (/NULL - ]; /9: (/NULL - ]), fd=(1)==(9), (9)==(1)] + │ │ └── i:7 IN (1, 2, 3) [outer=(7), constraints=(/7: [/1 - /1] [/2 - /2] [/3 - /3]; tight)] + │ ├── fd: (6)-->(7,9), (7)-->(9), (1)==(9), (9)==(1) + │ ├── scan small + │ │ └── columns: m:1 │ └── filters (true) └── filters (true) @@ -10275,9 +10198,11 @@ CREATE TABLE abc_part ( a INT NOT NULL, b INT, c INT, + v INT NOT NULL AS (a + 10) VIRTUAL, PRIMARY KEY (r, a), UNIQUE WITHOUT INDEX (a), UNIQUE WITHOUT INDEX (b), + UNIQUE WITHOUT INDEX (v), UNIQUE INDEX b_idx (r, b) PARTITION BY LIST (r) ( PARTITION east VALUES IN (('east')), PARTITION west VALUES IN (('west')), @@ -10287,6 +10212,11 @@ CREATE TABLE abc_part ( PARTITION east VALUES IN (('east')), PARTITION west VALUES IN (('west')), PARTITION central VALUES IN (('central')) + ), + UNIQUE INDEX v_idx (r, v) PARTITION BY LIST (r) ( + PARTITION east VALUES IN (('east')), + PARTITION west VALUES IN (('west')), + PARTITION central VALUES IN (('central')) ) ) PARTITION BY LIST (r) ( PARTITION east VALUES IN (('east')), @@ -10475,26 +10405,26 @@ anti-join (lookup abc_part) │ ├── distribution: east │ ├── locality-optimized-search │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + │ │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ │ ├── cardinality: [0 - 1] │ │ ├── key: () │ │ ├── fd: ()-->(1-4) │ │ ├── distribution: east │ │ ├── scan def_part - │ │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ │ ├── constraint: /13/14: [/'east'/1 - /'east'/1] + │ │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ │ ├── constraint: /14/15: [/'east'/1 - /'east'/1] │ │ │ ├── cardinality: [0 - 1] │ │ │ ├── key: () - │ │ │ └── fd: ()-->(13-16) + │ │ │ └── fd: ()-->(14-17) │ │ └── scan def_part - │ │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ │ ├── constraint: /19/20 + │ │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ │ ├── constraint: /20/21 │ │ │ ├── [/'central'/1 - /'central'/1] │ │ │ └── [/'west'/1 - /'west'/1] │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ └── fd: ()-->(19-22) + │ │ └── fd: ()-->(20-23) │ └── filters (true) └── filters (true) @@ -10526,26 +10456,26 @@ anti-join (lookup abc_part) │ ├── distribution: west │ ├── locality-optimized-search │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + │ │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ │ ├── cardinality: [0 - 1] │ │ ├── key: () │ │ ├── fd: ()-->(1-4) │ │ ├── distribution: west │ │ ├── scan def_part - │ │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ │ ├── constraint: /13/14: [/'west'/1 - /'west'/1] + │ │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ │ ├── constraint: /14/15: [/'west'/1 - /'west'/1] │ │ │ ├── cardinality: [0 - 1] │ │ │ ├── key: () - │ │ │ └── fd: ()-->(13-16) + │ │ │ └── fd: ()-->(14-17) │ │ └── scan def_part - │ │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ │ ├── constraint: /19/20 + │ │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ │ ├── constraint: /20/21 │ │ │ ├── [/'central'/1 - /'central'/1] │ │ │ └── [/'east'/1 - /'east'/1] │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ └── fd: ()-->(19-22) + │ │ └── fd: ()-->(20-23) │ └── filters (true) └── filters (true) @@ -10577,26 +10507,26 @@ anti-join (lookup abc_part@b_idx) │ ├── distribution: east │ ├── locality-optimized-search │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + │ │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ │ ├── cardinality: [0 - 1] │ │ ├── key: () │ │ ├── fd: ()-->(1-4) │ │ ├── distribution: east │ │ ├── scan def_part - │ │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ │ ├── constraint: /13/14: [/'east'/10 - /'east'/10] + │ │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ │ ├── constraint: /14/15: [/'east'/10 - /'east'/10] │ │ │ ├── cardinality: [0 - 1] │ │ │ ├── key: () - │ │ │ └── fd: ()-->(13-16) + │ │ │ └── fd: ()-->(14-17) │ │ └── scan def_part - │ │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ │ ├── constraint: /19/20 + │ │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ │ ├── constraint: /20/21 │ │ │ ├── [/'central'/10 - /'central'/10] │ │ │ └── [/'west'/10 - /'west'/10] │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ └── fd: ()-->(19-22) + │ │ └── fd: ()-->(20-23) │ └── filters (true) └── filters (true) @@ -10628,26 +10558,26 @@ anti-join (lookup abc_part) │ ├── distribution: east │ ├── locality-optimized-search │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + │ │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ │ ├── cardinality: [0 - 1] │ │ ├── key: () │ │ ├── fd: ()-->(1-4) │ │ ├── distribution: east │ │ ├── scan def_part - │ │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ │ ├── constraint: /13/14: [/'east'/1 - /'east'/1] + │ │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ │ ├── constraint: /14/15: [/'east'/1 - /'east'/1] │ │ │ ├── cardinality: [0 - 1] │ │ │ ├── key: () - │ │ │ └── fd: ()-->(13-16) + │ │ │ └── fd: ()-->(14-17) │ │ └── scan def_part - │ │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ │ ├── constraint: /19/20 + │ │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ │ ├── constraint: /20/21 │ │ │ ├── [/'central'/1 - /'central'/1] │ │ │ └── [/'west'/1 - /'west'/1] │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ └── fd: ()-->(19-22) + │ │ └── fd: ()-->(20-23) │ └── filters │ └── f:4 > b:9 [outer=(4,9), constraints=(/4: (/NULL - ]; /9: (/NULL - ])] └── filters @@ -10723,26 +10653,26 @@ anti-join (lookup abc_part@c_idx) │ ├── distribution: east │ ├── locality-optimized-search │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + │ │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ │ ├── cardinality: [0 - 1] │ │ ├── key: () │ │ ├── fd: ()-->(1-4) │ │ ├── distribution: east │ │ ├── scan def_part - │ │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ │ ├── constraint: /13/14: [/'east'/10 - /'east'/10] + │ │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ │ ├── constraint: /14/15: [/'east'/10 - /'east'/10] │ │ │ ├── cardinality: [0 - 1] │ │ │ ├── key: () - │ │ │ └── fd: ()-->(13-16) + │ │ │ └── fd: ()-->(14-17) │ │ └── scan def_part - │ │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ │ ├── constraint: /19/20 + │ │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ │ ├── constraint: /20/21 │ │ │ ├── [/'central'/10 - /'central'/10] │ │ │ └── [/'west'/10 - /'west'/10] │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ └── fd: ()-->(19-22) + │ │ └── fd: ()-->(20-23) │ └── filters (true) └── filters (true) @@ -10775,26 +10705,26 @@ anti-join (lookup abc_part@c_idx) │ ├── distribution: east │ ├── locality-optimized-search │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + │ │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ │ ├── cardinality: [0 - 1] │ │ ├── key: () │ │ ├── fd: ()-->(1-4) │ │ ├── distribution: east │ │ ├── scan def_part - │ │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ │ ├── constraint: /13/14: [/'east'/10 - /'east'/10] + │ │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ │ ├── constraint: /14/15: [/'east'/10 - /'east'/10] │ │ │ ├── cardinality: [0 - 1] │ │ │ ├── key: () - │ │ │ └── fd: ()-->(13-16) + │ │ │ └── fd: ()-->(14-17) │ │ └── scan def_part - │ │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ │ ├── constraint: /19/20 + │ │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ │ ├── constraint: /20/21 │ │ │ ├── [/'central'/10 - /'central'/10] │ │ │ └── [/'west'/10 - /'west'/10] │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ └── fd: ()-->(19-22) + │ │ └── fd: ()-->(20-23) │ └── filters │ └── (e:3 % a:8) = 0 [outer=(3,8), immutable] └── filters @@ -10821,26 +10751,26 @@ semi-join (lookup abc_part) ├── distribution: central ├── locality-optimized-search │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ ├── cardinality: [0 - 1] │ ├── key: () │ ├── fd: ()-->(1-4) │ ├── distribution: central │ ├── scan def_part - │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ ├── constraint: /13/14: [/'central'/1 - /'central'/1] + │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ ├── constraint: /14/15: [/'central'/1 - /'central'/1] │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ └── fd: ()-->(13-16) + │ │ └── fd: ()-->(14-17) │ └── scan def_part - │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ ├── constraint: /19/20 + │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ ├── constraint: /20/21 │ │ ├── [/'east'/1 - /'east'/1] │ │ └── [/'west'/1 - /'west'/1] │ ├── cardinality: [0 - 1] │ ├── key: () - │ └── fd: ()-->(19-22) + │ └── fd: ()-->(20-23) └── filters (true) # -------------------------------------------------- @@ -10851,87 +10781,105 @@ semi-join (lookup abc_part) opt locality=(region=east) expect=GenerateLocalityOptimizedLookupJoin SELECT * FROM def_part INNER JOIN abc_part ON e = a WHERE d = 1 ---- -inner-join (lookup abc_part) - ├── columns: r:1!null d:2!null e:3!null f:4 r:7!null a:8!null b:9 c:10 - ├── lookup expression - │ └── filters - │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] - │ └── abc_part.r:7 = 'east' [outer=(7), constraints=(/7: [/'east' - /'east']; tight), fd=()-->(7)] - ├── remote lookup expression - │ └── filters - │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] - │ └── abc_part.r:7 IN ('central', 'west') [outer=(7), constraints=(/7: [/'central' - /'central'] [/'west' - /'west']; tight)] - ├── lookup columns are key +project + ├── columns: r:1!null d:2!null e:3!null f:4 r:7!null a:8!null b:9 c:10 v:11!null ├── cardinality: [0 - 1] + ├── immutable ├── key: () - ├── fd: ()-->(1-4,7-10), (8)==(3), (3)==(8) + ├── fd: ()-->(1-4,7-11), (8)==(3), (3)==(8) ├── distribution: east - ├── locality-optimized-search - │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + ├── inner-join (lookup abc_part) + │ ├── columns: def_part.r:1!null d:2!null e:3!null f:4 abc_part.r:7!null a:8!null b:9 c:10 + │ ├── lookup expression + │ │ └── filters + │ │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] + │ │ └── abc_part.r:7 = 'east' [outer=(7), constraints=(/7: [/'east' - /'east']; tight), fd=()-->(7)] + │ ├── remote lookup expression + │ │ └── filters + │ │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] + │ │ └── abc_part.r:7 IN ('central', 'west') [outer=(7), constraints=(/7: [/'central' - /'central'] [/'west' - /'west']; tight)] + │ ├── lookup columns are key │ ├── cardinality: [0 - 1] │ ├── key: () - │ ├── fd: ()-->(1-4) + │ ├── fd: ()-->(1-4,7-10), (8)==(3), (3)==(8) │ ├── distribution: east - │ ├── scan def_part - │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ ├── constraint: /13/14: [/'east'/1 - /'east'/1] + │ ├── locality-optimized-search + │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 + │ │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ └── fd: ()-->(13-16) - │ └── scan def_part - │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ ├── constraint: /19/20 - │ │ ├── [/'central'/1 - /'central'/1] - │ │ └── [/'west'/1 - /'west'/1] - │ ├── cardinality: [0 - 1] - │ ├── key: () - │ └── fd: ()-->(19-22) - └── filters (true) + │ │ ├── fd: ()-->(1-4) + │ │ ├── distribution: east + │ │ ├── scan def_part + │ │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ │ ├── constraint: /14/15: [/'east'/1 - /'east'/1] + │ │ │ ├── cardinality: [0 - 1] + │ │ │ ├── key: () + │ │ │ └── fd: ()-->(14-17) + │ │ └── scan def_part + │ │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ │ ├── constraint: /20/21 + │ │ │ ├── [/'central'/1 - /'central'/1] + │ │ │ └── [/'west'/1 - /'west'/1] + │ │ ├── cardinality: [0 - 1] + │ │ ├── key: () + │ │ └── fd: ()-->(20-23) + │ └── filters (true) + └── projections + └── a:8 + 10 [as=v:11, outer=(8), immutable] # Locality optimized left join, in a different region. opt locality=(region=west) expect=GenerateLocalityOptimizedLookupJoin SELECT * FROM def_part LEFT JOIN abc_part ON e = a WHERE d = 1 ---- -left-join (lookup abc_part) - ├── columns: r:1!null d:2!null e:3 f:4 r:7 a:8 b:9 c:10 - ├── lookup expression - │ └── filters - │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] - │ └── abc_part.r:7 = 'west' [outer=(7), constraints=(/7: [/'west' - /'west']; tight), fd=()-->(7)] - ├── remote lookup expression - │ └── filters - │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] - │ └── abc_part.r:7 IN ('central', 'east') [outer=(7), constraints=(/7: [/'central' - /'central'] [/'east' - /'east']; tight)] - ├── lookup columns are key +project + ├── columns: r:1!null d:2!null e:3 f:4 r:7 a:8 b:9 c:10 v:11 ├── cardinality: [0 - 1] + ├── immutable ├── key: () - ├── fd: ()-->(1-4,7-10) + ├── fd: ()-->(1-4,7-11) ├── distribution: west - ├── locality-optimized-search - │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + ├── left-join (lookup abc_part) + │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 abc_part.r:7 a:8 b:9 c:10 + │ ├── lookup expression + │ │ └── filters + │ │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] + │ │ └── abc_part.r:7 = 'west' [outer=(7), constraints=(/7: [/'west' - /'west']; tight), fd=()-->(7)] + │ ├── remote lookup expression + │ │ └── filters + │ │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] + │ │ └── abc_part.r:7 IN ('central', 'east') [outer=(7), constraints=(/7: [/'central' - /'central'] [/'east' - /'east']; tight)] + │ ├── lookup columns are key │ ├── cardinality: [0 - 1] │ ├── key: () - │ ├── fd: ()-->(1-4) + │ ├── fd: ()-->(1-4,7-10) │ ├── distribution: west - │ ├── scan def_part - │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ ├── constraint: /13/14: [/'west'/1 - /'west'/1] + │ ├── locality-optimized-search + │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 + │ │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ └── fd: ()-->(13-16) - │ └── scan def_part - │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ ├── constraint: /19/20 - │ │ ├── [/'central'/1 - /'central'/1] - │ │ └── [/'east'/1 - /'east'/1] - │ ├── cardinality: [0 - 1] - │ ├── key: () - │ └── fd: ()-->(19-22) - └── filters (true) + │ │ ├── fd: ()-->(1-4) + │ │ ├── distribution: west + │ │ ├── scan def_part + │ │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ │ ├── constraint: /14/15: [/'west'/1 - /'west'/1] + │ │ │ ├── cardinality: [0 - 1] + │ │ │ ├── key: () + │ │ │ └── fd: ()-->(14-17) + │ │ └── scan def_part + │ │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ │ ├── constraint: /20/21 + │ │ │ ├── [/'central'/1 - /'central'/1] + │ │ │ └── [/'east'/1 - /'east'/1] + │ │ ├── cardinality: [0 - 1] + │ │ ├── key: () + │ │ └── fd: ()-->(20-23) + │ └── filters (true) + └── projections + └── CASE abc_part.r:7 IS NULL WHEN true THEN CAST(NULL AS INT8) ELSE a:8 + 10 END [as=v:11, outer=(7,8), immutable] # Locality optimized semi join, in a different region. opt locality=(region=central) expect=GenerateLocalityOptimizedLookupJoin @@ -10954,26 +10902,26 @@ semi-join (lookup abc_part) ├── distribution: central ├── locality-optimized-search │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ ├── cardinality: [0 - 1] │ ├── key: () │ ├── fd: ()-->(1-4) │ ├── distribution: central │ ├── scan def_part - │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ ├── constraint: /13/14: [/'central'/1 - /'central'/1] + │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ ├── constraint: /14/15: [/'central'/1 - /'central'/1] │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ └── fd: ()-->(13-16) + │ │ └── fd: ()-->(14-17) │ └── scan def_part - │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ ├── constraint: /19/20 + │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ ├── constraint: /20/21 │ │ ├── [/'east'/1 - /'east'/1] │ │ └── [/'west'/1 - /'west'/1] │ ├── cardinality: [0 - 1] │ ├── key: () - │ └── fd: ()-->(19-22) + │ └── fd: ()-->(20-23) └── filters (true) # Locality optimized semi join, with an extra ON filter. @@ -10997,26 +10945,26 @@ semi-join (lookup abc_part) ├── distribution: west ├── locality-optimized-search │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ ├── cardinality: [0 - 1] │ ├── key: () │ ├── fd: ()-->(1-4) │ ├── distribution: west │ ├── scan def_part - │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ ├── constraint: /13/14: [/'west'/1 - /'west'/1] + │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ ├── constraint: /14/15: [/'west'/1 - /'west'/1] │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ └── fd: ()-->(13-16) + │ │ └── fd: ()-->(14-17) │ └── scan def_part - │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ ├── constraint: /19/20 + │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ ├── constraint: /20/21 │ │ ├── [/'central'/1 - /'central'/1] │ │ └── [/'east'/1 - /'east'/1] │ ├── cardinality: [0 - 1] │ ├── key: () - │ └── fd: ()-->(19-22) + │ └── fd: ()-->(20-23) └── filters └── f:4 > b:9 [outer=(4,9), constraints=(/4: (/NULL - ]; /9: (/NULL - ])] @@ -11024,134 +10972,160 @@ semi-join (lookup abc_part) opt locality=(region=east) expect=GenerateLocalityOptimizedLookupJoin SELECT * FROM def_part INNER JOIN abc_part ON f = b WHERE d = 10 ---- -inner-join (lookup abc_part) - ├── columns: r:1!null d:2!null e:3 f:4!null r:7!null a:8!null b:9!null c:10 - ├── key columns: [7 8] = [7 8] - ├── lookup columns are key +project + ├── columns: r:1!null d:2!null e:3 f:4!null r:7!null a:8!null b:9!null c:10 v:11!null ├── cardinality: [0 - 1] + ├── immutable ├── key: () - ├── fd: ()-->(1-4,7-10), (9)==(4), (4)==(9) + ├── fd: ()-->(1-4,7-11), (9)==(4), (4)==(9) ├── distribution: east - ├── inner-join (lookup abc_part@b_idx) - │ ├── columns: def_part.r:1!null d:2!null e:3 f:4!null abc_part.r:7!null a:8!null b:9!null - │ ├── lookup expression - │ │ └── filters - │ │ ├── f:4 = b:9 [outer=(4,9), constraints=(/4: (/NULL - ]; /9: (/NULL - ]), fd=(4)==(9), (9)==(4)] - │ │ └── abc_part.r:7 = 'east' [outer=(7), constraints=(/7: [/'east' - /'east']; tight), fd=()-->(7)] - │ ├── remote lookup expression - │ │ └── filters - │ │ ├── f:4 = b:9 [outer=(4,9), constraints=(/4: (/NULL - ]; /9: (/NULL - ]), fd=(4)==(9), (9)==(4)] - │ │ └── abc_part.r:7 IN ('central', 'west') [outer=(7), constraints=(/7: [/'central' - /'central'] [/'west' - /'west']; tight)] + ├── inner-join (lookup abc_part) + │ ├── columns: def_part.r:1!null d:2!null e:3 f:4!null abc_part.r:7!null a:8!null b:9!null c:10 + │ ├── key columns: [7 8] = [7 8] │ ├── lookup columns are key │ ├── cardinality: [0 - 1] │ ├── key: () - │ ├── fd: ()-->(1-4,7-9), (9)==(4), (4)==(9) + │ ├── fd: ()-->(1-4,7-10), (9)==(4), (4)==(9) │ ├── distribution: east - │ ├── locality-optimized-search - │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + │ ├── inner-join (lookup abc_part@b_idx) + │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4!null abc_part.r:7!null a:8!null b:9!null + │ │ ├── lookup expression + │ │ │ └── filters + │ │ │ ├── f:4 = b:9 [outer=(4,9), constraints=(/4: (/NULL - ]; /9: (/NULL - ]), fd=(4)==(9), (9)==(4)] + │ │ │ └── abc_part.r:7 = 'east' [outer=(7), constraints=(/7: [/'east' - /'east']; tight), fd=()-->(7)] + │ │ ├── remote lookup expression + │ │ │ └── filters + │ │ │ ├── f:4 = b:9 [outer=(4,9), constraints=(/4: (/NULL - ]; /9: (/NULL - ]), fd=(4)==(9), (9)==(4)] + │ │ │ └── abc_part.r:7 IN ('central', 'west') [outer=(7), constraints=(/7: [/'central' - /'central'] [/'west' - /'west']; tight)] + │ │ ├── lookup columns are key │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ ├── fd: ()-->(1-4) + │ │ ├── fd: ()-->(1-4,7-9), (9)==(4), (4)==(9) │ │ ├── distribution: east - │ │ ├── scan def_part - │ │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ │ ├── constraint: /13/14: [/'east'/10 - /'east'/10] + │ │ ├── locality-optimized-search + │ │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 + │ │ │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ │ │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ │ │ ├── cardinality: [0 - 1] │ │ │ ├── key: () - │ │ │ └── fd: ()-->(13-16) - │ │ └── scan def_part - │ │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ │ ├── constraint: /19/20 - │ │ │ ├── [/'central'/10 - /'central'/10] - │ │ │ └── [/'west'/10 - /'west'/10] - │ │ ├── cardinality: [0 - 1] - │ │ ├── key: () - │ │ └── fd: ()-->(19-22) + │ │ │ ├── fd: ()-->(1-4) + │ │ │ ├── distribution: east + │ │ │ ├── scan def_part + │ │ │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ │ │ ├── constraint: /14/15: [/'east'/10 - /'east'/10] + │ │ │ │ ├── cardinality: [0 - 1] + │ │ │ │ ├── key: () + │ │ │ │ └── fd: ()-->(14-17) + │ │ │ └── scan def_part + │ │ │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ │ │ ├── constraint: /20/21 + │ │ │ │ ├── [/'central'/10 - /'central'/10] + │ │ │ │ └── [/'west'/10 - /'west'/10] + │ │ │ ├── cardinality: [0 - 1] + │ │ │ ├── key: () + │ │ │ └── fd: ()-->(20-23) + │ │ └── filters (true) │ └── filters (true) - └── filters (true) + └── projections + └── a:8 + 10 [as=v:11, outer=(8), immutable] # With an extra ON filter. opt locality=(region=east) expect=GenerateLocalityOptimizedLookupJoin SELECT * FROM def_part INNER JOIN abc_part ON e = a AND f > b WHERE d = 1 ---- -inner-join (lookup abc_part) - ├── columns: r:1!null d:2!null e:3!null f:4!null r:7!null a:8!null b:9!null c:10 - ├── lookup expression - │ └── filters - │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] - │ └── abc_part.r:7 = 'east' [outer=(7), constraints=(/7: [/'east' - /'east']; tight), fd=()-->(7)] - ├── remote lookup expression - │ └── filters - │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] - │ └── abc_part.r:7 IN ('central', 'west') [outer=(7), constraints=(/7: [/'central' - /'central'] [/'west' - /'west']; tight)] - ├── lookup columns are key +project + ├── columns: r:1!null d:2!null e:3!null f:4!null r:7!null a:8!null b:9!null c:10 v:11!null ├── cardinality: [0 - 1] + ├── immutable ├── key: () - ├── fd: ()-->(1-4,7-10), (8)==(3), (3)==(8) + ├── fd: ()-->(1-4,7-11), (8)==(3), (3)==(8) ├── distribution: east - ├── locality-optimized-search - │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + ├── inner-join (lookup abc_part) + │ ├── columns: def_part.r:1!null d:2!null e:3!null f:4!null abc_part.r:7!null a:8!null b:9!null c:10 + │ ├── lookup expression + │ │ └── filters + │ │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] + │ │ └── abc_part.r:7 = 'east' [outer=(7), constraints=(/7: [/'east' - /'east']; tight), fd=()-->(7)] + │ ├── remote lookup expression + │ │ └── filters + │ │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] + │ │ └── abc_part.r:7 IN ('central', 'west') [outer=(7), constraints=(/7: [/'central' - /'central'] [/'west' - /'west']; tight)] + │ ├── lookup columns are key │ ├── cardinality: [0 - 1] │ ├── key: () - │ ├── fd: ()-->(1-4) + │ ├── fd: ()-->(1-4,7-10), (8)==(3), (3)==(8) │ ├── distribution: east - │ ├── scan def_part - │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ ├── constraint: /13/14: [/'east'/1 - /'east'/1] + │ ├── locality-optimized-search + │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 + │ │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ └── fd: ()-->(13-16) - │ └── scan def_part - │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ ├── constraint: /19/20 - │ │ ├── [/'central'/1 - /'central'/1] - │ │ └── [/'west'/1 - /'west'/1] - │ ├── cardinality: [0 - 1] - │ ├── key: () - │ └── fd: ()-->(19-22) - └── filters - └── f:4 > b:9 [outer=(4,9), constraints=(/4: (/NULL - ]; /9: (/NULL - ])] + │ │ ├── fd: ()-->(1-4) + │ │ ├── distribution: east + │ │ ├── scan def_part + │ │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ │ ├── constraint: /14/15: [/'east'/1 - /'east'/1] + │ │ │ ├── cardinality: [0 - 1] + │ │ │ ├── key: () + │ │ │ └── fd: ()-->(14-17) + │ │ └── scan def_part + │ │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ │ ├── constraint: /20/21 + │ │ │ ├── [/'central'/1 - /'central'/1] + │ │ │ └── [/'west'/1 - /'west'/1] + │ │ ├── cardinality: [0 - 1] + │ │ ├── key: () + │ │ └── fd: ()-->(20-23) + │ └── filters + │ └── f:4 > b:9 [outer=(4,9), constraints=(/4: (/NULL - ]; /9: (/NULL - ])] + └── projections + └── a:8 + 10 [as=v:11, outer=(8), immutable] # Optimization applies even though the scan may produce more than one row. opt locality=(region=east) expect=GenerateLocalityOptimizedLookupJoin SELECT * FROM def_part INNER JOIN abc_part ON e = a WHERE f = 10 ---- distribute - ├── columns: r:1!null d:2!null e:3!null f:4!null r:7!null a:8!null b:9 c:10 + ├── columns: r:1!null d:2!null e:3!null f:4!null r:7!null a:8!null b:9 c:10 v:11!null + ├── immutable ├── key: (2) - ├── fd: ()-->(4), (2)-->(1,3), (3)-->(1,2), (8)-->(7,9,10), (9)~~>(7,8,10), (3)==(8), (8)==(3) + ├── fd: ()-->(4), (2)-->(1,3), (3)-->(1,2), (8)-->(7,9-11), (9)~~>(7,8,10), (3)==(8), (8)==(3) ├── distribution: east ├── input distribution: central,east,west - └── inner-join (lookup abc_part) - ├── columns: def_part.r:1!null d:2!null e:3!null f:4!null abc_part.r:7!null a:8!null b:9 c:10 - ├── lookup expression - │ └── filters - │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] - │ └── abc_part.r:7 = 'east' [outer=(7), constraints=(/7: [/'east' - /'east']; tight), fd=()-->(7)] - ├── remote lookup expression - │ └── filters - │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] - │ └── abc_part.r:7 IN ('central', 'west') [outer=(7), constraints=(/7: [/'central' - /'central'] [/'west' - /'west']; tight)] - ├── lookup columns are key + └── project + ├── columns: v:11!null def_part.r:1!null d:2!null e:3!null f:4!null abc_part.r:7!null a:8!null b:9 c:10 + ├── immutable ├── key: (2) - ├── fd: ()-->(4), (2)-->(1,3), (3)-->(1,2), (8)-->(7,9,10), (9)~~>(7,8,10), (3)==(8), (8)==(3) - ├── index-join def_part - │ ├── columns: def_part.r:1!null d:2!null e:3 f:4!null + ├── fd: ()-->(4), (2)-->(1,3), (3)-->(1,2), (8)-->(7,9-11), (9)~~>(7,8,10), (3)==(8), (8)==(3) + ├── inner-join (lookup abc_part) + │ ├── columns: def_part.r:1!null d:2!null e:3!null f:4!null abc_part.r:7!null a:8!null b:9 c:10 + │ ├── lookup expression + │ │ └── filters + │ │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] + │ │ └── abc_part.r:7 = 'east' [outer=(7), constraints=(/7: [/'east' - /'east']; tight), fd=()-->(7)] + │ ├── remote lookup expression + │ │ └── filters + │ │ ├── e:3 = a:8 [outer=(3,8), constraints=(/3: (/NULL - ]; /8: (/NULL - ]), fd=(3)==(8), (8)==(3)] + │ │ └── abc_part.r:7 IN ('central', 'west') [outer=(7), constraints=(/7: [/'central' - /'central'] [/'west' - /'west']; tight)] + │ ├── lookup columns are key │ ├── key: (2) - │ ├── fd: ()-->(4), (2)-->(1,3), (3)~~>(1,2) - │ └── scan def_part@f_idx - │ ├── columns: def_part.r:1!null d:2!null f:4!null - │ ├── constraint: /1/4/2 - │ │ ├── [/'central'/10 - /'central'/10] - │ │ ├── [/'east'/10 - /'east'/10] - │ │ └── [/'west'/10 - /'west'/10] - │ ├── key: (2) - │ └── fd: ()-->(4), (2)-->(1) - └── filters (true) + │ ├── fd: ()-->(4), (2)-->(1,3), (3)-->(1,2), (8)-->(7,9,10), (9)~~>(7,8,10), (3)==(8), (8)==(3) + │ ├── index-join def_part + │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4!null + │ │ ├── key: (2) + │ │ ├── fd: ()-->(4), (2)-->(1,3), (3)~~>(1,2) + │ │ └── scan def_part@f_idx + │ │ ├── columns: def_part.r:1!null d:2!null f:4!null + │ │ ├── constraint: /1/4/2 + │ │ │ ├── [/'central'/10 - /'central'/10] + │ │ │ ├── [/'east'/10 - /'east'/10] + │ │ │ └── [/'west'/10 - /'west'/10] + │ │ ├── key: (2) + │ │ └── fd: ()-->(4), (2)-->(1) + │ └── filters (true) + └── projections + └── a:8 + 10 [as=v:11, outer=(8), immutable] # Optimization applies for a semi join even though the lookup join may have more # than one matching row. @@ -11174,26 +11148,26 @@ semi-join (lookup abc_part@c_idx) ├── distribution: east ├── locality-optimized-search │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ ├── cardinality: [0 - 1] │ ├── key: () │ ├── fd: ()-->(1-4) │ ├── distribution: east │ ├── scan def_part - │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ ├── constraint: /13/14: [/'east'/10 - /'east'/10] + │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ ├── constraint: /14/15: [/'east'/10 - /'east'/10] │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ └── fd: ()-->(13-16) + │ │ └── fd: ()-->(14-17) │ └── scan def_part - │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ ├── constraint: /19/20 + │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ ├── constraint: /20/21 │ │ ├── [/'central'/10 - /'central'/10] │ │ └── [/'west'/10 - /'west'/10] │ ├── cardinality: [0 - 1] │ ├── key: () - │ └── fd: ()-->(19-22) + │ └── fd: ()-->(20-23) └── filters (true) # Optimization does not apply for a semi join that may have more than one @@ -11214,26 +11188,26 @@ semi-join (lookup abc_part@c_idx) ├── distribution: east ├── locality-optimized-search │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ ├── cardinality: [0 - 1] │ ├── key: () │ ├── fd: ()-->(1-4) │ ├── distribution: east │ ├── scan def_part - │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ ├── constraint: /13/14: [/'east'/10 - /'east'/10] + │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ ├── constraint: /14/15: [/'east'/10 - /'east'/10] │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ └── fd: ()-->(13-16) + │ │ └── fd: ()-->(14-17) │ └── scan def_part - │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ ├── constraint: /19/20 + │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ ├── constraint: /20/21 │ │ ├── [/'central'/10 - /'central'/10] │ │ └── [/'west'/10 - /'west'/10] │ ├── cardinality: [0 - 1] │ ├── key: () - │ └── fd: ()-->(19-22) + │ └── fd: ()-->(20-23) └── filters └── (e:3 % a:8) = 0 [outer=(3,8), immutable] @@ -11242,46 +11216,54 @@ semi-join (lookup abc_part@c_idx) opt locality=(region=east) expect-not=GenerateLocalityOptimizedLookupJoin SELECT * FROM def_part INNER JOIN abc_part ON f = c WHERE d = 10 ---- -inner-join (lookup abc_part) - ├── columns: r:1!null d:2!null e:3 f:4!null r:7!null a:8!null b:9 c:10!null - ├── key columns: [7 8] = [7 8] - ├── lookup columns are key +project + ├── columns: r:1!null d:2!null e:3 f:4!null r:7!null a:8!null b:9 c:10!null v:11!null + ├── immutable ├── key: (8) - ├── fd: ()-->(1-4,10), (8)-->(7,9), (9)~~>(7,8), (4)==(10), (10)==(4) + ├── fd: ()-->(1-4,10), (8)-->(7,9,11), (9)~~>(7,8), (4)==(10), (10)==(4) ├── distribution: east - ├── inner-join (lookup abc_part@c_idx) - │ ├── columns: def_part.r:1!null d:2!null e:3 f:4!null abc_part.r:7!null a:8!null c:10!null - │ ├── lookup expression - │ │ └── filters - │ │ ├── f:4 = c:10 [outer=(4,10), constraints=(/4: (/NULL - ]; /10: (/NULL - ]), fd=(4)==(10), (10)==(4)] - │ │ └── abc_part.r:7 IN ('central', 'east', 'west') [outer=(7), constraints=(/7: [/'central' - /'central'] [/'east' - /'east'] [/'west' - /'west']; tight)] + ├── inner-join (lookup abc_part) + │ ├── columns: def_part.r:1!null d:2!null e:3 f:4!null abc_part.r:7!null a:8!null b:9 c:10!null + │ ├── key columns: [7 8] = [7 8] + │ ├── lookup columns are key │ ├── key: (8) - │ ├── fd: ()-->(1-4,10), (8)-->(7), (4)==(10), (10)==(4) + │ ├── fd: ()-->(1-4,10), (8)-->(7,9), (9)~~>(7,8), (4)==(10), (10)==(4) │ ├── distribution: east - │ ├── locality-optimized-search - │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ │ ├── right columns: def_part.r:19 d:20 e:21 f:22 - │ │ ├── cardinality: [0 - 1] - │ │ ├── key: () - │ │ ├── fd: ()-->(1-4) + │ ├── inner-join (lookup abc_part@c_idx) + │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4!null abc_part.r:7!null a:8!null c:10!null + │ │ ├── lookup expression + │ │ │ └── filters + │ │ │ ├── f:4 = c:10 [outer=(4,10), constraints=(/4: (/NULL - ]; /10: (/NULL - ]), fd=(4)==(10), (10)==(4)] + │ │ │ └── abc_part.r:7 IN ('central', 'east', 'west') [outer=(7), constraints=(/7: [/'central' - /'central'] [/'east' - /'east'] [/'west' - /'west']; tight)] + │ │ ├── key: (8) + │ │ ├── fd: ()-->(1-4,10), (8)-->(7), (4)==(10), (10)==(4) │ │ ├── distribution: east - │ │ ├── scan def_part - │ │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ │ ├── constraint: /13/14: [/'east'/10 - /'east'/10] + │ │ ├── locality-optimized-search + │ │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 + │ │ │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ │ │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ │ │ ├── cardinality: [0 - 1] │ │ │ ├── key: () - │ │ │ └── fd: ()-->(13-16) - │ │ └── scan def_part - │ │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ │ ├── constraint: /19/20 - │ │ │ ├── [/'central'/10 - /'central'/10] - │ │ │ └── [/'west'/10 - /'west'/10] - │ │ ├── cardinality: [0 - 1] - │ │ ├── key: () - │ │ └── fd: ()-->(19-22) + │ │ │ ├── fd: ()-->(1-4) + │ │ │ ├── distribution: east + │ │ │ ├── scan def_part + │ │ │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ │ │ ├── constraint: /14/15: [/'east'/10 - /'east'/10] + │ │ │ │ ├── cardinality: [0 - 1] + │ │ │ │ ├── key: () + │ │ │ │ └── fd: ()-->(14-17) + │ │ │ └── scan def_part + │ │ │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ │ │ ├── constraint: /20/21 + │ │ │ │ ├── [/'central'/10 - /'central'/10] + │ │ │ │ └── [/'west'/10 - /'west'/10] + │ │ │ ├── cardinality: [0 - 1] + │ │ │ ├── key: () + │ │ │ └── fd: ()-->(20-23) + │ │ └── filters (true) │ └── filters (true) - └── filters (true) + └── projections + └── a:8 + 10 [as=v:11, outer=(8), immutable] # Optimization does not apply for anti join. opt locality=(region=east) expect-not=GenerateLocalityOptimizedLookupJoin @@ -11311,26 +11293,75 @@ anti-join (lookup abc_part) │ ├── distribution: east │ ├── locality-optimized-search │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 - │ │ ├── left columns: def_part.r:13 d:14 e:15 f:16 - │ │ ├── right columns: def_part.r:19 d:20 e:21 f:22 + │ │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ │ ├── right columns: def_part.r:20 d:21 e:22 f:23 │ │ ├── cardinality: [0 - 1] │ │ ├── key: () │ │ ├── fd: ()-->(1-4) │ │ ├── distribution: east │ │ ├── scan def_part - │ │ │ ├── columns: def_part.r:13!null d:14!null e:15 f:16 - │ │ │ ├── constraint: /13/14: [/'east'/1 - /'east'/1] + │ │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ │ ├── constraint: /14/15: [/'east'/1 - /'east'/1] │ │ │ ├── cardinality: [0 - 1] │ │ │ ├── key: () - │ │ │ └── fd: ()-->(13-16) + │ │ │ └── fd: ()-->(14-17) │ │ └── scan def_part - │ │ ├── columns: def_part.r:19!null d:20!null e:21 f:22 - │ │ ├── constraint: /19/20 + │ │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ │ ├── constraint: /20/21 │ │ │ ├── [/'central'/1 - /'central'/1] │ │ │ └── [/'west'/1 - /'west'/1] │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ └── fd: ()-->(19-22) + │ │ └── fd: ()-->(20-23) + │ └── filters (true) + └── filters (true) + +# We do not currently support locality optimized lookup joins on indexes with +# virtual columns. +opt locality=(region=east) expect-not=GenerateLocalityOptimizedLookupJoin +SELECT * FROM def_part INNER JOIN abc_part ON f = v WHERE d = 1 +---- +inner-join (lookup abc_part) + ├── columns: r:1!null d:2!null e:3 f:4!null r:7!null a:8!null b:9 c:10 v:11!null + ├── key columns: [7 8] = [7 8] + ├── lookup columns are key + ├── immutable + ├── key: (8) + ├── fd: ()-->(1-4,11), (8)-->(7,9,10), (9)~~>(7,8,10), (4)==(11), (11)==(4) + ├── distribution: east + ├── inner-join (lookup abc_part@v_idx) + │ ├── columns: def_part.r:1!null d:2!null e:3 f:4!null abc_part.r:7!null a:8!null v:11!null + │ ├── lookup expression + │ │ └── filters + │ │ ├── f:4 = v:11 [outer=(4,11), constraints=(/4: (/NULL - ]; /11: (/NULL - ]), fd=(4)==(11), (11)==(4)] + │ │ └── abc_part.r:7 IN ('central', 'east', 'west') [outer=(7), constraints=(/7: [/'central' - /'central'] [/'east' - /'east'] [/'west' - /'west']; tight)] + │ ├── lookup columns are key + │ ├── cardinality: [0 - 1] + │ ├── key: () + │ ├── fd: ()-->(1-4,7,8,11), (11)==(4), (4)==(11) + │ ├── distribution: east + │ ├── locality-optimized-search + │ │ ├── columns: def_part.r:1!null d:2!null e:3 f:4 + │ │ ├── left columns: def_part.r:14 d:15 e:16 f:17 + │ │ ├── right columns: def_part.r:20 d:21 e:22 f:23 + │ │ ├── cardinality: [0 - 1] + │ │ ├── key: () + │ │ ├── fd: ()-->(1-4) + │ │ ├── distribution: east + │ │ ├── scan def_part + │ │ │ ├── columns: def_part.r:14!null d:15!null e:16 f:17 + │ │ │ ├── constraint: /14/15: [/'east'/1 - /'east'/1] + │ │ │ ├── cardinality: [0 - 1] + │ │ │ ├── key: () + │ │ │ └── fd: ()-->(14-17) + │ │ └── scan def_part + │ │ ├── columns: def_part.r:20!null d:21!null e:22 f:23 + │ │ ├── constraint: /20/21 + │ │ │ ├── [/'central'/1 - /'central'/1] + │ │ │ └── [/'west'/1 - /'west'/1] + │ │ ├── cardinality: [0 - 1] + │ │ ├── key: () + │ │ └── fd: ()-->(20-23) │ └── filters (true) └── filters (true) @@ -11445,6 +11476,35 @@ inner-join (lookup metric_values) │ └── fd: (6)-->(7) └── filters (true) +# Lookup expressions in forms that are not supported by the execution engine +# should not be generated. In this case, a lookup expression in the form +# `time IN (...)` should be created rather than `time IN (...) AND time > ...`. +opt expect=GenerateLookupJoinsWithFilter +SELECT * +FROM metric_values +INNER JOIN metrics +ON metric_id=id +WHERE + time IN ('2022-04-07 00:00:00+00:00', '2022-04-08 00:00:00+00:00', '2022-04-09 00:00:00+00:00') AND + time > '2022-04-07 00:00:00+00:00' AND + name='cpu' +---- +inner-join (lookup metric_values) + ├── columns: metric_id:1!null time:2!null value:3 id:6!null name:7!null + ├── lookup expression + │ └── filters + │ ├── metric_id:1 = id:6 [outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ]), fd=(1)==(6), (6)==(1)] + │ └── time:2 IN ('2022-04-08 00:00:00+00:00', '2022-04-09 00:00:00+00:00') [outer=(2), constraints=(/2: [/'2022-04-08 00:00:00+00:00' - /'2022-04-08 00:00:00+00:00'] [/'2022-04-09 00:00:00+00:00' - /'2022-04-09 00:00:00+00:00']; tight)] + ├── key: (2,6) + ├── fd: ()-->(7), (1,2)-->(3), (1)==(6), (6)==(1) + ├── scan metrics@name_index + │ ├── columns: id:6!null name:7!null + │ ├── constraint: /7/6: [/'cpu' - /'cpu'] + │ ├── key: (6) + │ └── fd: ()-->(7) + └── filters + └── (time:2 IN ('2022-04-07 00:00:00+00:00', '2022-04-08 00:00:00+00:00', '2022-04-09 00:00:00+00:00')) AND (time:2 > '2022-04-07 00:00:00+00:00') [outer=(2), constraints=(/2: [/'2022-04-08 00:00:00+00:00' - /'2022-04-08 00:00:00+00:00'] [/'2022-04-09 00:00:00+00:00' - /'2022-04-09 00:00:00+00:00']; tight)] + # We don't support turning LIKE into scans yet, test that we fall back to a # filter. opt expect-not=GenerateLookupJoins diff --git a/pkg/sql/opt/xform/testdata/rules/join_order b/pkg/sql/opt/xform/testdata/rules/join_order index a02294127b6f..4aabdd558eda 100644 --- a/pkg/sql/opt/xform/testdata/rules/join_order +++ b/pkg/sql/opt/xform/testdata/rules/join_order @@ -312,7 +312,7 @@ New expression 3 of 3: memo join-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, ~28KB, required=[presentation: b:1,x:2,c:5,y:6,a:9,b:10,c:11,d:12]) +memo (optimized, ~27KB, 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) @@ -365,7 +365,7 @@ memo (optimized, ~28KB, required=[presentation: b:1,x:2,c:5,y:6,a:9,b:10,c:11,d: memo join-limit=2 SELECT * FROM bx, cy, abc WHERE a = 1 AND abc.b = bx.b AND abc.c = cy.c ---- -memo (optimized, ~46KB, required=[presentation: b:1,x:2,c:5,y:6,a:9,b:10,c:11,d:12]) +memo (optimized, ~43KB, 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))