1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

Fix MySQL BUG#12329653

In MariaDB, when running in ONLY_FULL_GROUP_BY mode,
the server produced in incorrect error message that there
is an aggregate function without GROUP BY, for artificially
created MIN/MAX functions during subquery MIN/MAX optimization.

The fix introduces a way to distinguish between artifially
created MIN/MAX functions as a result of a rewrite, and normal
ones present in the query. The test for ONLY_FULL_GROUP_BY violation
now tests in addition if a MIN/MAX function was part of a MIN/MAX
subquery rewrite.

In order to be able to distinguish these MIN/MAX functions, the
patch introduces an additional flag in Item_in_subselect::in_strategy -
SUBS_STRATEGY_CHOSEN. This flag is set when the optimizer makes its
final choice of a subuqery strategy. In order to make the choice
consistent, access to Item_in_subselect::in_strategy is provided
via new class methods.
******
Fix MySQL BUG#12329653

In MariaDB, when running in ONLY_FULL_GROUP_BY mode,
the server produced in incorrect error message that there
is an aggregate function without GROUP BY, for artificially
created MIN/MAX functions during subquery MIN/MAX optimization.

The fix introduces a way to distinguish between artifially
created MIN/MAX functions as a result of a rewrite, and normal
ones present in the query. The test for ONLY_FULL_GROUP_BY violation
now tests in addition if a MIN/MAX function was part of a MIN/MAX
subquery rewrite.

In order to be able to distinguish these MIN/MAX functions, the
patch introduces an additional flag in Item_in_subselect::in_strategy -
SUBS_STRATEGY_CHOSEN. This flag is set when the optimizer makes its
final choice of a subuqery strategy. In order to make the choice
consistent, access to Item_in_subselect::in_strategy is provided
via new class methods.
This commit is contained in:
unknown
2011-11-12 11:29:12 +02:00
parent b91a6bd88b
commit 1d721d0106
12 changed files with 286 additions and 66 deletions

View File

@@ -5636,4 +5636,30 @@ SUM(DISTINCT b) (SELECT t2.a FROM t1,t2 WHERE t.a != 0 or 1=2 LIMIT 1)
7 NULL 7 NULL
7 10 7 10
DROP TABLE t1,t2,t3; DROP TABLE t1,t2,t3;
#
# Bug#12329653
# EXPLAIN, UNION, PREPARED STATEMENT, CRASH, SQL_FULL_GROUP_BY
#
CREATE TABLE t1(a1 int);
INSERT INTO t1 VALUES (1),(2);
SELECT @@session.sql_mode INTO @old_sql_mode;
SET SESSION sql_mode='ONLY_FULL_GROUP_BY';
SELECT 1 FROM t1 WHERE 1 < SOME (SELECT a1 FROM t1);
1
1
1
PREPARE stmt FROM
'SELECT 1 UNION ALL
SELECT 1 FROM t1
ORDER BY
(SELECT 1 FROM t1 AS t1_0
WHERE 1 < SOME (SELECT a1 FROM t1)
)' ;
EXECUTE stmt ;
ERROR 21000: Subquery returns more than 1 row
EXECUTE stmt ;
ERROR 21000: Subquery returns more than 1 row
SET SESSION sql_mode=@old_sql_mode;
DEALLOCATE PREPARE stmt;
DROP TABLE t1;
set optimizer_switch=@subselect_tmp; set optimizer_switch=@subselect_tmp;

View File

@@ -5641,6 +5641,32 @@ SUM(DISTINCT b) (SELECT t2.a FROM t1,t2 WHERE t.a != 0 or 1=2 LIMIT 1)
7 NULL 7 NULL
7 10 7 10
DROP TABLE t1,t2,t3; DROP TABLE t1,t2,t3;
#
# Bug#12329653
# EXPLAIN, UNION, PREPARED STATEMENT, CRASH, SQL_FULL_GROUP_BY
#
CREATE TABLE t1(a1 int);
INSERT INTO t1 VALUES (1),(2);
SELECT @@session.sql_mode INTO @old_sql_mode;
SET SESSION sql_mode='ONLY_FULL_GROUP_BY';
SELECT 1 FROM t1 WHERE 1 < SOME (SELECT a1 FROM t1);
1
1
1
PREPARE stmt FROM
'SELECT 1 UNION ALL
SELECT 1 FROM t1
ORDER BY
(SELECT 1 FROM t1 AS t1_0
WHERE 1 < SOME (SELECT a1 FROM t1)
)' ;
EXECUTE stmt ;
ERROR 21000: Subquery returns more than 1 row
EXECUTE stmt ;
ERROR 21000: Subquery returns more than 1 row
SET SESSION sql_mode=@old_sql_mode;
DEALLOCATE PREPARE stmt;
DROP TABLE t1;
set optimizer_switch=@subselect_tmp; set optimizer_switch=@subselect_tmp;
set optimizer_switch=default; set optimizer_switch=default;
select @@optimizer_switch like '%materialization=on%'; select @@optimizer_switch like '%materialization=on%';

View File

@@ -5637,5 +5637,31 @@ SUM(DISTINCT b) (SELECT t2.a FROM t1,t2 WHERE t.a != 0 or 1=2 LIMIT 1)
7 NULL 7 NULL
7 10 7 10
DROP TABLE t1,t2,t3; DROP TABLE t1,t2,t3;
#
# Bug#12329653
# EXPLAIN, UNION, PREPARED STATEMENT, CRASH, SQL_FULL_GROUP_BY
#
CREATE TABLE t1(a1 int);
INSERT INTO t1 VALUES (1),(2);
SELECT @@session.sql_mode INTO @old_sql_mode;
SET SESSION sql_mode='ONLY_FULL_GROUP_BY';
SELECT 1 FROM t1 WHERE 1 < SOME (SELECT a1 FROM t1);
1
1
1
PREPARE stmt FROM
'SELECT 1 UNION ALL
SELECT 1 FROM t1
ORDER BY
(SELECT 1 FROM t1 AS t1_0
WHERE 1 < SOME (SELECT a1 FROM t1)
)' ;
EXECUTE stmt ;
ERROR 21000: Subquery returns more than 1 row
EXECUTE stmt ;
ERROR 21000: Subquery returns more than 1 row
SET SESSION sql_mode=@old_sql_mode;
DEALLOCATE PREPARE stmt;
DROP TABLE t1;
set optimizer_switch=@subselect_tmp; set optimizer_switch=@subselect_tmp;
set @optimizer_switch_for_subselect_test=null; set @optimizer_switch_for_subselect_test=null;

View File

@@ -5640,6 +5640,32 @@ SUM(DISTINCT b) (SELECT t2.a FROM t1,t2 WHERE t.a != 0 or 1=2 LIMIT 1)
7 NULL 7 NULL
7 10 7 10
DROP TABLE t1,t2,t3; DROP TABLE t1,t2,t3;
#
# Bug#12329653
# EXPLAIN, UNION, PREPARED STATEMENT, CRASH, SQL_FULL_GROUP_BY
#
CREATE TABLE t1(a1 int);
INSERT INTO t1 VALUES (1),(2);
SELECT @@session.sql_mode INTO @old_sql_mode;
SET SESSION sql_mode='ONLY_FULL_GROUP_BY';
SELECT 1 FROM t1 WHERE 1 < SOME (SELECT a1 FROM t1);
1
1
1
PREPARE stmt FROM
'SELECT 1 UNION ALL
SELECT 1 FROM t1
ORDER BY
(SELECT 1 FROM t1 AS t1_0
WHERE 1 < SOME (SELECT a1 FROM t1)
)' ;
EXECUTE stmt ;
ERROR 21000: Subquery returns more than 1 row
EXECUTE stmt ;
ERROR 21000: Subquery returns more than 1 row
SET SESSION sql_mode=@old_sql_mode;
DEALLOCATE PREPARE stmt;
DROP TABLE t1;
set optimizer_switch=@subselect_tmp; set optimizer_switch=@subselect_tmp;
set optimizer_switch=default; set optimizer_switch=default;
select @@optimizer_switch like '%subquery_cache=on%'; select @@optimizer_switch like '%subquery_cache=on%';

View File

@@ -5637,5 +5637,31 @@ SUM(DISTINCT b) (SELECT t2.a FROM t1,t2 WHERE t.a != 0 or 1=2 LIMIT 1)
7 NULL 7 NULL
7 10 7 10
DROP TABLE t1,t2,t3; DROP TABLE t1,t2,t3;
#
# Bug#12329653
# EXPLAIN, UNION, PREPARED STATEMENT, CRASH, SQL_FULL_GROUP_BY
#
CREATE TABLE t1(a1 int);
INSERT INTO t1 VALUES (1),(2);
SELECT @@session.sql_mode INTO @old_sql_mode;
SET SESSION sql_mode='ONLY_FULL_GROUP_BY';
SELECT 1 FROM t1 WHERE 1 < SOME (SELECT a1 FROM t1);
1
1
1
PREPARE stmt FROM
'SELECT 1 UNION ALL
SELECT 1 FROM t1
ORDER BY
(SELECT 1 FROM t1 AS t1_0
WHERE 1 < SOME (SELECT a1 FROM t1)
)' ;
EXECUTE stmt ;
ERROR 21000: Subquery returns more than 1 row
EXECUTE stmt ;
ERROR 21000: Subquery returns more than 1 row
SET SESSION sql_mode=@old_sql_mode;
DEALLOCATE PREPARE stmt;
DROP TABLE t1;
set optimizer_switch=@subselect_tmp; set optimizer_switch=@subselect_tmp;
set @optimizer_switch_for_subselect_test=null; set @optimizer_switch_for_subselect_test=null;

View File

@@ -4730,4 +4730,38 @@ GROUP BY 2;
DROP TABLE t1,t2,t3; DROP TABLE t1,t2,t3;
--echo #
--echo # Bug#12329653
--echo # EXPLAIN, UNION, PREPARED STATEMENT, CRASH, SQL_FULL_GROUP_BY
--echo #
CREATE TABLE t1(a1 int);
INSERT INTO t1 VALUES (1),(2);
SELECT @@session.sql_mode INTO @old_sql_mode;
SET SESSION sql_mode='ONLY_FULL_GROUP_BY';
## First a simpler query, illustrating the transformation
## '1 < some (...)' => '1 < max(...)'
SELECT 1 FROM t1 WHERE 1 < SOME (SELECT a1 FROM t1);
## The query which made the server crash.
PREPARE stmt FROM
'SELECT 1 UNION ALL
SELECT 1 FROM t1
ORDER BY
(SELECT 1 FROM t1 AS t1_0
WHERE 1 < SOME (SELECT a1 FROM t1)
)' ;
--error ER_SUBQUERY_NO_1_ROW
EXECUTE stmt ;
--error ER_SUBQUERY_NO_1_ROW
EXECUTE stmt ;
SET SESSION sql_mode=@old_sql_mode;
DEALLOCATE PREPARE stmt;
DROP TABLE t1;
set optimizer_switch=@subselect_tmp; set optimizer_switch=@subselect_tmp;

View File

@@ -158,9 +158,11 @@ void Item_in_subselect::cleanup()
delete left_expr_cache; delete left_expr_cache;
left_expr_cache= NULL; left_expr_cache= NULL;
} }
/*
TODO: This breaks the commented assert in add_strategy().
in_strategy&= ~SUBS_STRATEGY_CHOSEN;
*/
first_execution= TRUE; first_execution= TRUE;
if (in_strategy & SUBS_MATERIALIZATION)
in_strategy= 0;
pushed_cond_guards= NULL; pushed_cond_guards= NULL;
Item_subselect::cleanup(); Item_subselect::cleanup();
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
@@ -176,10 +178,9 @@ void Item_allany_subselect::cleanup()
*/ */
for (SELECT_LEX *sl= unit->first_select(); for (SELECT_LEX *sl= unit->first_select();
sl; sl= sl->next_select()) sl; sl= sl->next_select())
if (in_strategy & SUBS_MAXMIN_INJECTED) if (test_strategy(SUBS_MAXMIN_INJECTED))
sl->with_sum_func= false; sl->with_sum_func= false;
Item_in_subselect::cleanup(); Item_in_subselect::cleanup();
} }
@@ -722,7 +723,7 @@ bool Item_in_subselect::exec()
- on a cost-based basis, that takes into account the cost of a cache - on a cost-based basis, that takes into account the cost of a cache
lookup, the cache hit rate, and the savings per cache hit. lookup, the cache hit rate, and the savings per cache hit.
*/ */
if (!left_expr_cache && (in_strategy & SUBS_MATERIALIZATION)) if (!left_expr_cache && (test_strategy(SUBS_MATERIALIZATION)))
init_left_expr_cache(); init_left_expr_cache();
/* /*
@@ -1186,8 +1187,8 @@ bool Item_in_subselect::test_limit(st_select_lex_unit *unit_arg)
Item_in_subselect::Item_in_subselect(Item * left_exp, Item_in_subselect::Item_in_subselect(Item * left_exp,
st_select_lex *select_lex): st_select_lex *select_lex):
Item_exists_subselect(), Item_exists_subselect(),
left_expr_cache(0), first_execution(TRUE), left_expr_cache(0), first_execution(TRUE), in_strategy(SUBS_NOT_TRANSFORMED),
optimizer(0), pushed_cond_guards(NULL), emb_on_expr_nest(NULL), in_strategy(0), optimizer(0), pushed_cond_guards(NULL), emb_on_expr_nest(NULL),
is_jtbm_merged(FALSE), is_flattenable_semijoin(FALSE), is_jtbm_merged(FALSE), is_flattenable_semijoin(FALSE),
is_registered_semijoin(FALSE), is_registered_semijoin(FALSE),
upper_item(0) upper_item(0)
@@ -1608,7 +1609,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
bool Item_allany_subselect::transform_into_max_min(JOIN *join) bool Item_allany_subselect::transform_into_max_min(JOIN *join)
{ {
DBUG_ENTER("Item_allany_subselect::transform_into_max_min"); DBUG_ENTER("Item_allany_subselect::transform_into_max_min");
if (!(in_strategy & (SUBS_MAXMIN_INJECTED | SUBS_MAXMIN_ENGINE))) if (!test_strategy(SUBS_MAXMIN_INJECTED | SUBS_MAXMIN_ENGINE))
DBUG_RETURN(false); DBUG_RETURN(false);
Item **place= optimizer->arguments() + 1; Item **place= optimizer->arguments() + 1;
THD *thd= join->thd; THD *thd= join->thd;
@@ -1682,7 +1683,7 @@ bool Item_allany_subselect::transform_into_max_min(JOIN *join)
Remove other strategies if any (we already changed the query and Remove other strategies if any (we already changed the query and
can't apply other strategy). can't apply other strategy).
*/ */
in_strategy= SUBS_MAXMIN_INJECTED; set_strategy(SUBS_MAXMIN_INJECTED);
} }
else else
{ {
@@ -1694,7 +1695,7 @@ bool Item_allany_subselect::transform_into_max_min(JOIN *join)
Remove other strategies if any (we already changed the query and Remove other strategies if any (we already changed the query and
can't apply other strategy). can't apply other strategy).
*/ */
in_strategy= SUBS_MAXMIN_ENGINE; set_strategy(SUBS_MAXMIN_ENGINE);
} }
/* /*
The swap is needed for expressions of type 'f1 < ALL ( SELECT ....)' The swap is needed for expressions of type 'f1 < ALL ( SELECT ....)'
@@ -2414,7 +2415,7 @@ err:
void Item_in_subselect::print(String *str, enum_query_type query_type) void Item_in_subselect::print(String *str, enum_query_type query_type)
{ {
if (in_strategy & SUBS_IN_TO_EXISTS) if (test_strategy(SUBS_IN_TO_EXISTS))
str->append(STRING_WITH_LEN("<exists>")); str->append(STRING_WITH_LEN("<exists>"));
else else
{ {
@@ -2430,8 +2431,7 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref)
uint outer_cols_num; uint outer_cols_num;
List<Item> *inner_cols; List<Item> *inner_cols;
if (test_strategy(SUBS_SEMI_JOIN))
if (in_strategy & SUBS_SEMI_JOIN)
return !( (*ref)= new Item_int(1)); return !( (*ref)= new Item_int(1));
/* /*
@@ -2607,8 +2607,7 @@ Item_allany_subselect::select_transformer(JOIN *join)
{ {
DBUG_ENTER("Item_allany_subselect::select_transformer"); DBUG_ENTER("Item_allany_subselect::select_transformer");
DBUG_ASSERT((in_strategy & ~(SUBS_MAXMIN_INJECTED | SUBS_MAXMIN_ENGINE | DBUG_ASSERT((in_strategy & ~(SUBS_MAXMIN_INJECTED | SUBS_MAXMIN_ENGINE |
SUBS_IN_TO_EXISTS)) == 0); SUBS_IN_TO_EXISTS | SUBS_STRATEGY_CHOSEN)) == 0);
in_strategy|= SUBS_IN_TO_EXISTS;
if (upper_item) if (upper_item)
upper_item->show= 1; upper_item->show= 1;
DBUG_RETURN(select_in_like_transformer(join)); DBUG_RETURN(select_in_like_transformer(join));
@@ -2617,7 +2616,7 @@ Item_allany_subselect::select_transformer(JOIN *join)
void Item_allany_subselect::print(String *str, enum_query_type query_type) void Item_allany_subselect::print(String *str, enum_query_type query_type)
{ {
if (in_strategy & SUBS_IN_TO_EXISTS) if (test_strategy(SUBS_IN_TO_EXISTS))
str->append(STRING_WITH_LEN("<exists>")); str->append(STRING_WITH_LEN("<exists>"));
else else
{ {

View File

@@ -353,17 +353,19 @@ TABLE_LIST * const NO_JOIN_NEST=(TABLE_LIST*)0x1;
based on user-set optimizer switches, semantic analysis and cost comparison. based on user-set optimizer switches, semantic analysis and cost comparison.
*/ */
#define SUBS_NOT_TRANSFORMED 0 /* No execution method was chosen for this IN. */ #define SUBS_NOT_TRANSFORMED 0 /* No execution method was chosen for this IN. */
#define SUBS_SEMI_JOIN 1 /* IN was converted to semi-join. */ /* The Final decision about the strategy is made. */
#define SUBS_IN_TO_EXISTS 2 /* IN was converted to correlated EXISTS. */ #define SUBS_STRATEGY_CHOSEN 1
#define SUBS_MATERIALIZATION 4 /* Execute IN via subquery materialization. */ #define SUBS_SEMI_JOIN 2 /* IN was converted to semi-join. */
#define SUBS_IN_TO_EXISTS 4 /* IN was converted to correlated EXISTS. */
#define SUBS_MATERIALIZATION 8 /* Execute IN via subquery materialization. */
/* Partial matching substrategies of MATERIALIZATION. */ /* Partial matching substrategies of MATERIALIZATION. */
#define SUBS_PARTIAL_MATCH_ROWID_MERGE 8 #define SUBS_PARTIAL_MATCH_ROWID_MERGE 16
#define SUBS_PARTIAL_MATCH_TABLE_SCAN 16 #define SUBS_PARTIAL_MATCH_TABLE_SCAN 32
/* ALL/ANY will be transformed with max/min optimization */ /* ALL/ANY will be transformed with max/min optimization */
/* The subquery has not aggregates, transform it into a MAX/MIN query. */ /* The subquery has not aggregates, transform it into a MAX/MIN query. */
#define SUBS_MAXMIN_INJECTED 32 #define SUBS_MAXMIN_INJECTED 64
/* The subquery has aggregates, use a special max/min subselect engine. */ /* The subquery has aggregates, use a special max/min subselect engine. */
#define SUBS_MAXMIN_ENGINE 64 #define SUBS_MAXMIN_ENGINE 128
/** /**
@@ -398,6 +400,8 @@ protected:
Item *expr; Item *expr;
bool was_null; bool was_null;
bool abort_on_null; bool abort_on_null;
/* A bitmap of possible execution strategies for an IN predicate. */
uchar in_strategy;
public: public:
Item_in_optimizer *optimizer; Item_in_optimizer *optimizer;
protected: protected:
@@ -443,10 +447,6 @@ public:
bool sjm_scan_allowed; bool sjm_scan_allowed;
double jtbm_read_time; double jtbm_read_time;
double jtbm_record_count; double jtbm_record_count;
/* A bitmap of possible execution strategies for an IN predicate. */
uchar in_strategy;
bool is_jtbm_merged; bool is_jtbm_merged;
/* /*
@@ -487,9 +487,8 @@ public:
Item_in_subselect(Item * left_expr, st_select_lex *select_lex); Item_in_subselect(Item * left_expr, st_select_lex *select_lex);
Item_in_subselect() Item_in_subselect()
:Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE), :Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE),
abort_on_null(0), optimizer(0), abort_on_null(0), in_strategy(SUBS_NOT_TRANSFORMED), optimizer(0),
pushed_cond_guards(NULL), func(NULL), emb_on_expr_nest(NULL), pushed_cond_guards(NULL), func(NULL), emb_on_expr_nest(NULL),
in_strategy(SUBS_NOT_TRANSFORMED),
is_jtbm_merged(FALSE), is_jtbm_merged(FALSE),
upper_item(0) upper_item(0)
{} {}
@@ -539,6 +538,64 @@ public:
emb_on_expr_nest= embedding; emb_on_expr_nest= embedding;
} }
bool test_strategy(uchar strategy)
{ return test(in_strategy & strategy); }
/**
Test that the IN strategy was chosen for execution. This is so
when the CHOSEN flag is ON, and there is no other strategy.
*/
bool test_set_strategy(uchar strategy)
{
DBUG_ASSERT(strategy == SUBS_SEMI_JOIN ||
strategy == SUBS_IN_TO_EXISTS ||
strategy == SUBS_MATERIALIZATION ||
strategy == SUBS_PARTIAL_MATCH_ROWID_MERGE ||
strategy == SUBS_PARTIAL_MATCH_TABLE_SCAN ||
strategy == SUBS_MAXMIN_INJECTED ||
strategy == SUBS_MAXMIN_ENGINE);
return ((in_strategy & SUBS_STRATEGY_CHOSEN) &&
(in_strategy & ~SUBS_STRATEGY_CHOSEN) == strategy);
}
bool is_set_strategy()
{ return test(in_strategy & SUBS_STRATEGY_CHOSEN); }
bool has_strategy()
{ return in_strategy != SUBS_NOT_TRANSFORMED; }
void add_strategy (uchar strategy)
{
DBUG_ASSERT(strategy != SUBS_NOT_TRANSFORMED);
DBUG_ASSERT(!(strategy & SUBS_STRATEGY_CHOSEN));
/*
TODO: PS re-execution breaks this condition, because
check_and_do_in_subquery_rewrites() is called for each reexecution
and re-adds the same strategies.
DBUG_ASSERT(!(in_strategy & SUBS_STRATEGY_CHOSEN));
*/
in_strategy|= strategy;
}
void reset_strategy(uchar strategy)
{
DBUG_ASSERT(strategy != SUBS_NOT_TRANSFORMED);
in_strategy= strategy;
}
void set_strategy(uchar strategy)
{
/* Check that only one strategy is set for execution. */
DBUG_ASSERT(strategy == SUBS_SEMI_JOIN ||
strategy == SUBS_IN_TO_EXISTS ||
strategy == SUBS_MATERIALIZATION ||
strategy == SUBS_PARTIAL_MATCH_ROWID_MERGE ||
strategy == SUBS_PARTIAL_MATCH_TABLE_SCAN ||
strategy == SUBS_MAXMIN_INJECTED ||
strategy == SUBS_MAXMIN_ENGINE);
in_strategy= (SUBS_STRATEGY_CHOSEN | strategy);
}
friend class Item_ref_null_helper; friend class Item_ref_null_helper;
friend class Item_is_not_null_test; friend class Item_is_not_null_test;
friend class Item_in_optimizer; friend class Item_in_optimizer;

View File

@@ -411,7 +411,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
in_subs->emb_on_expr_nest && // 5 in_subs->emb_on_expr_nest && // 5
select_lex->outer_select()->join && // 6 select_lex->outer_select()->join && // 6
parent_unit->first_select()->leaf_tables.elements && // 7 parent_unit->first_select()->leaf_tables.elements && // 7
!in_subs->in_strategy && // 8 !in_subs->has_strategy() && // 8
select_lex->outer_select()->leaf_tables.elements && // 9 select_lex->outer_select()->leaf_tables.elements && // 9
!((join->select_options | // 10 !((join->select_options | // 10
select_lex->outer_select()->join->select_options) // 10 select_lex->outer_select()->join->select_options) // 10
@@ -451,7 +451,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
{ {
if (is_materialization_applicable(thd, in_subs, select_lex)) if (is_materialization_applicable(thd, in_subs, select_lex))
{ {
in_subs->in_strategy|= SUBS_MATERIALIZATION; in_subs->add_strategy(SUBS_MATERIALIZATION);
/* /*
If the subquery is an AND-part of WHERE register for being processed If the subquery is an AND-part of WHERE register for being processed
@@ -479,17 +479,18 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
possible. possible.
*/ */
if (optimizer_flag(thd, OPTIMIZER_SWITCH_IN_TO_EXISTS) || if (optimizer_flag(thd, OPTIMIZER_SWITCH_IN_TO_EXISTS) ||
!in_subs->in_strategy) !in_subs->has_strategy())
{ in_subs->add_strategy(SUBS_IN_TO_EXISTS);
in_subs->in_strategy|= SUBS_IN_TO_EXISTS;
}
} }
/* Check if max/min optimization applicable */ /* Check if max/min optimization applicable */
if (allany_subs) if (allany_subs && !allany_subs->is_set_strategy())
allany_subs->in_strategy|= (allany_subs->is_maxmin_applicable(join) ? {
(SUBS_MAXMIN_INJECTED | SUBS_MAXMIN_ENGINE) : uchar strategy= (allany_subs->is_maxmin_applicable(join) ?
SUBS_IN_TO_EXISTS); (SUBS_MAXMIN_INJECTED | SUBS_MAXMIN_ENGINE) :
SUBS_IN_TO_EXISTS);
allany_subs->add_strategy(strategy);
}
/* /*
Transform each subquery predicate according to its overloaded Transform each subquery predicate according to its overloaded
@@ -932,16 +933,12 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
/* /*
Revert to the IN->EXISTS strategy in the rare case when the subquery could Revert to the IN->EXISTS strategy in the rare case when the subquery could
not be flattened. not be flattened.
TODO: This is a limitation done for simplicity. Such subqueries could also
be executed via materialization. In order to determine this, we should
re-run the test for materialization that was done in
check_and_do_in_subquery_rewrites.
*/ */
in_subq->in_strategy= SUBS_IN_TO_EXISTS; in_subq->reset_strategy(SUBS_IN_TO_EXISTS);
if (is_materialization_applicable(thd, in_subq, if (is_materialization_applicable(thd, in_subq,
in_subq->unit->first_select())) in_subq->unit->first_select()))
{ {
in_subq->in_strategy|= SUBS_MATERIALIZATION; in_subq->add_strategy(SUBS_MATERIALIZATION);
} }
in_subq= li++; in_subq= li++;
@@ -1260,7 +1257,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
/* 3. Remove the original subquery predicate from the WHERE/ON */ /* 3. Remove the original subquery predicate from the WHERE/ON */
// The subqueries were replaced for Item_int(1) earlier // The subqueries were replaced for Item_int(1) earlier
subq_pred->in_strategy= SUBS_SEMI_JOIN; // for subsequent executions subq_pred->reset_strategy(SUBS_SEMI_JOIN); // for subsequent executions
/*TODO: also reset the 'with_subselect' there. */ /*TODO: also reset the 'with_subselect' there. */
/* n. Adjust the parent_join->table_count counter */ /* n. Adjust the parent_join->table_count counter */
@@ -1434,7 +1431,7 @@ static bool convert_subq_to_jtbm(JOIN *parent_join,
double read_time; double read_time;
DBUG_ENTER("convert_subq_to_jtbm"); DBUG_ENTER("convert_subq_to_jtbm");
subq_pred->in_strategy &= ~SUBS_IN_TO_EXISTS; subq_pred->set_strategy(SUBS_MATERIALIZATION);
if (subq_pred->optimize(&rows, &read_time)) if (subq_pred->optimize(&rows, &read_time))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
@@ -4514,8 +4511,8 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
} }
else else
return false; return false;
/* A strategy must be chosen earlier. */
DBUG_ASSERT(in_subs->in_strategy); /* A strategy must be chosen earlier. */ DBUG_ASSERT(in_subs->has_strategy());
DBUG_ASSERT(in_to_exists_where || in_to_exists_having); DBUG_ASSERT(in_to_exists_where || in_to_exists_having);
DBUG_ASSERT(!in_to_exists_where || in_to_exists_where->fixed); DBUG_ASSERT(!in_to_exists_where || in_to_exists_where->fixed);
DBUG_ASSERT(!in_to_exists_having || in_to_exists_having->fixed); DBUG_ASSERT(!in_to_exists_having || in_to_exists_having->fixed);
@@ -4525,8 +4522,8 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
strategies are possible and allowed by the user (checked during the prepare strategies are possible and allowed by the user (checked during the prepare
phase. phase.
*/ */
if (in_subs->in_strategy & SUBS_MATERIALIZATION && if (in_subs->test_strategy(SUBS_MATERIALIZATION) &&
in_subs->in_strategy & SUBS_IN_TO_EXISTS) in_subs->test_strategy(SUBS_IN_TO_EXISTS))
{ {
JOIN *outer_join; JOIN *outer_join;
JOIN *inner_join= this; JOIN *inner_join= this;
@@ -4630,9 +4627,9 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
/* C.3 Compare the costs and choose the cheaper strategy. */ /* C.3 Compare the costs and choose the cheaper strategy. */
if (materialize_strategy_cost >= in_exists_strategy_cost) if (materialize_strategy_cost >= in_exists_strategy_cost)
in_subs->in_strategy&= ~SUBS_MATERIALIZATION; in_subs->set_strategy(SUBS_IN_TO_EXISTS);
else else
in_subs->in_strategy&= ~SUBS_IN_TO_EXISTS; in_subs->set_strategy(SUBS_MATERIALIZATION);
DBUG_PRINT("info", DBUG_PRINT("info",
("mat_strategy_cost: %.2f, mat_cost: %.2f, write_cost: %.2f, lookup_cost: %.2f", ("mat_strategy_cost: %.2f, mat_cost: %.2f, write_cost: %.2f, lookup_cost: %.2f",
@@ -4653,7 +4650,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
otherwise otherwise
use materialization. use materialization.
*/ */
if (in_subs->in_strategy & SUBS_MATERIALIZATION && if (in_subs->test_strategy(SUBS_MATERIALIZATION) &&
in_subs->setup_mat_engine()) in_subs->setup_mat_engine())
{ {
/* /*
@@ -4661,11 +4658,10 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
but it is not possible to execute it due to limitations in the but it is not possible to execute it due to limitations in the
implementation, fall back to IN-TO-EXISTS. implementation, fall back to IN-TO-EXISTS.
*/ */
in_subs->in_strategy&= ~SUBS_MATERIALIZATION; in_subs->set_strategy(SUBS_IN_TO_EXISTS);
in_subs->in_strategy|= SUBS_IN_TO_EXISTS;
} }
if (in_subs->in_strategy & SUBS_MATERIALIZATION) if (in_subs->test_strategy(SUBS_MATERIALIZATION))
{ {
/* Restore the original query plan used for materialization. */ /* Restore the original query plan used for materialization. */
if (reopt_result == REOPT_NEW_PLAN) if (reopt_result == REOPT_NEW_PLAN)
@@ -4690,7 +4686,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
*/ */
select_limit= in_subs->unit->select_limit_cnt; select_limit= in_subs->unit->select_limit_cnt;
} }
else if (in_subs->in_strategy & SUBS_IN_TO_EXISTS) else if (in_subs->test_strategy(SUBS_IN_TO_EXISTS))
{ {
if (reopt_result == REOPT_NONE && in_to_exists_where && if (reopt_result == REOPT_NONE && in_to_exists_where &&
const_tables != table_count) const_tables != table_count)
@@ -4771,7 +4767,7 @@ bool JOIN::choose_tableless_subquery_plan()
{ {
Item_in_subselect *in_subs; Item_in_subselect *in_subs;
in_subs= (Item_in_subselect*) subs_predicate; in_subs= (Item_in_subselect*) subs_predicate;
in_subs->in_strategy= SUBS_IN_TO_EXISTS; in_subs->set_strategy(SUBS_IN_TO_EXISTS);
if (in_subs->create_in_to_exists_cond(this) || if (in_subs->create_in_to_exists_cond(this) ||
in_subs->inject_in_to_exists_cond(this)) in_subs->inject_in_to_exists_cond(this))
return TRUE; return TRUE;

View File

@@ -6453,8 +6453,8 @@ find_field_in_tables(THD *thd, Item_ident *item,
{ {
Item *subs= sl->master_unit()->item; Item *subs= sl->master_unit()->item;
if (subs->type() == Item::SUBSELECT_ITEM && if (subs->type() == Item::SUBSELECT_ITEM &&
((Item_subselect*)subs)->substype() == Item_subselect::IN_SUBS && ((Item_subselect*)subs)->substype() == Item_subselect::IN_SUBS &&
((Item_in_subselect*)subs)->in_strategy & SUBS_SEMI_JOIN) ((Item_in_subselect*)subs)->test_strategy(SUBS_SEMI_JOIN))
{ {
continue; continue;
} }

View File

@@ -3676,8 +3676,8 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor)
{ {
Item *subs= sl->master_unit()->item; Item *subs= sl->master_unit()->item;
if (subs && subs->type() == Item::SUBSELECT_ITEM && if (subs && subs->type() == Item::SUBSELECT_ITEM &&
((Item_subselect*)subs)->substype() == Item_subselect::IN_SUBS && ((Item_subselect*)subs)->substype() == Item_subselect::IN_SUBS &&
((Item_in_subselect*)subs)->in_strategy & SUBS_SEMI_JOIN) ((Item_in_subselect*)subs)->test_strategy(SUBS_SEMI_JOIN))
{ {
continue; continue;
} }

View File

@@ -689,6 +689,10 @@ JOIN::prepare(Item ***rref_pointer_array,
aggregate functions with implicit grouping (there is no GROUP BY). aggregate functions with implicit grouping (there is no GROUP BY).
*/ */
if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && !group_list && if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && !group_list &&
!(select_lex->master_unit()->item &&
select_lex->master_unit()->item->is_in_predicate() &&
((Item_in_subselect*)select_lex->master_unit()->item)->
test_set_strategy(SUBS_MAXMIN_INJECTED)) &&
select_lex->full_group_by_flag == (NON_AGG_FIELD_USED | SUM_FUNC_USED)) select_lex->full_group_by_flag == (NON_AGG_FIELD_USED | SUM_FUNC_USED))
{ {
my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS, my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS,
@@ -813,7 +817,7 @@ inject_jtbm_conds(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where)
double rows; double rows;
double read_time; double read_time;
subq_pred->in_strategy &= ~SUBS_IN_TO_EXISTS; DBUG_ASSERT(subq_pred->test_set_strategy(SUBS_MATERIALIZATION));
subq_pred->optimize(&rows, &read_time); subq_pred->optimize(&rows, &read_time);
subq_pred->jtbm_read_time= read_time; subq_pred->jtbm_read_time= read_time;
@@ -3181,7 +3185,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
*/ */
bool skip_unprefixed_keyparts= bool skip_unprefixed_keyparts=
!(join->is_in_subquery() && !(join->is_in_subquery() &&
((Item_in_subselect*)join->unit->item)->in_strategy & SUBS_IN_TO_EXISTS); ((Item_in_subselect*)join->unit->item)->test_strategy(SUBS_IN_TO_EXISTS));
if (keyuse_array->elements && if (keyuse_array->elements &&
sort_and_filter_keyuse(join->thd, keyuse_array, sort_and_filter_keyuse(join->thd, keyuse_array,