Commit 60a4b4fb authored by igor@rurik.mysql.com's avatar igor@rurik.mysql.com

Merge rurik.mysql.com:/home/igor/mysql-5.0

into  rurik.mysql.com:/home/igor/dev/mysql-5.0-0
parents 0960f5fe 33026e52
drop table if exists t1; drop table if exists t1, t2;
select 0,256,00000000000000065536,2147483647,-2147483648,2147483648,+4294967296; select 0,256,00000000000000065536,2147483647,-2147483648,2147483648,+4294967296;
0 256 00000000000000065536 2147483647 -2147483648 2147483648 4294967296 0 256 00000000000000065536 2147483647 -2147483648 2147483648 4294967296
0 256 65536 2147483647 -2147483648 2147483648 4294967296 0 256 65536 2147483647 -2147483648 2147483648 4294967296
......
...@@ -1403,3 +1403,67 @@ SELECT v2.x FROM v2 JOIN t2 ON e=b JOIN t3 ON e=c JOIN t4 USING(d); ...@@ -1403,3 +1403,67 @@ SELECT v2.x FROM v2 JOIN t2 ON e=b JOIN t3 ON e=c JOIN t4 USING(d);
ERROR 42S22: Unknown column 'v2.x' in 'field list' ERROR 42S22: Unknown column 'v2.x' in 'field list'
DROP VIEW v1, v2; DROP VIEW v1, v2;
DROP TABLE t1, t2, t3, t4, t5, t6; DROP TABLE t1, t2, t3, t4, t5, t6;
create table t1 (id1 int(11) not null);
insert into t1 values (1),(2);
create table t2 (id2 int(11) not null);
insert into t2 values (1),(2),(3),(4);
create table t3 (id3 char(16) not null);
insert into t3 values ('100');
create table t4 (id2 int(11) not null, id3 char(16));
create table t5 (id1 int(11) not null, key (id1));
insert into t5 values (1),(2),(1);
create view v1 as
select t4.id3 from t4 join t2 on t4.id2 = t2.id2;
select t1.id1 from t1 inner join (t3 left join v1 on t3.id3 = v1.id3);
id1
1
2
drop view v1;
drop table t1, t2, t3, t4, t5;
create table t0 (a int);
insert into t0 values (0),(1),(2),(3);
create table t1(a int);
insert into t1 select A.a + 10*(B.a) from t0 A, t0 B;
create table t2 (a int, b int);
insert into t2 values (1,1), (2,2), (3,3);
create table t3(a int, b int, filler char(200), key(a));
insert into t3 select a,a,'filler' from t1;
insert into t3 select a,a,'filler' from t1;
create table t4 like t3;
insert into t4 select * from t3;
insert into t4 select * from t3;
create table t5 like t4;
insert into t5 select * from t4;
insert into t5 select * from t4;
create table t6 like t5;
insert into t6 select * from t5;
insert into t6 select * from t5;
create table t7 like t6;
insert into t7 select * from t6;
insert into t7 select * from t6;
explain select * from t4 join
t2 left join (t3 join t5 on t5.a=t3.b) on t3.a=t2.b where t4.a<=>t3.b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t2 ALL NULL NULL NULL NULL X
1 SIMPLE t3 ref a a 5 test.t2.b X
1 SIMPLE t5 ref a a 5 test.t3.b X
1 SIMPLE t4 ref a a 5 test.t3.b X Using where
explain select * from (t4 join t6 on t6.a=t4.b) right join t3 on t4.a=t3.b
join t2 left join (t5 join t7 on t7.a=t5.b) on t5.a=t2.b where t3.a<=>t2.b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t2 ALL NULL NULL NULL NULL X
1 SIMPLE t3 ref a a 5 test.t2.b X Using where
1 SIMPLE t4 ref a a 5 test.t3.b X
1 SIMPLE t6 ref a a 5 test.t4.b X
1 SIMPLE t5 ref a a 5 test.t2.b X
1 SIMPLE t7 ref a a 5 test.t5.b X
explain select * from t2 left join
(t3 left join (t4 join t6 on t6.a=t4.b) on t4.a=t3.b
join t5 on t5.a=t3.b) on t3.a=t2.b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t2 ALL NULL NULL NULL NULL X
1 SIMPLE t3 ref a a 5 test.t2.b X
1 SIMPLE t4 ref a a 5 test.t3.b X
1 SIMPLE t6 ref a a 5 test.t4.b X
1 SIMPLE t5 ref a a 5 test.t3.b X
drop table t0, t1, t2, t4, t5, t6;
drop table if exists t1,t2,t9,`t1a``b`,v1,v2,v3,v4,v5,v6; drop table if exists t1,t2,t3,t4,t9,`t1a``b`,v1,v2,v3,v4,v5,v6;
drop view if exists t1,t2,`t1a``b`,v1,v2,v3,v4,v5,v6; drop view if exists t1,t2,`t1a``b`,v1,v2,v3,v4,v5,v6;
drop database if exists mysqltest; drop database if exists mysqltest;
use test; use test;
...@@ -2335,6 +2335,27 @@ f1 group_concat(f2 order by f2 desc) ...@@ -2335,6 +2335,27 @@ f1 group_concat(f2 order by f2 desc)
1 3,2,1 1 3,2,1
drop view v1,v2; drop view v1,v2;
drop table t1; drop table t1;
create table t1 (x int, y int);
create table t2 (x int, y int, z int);
create table t3 (x int, y int, z int);
create table t4 (x int, y int, z int);
create view v1 as
select t1.x
from (
(t1 join t2 on ((t1.y = t2.y)))
join
(t3 left join t4 on (t3.y = t4.y) and (t3.z = t4.z))
);
prepare stmt1 from "select count(*) from v1 where x = ?";
set @parm1=1;
execute stmt1 using @parm1;
count(*)
0
execute stmt1 using @parm1;
count(*)
0
drop view v1;
drop table t1,t2,t3,t4;
CREATE TABLE t1(id INT); CREATE TABLE t1(id INT);
CREATE VIEW v1 AS SELECT id FROM t1; CREATE VIEW v1 AS SELECT id FROM t1;
OPTIMIZE TABLE v1; OPTIMIZE TABLE v1;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# Initialize # Initialize
--disable_warnings --disable_warnings
drop table if exists t1; drop table if exists t1, t2;
--enable_warnings --enable_warnings
# #
......
...@@ -832,3 +832,71 @@ SELECT v2.x FROM v2 JOIN t2 ON e=b JOIN t3 ON e=c JOIN t4 USING(d); ...@@ -832,3 +832,71 @@ SELECT v2.x FROM v2 JOIN t2 ON e=b JOIN t3 ON e=c JOIN t4 USING(d);
DROP VIEW v1, v2; DROP VIEW v1, v2;
DROP TABLE t1, t2, t3, t4, t5, t6; DROP TABLE t1, t2, t3, t4, t5, t6;
#
# BUG#13126 -test case from bug report
#
create table t1 (id1 int(11) not null);
insert into t1 values (1),(2);
create table t2 (id2 int(11) not null);
insert into t2 values (1),(2),(3),(4);
create table t3 (id3 char(16) not null);
insert into t3 values ('100');
create table t4 (id2 int(11) not null, id3 char(16));
create table t5 (id1 int(11) not null, key (id1));
insert into t5 values (1),(2),(1);
create view v1 as
select t4.id3 from t4 join t2 on t4.id2 = t2.id2;
select t1.id1 from t1 inner join (t3 left join v1 on t3.id3 = v1.id3);
drop view v1;
drop table t1, t2, t3, t4, t5;
create table t0 (a int);
insert into t0 values (0),(1),(2),(3);
create table t1(a int);
insert into t1 select A.a + 10*(B.a) from t0 A, t0 B;
create table t2 (a int, b int);
insert into t2 values (1,1), (2,2), (3,3);
create table t3(a int, b int, filler char(200), key(a));
insert into t3 select a,a,'filler' from t1;
insert into t3 select a,a,'filler' from t1;
create table t4 like t3;
insert into t4 select * from t3;
insert into t4 select * from t3;
create table t5 like t4;
insert into t5 select * from t4;
insert into t5 select * from t4;
create table t6 like t5;
insert into t6 select * from t5;
insert into t6 select * from t5;
create table t7 like t6;
insert into t7 select * from t6;
insert into t7 select * from t6;
--replace_column 9 X
explain select * from t4 join
t2 left join (t3 join t5 on t5.a=t3.b) on t3.a=t2.b where t4.a<=>t3.b;
--replace_column 9 X
explain select * from (t4 join t6 on t6.a=t4.b) right join t3 on t4.a=t3.b
join t2 left join (t5 join t7 on t7.a=t5.b) on t5.a=t2.b where t3.a<=>t2.b;
--replace_column 9 X
explain select * from t2 left join
(t3 left join (t4 join t6 on t6.a=t4.b) on t4.a=t3.b
join t5 on t5.a=t3.b) on t3.a=t2.b;
drop table t0, t1, t2, t4, t5, t6;
--disable_warnings --disable_warnings
drop table if exists t1,t2,t9,`t1a``b`,v1,v2,v3,v4,v5,v6; drop table if exists t1,t2,t3,t4,t9,`t1a``b`,v1,v2,v3,v4,v5,v6;
drop view if exists t1,t2,`t1a``b`,v1,v2,v3,v4,v5,v6; drop view if exists t1,t2,`t1a``b`,v1,v2,v3,v4,v5,v6;
drop database if exists mysqltest; drop database if exists mysqltest;
--enable_warnings --enable_warnings
...@@ -2200,6 +2200,32 @@ select * from v1; ...@@ -2200,6 +2200,32 @@ select * from v1;
select * from v2; select * from v2;
drop view v1,v2; drop view v1,v2;
drop table t1; drop table t1;
#
# BUG#14026 Crash on second PS execution when using views
#
create table t1 (x int, y int);
create table t2 (x int, y int, z int);
create table t3 (x int, y int, z int);
create table t4 (x int, y int, z int);
create view v1 as
select t1.x
from (
(t1 join t2 on ((t1.y = t2.y)))
join
(t3 left join t4 on (t3.y = t4.y) and (t3.z = t4.z))
);
prepare stmt1 from "select count(*) from v1 where x = ?";
set @parm1=1;
execute stmt1 using @parm1;
execute stmt1 using @parm1;
drop view v1;
drop table t1,t2,t3,t4;
#
# Bug #14540: OPTIMIZE, ANALYZE, REPAIR applied to not a view # Bug #14540: OPTIMIZE, ANALYZE, REPAIR applied to not a view
# #
......
...@@ -44,6 +44,12 @@ ...@@ -44,6 +44,12 @@
typedef ulonglong table_map; /* Used for table bits in join */ typedef ulonglong table_map; /* Used for table bits in join */
typedef Bitmap<64> key_map; /* Used for finding keys */ typedef Bitmap<64> key_map; /* Used for finding keys */
typedef ulong key_part_map; /* Used for finding key parts */ typedef ulong key_part_map; /* Used for finding key parts */
/*
Used to identify NESTED_JOIN structures within a join (applicable only to
structures that have not been simplified away and embed more the one
element)
*/
typedef ulonglong nested_join_map;
/* query_id */ /* query_id */
typedef ulonglong query_id_t; typedef ulonglong query_id_t;
......
...@@ -2037,6 +2037,35 @@ void st_lex::cleanup_after_one_table_open() ...@@ -2037,6 +2037,35 @@ void st_lex::cleanup_after_one_table_open()
} }
/*
Do end-of-prepare fixup for list of tables and their merge-VIEWed tables
SYNOPSIS
fix_prepare_info_in_table_list()
thd Thread handle
tbl List of tables to process
DESCRIPTION
Perform end-end-of prepare fixup for list of tables, if any of the tables
is a merge-algorithm VIEW, recursively fix up its underlying tables as
well.
*/
static void fix_prepare_info_in_table_list(THD *thd, TABLE_LIST *tbl)
{
for (; tbl; tbl= tbl->next_local)
{
if (tbl->on_expr)
{
tbl->prep_on_expr= tbl->on_expr;
tbl->on_expr= tbl->on_expr->copy_andor_structure(thd);
}
fix_prepare_info_in_table_list(thd, tbl->merge_underlying_list);
}
}
/* /*
fix some structures at the end of preparation fix some structures at the end of preparation
...@@ -2056,16 +2085,7 @@ void st_select_lex::fix_prepare_information(THD *thd, Item **conds) ...@@ -2056,16 +2085,7 @@ void st_select_lex::fix_prepare_information(THD *thd, Item **conds)
prep_where= *conds; prep_where= *conds;
*conds= where= prep_where->copy_andor_structure(thd); *conds= where= prep_where->copy_andor_structure(thd);
} }
for (TABLE_LIST *tbl= (TABLE_LIST *)table_list.first; fix_prepare_info_in_table_list(thd, (TABLE_LIST *)table_list.first);
tbl;
tbl= tbl->next_local)
{
if (tbl->on_expr)
{
tbl->prep_on_expr= tbl->on_expr;
tbl->on_expr= tbl->on_expr->copy_andor_structure(thd);
}
}
} }
} }
......
...@@ -2109,8 +2109,6 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) ...@@ -2109,8 +2109,6 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
were closed in the end of previous prepare or execute call. were closed in the end of previous prepare or execute call.
*/ */
tables->table= 0; tables->table= 0;
if (tables->nested_join)
tables->nested_join->counter= 0;
if (tables->prep_on_expr) if (tables->prep_on_expr)
{ {
......
...@@ -98,6 +98,12 @@ static COND* substitute_for_best_equal_field(COND *cond, ...@@ -98,6 +98,12 @@ static COND* substitute_for_best_equal_field(COND *cond,
void *table_join_idx); void *table_join_idx);
static COND *simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, static COND *simplify_joins(JOIN *join, List<TABLE_LIST> *join_list,
COND *conds, bool top); COND *conds, bool top);
static bool check_interleaving_with_nj(JOIN_TAB *last, JOIN_TAB *next);
static void restore_prev_nj_state(JOIN_TAB *last);
static void reset_nj_counters(List<TABLE_LIST> *join_list);
static uint build_bitmap_for_nested_joins(List<TABLE_LIST> *join_list,
uint first_unused);
static COND *optimize_cond(JOIN *join, COND *conds, static COND *optimize_cond(JOIN *join, COND *conds,
List<TABLE_LIST> *join_list, List<TABLE_LIST> *join_list,
Item::cond_result *cond_value); Item::cond_result *cond_value);
...@@ -520,12 +526,14 @@ bool JOIN::test_in_subselect(Item **where) ...@@ -520,12 +526,14 @@ bool JOIN::test_in_subselect(Item **where)
return 0; return 0;
} }
/* /*
global select optimisation. global select optimisation.
return 0 - success return 0 - success
1 - error 1 - error
error code saved in field 'error' error code saved in field 'error'
*/ */
int int
JOIN::optimize() JOIN::optimize()
{ {
...@@ -588,6 +596,7 @@ JOIN::optimize() ...@@ -588,6 +596,7 @@ JOIN::optimize()
/* Convert all outer joins to inner joins if possible */ /* Convert all outer joins to inner joins if possible */
conds= simplify_joins(this, join_list, conds, TRUE); conds= simplify_joins(this, join_list, conds, TRUE);
build_bitmap_for_nested_joins(join_list, 0);
sel->prep_where= conds ? conds->copy_andor_structure(thd) : 0; sel->prep_where= conds ? conds->copy_andor_structure(thd) : 0;
...@@ -701,6 +710,7 @@ JOIN::optimize() ...@@ -701,6 +710,7 @@ JOIN::optimize()
DBUG_RETURN(1); DBUG_RETURN(1);
} }
reset_nj_counters(join_list);
make_outerjoin_info(this); make_outerjoin_info(this);
/* /*
...@@ -1968,14 +1978,19 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, ...@@ -1968,14 +1978,19 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
continue; continue;
} }
outer_join|= table->map; outer_join|= table->map;
s->embedding_map= 0;
for (;embedding; embedding= embedding->embedding)
s->embedding_map|= embedding->nested_join->nj_map;
continue; continue;
} }
if (embedding) if (embedding)
{ {
/* s belongs to a nested join, maybe to several embedded joins */ /* s belongs to a nested join, maybe to several embedded joins */
s->embedding_map= 0;
do do
{ {
NESTED_JOIN *nested_join= embedding->nested_join; NESTED_JOIN *nested_join= embedding->nested_join;
s->embedding_map|=nested_join->nj_map;
s->dependent|= embedding->dep_tables; s->dependent|= embedding->dep_tables;
embedding= embedding->embedding; embedding= embedding->embedding;
outer_join|= nested_join->used_tables; outer_join|= nested_join->used_tables;
...@@ -3549,6 +3564,8 @@ choose_plan(JOIN *join, table_map join_tables) ...@@ -3549,6 +3564,8 @@ choose_plan(JOIN *join, table_map join_tables)
bool straight_join= join->select_options & SELECT_STRAIGHT_JOIN; bool straight_join= join->select_options & SELECT_STRAIGHT_JOIN;
DBUG_ENTER("choose_plan"); DBUG_ENTER("choose_plan");
join->cur_embedding_map= 0;
reset_nj_counters(join->join_list);
/* /*
if (SELECT_STRAIGHT_JOIN option is set) if (SELECT_STRAIGHT_JOIN option is set)
reorder tables so dependent tables come after tables they depend reorder tables so dependent tables come after tables they depend
...@@ -4029,7 +4046,9 @@ best_extension_by_limited_search(JOIN *join, ...@@ -4029,7 +4046,9 @@ best_extension_by_limited_search(JOIN *join,
for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++) for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
{ {
table_map real_table_bit= s->table->map; table_map real_table_bit= s->table->map;
if ((remaining_tables & real_table_bit) && !(remaining_tables & s->dependent)) if ((remaining_tables & real_table_bit) &&
!(remaining_tables & s->dependent) &&
(!idx || !check_interleaving_with_nj(join->positions[idx-1].table, s)))
{ {
double current_record_count, current_read_time; double current_record_count, current_read_time;
...@@ -4045,6 +4064,7 @@ best_extension_by_limited_search(JOIN *join, ...@@ -4045,6 +4064,7 @@ best_extension_by_limited_search(JOIN *join,
{ {
DBUG_EXECUTE("opt", print_plan(join, read_time, record_count, idx, DBUG_EXECUTE("opt", print_plan(join, read_time, record_count, idx,
"prune_by_cost");); "prune_by_cost"););
restore_prev_nj_state(s);
continue; continue;
} }
...@@ -4073,6 +4093,7 @@ best_extension_by_limited_search(JOIN *join, ...@@ -4073,6 +4093,7 @@ best_extension_by_limited_search(JOIN *join,
{ {
DBUG_EXECUTE("opt", print_plan(join, read_time, record_count, idx, DBUG_EXECUTE("opt", print_plan(join, read_time, record_count, idx,
"pruned_by_heuristic");); "pruned_by_heuristic"););
restore_prev_nj_state(s);
continue; continue;
} }
} }
...@@ -4107,9 +4128,11 @@ best_extension_by_limited_search(JOIN *join, ...@@ -4107,9 +4128,11 @@ best_extension_by_limited_search(JOIN *join,
sizeof(POSITION) * (idx + 1)); sizeof(POSITION) * (idx + 1));
join->best_read= current_read_time - 0.001; join->best_read= current_read_time - 0.001;
} }
DBUG_EXECUTE("opt", DBUG_EXECUTE("opt", print_plan(join, current_read_time,
print_plan(join, current_read_time, current_record_count, idx, "full_plan");); current_record_count, idx,
"full_plan"););
} }
restore_prev_nj_state(s);
} }
} }
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
...@@ -4154,7 +4177,8 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, ...@@ -4154,7 +4177,8 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
for (JOIN_TAB **pos=join->best_ref+idx ; (s=*pos) ; pos++) for (JOIN_TAB **pos=join->best_ref+idx ; (s=*pos) ; pos++)
{ {
table_map real_table_bit=s->table->map; table_map real_table_bit=s->table->map;
if ((rest_tables & real_table_bit) && !(rest_tables & s->dependent)) if ((rest_tables & real_table_bit) && !(rest_tables & s->dependent) &&
(!idx|| !check_interleaving_with_nj(join->positions[idx-1].table, s)))
{ {
double best,best_time,records; double best,best_time,records;
best=best_time=records=DBL_MAX; best=best_time=records=DBL_MAX;
...@@ -4516,6 +4540,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, ...@@ -4516,6 +4540,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
return; return;
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos); swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
} }
restore_prev_nj_state(s);
if (join->select_options & SELECT_STRAIGHT_JOIN) if (join->select_options & SELECT_STRAIGHT_JOIN)
break; // Don't test all combinations break; // Don't test all combinations
} }
...@@ -7262,11 +7287,11 @@ propagate_cond_constants(THD *thd, I_List<COND_CMP> *save_list, ...@@ -7262,11 +7287,11 @@ propagate_cond_constants(THD *thd, I_List<COND_CMP> *save_list,
ascent all attributes are calculated, all outer joins that can be ascent all attributes are calculated, all outer joins that can be
converted are replaced and then all unnecessary braces are removed. converted are replaced and then all unnecessary braces are removed.
As join list contains join tables in the reverse order sequential As join list contains join tables in the reverse order sequential
elimination of outer joins does not requite extra recursive calls. elimination of outer joins does not require extra recursive calls.
EXAMPLES EXAMPLES
Here is an example of a join query with invalid cross references: 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 SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t3.a LEFT JOIN t3 ON t3.b=t1.b
RETURN VALUE RETURN VALUE
The new condition, if success The new condition, if success
...@@ -7425,6 +7450,256 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top) ...@@ -7425,6 +7450,256 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top)
} }
/*
Assign each nested join structure a bit in nested_join_map
SYNOPSIS
build_bitmap_for_nested_joins()
join Join being processed
join_list List of tables
first_unused Number of first unused bit in nested_join_map before the
call
DESCRIPTION
Assign each nested join structure (except "confluent" ones - those that
embed only one element) a bit in nested_join_map.
NOTE
This function is called after simplify_joins(), when there are no
redundant nested joins, #non_confluent_nested_joins <= #tables_in_join so
we will not run out of bits in nested_join_map.
RETURN
First unused bit in nested_join_map after the call.
*/
static uint build_bitmap_for_nested_joins(List<TABLE_LIST> *join_list,
uint first_unused)
{
List_iterator<TABLE_LIST> li(*join_list);
TABLE_LIST *table;
DBUG_ENTER("build_bitmap_for_nested_joins");
while ((table= li++))
{
NESTED_JOIN *nested_join;
if ((nested_join= table->nested_join))
{
/*
It is guaranteed by simplify_joins() function that a nested join
that has only one child represents a single table VIEW (and the child
is an underlying table). We don't assign bits to such nested join
structures because
1. it is redundant (a "sequence" of one table cannot be interleaved
with anything)
2. we could run out bits in nested_join_map otherwise.
*/
if (nested_join->join_list.elements != 1)
{
nested_join->nj_map= 1 << first_unused++;
first_unused= build_bitmap_for_nested_joins(&nested_join->join_list,
first_unused);
}
}
}
DBUG_RETURN(first_unused);
}
/*
Set NESTED_JOIN::counter=0 in all nested joins in passed list
SYNOPSIS
reset_nj_counters()
join_list List of nested joins to process. It may also contain base
tables which will be ignored.
DESCRIPTION
Recursively set NESTED_JOIN::counter=0 for all nested joins contained in
the passed join_list.
*/
static void reset_nj_counters(List<TABLE_LIST> *join_list)
{
List_iterator<TABLE_LIST> li(*join_list);
TABLE_LIST *table;
DBUG_ENTER("reset_nj_counters");
while ((table= li++))
{
NESTED_JOIN *nested_join;
if ((nested_join= table->nested_join))
{
nested_join->counter= 0;
reset_nj_counters(&nested_join->join_list);
}
}
DBUG_VOID_RETURN;
}
/*
Check interleaving with an inner tables of an outer join for extension table
SYNOPSIS
check_interleaving_with_nj()
join Join being processed
last_tab Last table in current partial join order (this function is
not called for empty partial join orders)
next_tab Table we're going to extend the current partial join with
DESCRIPTION
Check if table next_tab can be added to current partial join order, and
if yes, record that it has been added.
The function assumes that both current partial join order and its
extension with next_tab are valid wrt table dependencies.
IMPLEMENTATION
LIMITATIONS ON JOIN ORDER
The nested [outer] joins executioner algorithm imposes these limitations
on join order:
1. "Outer tables first" - any "outer" table must be before any
corresponding "inner" table.
2. "No interleaving" - tables inside a nested join must form a continuous
sequence in join order (i.e. the sequence must not be interrupted by
tables that are outside of this nested join).
#1 is checked elsewhere, this function checks #2 provided that #1 has
been already checked.
WHY NEED NON-INTERLEAVING
Consider an example:
select * from t0 join t1 left join (t2 join t3) on cond1
The join order "t1 t2 t0 t3" is invalid:
table t0 is outside of the nested join, so WHERE condition for t0 is
attached directly to t0 (without triggers, and it may be used to access
t0). Applying WHERE(t0) to (t2,t0,t3) record is invalid as we may miss
combinations of (t1, t2, t3) that satisfy condition cond1, and produce a
null-complemented (t1, t2.NULLs, t3.NULLs) row, which should not have
been produced.
If table t0 is not between t2 and t3, the problem doesn't exist:
* If t0 is located after (t2,t3), WHERE(t0) is applied after nested join
processing has finished.
* If t0 is located before (t2,t3), predicates like WHERE_cond(t0, t2) are
wrapped into condition triggers, which takes care of correct nested
join processing.
HOW IT IS IMPLEMENTED
The limitations on join order can be rephrased as follows: for valid
join order one must be able to:
1. write down the used tables in the join order on one line.
2. for each nested join, put one '(' and one ')' on the said line
3. write "LEFT JOIN" and "ON (...)" where appropriate
4. get a query equivalent to the query we're trying to execute.
Calls to check_interleaving_with_nj() are equivalent to writing the
above described line from left to right.
A single check_interleaving_with_nj(A,B) call is equivalent to writing
table B and appropriate brackets on condition that table A and
appropriate brackets is the last what was written. Graphically the
transition is as follows:
+---- current position
|
... last_tab ))) | ( next_tab ) )..) | ...
X Y Z |
+- need to move to this
position.
Notes about the position:
The caller guarantees that there is no more then one X-bracket by
checking "!(remaining_tables & s->dependent)" before calling this
function. X-bracket may have a pair in Y-bracket.
When "writing" we store/update this auxilary info about the current
position:
1. join->cur_embedding_map - bitmap of pairs of brackets (aka nested
joins) we've opened but didn't close.
2. {each NESTED_JOIN structure not simplified away}->counter - number
of this nested join's children that have already been added to to
the partial join order.
RETURN
FALSE Join order extended, nested joins info about current join order
(see NOTE section) updated.
TRUE Requested join order extension not allowed.
*/
static bool check_interleaving_with_nj(JOIN_TAB *last_tab, JOIN_TAB *next_tab)
{
TABLE_LIST *next_emb= next_tab->table->pos_in_table_list->embedding;
JOIN *join= last_tab->join;
if (join->cur_embedding_map & ~next_tab->embedding_map)
{
/*
next_tab is outside of the "pair of brackets" we're currently in.
Cannot add it.
*/
return TRUE;
}
/*
Do update counters for "pairs of brackets" that we've left (marked as
X,Y,Z in the above picture)
*/
for (;next_emb; next_emb= next_emb->embedding)
{
next_emb->nested_join->counter++;
if (next_emb->nested_join->counter == 1)
{
/*
next_emb is the first table inside a nested join we've "entered". In
the picture above, we're looking at the 'X' bracket. Don't exit yet as
X bracket might have Y pair bracket.
*/
join->cur_embedding_map |= next_emb->nested_join->nj_map;
}
if (next_emb->nested_join->join_list.elements !=
next_emb->nested_join->counter)
break;
/*
We're currently at Y or Z-bracket as depicted in the above picture.
Mark that we've left it and continue walking up the brackets hierarchy.
*/
join->cur_embedding_map &= ~next_emb->nested_join->nj_map;
}
return FALSE;
}
/*
Nested joins perspective: Remove the last table from the join order
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
check_interleaving_with_nj has not been called)
*/
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 && !(--last_emb->nested_join->counter))
{
join->cur_embedding_map &= last_emb->nested_join->nj_map;
last_emb= last_emb->embedding;
}
}
static COND * static COND *
optimize_cond(JOIN *join, COND *conds, List<TABLE_LIST> *join_list, optimize_cond(JOIN *join, COND *conds, List<TABLE_LIST> *join_list,
Item::cond_result *cond_value) Item::cond_result *cond_value)
......
...@@ -136,6 +136,8 @@ typedef struct st_join_table { ...@@ -136,6 +136,8 @@ typedef struct st_join_table {
TABLE_REF ref; TABLE_REF ref;
JOIN_CACHE cache; JOIN_CACHE cache;
JOIN *join; JOIN *join;
/* Bitmap of nested joins this table is part of */
nested_join_map embedding_map;
void cleanup(); void cleanup();
} JOIN_TAB; } JOIN_TAB;
...@@ -193,6 +195,13 @@ class JOIN :public Sql_alloc ...@@ -193,6 +195,13 @@ class JOIN :public Sql_alloc
*/ */
ha_rows fetch_limit; ha_rows fetch_limit;
POSITION positions[MAX_TABLES+1],best_positions[MAX_TABLES+1]; POSITION positions[MAX_TABLES+1],best_positions[MAX_TABLES+1];
/*
Bitmap of nested joins embedding the position at the end of the current
partial join (valid only during join optimizer run).
*/
nested_join_map cur_embedding_map;
double best_read; double best_read;
List<Item> *fields; List<Item> *fields;
List<Cached_item> group_fields, group_fields_cache; List<Cached_item> group_fields, group_fields_cache;
......
...@@ -795,7 +795,15 @@ typedef struct st_nested_join ...@@ -795,7 +795,15 @@ typedef struct st_nested_join
table_map used_tables; /* bitmap of tables in the nested join */ table_map used_tables; /* bitmap of tables in the nested join */
table_map not_null_tables; /* tables that rejects nulls */ table_map not_null_tables; /* tables that rejects nulls */
struct st_join_table *first_nested;/* the first nested table in the plan */ struct st_join_table *first_nested;/* the first nested table in the plan */
uint counter; /* to count tables in the nested join */ /*
Used to count tables in the nested join in 2 isolated places:
1. In make_outerjoin_info().
2. check_interleaving_with_nj/restore_prev_nj_state (these are called
by the join optimizer.
Before each use the counters are zeroed by reset_nj_counters.
*/
uint counter;
nested_join_map nj_map; /* Bit used to identify this nested join*/
} NESTED_JOIN; } NESTED_JOIN;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment