摘要:
在mysql的simplify_joins优化中,有一条是因为存在rejects nulls的情况, 可以使outer join转换为inner join. 本文分析其过程和原理.
我们把这种在外连接查询中,指定的WHERE子句中包含被驱动表中的列不为NULL值的条件称之为空值拒绝(英文名:reject-NULL)。
在被驱动表的WHERE子句符合空值拒绝的条件后,外连接和内连接可以相互转换。
DDL:
create database t3;
use t3;
create table t1( a int, b int );
create table t2( a int, b int );
insert t1(a,b) values(3,4);
insert t1(a,b) values(5,6);
insert t2(a,b) values(3,1);
insert t2(a,b) values(7,2);
SQL优化变换:
外连接对照SQL:
SELECT * FROM t1 LEFT JOIN t2 ON t2.a = t1.a;
+------+------+------+------+
| a | b | a | b |
+------+------+------+------+
| 3 | 4 | 3 | 1 |
| 5 | 6 | NULL | NULL |
+------+------+------+------+
外连接添加reject null条件
SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a WHERE t2.b < 5
+------+------+------+------+
| a | b | a | b |
+------+------+------+------+
| 3 | 4 | 3 | 1 |
+------+------+------+------+
1 row in set (0.00 sec)
mysql> EXPLAIN SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a WHERE t2.b < 5;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where |
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)
等价于=>
SELECT * FROM t1 INNER JOIN t2 ON t2.a=t1.a WHERE t2.b < 5;
+------+------+------+------+
| a | b | a | b |
+------+------+------+------+
| 3 | 4 | 3 | 1 |
+------+------+------+------+
1 row in set (0.00 sec)
mysql> EXPLAIN SELECT * FROM t1 INNER JOIN t2 ON t2.a=t1.a WHERE t2.b < 5;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where |
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.01 sec)
等价于=>
SELECT * FROM t1,t2 WHERE t2.b < 5 AND t2.a=t1.a;
+------+------+------+------+
| a | b | a | b |
+------+------+------+------+
| 3 | 4 | 3 | 1 |
+------+------+------+------+
1 row in set (0.00 sec)
mysql> EXPLAIN SELECT * FROM t1,t2 WHERE t2.b < 5 AND t2.a=t1.a;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where |
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)
对比where条件:
alter table t1 add column c int;
alter table t2 add column c int;
update t1 set c=1;
update t2 set c=2;
mysql> explain SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a WHERE t2.c < 5;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where |
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)
mysql> explain SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a WHERE t1.c < 5;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where |
| 1 | SIMPLE | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)
mysql> SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a WHERE t2.c < 5;
+------+------+------+------+------+------+
| a | b | c | a | b | c |
+------+------+------+------+------+------+
| 3 | 4 | 1 | 3 | 1 | 2 |
+------+------+------+------+------+------+
1 row in set (0.00 sec)
mysql> SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a WHERE t1.c < 5;
+------+------+------+------+------+------+
| a | b | c | a | b | c |
+------+------+------+------+------+------+
| 3 | 4 | 1 | 3 | 1 | 2 |
| 5 | 6 | 1 | NULL | NULL | NULL |
+------+------+------+------+------+------+
2 rows in set (0.00 sec)
函数逻辑:
调用堆栈:
(gdb) bt
#0 simplify_joins (join=0x7f0f38008f90, join_list=0x7f0f38002048, conds=0x7f0f38002218, top=true) at sql_select.cc:6710
#1 0x00000000005ea680 in simplify_joins (join=0x7f0f38008f90, join_list=0x2e91830, conds=0x7f0f38002218, top=true) at sql_select.cc:6685
#2 0x00000000005d76ce in JOIN::optimize (this=0x7f0f38008f90) at sql_select.cc:492
#3 0x00000000005dc4e6 in mysql_select (thd=0x2e912a0, rref_pointer_array=0x2e918c8, tables=0x7f0f380014a8, wild_num=1, fields=..., conds=0x7f0f38002218, og_num=0, order=0x0, group=0x0, having=0x0,
proc_param=0x0, select_options=2156153344, result=0x7f0f38002418, unit=0x2e91320, select_lex=0x2e916a0) at sql_select.cc:1577
#4 0x00000000005d6544 in handle_select (thd=0x2e912a0, lex=0x2e91308, result=0x7f0f38002418, setup_tables_done_option=0) at sql_select.cc:186
#5 0x0000000000596830 in mysql_execute_command (thd=0x2e912a0) at sql_parse.cc:2298
#6 0x00000000005a027a in mysql_parse (thd=0x2e912a0, inBuf=0x7f0f380012f0 "SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a WHERE t2.b < 5", length=57) at sql_parse.cc:5101
#7 0x00000000005942dd in dispatch_command (command=COM_QUERY, thd=0x2e912a0, packet=0x2e99261 "SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a WHERE t2.b < 5", packet_length=58) at sql_parse.cc:1579
#8 0x00000000005938c7 in do_command (thd=0x2e912a0) at sql_parse.cc:1386
#9 0x00000000005928ec in handle_one_connection (arg=0x2e912a0) at sql_parse.cc:1043
#10 0x00007f0f466f5ea5 in start_thread () from /lib64/libpthread.so.0
#11 0x00007f0f455a9b0d in clone () from /lib64/libc.so.6
核心函数 simplify_joins:
规则举例:
E.g. in the query:
SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a WHERE t2.b < 5
the predicate t2.b < 5 rejects nulls.
The query is converted first to:
SELECT * FROM t1 INNER JOIN t2 ON t2.a=t1.a WHERE t2.b < 5
then to the equivalent form:
SELECT * FROM t1,t2 WHERE t2.b < 5 AND t2.a=t1.a.
Similarly the following query:
SELECT * from t1 LEFT JOIN (t2, t3) ON t2.a=t1.a AND t3.b=t1.b WHERE t2.c < 5
is converted to:
SELECT * FROM t1, (t2, t3) WHERE t2.c < 5 AND t2.a=t1.a AND t3.b=t1.b
One conversion might trigger another:
SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a
LEFT JOIN t3 ON t3.b=t2.b
WHERE t3 IS NOT NULL =>
SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a, t3
WHERE t3 IS NOT NULL AND t3.b=t2.b =>
SELECT * FROM t1, t2, t3
WHERE t3 IS NOT NULL AND t3.b=t2.b AND t2.a=t1.a
The function removes all unnecessary braces from the expression
produced by the conversions.
E.g. SELECT * FROM t1, (t2, t3) WHERE t2.c < 5 AND t2.a=t1.a AND t3.b=t1.b
finally is converted to:
SELECT * FROM t1, t2, t3 WHERE t2.c < 5 AND t2.a=t1.a AND t3.b=t1.b
It also will remove braces from the following queries:
SELECT * from (t1 LEFT JOIN t2 ON t2.a=t1.a) LEFT JOIN t3 ON t3.b=t2.b
SELECT * from (t1, (t2,t3)) WHERE t1.a=t2.a AND t2.b=t3.b.
代码实现:
/*
Simplify joins replacing outer joins by inner joins whenever it's possible
SYNOPSIS
simplify_joins()
join reference to the query info
join_list list representation of the join to be converted
conds conditions to add on expressions for converted joins
top true <=> conds is the where condition
DESCRIPTION
The function, during a retrieval of join_list, eliminates those
outer joins that can be converted into inner join, possibly nested.
It also moves the on expressions for the converted outer joins
and from inner joins to conds.
The function also calculates some attributes for nested joins:
- used_tables
- not_null_tables
- dep_tables.
- on_expr_dep_tables
The first two attributes are used to test whether an outer join can
be substituted for an inner join. The third attribute represents the
relation 'to be dependent on' for tables. If table t2 is dependent
on table t1, then in any evaluated execution plan table access to
table t2 must precede access to table t2. This relation is used also
to check whether the query contains invalid cross-references.
The forth attribute is an auxiliary one and is used to calculate
dep_tables.
As the attribute dep_tables qualifies possibles orders of tables in the
execution plan, the dependencies required by the straight join
modifiers are reflected in this attribute as well.
The function also removes all braces that can be removed from the join
expression without changing its meaning.
NOTES
An outer join can be replaced by an inner join if the where condition
or the on expression for an embedding nested join contains a conjunctive
predicate rejecting null values for some attribute of the inner tables.
E.g. in the query:
SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a WHERE t2.b < 5
the predicate t2.b < 5 rejects nulls.
The query is converted first to:
SELECT * FROM t1 INNER JOIN t2 ON t2.a=t1.a WHERE t2.b < 5
then to the equivalent form:
SELECT * FROM t1,t2 WHERE t2.b < 5 AND t2.a=t1.a.
Similarly the following query:
SELECT * from t1 LEFT JOIN (t2, t3) ON t2.a=t1.a AND t3.b=t1.b
WHERE t2.c < 5
is converted to:
SELECT * FROM t1, (t2, t3) WHERE t2.c < 5 AND t2.a=t1.a t3.b=t1.b
One conversion might trigger another:
SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a
LEFT JOIN t3 ON t3.b=t2.b
WHERE t3 IS NOT NULL =>
SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a, t3
WHERE t3 IS NOT NULL AND t3.b=t2.b =>
SELECT * FROM t1, t2, t3
WHERE t3 IS NOT NULL AND t3.b=t2.b AND t2.a=t1.a
The function removes all unnecessary braces from the expression
produced by the conversions.
E.g. SELECT * FROM t1, (t2, t3) WHERE t2.c < 5 AND t2.a=t1.a AND t3.b=t1.b
finally is converted to:
SELECT * FROM t1, t2, t3 WHERE t2.c < 5 AND t2.a=t1.a AND t3.b=t1.b
It also will remove braces from the following queries:
SELECT * from (t1 LEFT JOIN t2 ON t2.a=t1.a) LEFT JOIN t3 ON t3.b=t2.b
SELECT * from (t1, (t2,t3)) WHERE t1.a=t2.a AND t2.b=t3.b.
The benefit of this simplification procedure is that it might return
a query for which the optimizer can evaluate execution plan with more
join orders. With a left join operation the optimizer does not
consider any plan where one of the inner tables is before some of outer
tables.
IMPLEMENTATION.
The function is implemented by a recursive procedure. On the recursive
ascent all attributes are calculated, all outer joins that can be
converted are replaced and then all unnecessary braces are removed.
As join list contains join tables in the reverse order sequential
elimination of outer joins does not requite extra recursive calls.
EXAMPLES
Here is an example of a join query with invalid cross references:
SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t3.a LEFT JOIN ON t3.b=t1.b
RETURN VALUE
The new condition, if success
0, otherwise
*/
static COND *simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top)
{
TABLE_LIST *table;
NESTED_JOIN *nested_join;
TABLE_LIST *prev_table = 0;
List_iterator<TABLE_LIST> li(*join_list);
DBUG_ENTER("simplify_joins");
/*
Try to simplify join operations from join_list.
The most outer join operation is checked for conversion first.
*/
while ((table = li++))
{
table_map used_tables;
table_map not_null_tables = (table_map)0;
if ((nested_join = table->nested_join))
{
/*
If the element of join_list is a nested join apply
the procedure to its nested join list first.
*/
if (table->on_expr)
{
Item *expr = table->prep_on_expr ? table->prep_on_expr : table->on_expr;
/*
If an on expression E is attached to the table,
check all null rejected predicates in this expression.
If such a predicate over an attribute belonging to
an inner table of an embedded outer join is found,
the outer join is converted to an inner join and
the corresponding on expression is added to E.
*/
expr = simplify_joins(join, &nested_join->join_list, expr, FALSE);
table->prep_on_expr = table->on_expr = expr;
}
nested_join->used_tables = (table_map)0;
nested_join->not_null_tables = (table_map)0;
conds = simplify_joins(join, &nested_join->join_list, conds, top);
used_tables = nested_join->used_tables;
not_null_tables = nested_join->not_null_tables;
}
else
{
if (!(table->prep_on_expr))
table->prep_on_expr = table->on_expr;
used_tables = table->table->map;
if (conds)
not_null_tables = conds->not_null_tables();
}
if (table->embedding)
{
table->embedding->nested_join->used_tables |= used_tables;
table->embedding->nested_join->not_null_tables |= not_null_tables;
}
if (!table->outer_join || (used_tables & not_null_tables))
{
/*
For some of the inner tables there are conjunctive predicates
that reject nulls => the outer join can be replaced by an inner join.
*/
table->outer_join = 0;
if (table->on_expr)
{
/* Add on expression to the where condition. */
if (conds)
{
conds = and_conds(conds, table->on_expr);
conds->top_level_item();
/* conds is always a new item as both cond and on_expr existed */
DBUG_ASSERT(!conds->fixed);
conds->fix_fields(join->thd, &conds);
}
else
conds = table->on_expr;
table->prep_on_expr = table->on_expr = 0;
}
}
if (!top)
continue;
/*
Only inner tables of non-convertible outer joins
remain with on_expr.
*/
if (table->on_expr)
{
table->dep_tables |= table->on_expr->used_tables();
if (table->embedding)
{
table->dep_tables &= ~table->embedding->nested_join->used_tables;
/*
Embedding table depends on tables used
in embedded on expressions.
*/
table->embedding->on_expr_dep_tables |= table->on_expr->used_tables();
}
else
table->dep_tables &= ~table->table->map;
}
if (prev_table)
{
/* The order of tables is reverse: prev_table follows table */
if (prev_table->straight)
prev_table->dep_tables |= used_tables;
if (prev_table->on_expr)
{
prev_table->dep_tables |= table->on_expr_dep_tables;
table_map prev_used_tables =
prev_table->nested_join ? prev_table->nested_join->used_tables : prev_table->table->map;
/*
If on expression contains only references to inner tables
we still make the inner tables dependent on the outer tables.
It would be enough to set dependency only on one outer table
for them. Yet this is really a rare case.
*/
if (!(prev_table->on_expr->used_tables() & ~prev_used_tables))
prev_table->dep_tables |= used_tables;
}
}
prev_table = table;
}
/* Flatten nested joins that can be flattened. */
li.rewind();
while ((table = li++))
{
nested_join = table->nested_join;
if (nested_join && !table->on_expr)
{
TABLE_LIST *tbl;
List_iterator<TABLE_LIST> it(nested_join->join_list);
while ((tbl = it++))
{
tbl->embedding = table->embedding;
tbl->join_list = table->join_list;
}
li.replace(nested_join->join_list);
}
}
DBUG_RETURN(conds);
}
执行过程分析:
SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a WHERE t2.b < 5;
| >vio_read [viosocket.c:36]
| >thr_end_alarm [thr_alarm.c:229]
| >vio_blocking [viosocket.c:126]
>dispatch_command [sql_parse.cc:1526]
| >my_b_flush_io_cache [mf_iocache.c:1127]
| | >my_write [my_write.c:29]
| >mysql_parse [sql_parse.cc:5493]
| | >mysql_init_query [sql_parse.cc:5269]
| | | >lex_start [sql_lex.cc:116]
| | | >mysql_reset_thd_for_next_command [sql_parse.cc:5291]
| | >Query_cache::send_result_to_client [sql_cache.cc:966]
| | >st_select_lex::add_item_to_list [sql_lex.cc:1425]
| | >add_table_to_list [sql_parse.cc:6124]
| | >add_joined_table [sql_parse.cc:6423]
| | >add_table_to_list [sql_parse.cc:6124]
| | >add_joined_table [sql_parse.cc:6423]
| | >nest_last_join [sql_parse.cc:6365]
| | >mysql_execute_command [sql_parse.cc:2328]
| | | >mysql_reset_errors [sql_error.cc:78]
| | | >check_grant [sql_acl.cc:3510]
| | | >open_and_lock_tables [sql_base.cc:2287]
| | | | >open_tables [sql_base.cc:1919]
| | | | | >init_alloc_root [my_alloc.c:51]
| | | | | >open_table [sql_base.cc:1062]
| | | | | | >hash_search [hash.c:197]
| | | | | >open_table [sql_base.cc:1062]
| | | | | | >hash_search [hash.c:197]
| | | | >lock_tables [sql_base.cc:2392]
| | | | | >mysql_lock_tables [lock.cc:120]
| | | | | | >lock_external [lock.cc:220]
| | | | | | | >mi_lock_database [mi_locking.c:35]
| | | | | | | | >my_lock [my_lock.c:38]
| | | | | | | >mi_lock_database [mi_locking.c:35]
| | | | | | | | >my_lock [my_lock.c:38]
| | | | | | >thr_multi_lock [thr_lock.c:914]
| | | | | | | >thr_lock [thr_lock.c:477]
| | | | | | | | >mi_get_status [mi_locking.c:255]
| | | | | | | >thr_lock [thr_lock.c:477]
| | | | | | | | >mi_get_status [mi_locking.c:255]
| | | >Query_cache::store_query [sql_cache.cc:787]
| | | >handle_select [sql_select.cc:222]
| | | | >mysql_select [sql_select.cc:1771]
| | | | | >JOIN::prepare [sql_select.cc:311]
| | | | | | >setup_tables [sql_base.cc:4349]
| | | | | | | >store_top_level_join_columns [sql_base.cc:3968]
| | | | | | >setup_wild [sql_base.cc:4176]
| | | | | | | >insert_fields [sql_base.cc:4496]
| | | | | | | | >Field_iterator_table_ref::set_field_iterator [table.cc:2621]
| | | | | | | | >Field_iterator_table_ref::set_field_iterator [table.cc:2621]
| | | | | | >setup_fields [sql_base.cc:4248]
| | | | | | >setup_without_group [sql_select.cc:276]
| | | | | | | >setup_conds [sql_base.cc:4716]
| | | | | | | | >find_field_in_table_ref [sql_base.cc:2982]
| | | | | | | | >find_field_in_table_ref [sql_base.cc:2982]
| | | | | | | | | >find_field_in_table [sql_base.cc:2884]
| | | | | | | | >find_field_in_table_ref [sql_base.cc:2982]
| | | | | | | | >find_field_in_table_ref [sql_base.cc:2982]
| | | | | | | | | >find_field_in_table [sql_base.cc:2884]
| | | | | | | | >find_field_in_table_ref [sql_base.cc:2982]
| | | | | | | | | >find_field_in_table [sql_base.cc:2884]
| | | | | | | | >find_field_in_table_ref [sql_base.cc:2982]
| | | | | | >setup_procedure [procedure.cc:83]
| | | | | | >alloc_func_list [sql_select.cc:12567]
| | | | | >JOIN::optimize [sql_select.cc:535]
| | | | | | >simplify_joins [sql_select.cc:7277]
| | | | | | | >simplify_joins [sql_select.cc:7277]
| | | | | | >optimize_cond [sql_select.cc:7428]
| | | | | | >get_sort_by_table [sql_select.cc:12234]
| | | | | | >make_join_statistics [sql_select.cc:1913]
| | | | | | | >mi_status [mi_info.c:40]
| | | | | | | >mi_status [mi_info.c:40]
| | | | | | | >init_dynamic_array [array.c:51]
| | | | | | | >choose_plan [sql_select.cc:3544]
| | | | | | | | >greedy_search [sql_select.cc:3831]
| | | | | | | | | >best_extension_by_limited_search [sql_select.cc:4010]
| | | | | | | | | | >best_access_path [sql_select.cc:3186]
| | | | | | | | | | >best_extension_by_limited_search [sql_select.cc:4010]
| | | | | | | | | | | >best_access_path [sql_select.cc:3186]
| | | | | | | | | | >best_access_path [sql_select.cc:3186]
| | | | | | >get_best_combination [sql_select.cc:4609]
| | | | | | >make_select [opt_range.cc:659]
| | | | | | >make_outerjoin_info [sql_select.cc:5102]
| | | | | | >make_join_select [sql_select.cc:5154]
| | | | | | | >add_not_null_conds [sql_select.cc:4974]
| | | | | | >remove_const [sql_select.cc:6050]
| | | | | | >remove_const [sql_select.cc:6050]
| | | | | | >make_join_readinfo [sql_select.cc:5490]
| | | | | | | >join_init_cache [sql_select.cc:11543]
| | | | | >JOIN::exec [sql_select.cc:1176]
| | | | | | >send_fields [protocol.cc:543]
| | | | | | | >Protocol::write [protocol.cc:666]
| | | | | | | >Protocol::write [protocol.cc:666]
| | | | | | | >Protocol::write [protocol.cc:666]
| | | | | | | >Protocol::write [protocol.cc:666]
| | | | | | >do_select [sql_select.cc:9020]
| | | | | | | >init_read_record [records.cc:40]
| | | | | | | | >ha_rnd_init [handler.h:604]
| | | | | | | | >mi_scan_init [mi_scan.c:24]
| | | | | | | | >mi_extra [mi_extra.c:47]
| | | | | | | | | >init_io_cache [mf_iocache.c:164]
| | | | | | | | | | >my_tell [my_seek.c:54]
| | | | | | | | | | >my_seek [my_seek.c:27]
| | | | | | | >mi_scan [mi_scan.c:43]
| | | | | | | >_mi_read_rnd_static_record [mi_statrec.c:212]
| | | | | | | | >_my_b_read [mf_iocache.c:414]
| | | | | | | | | >my_seek [my_seek.c:27]
| | | | | | | | | >my_read [my_read.c:41]
| | | | | | | >mi_scan [mi_scan.c:43]
| | | | | | | >_mi_read_rnd_static_record [mi_statrec.c:212]
| | | | | | | >mi_scan [mi_scan.c:43]
| | | | | | | >_mi_read_rnd_static_record [mi_statrec.c:212]
| | | | | | | >init_read_record [records.cc:40]
| | | | | | | | >ha_rnd_init [handler.h:604]
| | | | | | | | >mi_scan_init [mi_scan.c:24]
| | | | | | | | >mi_extra [mi_extra.c:47]
| | | | | | | | | >init_io_cache [mf_iocache.c:164]
| | | | | | | | | | >my_tell [my_seek.c:54]
| | | | | | | | | | >my_seek [my_seek.c:27]
| | | | | | | >mi_scan [mi_scan.c:43]
| | | | | | | >_mi_read_rnd_static_record [mi_statrec.c:212]
| | | | | | | | >_my_b_read [mf_iocache.c:414]
| | | | | | | | | >my_seek [my_seek.c:27]
| | | | | | | | | >my_read [my_read.c:41]
| | | | | | | >end_send [sql_select.cc:10074]
| | | | | | | | >send_data [sql_class.cc:914]
| | | | | | | | >Protocol::write [protocol.cc:666]
| | | | | | | >mi_scan [mi_scan.c:43]
| | | | | | | >_mi_read_rnd_static_record [mi_statrec.c:212]
| | | | | | | >mi_scan [mi_scan.c:43]
| | | | | | | >_mi_read_rnd_static_record [mi_statrec.c:212]
| | | | | | | >end_send [sql_select.cc:10074]
| | | | | | | >JOIN::join_free [sql_select.cc:5776]
| | | | | | | | >JOIN::cleanup [sql_select.cc:5835]
| | | | | | | | | >free_io_cache [sql_base.cc:250]
| | | | | | | | | >ha_rnd_end [handler.h:611]
| | | | | | | | | >mi_extra [mi_extra.c:47]
| | | | | | | | | | >end_io_cache [mf_iocache.c:1217]
| | | | | | | | | | | >my_b_flush_io_cache [mf_iocache.c:1127]
| | | | | | | | | >close_cached_file [mf_cache.c:102]
| | | | | | | | | >ha_rnd_end [handler.h:611]
| | | | | | | | | >mi_extra [mi_extra.c:47]
| | | | | | | | | | >end_io_cache [mf_iocache.c:1217]
| | | | | | | | | | | >my_b_flush_io_cache [mf_iocache.c:1127]
| | | | | | | | >mysql_unlock_read_tables [lock.cc:283]
| | | | | | | | | >thr_multi_unlock [thr_lock.c:987]
| | | | | | | | | | >thr_unlock [thr_lock.c:740]
| | | | | | | | | | >thr_unlock [thr_lock.c:740]
| | | | | | | | | >unlock_external [lock.cc:432]
| | | | | | | | | | >mi_lock_database [mi_locking.c:35]
| | | | | | | | | | | >my_lock [my_lock.c:38]
| | | | | | | | | | >mi_lock_database [mi_locking.c:35]
| | | | | | | | | | | >my_lock [my_lock.c:38]
| | | | | | | >mysql_unlock_tables [lock.cc:253]
| | | | | | | >send_eof [protocol.cc:370]
| | | | | | | | >net_flush [net_serv.cc:227]
| | | | | | | | | >vio_is_blocking [viosocket.c:187]
| | | | | | | | | >net_real_write [net_serv.cc:450]
| | | | | | | | | | >vio_write [viosocket.c:105]
| | | | | >st_select_lex::cleanup() [sql_union.cc:707]
| | | | | | >JOIN::destroy [sql_select.cc:1686]
| | | | | | | >JOIN::cleanup [sql_select.cc:5835]
| | | | | | | >close_cached_file [mf_cache.c:102]
| | >query_cache_end_of_result [sql_cache.cc:665]
| | >st_select_lex_unit::cleanup [sql_union.cc:588]
| | | >st_select_lex::cleanup() [sql_union.cc:707]
| | >Query_arena::free_items [sql_class.cc:1530]
| | | >Item_result_field::cleanup() [item.cc:5954]
| | | | >Item::cleanup [item.cc:395]
| | | >Item_result_field::cleanup() [item.cc:5954]
| | | | >Item::cleanup [item.cc:395]
| | | >Item_result_field::cleanup() [item.cc:5954]
| | | | >Item::cleanup [item.cc:395]
| | | >Item_result_field::cleanup() [item.cc:5954]
| | | | >Item::cleanup [item.cc:395]
| | | >Item_result_field::cleanup() [item.cc:5954]
| | | | >Item::cleanup [item.cc:395]
| | | >Item_result_field::cleanup() [item.cc:5954]
| | | | >Item::cleanup [item.cc:395]
| | | >Item_result_field::cleanup() [item.cc:5954]
| | | | >Item::cleanup [item.cc:395]
| | | >Item_result_field::cleanup() [item.cc:5954]
| | | | >Item::cleanup [item.cc:395]
| | | >Item_field::cleanup [item.cc:3489]
| | | | >Item_ident::cleanup [item.cc:476]
| | | | | >Item::cleanup [item.cc:395]
| | | >Item_field::cleanup [item.cc:3489]
| | | | >Item_ident::cleanup [item.cc:476]
| | | | | >Item::cleanup [item.cc:395]
| | | >Item_field::cleanup [item.cc:3489]
| | | | >Item_ident::cleanup [item.cc:476]
| | | | | >Item::cleanup [item.cc:395]
| | | >Item_field::cleanup [item.cc:3489]
| | | | >Item_ident::cleanup [item.cc:476]
| | | | | >Item::cleanup [item.cc:395]
| | | >Item_result_field::cleanup() [item.cc:5954]
| | | | >Item::cleanup [item.cc:395]
| | | >Item_field::cleanup [item.cc:3489]
| | | | >Item_ident::cleanup [item.cc:476]
| | | | | >Item::cleanup [item.cc:395]
| | | >Item_result_field::cleanup() [item.cc:5954]
| | | | >Item::cleanup [item.cc:395]
| | | >Item_field::cleanup [item.cc:3489]
| | | | >Item_ident::cleanup [item.cc:476]
| | | | | >Item::cleanup [item.cc:395]
| | | >Item_field::cleanup [item.cc:3489]
| | | | >Item_ident::cleanup [item.cc:476]
| | | | | >Item::cleanup [item.cc:395]
| | | >Item_field::cleanup [item.cc:3489]
| | | | >Item_ident::cleanup [item.cc:476]
| | | | | >Item::cleanup [item.cc:395]
| >close_thread_tables [sql_base.cc:409]
| | >close_thread_table [sql_base.cc:548]
| | | >mi_extra [mi_extra.c:47]
| | >close_thread_table [sql_base.cc:548]
| | | >mi_extra [mi_extra.c:47]
>do_command [sql_parse.cc:1448]
| >vio_is_blocking [viosocket.c:187]
| >vio_read [viosocket.c:36]
| >thr_alarm [thr_alarm.c:152]
| >vio_blocking [viosocket.c:126]
| >vio_read [viosocket.c:36]
| >process_alarm [thr_alarm.c:316]