Skip to content

Commit 66b578a

Browse files
committed
Bug#29897573: A FULL TABLE AGGREGATE QUERY IN DERIVED TABLE DOES NOT OFFLOAD
Bug#29994087: SIG11 AT MATERIALIZEITERATOR::INIT() IN SQL/COMPOSITE_ITERATORS.CC Some queries that contained subqueries were not offloaded to a secondary storage engine. The optimizer tried to evaluate the subqueries during optimization, and this failed because the secondary engine could not return results until execution. This is fixed by preventing evaluation of subqueries during optimization when using a secondary storage engine. Add a new query option that secondary storage engines can set to prevent subqueries from being evaluated during optimization. The option is checked before evaluating subqueries in the optimizer. Setting of query options for secondary storage engines is removed from open_secondary_engine_tables(). Instead, the engines can set the options they need in their implementation of handlerton::prepare_secondary_engine(). Item_bool_func2::update_used_tables() didn't properly accumulate properties from the ESCAPE clause of a LIKE expression, so that it was not detected that a LIKE expression contained a subquery in some cases. A specialization of the function is added in Item_func_like to make sure properties of the ESCAPE clause are accumulated in the LIKE expression. The test secondary_engine.no_constant_tables is removed, as it no longer exercises the code that it was supposed to test. The code is covered by tests in other test suites. Change-Id: I945df19452cbca0d5f5621f0bb68351009c9e79c
1 parent e6e5b5b commit 66b578a

15 files changed

+136
-70
lines changed

mysql-test/suite/secondary_engine/r/no_constant_tables.result

-19
This file was deleted.

mysql-test/suite/secondary_engine/r/query_preparation.result

+10
Original file line numberDiff line numberDiff line change
@@ -1127,3 +1127,13 @@ id select_type table partitions type possible_keys key key_len ref rows filtered
11271127
Warnings:
11281128
Note 1003 /* select#1 */ select `tt1`.`x` AS `x`,`tt1`.`y` AS `y`,`tt1`.`z` AS `z`,`tt1`.`c` AS `c`,`tt1`.`d` AS `d`,`tt2`.`x` AS `x`,`tt2`.`y` AS `y`,`tt2`.`z` AS `z`,`tt2`.`c` AS `c`,`tt2`.`d` AS `d` from (/* select#2 */ select `test`.`t1`.`x` AS `x`,`test`.`t1`.`y` AS `y`,`test`.`t1`.`z` AS `z`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`d` AS `d` from `test`.`t1` where (`test`.`t1`.`x` < 7) limit 20) `tt1` join (/* select#3 */ select `test`.`t1`.`x` AS `x`,`test`.`t1`.`y` AS `y`,`test`.`t1`.`z` AS `z`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`d` AS `d` from `test`.`t1` where (`test`.`t1`.`x` < `test`.`t1`.`y`) limit 20) `tt2` where ((`tt2`.`x` = `tt1`.`x`) and (`tt1`.`x` > 5))
11291129
DROP TABLE t1;
1130+
#
1131+
# Bug#29994087: SIG11 AT MATERIALIZEITERATOR::INIT() IN
1132+
# SQL/COMPOSITE_ITERATORS.CC
1133+
#
1134+
CREATE TABLE t (a INT, b INT) SECONDARY_ENGINE MOCK;
1135+
ALTER TABLE t SECONDARY_LOAD;
1136+
SELECT 1 FROM t
1137+
WHERE (1, 2) IN (SELECT 3, 4 UNION SELECT 5, 6) OR b <= 10;
1138+
1
1139+
DROP TABLE t;

mysql-test/suite/secondary_engine/t/no_constant_tables.test

-34
This file was deleted.

mysql-test/suite/secondary_engine/t/query_preparation.test

+10
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,16 @@ WHERE tt1.x> 5 and tt1.x=tt2.x;
739739

740740
DROP TABLE t1;
741741

742+
--echo #
743+
--echo # Bug#29994087: SIG11 AT MATERIALIZEITERATOR::INIT() IN
744+
--echo # SQL/COMPOSITE_ITERATORS.CC
745+
--echo #
746+
CREATE TABLE t (a INT, b INT) SECONDARY_ENGINE MOCK;
747+
ALTER TABLE t SECONDARY_LOAD;
748+
SELECT 1 FROM t
749+
WHERE (1, 2) IN (SELECT 3, 4 UNION SELECT 5, 6) OR b <= 10;
750+
DROP TABLE t;
751+
742752
--disable_query_log
743753
UNINSTALL PLUGIN mock;
744754
--enable_query_log

sql/item_cmpfunc.cc

+16-1
Original file line numberDiff line numberDiff line change
@@ -5097,7 +5097,7 @@ bool Item_cond::fix_fields(THD *thd, Item **ref) {
50975097
Do this optimization if fix_fields is allowed to change the condition
50985098
and if this is the first execution.
50995099
Check if the const item does not contain param's, SP args etc. We also
5100-
cannot optimize conditions if its a view. The condition has to be a
5100+
cannot optimize conditions if it's a view. The condition has to be a
51015101
top_level_item to get optimized as they can have only two return values,
51025102
true or false. A non-top_level_item can have true, false and NULL return.
51035103
Fulltext funcs cannot be removed as ftfunc_list stores the list
@@ -5821,8 +5821,16 @@ longlong Item_func_like::val_int() {
58215821
*/
58225822

58235823
Item_func::optimize_type Item_func_like::select_optimize(const THD *thd) {
5824+
/*
5825+
Can be called both during preparation (from prune_partitions()) and
5826+
optimization. Check if the pattern can be evaluated in the current phase.
5827+
*/
58245828
if (!args[1]->may_evaluate_const(thd)) return OPTIMIZE_NONE;
58255829

5830+
// Don't evaluate the pattern if evaluation during optimization is disabled.
5831+
if (!evaluate_during_optimization(args[1], thd->lex->current_select()))
5832+
return OPTIMIZE_NONE;
5833+
58265834
String *res2 = args[1]->val_str(&cmp.value2);
58275835
if (!res2) return OPTIMIZE_NONE;
58285836

@@ -5961,6 +5969,13 @@ bool Item_func_like::eval_escape_clause(THD *thd) {
59615969
return false;
59625970
}
59635971

5972+
void Item_func_like::update_used_tables() {
5973+
Item_bool_func2::update_used_tables();
5974+
escape_item->update_used_tables();
5975+
used_tables_cache |= escape_item->used_tables();
5976+
add_accum_properties(escape_item);
5977+
}
5978+
59645979
bool Item_func_xor::itemize(Parse_context *pc, Item **res) {
59655980
if (skip_itemize(res)) return false;
59665981
if (super::itemize(pc, res)) return true;

sql/item_cmpfunc.h

+2
Original file line numberDiff line numberDiff line change
@@ -2193,6 +2193,8 @@ class Item_func_like final : public Item_bool_func2 {
21932193
const MY_BITMAP *fields_to_ignore,
21942194
double rows_in_table) override;
21952195

2196+
void update_used_tables() override;
2197+
21962198
private:
21972199
/**
21982200
The method updates covering keys depending on the

sql/opt_range.cc

+11
Original file line numberDiff line numberDiff line change
@@ -7244,6 +7244,17 @@ static bool save_value_and_handle_conversion(SEL_ROOT **tree, Item *value,
72447244
return true;
72457245
}
72467246

7247+
/*
7248+
Don't evaluate subqueries during optimization if they are disabled. This
7249+
function can be called during execution when doing dynamic range access, and
7250+
we only want to disable subquery evaluation during optimization, so check if
7251+
we're in the optimization phase by calling SELECT_LEX_UNIT::is_optimized().
7252+
*/
7253+
const SELECT_LEX *const select = thd->lex->current_select();
7254+
if (!select->master_unit()->is_optimized() &&
7255+
!evaluate_during_optimization(value, select))
7256+
return true;
7257+
72477258
// For comparison purposes allow invalid dates like 2000-01-32
72487259
const sql_mode_t orig_sql_mode = thd->variables.sql_mode;
72497260
thd->variables.sql_mode |= MODE_INVALID_DATES;

sql/query_options.h

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License, version 2.0,
@@ -104,7 +104,7 @@
104104
Is set in slave SQL thread when there was an
105105
error on master, which, when is not reproducible
106106
on slave (i.e. the query succeeds on slave),
107-
is not terminal to the state of repliation,
107+
is not terminal to the state of replication,
108108
and should be ignored. The slave SQL thread,
109109
however, needs to rollback the effects of the
110110
succeeded statement to keep replication consistent.
@@ -113,7 +113,7 @@
113113

114114
/*
115115
Dont report errors for individual rows,
116-
But just report error on commit (or read ofcourse)
116+
But just report error on commit (or read, of course)
117117
Note! Reserved for use in MySQL Cluster
118118
*/
119119
#define OPTION_ALLOW_BATCH (1ULL << 36) // THD, intern (slave)
@@ -122,4 +122,11 @@
122122

123123
// Is set while thread is updating the data dictionary tables.
124124
#define OPTION_DD_UPDATE_CONTEXT (1ULL << 38) // intern
125-
#endif /* QUERY_OPTIONS_INCLUDED */
125+
126+
/**
127+
If this option is set, subqueries should not be evaluated during
128+
optimization, even if they are known to produce a constant result.
129+
*/
130+
#define OPTION_NO_SUBQUERY_DURING_OPTIMIZATION (1ULL << 39) // intern
131+
132+
#endif /* QUERY_OPTIONS_INCLUDED */

sql/sql_base.cc

-1
Original file line numberDiff line numberDiff line change
@@ -6438,7 +6438,6 @@ static bool open_secondary_engine_tables(THD *thd, uint flags) {
64386438

64396439
auto hton = plugin_data<const handlerton *>(secondary_engine_plugin);
64406440
sql_cmd->use_secondary_storage_engine(hton);
6441-
lex->add_statement_options(OPTION_NO_CONST_TABLES);
64426441

64436442
// Replace the TABLE objects in the TABLE_LIST with secondary tables.
64446443
Open_table_context ot_ctx(thd, flags | MYSQL_OPEN_SECONDARY_ENGINE);

sql/sql_lex.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -3455,7 +3455,13 @@ struct LEX : public Query_tables_list {
34553455
ulonglong m_statement_options{0};
34563456

34573457
public:
3458-
/// @return a bit set of options set for this statement
3458+
/**
3459+
Gets the options that have been set for this statement. The options are
3460+
propagated to the SELECT_LEX objects and should usually be read with
3461+
#SELECT_LEX::active_options().
3462+
3463+
@return a bit set of options set for this statement
3464+
*/
34593465
ulonglong statement_options() { return m_statement_options; }
34603466
/**
34613467
Add options to values of m_statement_options. options is an ORed

sql/sql_optimizer.cc

+42-7
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
#include "sql/enum_query_type.h"
6262
#include "sql/error_handler.h" // Functional_index_error_handler
6363
#include "sql/handler.h"
64+
#include "sql/item.h"
6465
#include "sql/item_cmpfunc.h"
6566
#include "sql/item_func.h"
6667
#include "sql/item_row.h"
@@ -3556,6 +3557,11 @@ static bool check_simple_equality(THD *thd, Item *left_item, Item *right_item,
35563557
const_item = left_item;
35573558
}
35583559

3560+
// Don't evaluate subqueries if they are disabled during optimization.
3561+
if (const_item != nullptr &&
3562+
!evaluate_during_optimization(const_item, thd->lex->current_select()))
3563+
return false;
3564+
35593565
/*
35603566
If the constant expression contains a reference to the field
35613567
(for example, a = (a IS NULL)), we don't want to replace the
@@ -3650,7 +3656,7 @@ static bool check_row_equality(THD *thd, Item *left_row, Item_row *right_row,
36503656
return true;
36513657
if (!is_converted) thd->lex->current_select()->cond_count++;
36523658
} else {
3653-
if (check_simple_equality(thd, left_item, right_item, 0, cond_equal,
3659+
if (check_simple_equality(thd, left_item, right_item, nullptr, cond_equal,
36543660
&is_converted))
36553661
return true;
36563662
thd->lex->current_select()->cond_count++;
@@ -9108,7 +9114,8 @@ static bool make_join_select(JOIN *join, Item *cond) {
91089114
}
91099115
DBUG_EXECUTE("where",
91109116
print_where(thd, const_cond, "constants", QT_ORDINARY););
9111-
if (const_cond != NULL) {
9117+
if (const_cond != nullptr &&
9118+
evaluate_during_optimization(const_cond, join->select_lex)) {
91129119
const bool const_cond_result = const_cond->val_int() != 0;
91139120
if (thd->is_error()) return true;
91149121

@@ -9695,7 +9702,8 @@ ORDER *JOIN::remove_const(ORDER *first_order, Item *cond, bool change_list,
96959702
(primary_tables > 1 && rollup.state == ROLLUP::STATE_INITED &&
96969703
select_lex->outer_join))
96979704
*simple_order = 0; // Must do a temp table to sort
9698-
else if (!(order_tables & not_const_tables)) {
9705+
else if ((order_tables & not_const_tables) == 0 &&
9706+
evaluate_during_optimization(order->item[0], select_lex)) {
96999707
if (order->item[0]->has_subquery()) {
97009708
if (!thd->lex->is_explain()) {
97019709
Opt_trace_array trace_subselect(trace, "subselect_evaluation");
@@ -9863,15 +9871,26 @@ bool optimize_cond(THD *thd, Item **cond, COND_EQUAL **cond_equal,
98639871
return false;
98649872
}
98659873

9874+
/**
9875+
Checks if a condition can be evaluated during constant folding. It can be
9876+
evaluated if it is constant during execution and not expensive to evaluate. If
9877+
it contains a subquery, it should not be evaluated if the option
9878+
OPTION_NO_SUBQUERY_DURING_OPTIMIZATION is active.
9879+
*/
9880+
static bool can_evaluate_condition(THD *thd, Item *condition) {
9881+
return condition->const_for_execution() && !condition->is_expensive() &&
9882+
evaluate_during_optimization(condition, thd->lex->current_select());
9883+
}
9884+
98669885
/**
98679886
Calls fold_condition. If that made the condition constant for execution,
98689887
simplify and fold again. @see fold_condition() for arguments.
98699888
*/
98709889
static bool fold_condition_exec(THD *thd, Item *cond, Item **retcond,
98719890
Item::cond_result *cond_value) {
98729891
if (fold_condition(thd, cond, retcond, cond_value)) return true;
9873-
if (*retcond != nullptr && (*retcond)->const_for_execution() &&
9874-
!(*retcond)->is_expensive()) // simplify further maybe
9892+
if (*retcond != nullptr &&
9893+
can_evaluate_condition(thd, *retcond)) // simplify further maybe
98759894
return remove_eq_conds(thd, *retcond, retcond, cond_value);
98769895
return false;
98779896
}
@@ -9974,13 +9993,13 @@ bool remove_eq_conds(THD *thd, Item *cond, Item **retcond,
99749993
*retcond = item;
99759994
return false;
99769995
}
9977-
} else if (cond->const_for_execution() && !cond->is_expensive()) {
9996+
} else if (can_evaluate_condition(thd, cond)) {
99789997
bool value;
99799998
if (eval_const_cond(thd, cond, &value)) return true;
99809999
*cond_value = value ? Item::COND_TRUE : Item::COND_FALSE;
998110000
*retcond = NULL;
998210001
return false;
9983-
} else { // boolan compare function
10002+
} else { // Boolean compare function
998410003
*cond_value = cond->eq_cmp_result();
998510004
if (*cond_value == Item::COND_OK) {
998610005
return fold_condition_exec(thd, cond, retcond, cond_value);
@@ -10877,3 +10896,19 @@ static uint32 get_key_length_tmp_table(Item *item) {
1087710896

1087810897
return len;
1087910898
}
10899+
10900+
bool evaluate_during_optimization(const Item *item, const SELECT_LEX *select) {
10901+
/*
10902+
Should only be called on items that are const_for_execution(), as those
10903+
items are the only ones that are allowed to be evaluated during optimization
10904+
in the first place.
10905+
10906+
Additionally, allow items that only access tables in JOIN::const_table_map.
10907+
This should not be necessary, but the const_for_execution() property is not
10908+
always updated correctly by update_used_tables() for certain subqueries.
10909+
*/
10910+
DBUG_ASSERT(item->const_for_execution() ||
10911+
(item->used_tables() & ~select->join->const_table_map) == 0);
10912+
return !item->has_subquery() || (select->active_options() &
10913+
OPTION_NO_SUBQUERY_DURING_OPTIMIZATION) == 0;
10914+
}

sql/sql_optimizer.h

+12
Original file line numberDiff line numberDiff line change
@@ -1316,4 +1316,16 @@ class Candidate_table_order {
13161316

13171317
extern const char *antijoin_null_cond;
13181318

1319+
/**
1320+
Checks if an Item, which is constant for execution, can be evaluated during
1321+
optimization. It cannot be evaluated if it contains a subquery and the
1322+
OPTION_NO_SUBQUERY_DURING_OPTIMIZATION query option is active.
1323+
1324+
@param item the Item to check
1325+
@param select the query block that contains the Item
1326+
@return false if this Item contains a subquery and subqueries cannot be
1327+
evaluated during optimization, or true otherwise
1328+
*/
1329+
bool evaluate_during_optimization(const Item *item, const SELECT_LEX *select);
1330+
13191331
#endif /* SQL_OPTIMIZER_INCLUDED */

sql/table.cc

+4-2
Original file line numberDiff line numberDiff line change
@@ -6469,10 +6469,12 @@ bool TABLE_LIST::is_mergeable() const {
64696469
derived->is_mergeable();
64706470
}
64716471

6472-
/// @returns true if materializable table contains one or zero rows
64736472
bool TABLE_LIST::materializable_is_const() const {
64746473
DBUG_ASSERT(uses_materialization());
6475-
return derived_unit()->query_result()->estimated_rowcount <= 1;
6474+
const SELECT_LEX_UNIT *unit = derived_unit();
6475+
return unit->query_result()->estimated_rowcount <= 1 &&
6476+
(unit->first_select()->active_options() &
6477+
OPTION_NO_SUBQUERY_DURING_OPTIMIZATION) == 0;
64766478
}
64776479

64786480
/**

sql/table.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -2725,7 +2725,11 @@ struct TABLE_LIST {
27252725
bool is_mergeable() const;
27262726

27272727
/**
2728-
@returns true if materializable table contains one or zero rows.
2728+
Checks if this is a table that contains zero rows or one row, and that can
2729+
be materialized during optimization.
2730+
2731+
@returns true if materializable table contains one or zero rows, and
2732+
materialization during optimization is permitted
27292733

27302734
Returning true implies that the table is materialized during optimization,
27312735
so it need not be optimized during execution.

storage/secondary_engine_mock/ha_mock.cc

+6
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,12 @@ static bool PrepareSecondaryEngine(THD *thd, LEX *lex) {
235235
auto context = new (thd->mem_root) Mock_execution_context;
236236
if (context == nullptr) return true;
237237
lex->set_secondary_engine_execution_context(context);
238+
239+
// Disable use of constant tables and evaluation of subqueries during
240+
// optimization.
241+
lex->add_statement_options(OPTION_NO_CONST_TABLES |
242+
OPTION_NO_SUBQUERY_DURING_OPTIMIZATION);
243+
238244
return false;
239245
}
240246

0 commit comments

Comments
 (0)