mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 04:22:27 +01:00
Merge of fix for Bug#52357
This commit is contained in:
commit
0c82d3f3f4
4 changed files with 119 additions and 12 deletions
|
@ -1308,4 +1308,34 @@ WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2));
|
|||
f1 f2 f3 f1 f2
|
||||
1 NULL 3 NULL NULL
|
||||
DROP TABLE t1, t2;
|
||||
#
|
||||
# Bug#52357: Assertion failed: join->best_read in greedy_search
|
||||
# optimizer_search_depth=0
|
||||
#
|
||||
CREATE TABLE t1( a INT );
|
||||
INSERT INTO t1 VALUES (1),(2);
|
||||
SET optimizer_search_depth = 0;
|
||||
# Should not core dump on query preparation
|
||||
EXPLAIN
|
||||
SELECT 1
|
||||
FROM t1 tt3 LEFT OUTER JOIN t1 tt4 ON 1
|
||||
LEFT OUTER JOIN t1 tt5 ON 1
|
||||
LEFT OUTER JOIN t1 tt6 ON 1
|
||||
LEFT OUTER JOIN t1 tt7 ON 1
|
||||
LEFT OUTER JOIN t1 tt8 ON 1
|
||||
RIGHT OUTER JOIN t1 tt2 ON 1
|
||||
RIGHT OUTER JOIN t1 tt1 ON 1
|
||||
STRAIGHT_JOIN t1 tt9 ON 1;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE tt1 ALL NULL NULL NULL NULL 2
|
||||
1 SIMPLE tt2 ALL NULL NULL NULL NULL 2
|
||||
1 SIMPLE tt3 ALL NULL NULL NULL NULL 2
|
||||
1 SIMPLE tt4 ALL NULL NULL NULL NULL 2
|
||||
1 SIMPLE tt5 ALL NULL NULL NULL NULL 2
|
||||
1 SIMPLE tt6 ALL NULL NULL NULL NULL 2
|
||||
1 SIMPLE tt7 ALL NULL NULL NULL NULL 2
|
||||
1 SIMPLE tt8 ALL NULL NULL NULL NULL 2
|
||||
1 SIMPLE tt9 ALL NULL NULL NULL NULL 2 Using join buffer
|
||||
SET optimizer_search_depth = DEFAULT;
|
||||
DROP TABLE t1;
|
||||
End of 5.1 tests
|
||||
|
|
|
@ -913,4 +913,27 @@ WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2));
|
|||
|
||||
DROP TABLE t1, t2;
|
||||
|
||||
--echo #
|
||||
--echo # Bug#52357: Assertion failed: join->best_read in greedy_search
|
||||
--echo # optimizer_search_depth=0
|
||||
--echo #
|
||||
CREATE TABLE t1( a INT );
|
||||
|
||||
INSERT INTO t1 VALUES (1),(2);
|
||||
SET optimizer_search_depth = 0;
|
||||
|
||||
--echo # Should not core dump on query preparation
|
||||
EXPLAIN
|
||||
SELECT 1
|
||||
FROM t1 tt3 LEFT OUTER JOIN t1 tt4 ON 1
|
||||
LEFT OUTER JOIN t1 tt5 ON 1
|
||||
LEFT OUTER JOIN t1 tt6 ON 1
|
||||
LEFT OUTER JOIN t1 tt7 ON 1
|
||||
LEFT OUTER JOIN t1 tt8 ON 1
|
||||
RIGHT OUTER JOIN t1 tt2 ON 1
|
||||
RIGHT OUTER JOIN t1 tt1 ON 1
|
||||
STRAIGHT_JOIN t1 tt9 ON 1;
|
||||
|
||||
SET optimizer_search_depth = DEFAULT;
|
||||
DROP TABLE t1;
|
||||
--echo End of 5.1 tests
|
||||
|
|
|
@ -9091,6 +9091,46 @@ static bool check_interleaving_with_nj(JOIN_TAB *next_tab)
|
|||
/**
|
||||
Nested joins perspective: Remove the last table from the join order.
|
||||
|
||||
The algorithm is the reciprocal of check_interleaving_with_nj(), hence
|
||||
parent join nest nodes are updated only when the last table in its child
|
||||
node is removed. The ASCII graphic below will clarify.
|
||||
|
||||
%A table nesting such as <tt> t1 x [ ( t2 x t3 ) x ( t4 x t5 ) ] </tt>is
|
||||
represented by the below join nest tree.
|
||||
|
||||
@verbatim
|
||||
NJ1
|
||||
_/ / \
|
||||
_/ / NJ2
|
||||
_/ / / \
|
||||
/ / / \
|
||||
t1 x [ (t2 x t3) x (t4 x t5) ]
|
||||
@endverbatim
|
||||
|
||||
At the point in time when check_interleaving_with_nj() adds the table t5 to
|
||||
the query execution plan, QEP, it also directs the node named NJ2 to mark
|
||||
the table as covered. NJ2 does so by incrementing its @c counter
|
||||
member. Since all of NJ2's tables are now covered by the QEP, the algorithm
|
||||
proceeds up the tree to NJ1, incrementing its counter as well. All join
|
||||
nests are now completely covered by the QEP.
|
||||
|
||||
restore_prev_nj_state() does the above in reverse. As seen above, the node
|
||||
NJ1 contains the nodes t2, t3, and NJ2. Its counter being equal to 3 means
|
||||
that the plan covers t2, t3, and NJ2, @e and that the sub-plan (t4 x t5)
|
||||
completely covers NJ2. The removal of t5 from the partial plan will first
|
||||
decrement NJ2's counter to 1. It will then detect that NJ2 went from being
|
||||
completely to partially covered, and hence the algorithm must continue
|
||||
upwards to NJ1 and decrement its counter to 2. %A subsequent removal of t4
|
||||
will however not influence NJ1 since it did not un-cover the last table in
|
||||
NJ2.
|
||||
|
||||
SYNOPSIS
|
||||
restore_prev_nj_state()
|
||||
last join table to remove, it is assumed to be the last in current
|
||||
partial join order.
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
Remove the last table from the partial join order and update the nested
|
||||
joins counters and join->cur_embedding_map. It is ok to call this
|
||||
function for the first table in join order (for which
|
||||
|
@ -9104,19 +9144,20 @@ static void restore_prev_nj_state(JOIN_TAB *last)
|
|||
{
|
||||
TABLE_LIST *last_emb= last->table->pos_in_table_list->embedding;
|
||||
JOIN *join= last->join;
|
||||
while (last_emb)
|
||||
for (;last_emb != NULL; last_emb= last_emb->embedding)
|
||||
{
|
||||
if (!(--last_emb->nested_join->counter))
|
||||
join->cur_embedding_map&= ~last_emb->nested_join->nj_map;
|
||||
else if (last_emb->nested_join->join_list.elements-1 ==
|
||||
last_emb->nested_join->counter)
|
||||
{
|
||||
join->cur_embedding_map|= last_emb->nested_join->nj_map;
|
||||
NESTED_JOIN *nest= last_emb->nested_join;
|
||||
DBUG_ASSERT(nest->counter > 0);
|
||||
|
||||
bool was_fully_covered= nest->is_fully_covered();
|
||||
|
||||
if (--nest->counter == 0)
|
||||
join->cur_embedding_map&= ~nest->nj_map;
|
||||
|
||||
if (!was_fully_covered)
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
last_emb= last_emb->embedding;
|
||||
|
||||
join->cur_embedding_map|= nest->nj_map;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
15
sql/table.h
15
sql/table.h
|
@ -1691,7 +1691,11 @@ typedef struct st_nested_join
|
|||
List<TABLE_LIST> join_list; /* list of elements in the nested join */
|
||||
table_map used_tables; /* bitmap of tables in the nested join */
|
||||
table_map not_null_tables; /* tables that rejects nulls */
|
||||
struct st_join_table *first_nested;/* the first nested table in the plan */
|
||||
/**
|
||||
Used for pointing out the first table in the plan being covered by this
|
||||
join nest. It is used exclusively within make_outerjoin_info().
|
||||
*/
|
||||
struct st_join_table *first_nested;
|
||||
/*
|
||||
Used to count tables in the nested join in 2 isolated places:
|
||||
1. In make_outerjoin_info().
|
||||
|
@ -1701,6 +1705,15 @@ typedef struct st_nested_join
|
|||
*/
|
||||
uint counter;
|
||||
nested_join_map nj_map; /* Bit used to identify this nested join*/
|
||||
/**
|
||||
True if this join nest node is completely covered by the query execution
|
||||
plan. This means two things.
|
||||
|
||||
1. All tables on its @c join_list are covered by the plan.
|
||||
|
||||
2. All child join nest nodes are fully covered.
|
||||
*/
|
||||
bool is_fully_covered() const { return join_list.elements == counter; }
|
||||
} NESTED_JOIN;
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue