Commit 9482e387 authored by Sergey Petrunya's avatar Sergey Petrunya

Change optimizer_use_mrr=auto|disable|force

to be optimizer_switch flags mrr=on|off and mrr_cost_based=on|off.
parent c44c1bd1
...@@ -665,8 +665,8 @@ set join_buffer_size=@my_save_join_buffer_size; ...@@ -665,8 +665,8 @@ set join_buffer_size=@my_save_join_buffer_size;
# #
create table t1 (pk int primary key, b int, c int default 0, index idx(b)) engine=innodb; create table t1 (pk int primary key, b int, c int default 0, index idx(b)) engine=innodb;
insert into t1(pk,b) values (3, 30), (2, 20), (9, 90), (7, 70), (4, 40), (5, 50), (10, 100), (12, 120); insert into t1(pk,b) values (3, 30), (2, 20), (9, 90), (7, 70), (4, 40), (5, 50), (10, 100), (12, 120);
set @my_save_optimizer_use_mrr=@@optimizer_use_mrr; set @bug665669_tmp=@@optimizer_switch;
set optimizer_use_mrr='disable'; set optimizer_switch='mrr=off';
explain select * from t1 where b > 1000; explain select * from t1 where b > 1000;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range idx idx 5 NULL 1 Using index condition 1 SIMPLE t1 range idx idx 5 NULL 1 Using index condition
...@@ -680,7 +680,7 @@ pk b c ...@@ -680,7 +680,7 @@ pk b c
3 30 0 3 30 0
4 40 0 4 40 0
drop table t1; drop table t1;
set optimizer_use_mrr = @my_save_optimizer_use_mrr; set optimizer_switch = @bug665669_tmp;
# #
# Bug#43360 - Server crash with a simple multi-table update # Bug#43360 - Server crash with a simple multi-table update
# #
......
...@@ -370,7 +370,6 @@ DROP TABLE t1,t2,t3; ...@@ -370,7 +370,6 @@ DROP TABLE t1,t2,t3;
# BUG#671361: virtual int Mrr_ordered_index_reader::refill_buffer(): Assertion `!know_key_tuple_params # BUG#671361: virtual int Mrr_ordered_index_reader::refill_buffer(): Assertion `!know_key_tuple_params
# (works only on Maria because we need 1024-byte long key) # (works only on Maria because we need 1024-byte long key)
# #
SET SESSION optimizer_use_mrr = 'force';
SET SESSION join_cache_level = 6; SET SESSION join_cache_level = 6;
SET SESSION join_buffer_size = 1024; SET SESSION join_buffer_size = 1024;
CREATE TABLE t1 ( CREATE TABLE t1 (
......
This diff is collapsed.
...@@ -259,17 +259,14 @@ KEY `varchar_key` (`varchar_key`) ...@@ -259,17 +259,14 @@ KEY `varchar_key` (`varchar_key`)
) AUTO_INCREMENT=12 DEFAULT CHARSET=latin1; ) AUTO_INCREMENT=12 DEFAULT CHARSET=latin1;
INSERT INTO `t1` VALUES (10,'00:00:00','i','i'),(11,'00:00:00','',''); INSERT INTO `t1` VALUES (10,'00:00:00','i','i'),(11,'00:00:00','','');
set @old_optimizer_switch = @@session.optimizer_switch, set @old_optimizer_switch = @@session.optimizer_switch,
@old_optimizer_use_mrr = @@session.optimizer_use_mrr,
@old_engine_condition_pushdown = @@session.engine_condition_pushdown; @old_engine_condition_pushdown = @@session.engine_condition_pushdown;
SET SESSION OPTIMIZER_SWITCH = 'materialization=off,semijoin=off,loosescan=off,firstmatch=off'; SET SESSION OPTIMIZER_SWITCH = 'materialization=off,semijoin=off,loosescan=off,firstmatch=off,mrr=on';
SET SESSION optimizer_use_mrr = 'force';
SET SESSION engine_condition_pushdown = 1; SET SESSION engine_condition_pushdown = 1;
SELECT `time_nokey` G1 FROM t1 WHERE ( `varchar_nokey` , `varchar_key` ) IN ( SELECT `time_nokey` G1 FROM t1 WHERE ( `varchar_nokey` , `varchar_key` ) IN (
SELECT `varchar_nokey` , `varchar_nokey` ) AND `varchar_key` >= 'c' HAVING G1 ORDER SELECT `varchar_nokey` , `varchar_nokey` ) AND `varchar_key` >= 'c' HAVING G1 ORDER
BY `pk` ; BY `pk` ;
G1 G1
set @@session.optimizer_switch = @old_optimizer_switch, set @@session.optimizer_switch = @old_optimizer_switch,
@@session.optimizer_use_mrr = @old_optimizer_use_mrr,
@@session.engine_condition_pushdown = @old_engine_condition_pushdown; @@session.engine_condition_pushdown = @old_engine_condition_pushdown;
DROP TABLE t1; DROP TABLE t1;
# #
......
set optimizer_use_mrr='disable'; set @bug41996_tmp=@@optimizer_switch;
set optimizer_switch='mrr=off';
DROP DATABASE IF EXISTS d1; DROP DATABASE IF EXISTS d1;
DROP DATABASE IF EXISTS d2; DROP DATABASE IF EXISTS d2;
DROP DATABASE IF EXISTS d3; DROP DATABASE IF EXISTS d3;
...@@ -1106,3 +1107,4 @@ c1 c2 ...@@ -1106,3 +1107,4 @@ c1 c2
DROP DATABASE d1; DROP DATABASE d1;
DROP DATABASE d2; DROP DATABASE d2;
DROP DATABASE d3; DROP DATABASE d3;
set optimizer_switch=@bug41996_tmp;
set optimizer_use_mrr='disable'; set @bug41996_tmp=@@optimizer_switch;
set optimizer_switch='mrr=off';
--disable_warnings --disable_warnings
DROP DATABASE IF EXISTS d1; DROP DATABASE IF EXISTS d1;
DROP DATABASE IF EXISTS d2; DROP DATABASE IF EXISTS d2;
...@@ -484,4 +485,4 @@ SELECT * FROM d3.t3 ORDER BY c1; ...@@ -484,4 +485,4 @@ SELECT * FROM d3.t3 ORDER BY c1;
DROP DATABASE d1; DROP DATABASE d1;
DROP DATABASE d2; DROP DATABASE d2;
DROP DATABASE d3; DROP DATABASE d3;
set optimizer_switch=@bug41996_tmp;
...@@ -370,14 +370,14 @@ set join_buffer_size=@my_save_join_buffer_size; ...@@ -370,14 +370,14 @@ set join_buffer_size=@my_save_join_buffer_size;
--echo # --echo #
create table t1 (pk int primary key, b int, c int default 0, index idx(b)) engine=innodb; create table t1 (pk int primary key, b int, c int default 0, index idx(b)) engine=innodb;
insert into t1(pk,b) values (3, 30), (2, 20), (9, 90), (7, 70), (4, 40), (5, 50), (10, 100), (12, 120); insert into t1(pk,b) values (3, 30), (2, 20), (9, 90), (7, 70), (4, 40), (5, 50), (10, 100), (12, 120);
set @my_save_optimizer_use_mrr=@@optimizer_use_mrr; set @bug665669_tmp=@@optimizer_switch;
set optimizer_use_mrr='disable'; set optimizer_switch='mrr=off';
explain select * from t1 where b > 1000; explain select * from t1 where b > 1000;
--echo # The following two must produce indentical results: --echo # The following two must produce indentical results:
select * from t1 where pk < 2 or pk between 3 and 4; select * from t1 where pk < 2 or pk between 3 and 4;
select * from t1 where pk < 2 or pk between 3 and 4; select * from t1 where pk < 2 or pk between 3 and 4;
drop table t1; drop table t1;
set optimizer_use_mrr = @my_save_optimizer_use_mrr; set optimizer_switch = @bug665669_tmp;
--echo # --echo #
--echo # Bug#43360 - Server crash with a simple multi-table update --echo # Bug#43360 - Server crash with a simple multi-table update
--echo # --echo #
......
...@@ -92,7 +92,6 @@ DROP TABLE t1,t2,t3; ...@@ -92,7 +92,6 @@ DROP TABLE t1,t2,t3;
--echo # (works only on Maria because we need 1024-byte long key) --echo # (works only on Maria because we need 1024-byte long key)
--echo # --echo #
SET SESSION optimizer_use_mrr = 'force';
SET SESSION join_cache_level = 6; SET SESSION join_cache_level = 6;
SET SESSION join_buffer_size = 1024; SET SESSION join_buffer_size = 1024;
CREATE TABLE t1 ( CREATE TABLE t1 (
......
...@@ -219,11 +219,9 @@ CREATE TABLE `t1` ( ...@@ -219,11 +219,9 @@ CREATE TABLE `t1` (
INSERT INTO `t1` VALUES (10,'00:00:00','i','i'),(11,'00:00:00','',''); INSERT INTO `t1` VALUES (10,'00:00:00','i','i'),(11,'00:00:00','','');
set @old_optimizer_switch = @@session.optimizer_switch, set @old_optimizer_switch = @@session.optimizer_switch,
@old_optimizer_use_mrr = @@session.optimizer_use_mrr,
@old_engine_condition_pushdown = @@session.engine_condition_pushdown; @old_engine_condition_pushdown = @@session.engine_condition_pushdown;
SET SESSION OPTIMIZER_SWITCH = 'materialization=off,semijoin=off,loosescan=off,firstmatch=off'; SET SESSION OPTIMIZER_SWITCH = 'materialization=off,semijoin=off,loosescan=off,firstmatch=off,mrr=on';
SET SESSION optimizer_use_mrr = 'force';
SET SESSION engine_condition_pushdown = 1; SET SESSION engine_condition_pushdown = 1;
SELECT `time_nokey` G1 FROM t1 WHERE ( `varchar_nokey` , `varchar_key` ) IN ( SELECT `time_nokey` G1 FROM t1 WHERE ( `varchar_nokey` , `varchar_key` ) IN (
...@@ -231,7 +229,6 @@ SELECT `varchar_nokey` , `varchar_nokey` ) AND `varchar_key` >= 'c' HAVING G ...@@ -231,7 +229,6 @@ SELECT `varchar_nokey` , `varchar_nokey` ) AND `varchar_key` >= 'c' HAVING G
BY `pk` ; BY `pk` ;
set @@session.optimizer_switch = @old_optimizer_switch, set @@session.optimizer_switch = @old_optimizer_switch,
@@session.optimizer_use_mrr = @old_optimizer_use_mrr,
@@session.engine_condition_pushdown = @old_engine_condition_pushdown; @@session.engine_condition_pushdown = @old_engine_condition_pushdown;
DROP TABLE t1; DROP TABLE t1;
......
...@@ -1416,7 +1416,7 @@ ha_rows DsMrr_impl::dsmrr_info_const(uint keyno, RANGE_SEQ_IF *seq, ...@@ -1416,7 +1416,7 @@ ha_rows DsMrr_impl::dsmrr_info_const(uint keyno, RANGE_SEQ_IF *seq,
/* /*
If HA_MRR_USE_DEFAULT_IMPL has been passed to us, that is an order to If HA_MRR_USE_DEFAULT_IMPL has been passed to us, that is an order to
use the default MRR implementation (we need it for UPDATE/DELETE). use the default MRR implementation (we need it for UPDATE/DELETE).
Otherwise, make a choice based on cost and @@optimizer_use_mrr. Otherwise, make a choice based on cost and @@optimizer_switch settings
*/ */
if ((*flags & HA_MRR_USE_DEFAULT_IMPL) || if ((*flags & HA_MRR_USE_DEFAULT_IMPL) ||
choose_mrr_impl(keyno, rows, flags, bufsz, cost)) choose_mrr_impl(keyno, rows, flags, bufsz, cost))
...@@ -1520,7 +1520,8 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags, ...@@ -1520,7 +1520,8 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
bool using_cpk= test(keyno == table->s->primary_key && bool using_cpk= test(keyno == table->s->primary_key &&
primary_file->primary_key_is_clustered()); primary_file->primary_key_is_clustered());
*flags &= ~HA_MRR_IMPLEMENTATION_FLAGS; *flags &= ~HA_MRR_IMPLEMENTATION_FLAGS;
if (thd->variables.optimizer_use_mrr == 2 || *flags & HA_MRR_INDEX_ONLY || if (!optimizer_flag(thd, OPTIMIZER_SWITCH_MRR) ||
*flags & HA_MRR_INDEX_ONLY ||
(using_cpk && !doing_cpk_scan) || key_uses_partial_cols(table, keyno)) (using_cpk && !doing_cpk_scan) || key_uses_partial_cols(table, keyno))
{ {
/* Use the default implementation */ /* Use the default implementation */
...@@ -1537,12 +1538,12 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags, ...@@ -1537,12 +1538,12 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
bool force_dsmrr; bool force_dsmrr;
/* /*
If @@optimizer_use_mrr==force, then set cost of DS-MRR to be minimum of If mrr_cost_based flag is not set, then set cost of DS-MRR to be minimum of
DS-MRR and Default implementations cost. This allows one to force use of DS-MRR and Default implementations cost. This allows one to force use of
DS-MRR whenever it is applicable without affecting other cost-based DS-MRR whenever it is applicable without affecting other cost-based
choices. choices.
*/ */
if ((force_dsmrr= (thd->variables.optimizer_use_mrr == 1)) && if ((force_dsmrr= !optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_COST_BASED)) &&
dsmrr_cost.total_cost() > cost->total_cost()) dsmrr_cost.total_cost() > cost->total_cost())
dsmrr_cost= *cost; dsmrr_cost= *cost;
......
...@@ -575,18 +575,27 @@ class Default_object_creation_ctx : public Object_creation_ctx ...@@ -575,18 +575,27 @@ class Default_object_creation_ctx : public Object_creation_ctx
#define OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE (1<<11) #define OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE (1<<11)
#define OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN (1<<12) #define OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN (1<<12)
#define OPTIMIZER_SWITCH_SUBQUERY_CACHE (1<<13) #define OPTIMIZER_SWITCH_SUBQUERY_CACHE (1<<13)
#define OPTIMIZER_SWITCH_MRR_SORT_KEYS (1<<14) /** If this is off, MRR is never used. */
#define OPTIMIZER_SWITCH_OUTER_JOIN_WITH_CACHE (1<<15) #define OPTIMIZER_SWITCH_MRR (1ULL << 14)
#define OPTIMIZER_SWITCH_SEMIJOIN_WITH_CACHE (1<<16) /**
#define OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL (1<<17) If OPTIMIZER_SWITCH_MRR is on and this is on, MRR is used depending on a
#define OPTIMIZER_SWITCH_JOIN_CACHE_HASHED (1<<18) cost-based choice ("automatic"). If OPTIMIZER_SWITCH_MRR is on and this is
#define OPTIMIZER_SWITCH_JOIN_CACHE_BKA (1<<19) off, MRR is "forced" (i.e. used as long as the storage engine is capable of
#define OPTIMIZER_SWITCH_OPTIMIZE_JOIN_BUFFER_SIZE (1<<20) doing it).
*/
#define OPTIMIZER_SWITCH_MRR_COST_BASED (1ULL << 15)
#define OPTIMIZER_SWITCH_MRR_SORT_KEYS (1ULL << 16)
#define OPTIMIZER_SWITCH_OUTER_JOIN_WITH_CACHE (1ULL << 17)
#define OPTIMIZER_SWITCH_SEMIJOIN_WITH_CACHE (1ULL << 18)
#define OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL (1ULL << 19)
#define OPTIMIZER_SWITCH_JOIN_CACHE_HASHED (1ULL << 20)
#define OPTIMIZER_SWITCH_JOIN_CACHE_BKA (1ULL << 21)
#define OPTIMIZER_SWITCH_OPTIMIZE_JOIN_BUFFER_SIZE (1ULL << 22)
#ifdef DBUG_OFF #ifdef DBUG_OFF
# define OPTIMIZER_SWITCH_LAST (1<<21) # define OPTIMIZER_SWITCH_LAST (1ULL << 23)
#else #else
# define OPTIMIZER_SWITCH_TABLE_ELIMINATION (1<<21) # define OPTIMIZER_SWITCH_TABLE_ELIMINATION (1ULL << 23)
# define OPTIMIZER_SWITCH_LAST (1<<22) # define OPTIMIZER_SWITCH_LAST (1ULL << 24)
#endif #endif
#ifdef DBUG_OFF #ifdef DBUG_OFF
...@@ -608,6 +617,7 @@ enabled by default, add OPTIMIZER_SWITCH_MATERIALIZATION ...@@ -608,6 +617,7 @@ enabled by default, add OPTIMIZER_SWITCH_MATERIALIZATION
OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE|\ OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE|\
OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN|\ OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN|\
OPTIMIZER_SWITCH_SUBQUERY_CACHE|\ OPTIMIZER_SWITCH_SUBQUERY_CACHE|\
OPTIMIZER_SWITCH_MRR|\
OPTIMIZER_SWITCH_MRR_SORT_KEYS|\ OPTIMIZER_SWITCH_MRR_SORT_KEYS|\
OPTIMIZER_SWITCH_SUBQUERY_CACHE | \ OPTIMIZER_SWITCH_SUBQUERY_CACHE | \
OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL | \ OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL | \
...@@ -628,6 +638,7 @@ enabled by default, add OPTIMIZER_SWITCH_MATERIALIZATION ...@@ -628,6 +638,7 @@ enabled by default, add OPTIMIZER_SWITCH_MATERIALIZATION
OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE|\ OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE|\
OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN|\ OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN|\
OPTIMIZER_SWITCH_SUBQUERY_CACHE|\ OPTIMIZER_SWITCH_SUBQUERY_CACHE|\
OPTIMIZER_SWITCH_MRR|\
OPTIMIZER_SWITCH_MRR_SORT_KEYS|\ OPTIMIZER_SWITCH_MRR_SORT_KEYS|\
OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL | \ OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL | \
OPTIMIZER_SWITCH_JOIN_CACHE_HASHED | \ OPTIMIZER_SWITCH_JOIN_CACHE_HASHED | \
......
...@@ -340,6 +340,8 @@ static const char *optimizer_switch_names[]= ...@@ -340,6 +340,8 @@ static const char *optimizer_switch_names[]=
"partial_match_rowid_merge", "partial_match_rowid_merge",
"partial_match_table_scan", "partial_match_table_scan",
"subquery_cache", "subquery_cache",
"mrr",
"mrr_cost_based",
"mrr_sort_keys", "mrr_sort_keys",
"outer_join_with_cache", "outer_join_with_cache",
"semijoin_with_cache", "semijoin_with_cache",
...@@ -370,6 +372,8 @@ static const unsigned int optimizer_switch_names_len[]= ...@@ -370,6 +372,8 @@ static const unsigned int optimizer_switch_names_len[]=
sizeof("partial_match_rowid_merge") - 1, sizeof("partial_match_rowid_merge") - 1,
sizeof("partial_match_table_scan") - 1, sizeof("partial_match_table_scan") - 1,
sizeof("subquery_cache") - 1, sizeof("subquery_cache") - 1,
sizeof("mrr") - 1,
sizeof("mrr_cost_based") - 1,
sizeof("mrr_sort_keys") - 1, sizeof("mrr_sort_keys") - 1,
sizeof("outer_join_with_cache") - 1, sizeof("outer_join_with_cache") - 1,
sizeof("semijoin_with_cache") - 1, sizeof("semijoin_with_cache") - 1,
...@@ -487,6 +491,8 @@ static const char *optimizer_switch_str="index_merge=on,index_merge_union=on," ...@@ -487,6 +491,8 @@ static const char *optimizer_switch_str="index_merge=on,index_merge_union=on,"
"partial_match_rowid_merge=on," "partial_match_rowid_merge=on,"
"partial_match_table_scan=on," "partial_match_table_scan=on,"
"subquery_cache=on," "subquery_cache=on,"
"mrr=on,"
"mrr_cost_based=off,"
"mrr_sort_keys=on," "mrr_sort_keys=on,"
"join_cache_incremental=on," "join_cache_incremental=on,"
"join_cache_hashed=on," "join_cache_hashed=on,"
...@@ -3731,8 +3737,6 @@ static int init_common_variables(const char *conf_file_name, int argc, ...@@ -3731,8 +3737,6 @@ static int init_common_variables(const char *conf_file_name, int argc,
global_system_variables.character_set_results= default_charset_info; global_system_variables.character_set_results= default_charset_info;
global_system_variables.character_set_client= default_charset_info; global_system_variables.character_set_client= default_charset_info;
global_system_variables.optimizer_use_mrr= 1;
if (!(character_set_filesystem= if (!(character_set_filesystem=
get_charset_by_csname(character_set_filesystem_name, get_charset_by_csname(character_set_filesystem_name,
MY_CS_PRIMARY, MYF(MY_WME)))) MY_CS_PRIMARY, MYF(MY_WME))))
......
...@@ -527,17 +527,6 @@ static sys_var_thd_ulong sys_optimizer_search_depth(&vars, "optimizer_sea ...@@ -527,17 +527,6 @@ static sys_var_thd_ulong sys_optimizer_search_depth(&vars, "optimizer_sea
static sys_var_thd_optimizer_switch sys_optimizer_switch(&vars, "optimizer_switch", static sys_var_thd_optimizer_switch sys_optimizer_switch(&vars, "optimizer_switch",
&SV::optimizer_switch); &SV::optimizer_switch);
const char *optimizer_use_mrr_names[] = {"auto", "force", "disable", NullS};
TYPELIB optimizer_use_mrr_typelib= {
array_elements(optimizer_use_mrr_names) - 1, "",
optimizer_use_mrr_names, NULL
};
static sys_var_thd_enum sys_optimizer_use_mrr(&vars, "optimizer_use_mrr",
&SV::optimizer_use_mrr,
&optimizer_use_mrr_typelib,
NULL);
static sys_var_const sys_pid_file(&vars, "pid_file", static sys_var_const sys_pid_file(&vars, "pid_file",
OPT_GLOBAL, SHOW_CHAR, OPT_GLOBAL, SHOW_CHAR,
(uchar*) pidfile_name); (uchar*) pidfile_name);
......
...@@ -408,14 +408,6 @@ struct system_variables ...@@ -408,14 +408,6 @@ struct system_variables
ulong optimizer_search_depth; ulong optimizer_search_depth;
/* A bitmap for switching optimizations on/off */ /* A bitmap for switching optimizations on/off */
ulong optimizer_switch; ulong optimizer_switch;
/*
Controls use of Engine-MRR:
0 - auto, based on cost
1 - force MRR when the storage engine is capable of doing it
2 - disable MRR.
*/
ulong optimizer_use_mrr;
ulong preload_buff_size; ulong preload_buff_size;
ulong profiling_history_size; ulong profiling_history_size;
ulong query_cache_type; ulong query_cache_type;
......
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