Commit dbe898bb authored by Sergey Petrunia's avatar Sergey Petrunia

- Backport @@optimizer_switch support from 6.0

- Add support for setting it as a server commandline argument
- Add support for those switches:
  = no_index_merge
  = no_index_merge_union
  = no_index_merge_sort_union
  = no_index_merge_intersection

mysql-test/r/index_merge_myisam.result:
  Testcases for index_merge related @@optimizer_switch flags.
mysql-test/t/index_merge_myisam.test:
  Testcases for index_merge related @@optimizer_switch flags.
sql/set_var.cc:
  - Backport @@optimizer_switch support from 6.0
  - Add support for setting it as a server commandline argument
sql/sql_class.h:
  - Backport @@optimizer_switch support from 6.0
sql/sql_select.h:
  - Backport @@optimizer_switch support from 6.0
parent ef68d9a5
......@@ -1391,3 +1391,120 @@ WHERE
`RUNID`= '' AND `SUBMITNR`= '' AND `ORDERNR`='' AND `PROGRAMM`='' AND
`TESTID`='' AND `UCCHECK`='';
drop table t1;
#
# @@optimizer_switch support and check
#
select @@optimizer_switch;
@@optimizer_switch
create table t0 (a int);
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t1 (a int, b int, c int, filler char(100),
key(a), key(b), key(c));
insert into t1 select
A.a * B.a*10 + C.a*100,
A.a * B.a*10 + C.a*100,
A.a,
'filler'
from t0 A, t0 B, t0 C;
This should use union:
explain select * from t1 where a=1 or b=1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index_merge a,b a,b 5,5 NULL 2 Using union(a,b); Using where
This should use ALL:
set optimizer_switch='no_index_merge';
explain select * from t1 where a=1 or b=1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL a,b NULL NULL NULL 1000 Using where
This should use sort-union:
set optimizer_switch='no_index_merge_union';
explain select * from t1 where a=1 or b=1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index_merge a,b a,b 5,5 NULL 2 Using sort_union(a,b); Using where
This will use sort-union:
set optimizer_switch='';
explain select * from t1 where a<1 or b <1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index_merge a,b a,b 5,5 NULL 38 Using sort_union(a,b); Using where
This should use ALL:
set optimizer_switch='no_index_merge_sort_union';
explain select * from t1 where a<1 or b <1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL a,b NULL NULL NULL 1000 Using where
This should use ALL:
set optimizer_switch='no_index_merge';
explain select * from t1 where a<1 or b <1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL a,b NULL NULL NULL 1000 Using where
This will use sort-union:
set optimizer_switch='no_index_merge_union';
explain select * from t1 where a<1 or b <1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index_merge a,b a,b 5,5 NULL 38 Using sort_union(a,b); Using where
alter table t1 add d int, add key(d);
update t1 set d=a;
This will use sort_union:
set optimizer_switch='';
explain select * from t1 where (a=3 or b in (1,2)) and (c=3 or d=4);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index_merge a,b,c,d a,b 5,5 NULL 3 Using sort_union(a,b); Using where
And if we disable sort_union, union:
set optimizer_switch='no_index_merge_sort_union';
explain select * from t1 where (a=3 or b in (1,2)) and (c=3 or d=4);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index_merge a,b,c,d c,d 5,5 NULL 100 Using union(c,d); Using where
drop table t1;
create table t1 (
a int, b int, c int,
filler1 char(200), filler2 char(200),
key(a),key(b),key(c)
);
insert into t1
select A.a+10*B.a, A.a+10*B.a, A.a+10*B.a+100*C.a, 'foo', 'bar'
from t0 A, t0 B, t0 C, t0 D where D.a<5;
This should be intersect:
set optimizer_switch='';
explain select * from t1 where a=10 and b=10;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index_merge a,b a,b 5,5 NULL 1 Using intersect(a,b); Using where
No intersect when index_merge is disabled:
set optimizer_switch='no_index_merge';
explain select * from t1 where a=10 and b=10;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref a,b a 5 const 49 Using where
No intersect if it is disabled:
set optimizer_switch='no_index_merge_intersection';
explain select * from t1 where a=10 and b=10;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref a,b a 5 const 49 Using where
Do intersect when union was disabled
set optimizer_switch='no_index_merge_union';
explain select * from t1 where a=10 and b=10;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index_merge a,b a,b 5,5 NULL 1 Using intersect(a,b); Using where
Do intersect when sort_union was disabled
set optimizer_switch='no_index_merge_sort_union';
explain select * from t1 where a=10 and b=10;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index_merge a,b a,b 5,5 NULL 1 Using intersect(a,b); Using where
This will use intersection inside a union:
set optimizer_switch='';
explain select * from t1 where a=10 and b=10 or c=10;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index_merge a,b,c a,b,c 5,5,5 NULL 6 Using union(intersect(a,b),c); Using where
Should be only union left:
set optimizer_switch='no_index_merge_intersection';
explain select * from t1 where a=10 and b=10 or c=10;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index_merge a,b,c a,c 5,5 NULL 54 Using union(a,c); Using where
This will switch to sort-union (intersection will be gone, too,
thats a known limitation:
set optimizer_switch='no_index_merge_union';
explain select * from t1 where a=10 and b=10 or c=10;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index_merge a,b,c a,c 5,5 NULL 54 Using sort_union(a,c); Using where
set optimizer_switch=default;
show variables like 'optimizer_switch';
Variable_name Value
optimizer_switch
drop table t0, t1;
......@@ -19,3 +19,110 @@ let $merge_table_support= 1;
--source include/index_merge2.inc
--source include/index_merge_2sweeps.inc
--source include/index_merge_ror_cpk.inc
--echo #
--echo # @@optimizer_switch support and check
--echo #
select @@optimizer_switch;
create table t0 (a int);
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t1 (a int, b int, c int, filler char(100),
key(a), key(b), key(c));
insert into t1 select
A.a * B.a*10 + C.a*100,
A.a * B.a*10 + C.a*100,
A.a,
'filler'
from t0 A, t0 B, t0 C;
--echo This should use union:
explain select * from t1 where a=1 or b=1;
--echo This should use ALL:
set optimizer_switch='no_index_merge';
explain select * from t1 where a=1 or b=1;
--echo This should use sort-union:
set optimizer_switch='no_index_merge_union';
explain select * from t1 where a=1 or b=1;
--echo This will use sort-union:
set optimizer_switch='';
explain select * from t1 where a<1 or b <1;
--echo This should use ALL:
set optimizer_switch='no_index_merge_sort_union';
explain select * from t1 where a<1 or b <1;
--echo This should use ALL:
set optimizer_switch='no_index_merge';
explain select * from t1 where a<1 or b <1;
--echo This will use sort-union:
set optimizer_switch='no_index_merge_union';
explain select * from t1 where a<1 or b <1;
alter table t1 add d int, add key(d);
update t1 set d=a;
--echo This will use sort_union:
set optimizer_switch='';
explain select * from t1 where (a=3 or b in (1,2)) and (c=3 or d=4);
--echo And if we disable sort_union, union:
set optimizer_switch='no_index_merge_sort_union';
explain select * from t1 where (a=3 or b in (1,2)) and (c=3 or d=4);
drop table t1;
# Now test that intersection can be disabled
create table t1 (
a int, b int, c int,
filler1 char(200), filler2 char(200),
key(a),key(b),key(c)
);
insert into t1
select A.a+10*B.a, A.a+10*B.a, A.a+10*B.a+100*C.a, 'foo', 'bar'
from t0 A, t0 B, t0 C, t0 D where D.a<5;
--echo This should be intersect:
set optimizer_switch='';
explain select * from t1 where a=10 and b=10;
--echo No intersect when index_merge is disabled:
set optimizer_switch='no_index_merge';
explain select * from t1 where a=10 and b=10;
--echo No intersect if it is disabled:
set optimizer_switch='no_index_merge_intersection';
explain select * from t1 where a=10 and b=10;
--echo Do intersect when union was disabled
set optimizer_switch='no_index_merge_union';
explain select * from t1 where a=10 and b=10;
--echo Do intersect when sort_union was disabled
set optimizer_switch='no_index_merge_sort_union';
explain select * from t1 where a=10 and b=10;
# Now take union-of-intersection and see how we can disable parts of it
--echo This will use intersection inside a union:
set optimizer_switch='';
explain select * from t1 where a=10 and b=10 or c=10;
--echo Should be only union left:
set optimizer_switch='no_index_merge_intersection';
explain select * from t1 where a=10 and b=10 or c=10;
--echo This will switch to sort-union (intersection will be gone, too,
--echo thats a known limitation:
set optimizer_switch='no_index_merge_union';
explain select * from t1 where a=10 and b=10 or c=10;
set optimizer_switch=default;
show variables like 'optimizer_switch';
drop table t0, t1;
......@@ -523,6 +523,12 @@ class Default_object_creation_ctx : public Object_creation_ctx
#define MODE_NO_ENGINE_SUBSTITUTION (MODE_HIGH_NOT_PRECEDENCE*2)
#define MODE_PAD_CHAR_TO_FULL_LENGTH (ULL(1) << 31)
/* @@optimizer_switch flags */
#define OPTIMIZER_SWITCH_NO_INDEX_MERGE 1
#define OPTIMIZER_SWITCH_NO_INDEX_MERGE_UNION 2
#define OPTIMIZER_SWITCH_NO_INDEX_MERGE_SORT_UNION 4
#define OPTIMIZER_SWITCH_NO_INDEX_MERGE_INTERSECT 8
/*
Replication uses 8 bytes to store SQL_MODE in the binary log. The day you
use strictly more than 64 bits by adding one more define above, you should
......
......@@ -297,6 +297,24 @@ static const unsigned int sql_mode_names_len[]=
TYPELIB sql_mode_typelib= { array_elements(sql_mode_names)-1,"",
sql_mode_names,
(unsigned int *)sql_mode_names_len };
static const char *optimizer_switch_names[]=
{
"no_index_merge","no_index_merge_union","no_index_merge_sort_union",
"no_index_merge_intersection", NullS
};
/* Corresponding defines are named OPTIMIZER_SWITCH_XXX */
static const unsigned int optimizer_switch_names_len[]=
{
sizeof("no_index_merge") - 1,
sizeof("no_index_merge_union") - 1,
sizeof("ni_index_merge_sort_union") - 1,
sizeof("no_index_merge_intersection") - 1
};
TYPELIB optimizer_switch_typelib= { array_elements(optimizer_switch_names)-1,"",
optimizer_switch_names,
(unsigned int *)optimizer_switch_names_len };
static const char *tc_heuristic_recover_names[]=
{
"COMMIT", "ROLLBACK", NullS
......@@ -362,6 +380,7 @@ static ulong max_used_connections;
static ulong my_bind_addr; /**< the address we bind to */
static volatile ulong cached_thread_count= 0;
static const char *sql_mode_str= "OFF";
static const char *optimizer_switch_str="";
static char *mysqld_user, *mysqld_chroot, *log_error_file_ptr;
static char *opt_init_slave, *language_ptr, *opt_init_connect;
static char *default_character_set_name;
......@@ -5565,6 +5584,7 @@ enum options_mysqld
OPT_SYSDATE_IS_NOW,
OPT_OPTIMIZER_SEARCH_DEPTH,
OPT_OPTIMIZER_PRUNE_LEVEL,
OPT_OPTIMIZER_SWITCH,
OPT_UPDATABLE_VIEWS_WITH_LIMIT,
OPT_SP_AUTOMATIC_PRIVILEGES,
OPT_MAX_SP_RECURSION_DEPTH,
......@@ -6717,6 +6737,10 @@ The minimum value for this variable is 4096.",
(uchar**) &global_system_variables.optimizer_search_depth,
(uchar**) &max_system_variables.optimizer_search_depth,
0, GET_ULONG, OPT_ARG, MAX_TABLES+1, 0, MAX_TABLES+2, 0, 1, 0},
{"optimizer_switch", OPT_OPTIMIZER_SWITCH,
"optimizer_switch=option[,option[,option...]] where option can be one of: no_index_merge, no_index_merge_union, no_index_merge_sort_union, no_index_merge_intersection",
(uchar**) &optimizer_switch_str, (uchar**) &optimizer_switch_str, 0, GET_STR, REQUIRED_ARG, 0,
0, 0, 0, 0, 0},
{"plugin_dir", OPT_PLUGIN_DIR,
"Directory for plugins.",
(uchar**) &opt_plugin_dir_ptr, (uchar**) &opt_plugin_dir_ptr, 0,
......@@ -8206,6 +8230,13 @@ mysqld_get_one_option(int optid,
sql_mode);
break;
}
case OPT_OPTIMIZER_SWITCH:
{
optimizer_switch_str= argument;
global_system_variables.optimizer_switch=
find_bit_type_or_exit(argument, &optimizer_switch_typelib, opt->name);
break;
}
case OPT_ONE_THREAD:
global_system_variables.thread_handling=
SCHEDULER_ONE_THREAD_PER_CONNECTION;
......
......@@ -2386,7 +2386,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
objects are not allowed so don't use ROR-intersection for
table deletes.
*/
if ((thd->lex->sql_command != SQLCOM_DELETE))
if ((thd->lex->sql_command != SQLCOM_DELETE) &&
!optimizer_flag(thd, OPTIMIZER_SWITCH_NO_INDEX_MERGE))
{
/*
Get best non-covering ROR-intersection plan and prepare data for
......@@ -2410,25 +2411,28 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
}
else
{
/* Try creating index_merge/ROR-union scan. */
SEL_IMERGE *imerge;
TABLE_READ_PLAN *best_conj_trp= NULL, *new_conj_trp;
LINT_INIT(new_conj_trp); /* no empty index_merge lists possible */
DBUG_PRINT("info",("No range reads possible,"
" trying to construct index_merge"));
List_iterator_fast<SEL_IMERGE> it(tree->merges);
while ((imerge= it++))
if (!optimizer_flag(thd, OPTIMIZER_SWITCH_NO_INDEX_MERGE))
{
new_conj_trp= get_best_disjunct_quick(&param, imerge, best_read_time);
if (new_conj_trp)
set_if_smaller(param.table->quick_condition_rows,
new_conj_trp->records);
if (!best_conj_trp || (new_conj_trp && new_conj_trp->read_cost <
best_conj_trp->read_cost))
best_conj_trp= new_conj_trp;
/* Try creating index_merge/ROR-union scan. */
SEL_IMERGE *imerge;
TABLE_READ_PLAN *best_conj_trp= NULL, *new_conj_trp;
LINT_INIT(new_conj_trp); /* no empty index_merge lists possible */
DBUG_PRINT("info",("No range reads possible,"
" trying to construct index_merge"));
List_iterator_fast<SEL_IMERGE> it(tree->merges);
while ((imerge= it++))
{
new_conj_trp= get_best_disjunct_quick(&param, imerge, best_read_time);
if (new_conj_trp)
set_if_smaller(param.table->quick_condition_rows,
new_conj_trp->records);
if (!best_conj_trp || (new_conj_trp && new_conj_trp->read_cost <
best_conj_trp->read_cost))
best_conj_trp= new_conj_trp;
}
if (best_conj_trp)
best_trp= best_conj_trp;
}
if (best_conj_trp)
best_trp= best_conj_trp;
}
}
......@@ -3767,11 +3771,19 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
"full table scan, bailing out"));
DBUG_RETURN(NULL);
}
if (all_scans_rors)
/*
If all scans happen to be ROR, proceed to generate a ROR-union plan (it's
guaranteed to be cheaper than non-ROR union), unless ROR-unions are
disabled in @@optimizer_switch
*/
if (all_scans_rors &&
!optimizer_flag(param->thd, OPTIMIZER_SWITCH_NO_INDEX_MERGE_UNION))
{
roru_read_plans= (TABLE_READ_PLAN**)range_scans;
goto skip_to_ror_scan;
}
if (cpk_scan)
{
/*
......@@ -3785,8 +3797,11 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
imerge_cost += get_sweep_read_cost(param, non_cpk_scan_records);
DBUG_PRINT("info",("index_merge cost with rowid-to-row scan: %g",
imerge_cost));
if (imerge_cost > read_time)
if (imerge_cost > read_time ||
optimizer_flag(param->thd, OPTIMIZER_SWITCH_NO_INDEX_MERGE_SORT_UNION))
{
goto build_ror_index_merge;
}
/* Add Unique operations cost */
unique_calc_buff_size=
......@@ -3822,7 +3837,9 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
}
build_ror_index_merge:
if (!all_scans_ror_able || param->thd->lex->sql_command == SQLCOM_DELETE)
if (!all_scans_ror_able ||
param->thd->lex->sql_command == SQLCOM_DELETE ||
optimizer_flag(param->thd, OPTIMIZER_SWITCH_NO_INDEX_MERGE_UNION))
DBUG_RETURN(imerge_trp);
/* Ok, it is possible to build a ROR-union, try it. */
......@@ -4495,7 +4512,8 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
double min_cost= DBL_MAX;
DBUG_ENTER("get_best_ror_intersect");
if ((tree->n_ror_scans < 2) || !param->table->file->stats.records)
if ((tree->n_ror_scans < 2) || !param->table->file->stats.records ||
optimizer_flag(param->thd, OPTIMIZER_SWITCH_NO_INDEX_MERGE_INTERSECT))
DBUG_RETURN(NULL);
/*
......@@ -4685,6 +4703,9 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
ROR_SCAN_INFO **ror_scans_end= tree->ror_scans_end;
DBUG_ENTER("get_best_covering_ror_intersect");
if (optimizer_flag(param->thd, OPTIMIZER_SWITCH_NO_INDEX_MERGE_INTERSECT))
DBUG_RETURN(NULL);
for (ROR_SCAN_INFO **scan= tree->ror_scans; scan != ror_scans_end; ++scan)
(*scan)->key_components=
param->table->key_info[(*scan)->keynr].key_parts;
......
......@@ -468,6 +468,8 @@ static sys_var_thd_ulong sys_optimizer_prune_level(&vars, "optimizer_prun
&SV::optimizer_prune_level);
static sys_var_thd_ulong sys_optimizer_search_depth(&vars, "optimizer_search_depth",
&SV::optimizer_search_depth);
static sys_var_thd_optimizer_switch sys_optimizer_switch(&vars, "optimizer_switch",
&SV::optimizer_switch);
static sys_var_const sys_pid_file(&vars, "pid_file",
OPT_GLOBAL, SHOW_CHAR,
(uchar*) pidfile_name);
......@@ -3837,6 +3839,56 @@ ulong fix_sql_mode(ulong sql_mode)
}
bool
sys_var_thd_optimizer_switch::
symbolic_mode_representation(THD *thd, ulonglong val, LEX_STRING *rep)
{
char buff[STRING_BUFFER_USUAL_SIZE*8];
String tmp(buff, sizeof(buff), &my_charset_latin1);
tmp.length(0);
for (uint i= 0; val; val>>= 1, i++)
{
if (val & 1)
{
tmp.append(optimizer_switch_typelib.type_names[i],
optimizer_switch_typelib.type_lengths[i]);
tmp.append(',');
}
}
if (tmp.length())
tmp.length(tmp.length() - 1); /* trim the trailing comma */
rep->str= thd->strmake(tmp.ptr(), tmp.length());
rep->length= rep->str ? tmp.length() : 0;
return rep->length != tmp.length();
}
uchar *sys_var_thd_optimizer_switch::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
LEX_STRING opts;
ulonglong val= ((type == OPT_GLOBAL) ? global_system_variables.*offset :
thd->variables.*offset);
(void) symbolic_mode_representation(thd, val, &opts);
return (uchar *) opts.str;
}
void sys_var_thd_optimizer_switch::set_default(THD *thd, enum_var_type type)
{
if (type == OPT_GLOBAL)
global_system_variables.*offset= 0;
else
thd->variables.*offset= global_system_variables.*offset;
}
/****************************************************************************
Named list handling
****************************************************************************/
......
......@@ -31,7 +31,7 @@ typedef struct system_variables SV;
typedef struct my_locale_st MY_LOCALE;
extern TYPELIB bool_typelib, delay_key_write_typelib, sql_mode_typelib,
slave_exec_mode_typelib;
optimizer_switch_typelib, slave_exec_mode_typelib;
typedef int (*sys_check_func)(THD *, set_var *);
typedef bool (*sys_update_func)(THD *, set_var *);
......@@ -532,6 +532,23 @@ class sys_var_thd_enum :public sys_var_thd
};
class sys_var_thd_optimizer_switch :public sys_var_thd_enum
{
public:
sys_var_thd_optimizer_switch(sys_var_chain *chain, const char *name_arg,
ulong SV::*offset_arg)
:sys_var_thd_enum(chain, name_arg, offset_arg, &optimizer_switch_typelib)
{}
bool check(THD *thd, set_var *var)
{
return check_set(thd, var, enum_names);
}
void set_default(THD *thd, enum_var_type type);
uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
static bool symbolic_mode_representation(THD *thd, ulonglong sql_mode,
LEX_STRING *rep);
};
extern void fix_sql_mode_var(THD *thd, enum_var_type type);
class sys_var_thd_sql_mode :public sys_var_thd_enum
......
......@@ -321,6 +321,8 @@ struct system_variables
ulong net_write_timeout;
ulong optimizer_prune_level;
ulong optimizer_search_depth;
/* A bitmap for switching optimizations on/off */
ulong optimizer_switch;
ulong preload_buff_size;
ulong profiling_history_size;
ulong query_cache_type;
......
......@@ -729,3 +729,9 @@ bool error_if_full_join(JOIN *join);
int report_error(TABLE *table, int error);
int safe_index_read(JOIN_TAB *tab);
COND *remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value);
inline bool optimizer_flag(THD *thd, uint flag)
{
return (thd->variables.optimizer_switch & flag);
}
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