summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/mysql_priv.h5
-rw-r--r--sql/sql_select.cc167
2 files changed, 110 insertions, 62 deletions
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 198a90dbd8a..e7dab6a6377 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -45,8 +45,9 @@ 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 for nested join bits within a scope of a join (applicable to non-unary
- nested joins that have not been simplified away)
+ 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;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index a74e01b866b..219f707b0da 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -101,8 +101,8 @@ static COND *simplify_joins(JOIN *join, List<TABLE_LIST> *join_list,
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_nj_bitmap_for_tables(JOIN *join, List<TABLE_LIST> *join_list,
- uint first_unused);
+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,
@@ -595,7 +595,7 @@ JOIN::optimize()
/* Convert all outer joins to inner joins if possible */
conds= simplify_joins(this, join_list, conds, TRUE);
- build_nj_bitmap_for_tables(this, join_list, 0);
+ build_bitmap_for_nested_joins(join_list, 0);
sel->prep_where= conds ? conds->copy_andor_structure(thd) : 0;
@@ -7447,31 +7447,31 @@ 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_nj_bitmap_for_tables()
+ 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 for unary nested joins, see
- below) a bit in nested_join_map.
+ 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_unary_nested_joins <= #tables_in_join so we
- will not run out of bits in nested_join_map.
+ 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_nj_bitmap_for_tables(JOIN *join, List<TABLE_LIST> *join_list,
- uint first_unused)
+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_nj_bitmap_for_tables");
+ DBUG_ENTER("build_bitmap_for_nested_joins");
while ((table= li++))
{
NESTED_JOIN *nested_join;
@@ -7479,9 +7479,9 @@ static uint build_nj_bitmap_for_tables(JOIN *join, List<TABLE_LIST> *join_list,
{
/*
It is guaranteed by simplify_joins() function that a nested join
- that has only one child represents a single table VIEW (and a child
- is an underlying table). We don't assign a bit to the nested join
- because
+ 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.
@@ -7489,8 +7489,8 @@ static uint build_nj_bitmap_for_tables(JOIN *join, List<TABLE_LIST> *join_list,
if (nested_join->join_list.elements != 1)
{
nested_join->nj_map= 1 << first_unused++;
- first_unused= build_nj_bitmap_for_tables(join, &nested_join->join_list,
- first_unused);
+ first_unused= build_bitmap_for_nested_joins(&nested_join->join_list,
+ first_unused);
}
}
}
@@ -7546,73 +7546,120 @@ static void reset_nj_counters(List<TABLE_LIST> *join_list)
The function assumes that both current partial join order and its
extension with next_tab are valid wrt table dependencies.
- NOTE
- The nested joins aspect of information about current partial join order is
- stored in join->cur_embedding_map and
- {each_join_table}->pos_in_table_list (->embedding)+ ->nested_join->counter
- Joins optimizer calls check_interleaving_with_nj() and
- restore_prev_nj_state() to update this info and avoid constructing invalid
- join orders. There is no need for any initialization of
- {each_join_table}->...->counter before the join optimizer run.
-
IMPLEMENTATION
- 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.
+ 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.
-
+ 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, JOIN_TAB *next)
+static bool check_interleaving_with_nj(JOIN_TAB *last_tab, JOIN_TAB *next_tab)
{
- TABLE_LIST *next_emb= next->table->pos_in_table_list->embedding;
- JOIN *join= last->join;
+ TABLE_LIST *next_emb= next_tab->table->pos_in_table_list->embedding;
+ JOIN *join= last_tab->join;
- if (join->cur_embedding_map & ~next->embedding_map)
+ 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 a first table inside a nested join */
+ /*
+ 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;