From 77919598884e5edb503369356ccb1c6fba11ed2a Mon Sep 17 00:00:00 2001 From: Shu Lin Date: Fri, 17 Sep 2021 15:21:34 -0400 Subject: [PATCH] Place conditions at the correct position in the iterators Prior to this patch, if hash joins are used, the conditions can be pushed too high up, which results on "no condition" in hash join iterator; or the condition can be left too low, which forces hash join to become nest-loop-join (as hash join doesn't allow inner to correlate with outer). --- sql/sql_executor.cc | 364 ++++++++++++++++++++++++++++------------------------ sql/sql_executor.h | 2 +- 2 files changed, 195 insertions(+), 171 deletions(-) diff --git a/sql/sql_executor.cc b/sql/sql_executor.cc index d0c6bb3..d0ec89a 100644 --- a/sql/sql_executor.cc +++ b/sql/sql_executor.cc @@ -1016,6 +1016,28 @@ static Item *GetInnermostCondition(Item *item) { return item; } +// Try to substitute the multi_equality_fields in "item" so that it can be +// applied as join predicates between left_table_map and right_table_map +// @returns true if success; false otherwise +static bool TryToMakeJoinPredicatesForTables(Item *item, + table_map left_table_map, + table_map right_table_map) { + if (item->type() == Item::FUNC_ITEM || item->type() == Item::COND_ITEM) { + Item_func *func_item = down_cast(item); + if (func_item->functype() == Item_func::EQ_FUNC) { + down_cast(func_item) + ->ensure_multi_equality_fields_are_available(left_table_map, + right_table_map); + table_map available_tables = left_table_map | right_table_map; + if (((func_item->used_tables() & ~PSEUDO_TABLE_BITS) & + ~available_tables) == 0) { + return true; + } + } + } + return false; +} + /* There are three kinds of conditions stored into a table's QEP_TAB object: @@ -1055,9 +1077,10 @@ static Item *GetInnermostCondition(Item *item) { it here. */ void SplitConditions(Item *condition, QEP_TAB *current_table, + table_map left_tables, table_map right_tables, vector *predicates_below_join, - vector *predicates_above_join, - vector *join_conditions) { + vector *predicates_above_join) { + table_map available_tables = left_tables | right_tables; Mem_root_array condition_parts(*THR_MALLOC); ExtractConditions(condition, &condition_parts); for (Item *item : condition_parts) { @@ -1067,7 +1090,9 @@ void SplitConditions(Item *condition, QEP_TAB *current_table, if (trig_cond->get_trig_type() == Item_func_trig_cond::FOUND_MATCH) { // A WHERE predicate on the table that needs to be pushed up above the // join (case #3 above). Push it up to above the last outer join. - predicates_above_join->push_back(PendingCondition{inner_cond, -1}); + Item *inner_inner_cond = GetInnermostCondition(inner_cond); + predicates_above_join->push_back( + PendingCondition{inner_inner_cond, NO_PLAN_IDX}); } else if (trig_cond->get_trig_type() == Item_func_trig_cond::IS_NOT_NULL_COMPL) { // It's a join condition, so it should nominally go directly onto the @@ -1083,33 +1108,32 @@ void SplitConditions(Item *condition, QEP_TAB *current_table, // trigger condition. We want the innermost condition, as we really do // not care about trigger conditions after this point. Item *inner_inner_cond = GetInnermostCondition(inner_trig_cond); - if (join_conditions != nullptr) { - // If join_conditions is set, it indicates that we are on the right - // side of an outer join that will be executed using hash join. The - // condition must be moved to the point where the hash join iterator - // is created, so the condition can be attached to the iterator. - join_conditions->push_back( - PendingCondition{inner_inner_cond, trig_cond->idx()}); - } else { + Mem_root_array sub_parts(*THR_MALLOC); + ExtractConditions(inner_inner_cond, &sub_parts); + for (Item *sub_item : sub_parts) { predicates_above_join->push_back( - PendingCondition{inner_inner_cond, inner_trig_cond->idx()}); + PendingCondition{sub_item, trig_cond->idx()}); } } else { - if (join_conditions != nullptr) { - // Similar to the left join above: If join_conditions is set, - // it indicates that we are on the inner side of an antijoin (we are - // dealing with the NOT IN side in the below example), and the - // antijoin will be executed using hash join: - // - // SELECT * FROM t1 WHERE t1.col1 NOT IN (SELECT t2.col1 FROM t2); - // - // In this case, the condition must be moved up to the outer side - // where the hash join iterator is created, so it can be attached - // to the iterator. - join_conditions->push_back( - PendingCondition{inner_cond, trig_cond->idx()}); - } else { - predicates_below_join->push_back(inner_cond); + Mem_root_array sub_parts(*THR_MALLOC); + ExtractConditions(inner_cond, &sub_parts); + for (Item *sub_item : sub_parts) { + if (((sub_item->used_tables() & ~PSEUDO_TABLE_BITS) & + ~available_tables) == 0) { + predicates_below_join->push_back(sub_item); + } else { + if (TryToMakeJoinPredicatesForTables(sub_item, left_tables, + right_tables)) { + predicates_below_join->push_back(sub_item); + } else { + // This happens with hash join. The required tables are not + // accessible in this part of join tree. We need to push the + // predicate up so that it can be attached to the hash join + // iterator. + predicates_above_join->push_back( + PendingCondition{sub_item, trig_cond->idx()}); + } + } } } } else { @@ -1117,14 +1141,14 @@ void SplitConditions(Item *condition, QEP_TAB *current_table, } } else { if (current_table->match_tab != NO_PLAN_IDX && - join_conditions != nullptr && IsJoinCondition(item, current_table)) { + ((item->used_tables() & ~PSEUDO_TABLE_BITS) & ~available_tables) != + 0) { // We are on the inner side of a semijoin, and the item we are looking // at is a join condition. In addition, the join will be executed using // hash join. Move the join condition up to the table we are semijoining // against (where the join iterator is created), so that it can be // attached to the hash join iterator. - join_conditions->push_back( - PendingCondition{item, current_table->match_tab}); + predicates_above_join->push_back(PendingCondition{item, NO_PLAN_IDX}); } else { predicates_below_join->push_back(item); } @@ -1399,10 +1423,10 @@ static Substructure FindSubstructure( /// @cond Doxygen_is_confused static AccessPath *ConnectJoins( plan_idx upper_first_idx, plan_idx first_idx, plan_idx last_idx, - QEP_TAB *qep_tabs, THD *thd, CallingContext calling_context, + table_map available_tables, QEP_TAB *qep_tabs, THD *thd, + CallingContext calling_context, vector *pending_conditions, vector *pending_invalidators, - vector *pending_join_conditions, qep_tab_map *unhandled_duplicates, table_map *conditions_depend_on_outer_tables); /// @endcond @@ -1560,11 +1584,10 @@ AccessPath *GetTableAccessPath(THD *thd, QEP_TAB *qep_tab, QEP_TAB *qep_tabs) { table_map conditions_depend_on_outer_tables = 0; vector pending_invalidators; AccessPath *subtree_path = ConnectJoins( - /*upper_first_idx=*/NO_PLAN_IDX, join_start, join_end, qep_tabs, thd, + /*upper_first_idx=*/NO_PLAN_IDX, join_start, join_end, 0, qep_tabs, thd, TOP_LEVEL, /*pending_conditions=*/nullptr, &pending_invalidators, - /*pending_join_conditions=*/nullptr, &unhandled_duplicates, - &conditions_depend_on_outer_tables); + &unhandled_duplicates, &conditions_depend_on_outer_tables); // If there were any weedouts that we had to drop during ConnectJoins() // (ie., the join left some tables that were supposed to be deduplicated @@ -2048,8 +2071,8 @@ static bool SubtreeHasIncompletePushedJoin(JOIN *join, qep_tab_map subtree) { // join types. static bool PushedJoinRejectsHashJoin(JOIN *join, qep_tab_map left_subtree, qep_tab_map right_subtree, - JoinType join_type) { - if (join_type == JoinType::INNER) { + bool is_inner_join) { + if (is_inner_join) { // Inner hash join works fine with pushed joins as long as we ensure that we // do not spill to disk, _and_ the left subtree (the build input) does not // have an incomplete pushed join. @@ -2112,11 +2135,16 @@ static bool InsideOuterOrAntiJoin(QEP_TAB *qep_tab) { return qep_tab->last_inner() != NO_PLAN_IDX; } -void PickOutConditionsForTableIndex(int table_idx, +void PickOutConditionsForTableIndex(int table_idx, table_map available_tables, vector *from, vector *to) { for (auto it = from->begin(); it != from->end();) { - if (it->table_index_to_attach_to == table_idx) { + if (it->table_index_to_attach_to == table_idx && + // Don't allow antijoin_null_cond as it should be further pushed to the + // ANTI-JOIN and discarded at that point + it->cond->item_name.ptr() != antijoin_null_cond && + ((it->cond->used_tables() & ~PSEUDO_TABLE_BITS) & ~available_tables) == + 0) { to->push_back(*it); it = from->erase(it); } else { @@ -2125,6 +2153,33 @@ void PickOutConditionsForTableIndex(int table_idx, } } +void PickOutConditionsForTableIndex(int table_idx, table_map left_tables, + table_map right_tables, + vector *from, + vector *to) { + table_map available_tables = left_tables | right_tables; + for (auto it = from->begin(); it != from->end();) { + if (it->table_index_to_attach_to == table_idx && + // Don't allow antijoin_null_cond as it should be further pushed to the + // ANTI-JOIN and discarded at that point + it->cond->item_name.ptr() != antijoin_null_cond) { + if (((it->cond->used_tables() & ~PSEUDO_TABLE_BITS) & + ~available_tables) == 0) { + // the condition can be applied on the available tables + to->push_back(it->cond); + it = from->erase(it); + continue; + } else if (TryToMakeJoinPredicatesForTables(it->cond, left_tables, + right_tables)) { + to->push_back(it->cond); + it = from->erase(it); + continue; + } + } + ++it; + } +} + void PickOutConditionsForTableIndex(int table_idx, vector *from, vector *to) { @@ -2229,10 +2284,10 @@ AccessPath *FinishPendingOperations( */ static AccessPath *ConnectJoins( plan_idx upper_first_idx, plan_idx first_idx, plan_idx last_idx, - QEP_TAB *qep_tabs, THD *thd, CallingContext calling_context, + table_map available_table_map, QEP_TAB *qep_tabs, THD *thd, + CallingContext calling_context, vector *pending_conditions, vector *pending_invalidators, - vector *pending_join_conditions, qep_tab_map *unhandled_duplicates, table_map *conditions_depend_on_outer_tables) { assert(last_idx > first_idx); @@ -2245,12 +2300,8 @@ static AccessPath *ConnectJoins( calling_context == TOP_LEVEL && qep_tabs[first_idx].last_inner() != NO_PLAN_IDX; - vector top_level_pending_conditions; - vector top_level_pending_join_conditions; if (is_top_level_outer_join) { path = NewFakeSingleRowAccessPath(thd, /*count_examined_rows=*/false); - pending_conditions = &top_level_pending_conditions; - pending_join_conditions = &top_level_pending_join_conditions; } // NOTE: i is advanced in one of two ways: @@ -2278,17 +2329,6 @@ static AccessPath *ConnectJoins( } } - if (is_top_level_outer_join && i == qep_tabs[first_idx].last_inner() + 1) { - // Finished the top level outer join. - path = FinishPendingOperations( - thd, path, /*remove_duplicates_loose_scan_qep_tab=*/nullptr, - top_level_pending_conditions, conditions_depend_on_outer_tables); - - is_top_level_outer_join = false; - pending_conditions = nullptr; - pending_join_conditions = nullptr; - } - bool add_limit_1; plan_idx substructure_end; Substructure substructure = @@ -2300,65 +2340,75 @@ static AccessPath *ConnectJoins( substructure == Substructure::SEMIJOIN) { qep_tab_map left_tables = TablesBetween(first_idx, i); qep_tab_map right_tables = TablesBetween(i, substructure_end); + table_map right_table_map = + ConvertQepTabMapToTableMap(qep_tab->join(), right_tables); // Outer or semijoin, consisting of a subtree (possibly of only one // table), so we send the entire subtree down to a recursive invocation // and then join the returned root into our existing tree. AccessPath *subtree_path; vector subtree_pending_conditions; - vector subtree_pending_join_conditions; table_map conditions_depend_on_outer_tables_subtree = 0; - if (substructure == Substructure::SEMIJOIN) { - // Semijoins don't have special handling of WHERE, so simply recurse. - if (UseHashJoin(qep_tab) && - !PushedJoinRejectsHashJoin(qep_tab->join(), left_tables, - right_tables, JoinType::SEMI) && - !QueryMixesOuterBKAAndBNL(qep_tab->join())) { - // We must move any join conditions inside the subtructure up to this - // level so that they can be attached to the hash join iterator. - subtree_path = ConnectJoins( - first_idx, i, substructure_end, qep_tabs, thd, - DIRECTLY_UNDER_SEMIJOIN, &subtree_pending_conditions, - pending_invalidators, &subtree_pending_join_conditions, - unhandled_duplicates, &conditions_depend_on_outer_tables_subtree); - } else { - // Send in "subtree_pending_join_conditions", so that any semijoin - // conditions are moved up to this level, where they will be attached - // as conditions to the hash join iterator. - subtree_path = ConnectJoins( - first_idx, i, substructure_end, qep_tabs, thd, - DIRECTLY_UNDER_SEMIJOIN, pending_conditions, pending_invalidators, - &subtree_pending_join_conditions, unhandled_duplicates, - &conditions_depend_on_outer_tables_subtree); - } - } else if (pending_conditions != nullptr) { - // We are already on the right (inner) side of an outer join, - // so we need to keep deferring WHERE predicates. - subtree_path = ConnectJoins( - first_idx, i, substructure_end, qep_tabs, thd, - DIRECTLY_UNDER_OUTER_JOIN, pending_conditions, pending_invalidators, - pending_join_conditions, unhandled_duplicates, - &conditions_depend_on_outer_tables_subtree); - - // Pick out any conditions that should be directly above this join - // (ie., the ON conditions for this specific join). - PickOutConditionsForTableIndex(i, pending_conditions, - &subtree_pending_conditions); - - // Similarly, for join conditions. - if (pending_join_conditions != nullptr) { - PickOutConditionsForTableIndex(i, pending_join_conditions, - &subtree_pending_join_conditions); - } + + const bool substructure_is_hash_join = + (UseHashJoin(qep_tab) && + !PushedJoinRejectsHashJoin(qep_tab->join(), left_tables, + right_tables, false /*is_inner_join*/) && + !QueryMixesOuterBKAAndBNL(qep_tab->join())); + + vector *local_pending_conditions = + nullptr == pending_conditions ? &subtree_pending_conditions + : pending_conditions; + + subtree_path = ConnectJoins( + first_idx, i, substructure_end, + (substructure_is_hash_join ? 0 : available_table_map), qep_tabs, thd, + (Substructure::SEMIJOIN == substructure ? DIRECTLY_UNDER_SEMIJOIN + : DIRECTLY_UNDER_OUTER_JOIN), + local_pending_conditions, pending_invalidators, unhandled_duplicates, + &conditions_depend_on_outer_tables_subtree); + + // Join conditions that were inside the substructure are placed in the + // vector 'local_pending_conditions'. Find out which of these + // conditions that should be attached here + + vector join_conditions; + vector above_join_conditions; + + // Pick out any conditions that should be directly at this join + // (ie., the ON conditions for this specific join). + // We don't check available tables here because this is the last position + // to apply ON predicates, further deferring it means wrong result. + PickOutConditionsForTableIndex(i, local_pending_conditions, + &join_conditions); + + // Set pending_plan_idx to NO_PALN_IDX if we are at the top level; if + // we are at outer join inner, then set to the first inner of the outer + // join + plan_idx pending_plan_idx = NO_PLAN_IDX; + if (qep_tabs[first_idx].first_inner() != NO_PLAN_IDX && + !is_top_level_outer_join) { + pending_plan_idx = first_idx; + } + if (substructure == Substructure::OUTER_JOIN) { + // Pick out any conditions above this join. If the conditions can't be + // applied due to tables not available, then they are left in + // pending_conditions and are pushed up further + PickOutConditionsForTableIndex( + pending_plan_idx, available_table_map | right_table_map, + local_pending_conditions, &above_join_conditions); } else { - // We can check the WHERE predicates on this table right away - // after the join (and similarly, set up invalidators). - subtree_path = ConnectJoins( - first_idx, i, substructure_end, qep_tabs, thd, - DIRECTLY_UNDER_OUTER_JOIN, &subtree_pending_conditions, - pending_invalidators, &subtree_pending_join_conditions, - unhandled_duplicates, &conditions_depend_on_outer_tables_subtree); + // Pick out any conditions at this join. If the conditions can't be + // applied due to tables not available, then they are left in + // pending_conditions and are pushed up further + PickOutConditionsForTableIndex( + pending_plan_idx, available_table_map, right_table_map, + local_pending_conditions, &join_conditions); } + + // At the top level, there should be no leftover conditions. + assert(subtree_pending_conditions.empty()); + *conditions_depend_on_outer_tables |= conditions_depend_on_outer_tables_subtree; @@ -2369,8 +2419,8 @@ static AccessPath *ConnectJoins( // (inner) side of another outer join. Otherwise, we would cause the // higher-up outer join to create NULL rows where there should be none. assert(substructure != Substructure::SEMIJOIN); - join_type = - (pending_conditions == nullptr) ? JoinType::ANTI : JoinType::OUTER; + join_type = (qep_tab->first_upper() == NO_PLAN_IDX) ? JoinType::ANTI + : JoinType::OUTER; // Normally, a ”found” trigger means that the condition should be moved // up above some outer join (ie., it's a WHERE, not an ON condition). @@ -2388,26 +2438,11 @@ static AccessPath *ConnectJoins( // conditions (as they would otherwise kill all of our output rows) and // use them to mark the join as _really_ antijoin, even when it's // within an outer join. - for (auto it = subtree_pending_conditions.begin(); - it != subtree_pending_conditions.end();) { - if (it->table_index_to_attach_to == int(i) && - it->cond->item_name.ptr() == antijoin_null_cond) { - assert(nullptr != dynamic_cast(it->cond)); - join_type = JoinType::ANTI; - it = subtree_pending_conditions.erase(it); - } else { - ++it; - } - } - - // Do the same for antijoin-marking conditions. - for (auto it = subtree_pending_join_conditions.begin(); - it != subtree_pending_join_conditions.end();) { - if (it->table_index_to_attach_to == int(i) && - it->cond->item_name.ptr() == antijoin_null_cond) { - assert(nullptr != dynamic_cast(it->cond)); + for (auto it = join_conditions.begin(); it != join_conditions.end();) { + if ((*it)->item_name.ptr() == antijoin_null_cond) { + assert(nullptr != dynamic_cast(*it)); join_type = JoinType::ANTI; - it = subtree_pending_join_conditions.erase(it); + it = join_conditions.erase(it); } else { ++it; } @@ -2480,20 +2515,9 @@ static AccessPath *ConnectJoins( } else if (path == nullptr) { assert(substructure == Substructure::SEMIJOIN); path = subtree_path; - } else if (((UseHashJoin(qep_tab) && - !PushedJoinRejectsHashJoin(qep_tab->join(), left_tables, - right_tables, join_type) && - !right_side_depends_on_outer) || + } else if (((substructure_is_hash_join && !right_side_depends_on_outer) || UseBKA(qep_tab)) && !QueryMixesOuterBKAAndBNL(qep_tab->join())) { - // Join conditions that were inside the substructure are placed in the - // vector 'subtree_pending_join_conditions'. Find out which of these - // conditions that should be attached to this table, and attach them - // to the hash join iterator. - vector join_conditions; - PickOutConditionsForTableIndex(i, &subtree_pending_join_conditions, - &join_conditions); - if (UseBKA(qep_tab)) { path = CreateBKAAccessPath(thd, qep_tab->join(), path, left_tables, subtree_path, right_tables, @@ -2508,15 +2532,6 @@ static AccessPath *ConnectJoins( path = PossiblyAttachFilter(path, join_conditions, thd, conditions_depend_on_outer_tables); } else { - // Normally, subtree_pending_join_conditions should be empty when we - // create a nested loop iterator. However, in the case where we thought - // we would be making a hash join but changed our minds (due to - // right_side_depends_on_outer), there may be conditions there. - // Similar to hash join above, pick out those conditions and add them - // here. - vector join_conditions; - PickOutConditionsForTableIndex(i, &subtree_pending_join_conditions, - &join_conditions); subtree_path = PossiblyAttachFilter(subtree_path, join_conditions, thd, conditions_depend_on_outer_tables); @@ -2530,14 +2545,15 @@ static AccessPath *ConnectJoins( remove_duplicates_loose_scan ? &qep_tabs[i - 1] : nullptr; path = FinishPendingOperations( thd, path, remove_duplicates_loose_scan_qep_tab, - subtree_pending_conditions, conditions_depend_on_outer_tables); + above_join_conditions, conditions_depend_on_outer_tables); + available_table_map |= right_table_map; i = substructure_end; continue; } else if (substructure == Substructure::WEEDOUT) { AccessPath *subtree_path = ConnectJoins( - first_idx, i, substructure_end, qep_tabs, thd, DIRECTLY_UNDER_WEEDOUT, - pending_conditions, pending_invalidators, pending_join_conditions, + first_idx, i, substructure_end, available_table_map, qep_tabs, thd, + DIRECTLY_UNDER_WEEDOUT, pending_conditions, pending_invalidators, unhandled_duplicates, conditions_depend_on_outer_tables); AccessPath *child_path = subtree_path; subtree_path = CreateWeedoutOrLimitAccessPath( @@ -2556,6 +2572,9 @@ static AccessPath *ConnectJoins( path); } + for (plan_idx j = i; j < substructure_end; ++j) { + available_table_map |= qep_tabs[j].table_ref->map(); + } i = substructure_end; continue; } else if (qep_tab->do_loosescan() && qep_tab->match_tab != i && @@ -2568,8 +2587,8 @@ static AccessPath *ConnectJoins( // in “iterator” if we just kept on going, so we need to create a separate // tree by recursing here. AccessPath *subtree_path = ConnectJoins( - first_idx, i, qep_tab->match_tab + 1, qep_tabs, thd, TOP_LEVEL, - pending_conditions, pending_invalidators, pending_join_conditions, + first_idx, i, qep_tab->match_tab + 1, available_table_map, qep_tabs, + thd, TOP_LEVEL, pending_conditions, pending_invalidators, unhandled_duplicates, conditions_depend_on_outer_tables); path = @@ -2577,6 +2596,10 @@ static AccessPath *ConnectJoins( /*pfs_batch_mode=*/false); SetCostOnNestedLoopAccessPath(*thd->cost_model(), qep_tab->position(), path); + + for (plan_idx j = i; j < qep_tab->match_tab + 1; ++j) { + available_table_map |= qep_tabs[j].table_ref->map(); + } i = qep_tab->match_tab + 1; continue; } @@ -2584,6 +2607,7 @@ static AccessPath *ConnectJoins( AccessPath *table_path = GetTableAccessPath(thd, qep_tab, qep_tabs); qep_tab_map right_tables = qep_tab->idx_map(); + table_map right_table_map = qep_tab->table_ref->map(); qep_tab_map left_tables = 0; // Get the left side tables of this join. @@ -2604,7 +2628,7 @@ static AccessPath *ConnectJoins( const bool replace_with_hash_join = UseHashJoin(qep_tab) && !QueryMixesOuterBKAAndBNL(qep_tab->join()) && !PushedJoinRejectsHashJoin(qep_tab->join(), left_tables, right_tables, - JoinType::INNER); + true /* is_inner_join */); vector predicates_below_join; vector join_conditions; @@ -2615,9 +2639,9 @@ static AccessPath *ConnectJoins( // SplitConditions() will put all join conditions in // pending_join_conditions. These conditions will later be attached to the // hash join iterator when we are done handling the inner side. - SplitConditions(qep_tab->condition(), qep_tab, &predicates_below_join, - &predicates_above_join, - replace_with_hash_join ? pending_join_conditions : nullptr); + SplitConditions(qep_tab->condition(), qep_tab, available_table_map, + right_table_map, &predicates_below_join, + &predicates_above_join); // We can always do BKA. The setup is very similar to hash join. const bool is_bka = @@ -2635,7 +2659,7 @@ static AccessPath *ConnectJoins( *conditions_depend_on_outer_tables |= ref.items[key_part_idx]->used_tables(); } - } else if (replace_with_hash_join) { + } else if (replace_with_hash_join && nullptr != path) { // We will now take all the join conditions (both equi- and // non-equi-join conditions) and move them to a separate vector so we // can attach them to the hash join iterator later. Conditions that @@ -2758,6 +2782,8 @@ static AccessPath *ConnectJoins( path); } } + + available_table_map |= right_table_map; ++i; // If we have any predicates that should be above an outer join, @@ -2767,12 +2793,6 @@ static AccessPath *ConnectJoins( pending_conditions->push_back(cond); } } - if (is_top_level_outer_join) { - assert(last_idx == qep_tabs[first_idx].last_inner() + 1); - path = FinishPendingOperations( - thd, path, /*remove_duplicates_loose_scan_qep_tab=*/nullptr, - top_level_pending_conditions, conditions_depend_on_outer_tables); - } return path; } @@ -2835,10 +2855,14 @@ AccessPath *JOIN::create_root_access_path_for_join() { qep_tab_map unhandled_duplicates = 0; qep_tab_map conditions_depend_on_outer_tables = 0; vector pending_invalidators; + table_map available_tables = 0; + for (plan_idx j = 0; j < (plan_idx)const_tables; ++j) { + available_tables |= qep_tab[j].table_ref->map(); + } path = ConnectJoins( - /*upper_first_idx=*/NO_PLAN_IDX, const_tables, primary_tables, qep_tab, - thd, TOP_LEVEL, nullptr, &pending_invalidators, - /*pending_join_conditions=*/nullptr, &unhandled_duplicates, + /*upper_first_idx=*/NO_PLAN_IDX, const_tables, primary_tables, + available_tables, qep_tab, thd, TOP_LEVEL, nullptr, + &pending_invalidators, &unhandled_duplicates, &conditions_depend_on_outer_tables); // If there were any weedouts that we had to drop during ConnectJoins() @@ -4284,9 +4308,9 @@ AccessPath *QEP_TAB::access_path() { if (condition()) { vector predicates_below_join; vector predicates_above_join; - SplitConditions(condition(), this, &predicates_below_join, - &predicates_above_join, - /*join_conditions=*/nullptr); + SplitConditions(condition(), this, 0, + ~PSEUDO_TABLE_BITS /* assume all tables are available */, + &predicates_below_join, &predicates_above_join); table_map conditions_depend_on_outer_tables = 0; path = PossiblyAttachFilter(path, predicates_below_join, join()->thd, diff --git a/sql/sql_executor.h b/sql/sql_executor.h index 73d6ecd..741214a 100644 --- a/sql/sql_executor.h +++ b/sql/sql_executor.h @@ -540,7 +540,7 @@ Item *unwrap_rollup_group(Item *item); */ struct PendingCondition { Item *cond; - int table_index_to_attach_to; // -1 means “on the last possible outer join”. + int table_index_to_attach_to; // NO_PLAN_IDX means where condition. }; unique_ptr_destroy_only PossiblyAttachFilterIterator( -- 1.8.3.1