summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <sergefp@mysql.com>2005-11-03 16:23:47 +0300
committerunknown <sergefp@mysql.com>2005-11-03 16:23:47 +0300
commit46865aae34fb31543e3011363e0b6bef349b13c4 (patch)
tree053334b1ce2c9fcd55ac6c268ed6a4cc354160b5
parentdd490fd2d585640614d4390cb2e7a6da8a0e869a (diff)
parent4d155f06eeb722a55974608778ec5ac66affc5da (diff)
downloadmariadb-git-46865aae34fb31543e3011363e0b6bef349b13c4.tar.gz
Merge spetrunia@bk-internal.mysql.com:/home/bk/mysql-5.0
into mysql.com:/home/psergey/mysql-5.0-oct03-push
-rw-r--r--mysql-test/r/bigint.result2
-rw-r--r--mysql-test/r/join_nested.result64
-rw-r--r--mysql-test/r/view.result23
-rw-r--r--mysql-test/t/bigint.test2
-rw-r--r--mysql-test/t/join_nested.test68
-rw-r--r--mysql-test/t/view.test26
-rw-r--r--sql/mysql_priv.h6
-rw-r--r--sql/sql_lex.cc40
-rw-r--r--sql/sql_prepare.cc2
-rw-r--r--sql/sql_select.cc297
-rw-r--r--sql/sql_select.h11
-rw-r--r--sql/table.h10
12 files changed, 522 insertions, 29 deletions
diff --git a/mysql-test/r/bigint.result b/mysql-test/r/bigint.result
index ca9a2662f94..84779858b75 100644
--- a/mysql-test/r/bigint.result
+++ b/mysql-test/r/bigint.result
@@ -1,4 +1,4 @@
-drop table if exists t1;
+drop table if exists t1, t2;
select 0,256,00000000000000065536,2147483647,-2147483648,2147483648,+4294967296;
0 256 00000000000000065536 2147483647 -2147483648 2147483648 4294967296
0 256 65536 2147483647 -2147483648 2147483648 4294967296
diff --git a/mysql-test/r/join_nested.result b/mysql-test/r/join_nested.result
index 9d514be76e8..c4dd6cf9a86 100644
--- a/mysql-test/r/join_nested.result
+++ b/mysql-test/r/join_nested.result
@@ -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'
DROP VIEW v1, v2;
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;
diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result
index a74feb4de17..22edc4969e4 100644
--- a/mysql-test/r/view.result
+++ b/mysql-test/r/view.result
@@ -1,4 +1,4 @@
-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 database if exists mysqltest;
use test;
@@ -2335,3 +2335,24 @@ f1 group_concat(f2 order by f2 desc)
1 3,2,1
drop view v1,v2;
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;
diff --git a/mysql-test/t/bigint.test b/mysql-test/t/bigint.test
index d9c1abd9ba9..7871b3647e3 100644
--- a/mysql-test/t/bigint.test
+++ b/mysql-test/t/bigint.test
@@ -2,7 +2,7 @@
# Initialize
--disable_warnings
-drop table if exists t1;
+drop table if exists t1, t2;
--enable_warnings
#
diff --git a/mysql-test/t/join_nested.test b/mysql-test/t/join_nested.test
index 0592ec3152f..9dbf153ec55 100644
--- a/mysql-test/t/join_nested.test
+++ b/mysql-test/t/join_nested.test
@@ -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 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;
diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test
index 0bd0e572193..7ab2c4779b9 100644
--- a/mysql-test/t/view.test
+++ b/mysql-test/t/view.test
@@ -1,5 +1,5 @@
--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 database if exists mysqltest;
--enable_warnings
@@ -2200,3 +2200,27 @@ select * from v1;
select * from v2;
drop view v1,v2;
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;
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 5f1d0fe18db..c7df3f416c6 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -44,6 +44,12 @@
typedef ulonglong table_map; /* Used for table bits in join */
typedef Bitmap<64> key_map; /* Used for finding keys */
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 */
typedef ulonglong query_id_t;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 11f056d6510..a302db15736 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2038,6 +2038,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
SYNOPSIS
@@ -2056,16 +2085,7 @@ void st_select_lex::fix_prepare_information(THD *thd, Item **conds)
prep_where= *conds;
*conds= where= prep_where->copy_andor_structure(thd);
}
- for (TABLE_LIST *tbl= (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);
- }
- }
+ fix_prepare_info_in_table_list(thd, (TABLE_LIST *)table_list.first);
}
}
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 0d0afb5ca7d..ced862170ec 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2109,8 +2109,6 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
were closed in the end of previous prepare or execute call.
*/
tables->table= 0;
- if (tables->nested_join)
- tables->nested_join->counter= 0;
if (tables->prep_on_expr)
{
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 285a5c5696b..3c5506589eb 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -98,6 +98,12 @@ static COND* substitute_for_best_equal_field(COND *cond,
void *table_join_idx);
static COND *simplify_joins(JOIN *join, List<TABLE_LIST> *join_list,
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,
List<TABLE_LIST> *join_list,
Item::cond_result *cond_value);
@@ -520,12 +526,14 @@ bool JOIN::test_in_subselect(Item **where)
return 0;
}
+
/*
global select optimisation.
return 0 - success
1 - error
error code saved in field 'error'
*/
+
int
JOIN::optimize()
{
@@ -588,6 +596,7 @@ JOIN::optimize()
/* Convert all outer joins to inner joins if possible */
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;
@@ -700,7 +709,8 @@ JOIN::optimize()
DBUG_PRINT("error",("Error: make_select() failed"));
DBUG_RETURN(1);
}
-
+
+ reset_nj_counters(join_list);
make_outerjoin_info(this);
/*
@@ -1968,14 +1978,19 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
continue;
}
outer_join|= table->map;
+ s->embedding_map= 0;
+ for (;embedding; embedding= embedding->embedding)
+ s->embedding_map|= embedding->nested_join->nj_map;
continue;
}
if (embedding)
{
/* s belongs to a nested join, maybe to several embedded joins */
+ s->embedding_map= 0;
do
{
NESTED_JOIN *nested_join= embedding->nested_join;
+ s->embedding_map|=nested_join->nj_map;
s->dependent|= embedding->dep_tables;
embedding= embedding->embedding;
outer_join|= nested_join->used_tables;
@@ -3549,6 +3564,8 @@ choose_plan(JOIN *join, table_map join_tables)
bool straight_join= join->select_options & SELECT_STRAIGHT_JOIN;
DBUG_ENTER("choose_plan");
+ join->cur_embedding_map= 0;
+ reset_nj_counters(join->join_list);
/*
if (SELECT_STRAIGHT_JOIN option is set)
reorder tables so dependent tables come after tables they depend
@@ -4029,7 +4046,9 @@ best_extension_by_limited_search(JOIN *join,
for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
{
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;
@@ -4045,6 +4064,7 @@ best_extension_by_limited_search(JOIN *join,
{
DBUG_EXECUTE("opt", print_plan(join, read_time, record_count, idx,
"prune_by_cost"););
+ restore_prev_nj_state(s);
continue;
}
@@ -4073,6 +4093,7 @@ best_extension_by_limited_search(JOIN *join,
{
DBUG_EXECUTE("opt", print_plan(join, read_time, record_count, idx,
"pruned_by_heuristic"););
+ restore_prev_nj_state(s);
continue;
}
}
@@ -4107,9 +4128,11 @@ best_extension_by_limited_search(JOIN *join,
sizeof(POSITION) * (idx + 1));
join->best_read= current_read_time - 0.001;
}
- DBUG_EXECUTE("opt",
- print_plan(join, current_read_time, current_record_count, idx, "full_plan"););
+ DBUG_EXECUTE("opt", print_plan(join, current_read_time,
+ current_record_count, idx,
+ "full_plan"););
}
+ restore_prev_nj_state(s);
}
}
DBUG_VOID_RETURN;
@@ -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++)
{
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;
best=best_time=records=DBL_MAX;
@@ -4492,10 +4516,10 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
join->unit->select_limit_cnt >= records)
join->sort_by_table= (TABLE*) 1; // Must use temporary table
- /*
+ /*
Go to the next level only if there hasn't been a better key on
this level! This will cut down the search for a lot simple cases!
- */
+ */
double current_record_count=record_count*records;
double current_read_time=read_time+best;
if (best_record_count > current_record_count ||
@@ -4516,6 +4540,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
return;
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
}
+ restore_prev_nj_state(s);
if (join->select_options & SELECT_STRAIGHT_JOIN)
break; // Don't test all combinations
}
@@ -5101,7 +5126,7 @@ add_found_match_trig_cond(JOIN_TAB *tab, COND *cond, JOIN_TAB *root_tab)
This function can be called only after the execution plan
has been chosen.
*/
-
+
static void
make_outerjoin_info(JOIN *join)
{
@@ -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
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.
+ elimination of outer joins does not require 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
+ SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t3.a LEFT JOIN t3 ON t3.b=t1.b
RETURN VALUE
The new condition, if success
@@ -7423,7 +7448,257 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top)
}
DBUG_RETURN(conds);
}
-
+
+
+/*
+ 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 *
optimize_cond(JOIN *join, COND *conds, List<TABLE_LIST> *join_list,
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 28f5888af76..2f53c9a3b35 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -136,7 +136,9 @@ typedef struct st_join_table {
TABLE_REF ref;
JOIN_CACHE cache;
JOIN *join;
-
+ /* Bitmap of nested joins this table is part of */
+ nested_join_map embedding_map;
+
void cleanup();
} JOIN_TAB;
@@ -193,6 +195,13 @@ class JOIN :public Sql_alloc
*/
ha_rows fetch_limit;
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;
List<Item> *fields;
List<Cached_item> group_fields, group_fields_cache;
diff --git a/sql/table.h b/sql/table.h
index 9e94c5fa2f6..d919b9a4203 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -795,7 +795,15 @@ typedef struct st_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 */
- 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;