
[Commits] 00a635f4eb0: MDEV-19776: Assertion `to_len >= 8' failed in convert_to_printable with optimizer trace enabled
by Varun 18 Jun '19
by Varun 18 Jun '19
18 Jun '19
revision-id: 00a635f4eb0529d2a49a910455930b4bd8b3754d (mariadb-10.4.5-75-g00a635f4eb0)
parent(s): 48570eb65cee840be46497c8411a147280e404c5
author: Varun Gupta
committer: Varun Gupta
timestamp: 2019-06-18 16:36:26 +0530
message:
MDEV-19776: Assertion `to_len >= 8' failed in convert_to_printable with optimizer trace enabled
Introduced the convert_to_printable_required_length to return the correct length(taking into
consideration of dots in the case of error messages)
---
mysql-test/main/opt_trace.result | 8 ++++++++
mysql-test/main/opt_trace.test | 10 ++++++++++
sql/sql_string.cc | 6 +++++-
sql/sql_string.h | 1 +
4 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/mysql-test/main/opt_trace.result b/mysql-test/main/opt_trace.result
index c0e0b4807a1..b521447c37c 100644
--- a/mysql-test/main/opt_trace.result
+++ b/mysql-test/main/opt_trace.result
@@ -6554,4 +6554,12 @@ JSON_DETAILED(JSON_EXTRACT(trace, '$**.analyzing_range_alternatives'))
}
]
drop table t1, t0, one_k;
+#
+# MDEV-19776: Assertion `to_len >= 8' failed in convert_to_printable with optimizer trace enabled
+#
+CREATE TABLE t1 (f VARBINARY(16) NOT NULL, KEY(f));
+INSERT INTO t1 VALUES ('a'),('b');
+SET optimizer_trace = 'enabled=on';
+DELETE FROM t1 WHERE f = 'x';
+DROP TABLE t1;
set optimizer_trace='enabled=off';
diff --git a/mysql-test/main/opt_trace.test b/mysql-test/main/opt_trace.test
index 916b9313ca6..c1ed050062b 100644
--- a/mysql-test/main/opt_trace.test
+++ b/mysql-test/main/opt_trace.test
@@ -498,4 +498,14 @@ explain format=json select * from t1 force index(start_date) where start_date >=
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.analyzing_range_alternatives')) from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
drop table t1, t0, one_k;
+--echo #
+--echo # MDEV-19776: Assertion `to_len >= 8' failed in convert_to_printable with optimizer trace enabled
+--echo #
+
+CREATE TABLE t1 (f VARBINARY(16) NOT NULL, KEY(f));
+INSERT INTO t1 VALUES ('a'),('b');
+SET optimizer_trace = 'enabled=on';
+DELETE FROM t1 WHERE f = 'x';
+DROP TABLE t1;
+
set optimizer_trace='enabled=off';
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 1b567ecb325..c5e0e7e68f5 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -1197,10 +1197,14 @@ uint convert_to_printable(char *to, size_t to_len,
return (uint) (t - to);
}
+size_t convert_to_printable_required_length(uint len)
+{
+ return len * 4 + 3/*dots*/ + 1/*trailing \0 */;
+}
bool String::append_semi_hex(const char *s, uint len, CHARSET_INFO *cs)
{
- size_t dst_len= len * 4 + 1; //extra length for the '\0' character
+ size_t dst_len= convert_to_printable_required_length(len);
if (reserve(dst_len))
return true;
uint nbytes= convert_to_printable(Ptr + str_length, dst_len, s, len, cs);
diff --git a/sql/sql_string.h b/sql/sql_string.h
index d8edf5e81f0..f21f23e42ef 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -126,6 +126,7 @@ size_t my_copy_with_hex_escaping(CHARSET_INFO *cs,
uint convert_to_printable(char *to, size_t to_len,
const char *from, size_t from_len,
CHARSET_INFO *from_cs, size_t nbytes= 0);
+size_t convert_to_printable_required_length(uint len);
class Charset
1
0

[Commits] 81850491768: MDEV-19716: ASAN use-after-poison in Query_log_event::Query_log_event / THD::log_events_and_free_tmp_shares
by sujatha 18 Jun '19
by sujatha 18 Jun '19
18 Jun '19
revision-id: 818504917687b6454b91ac0ac16dd35937a3a3ca (mariadb-10.4.5-74-g81850491768)
parent(s): e85e4814eeca9123b23c23b40dd776416bfba2ca
author: Sujatha
committer: Sujatha
timestamp: 2019-06-18 13:58:02 +0530
message:
MDEV-19716: ASAN use-after-poison in Query_log_event::Query_log_event / THD::log_events_and_free_tmp_shares
Analysis:
========
When a given client session ends on a master, the server logs a DROP TEMPORARY
TABLE IF EXISTS statement for each temporary table that still exists in the
current session. It ensures a proper temporary table cleanup on the slave. In
order to write the DROP TEMPORARY TABLE query in binary log a 'Query_log_event'
object is created. Within the 'Query_log_event' constructor
'thd->lex->sql_command' is read to identify what type of cache needs to be
used to write the query. When the code reaches here as part of THD::cleanup
the 'thd->lex->sql_command' will be in an invalid state. The 'thd->lex' could
have been cleared or it could be pointing to a statement which was in the
middle of execution when the session ended. In such cases ASAN reports
use-after-poison error.
Fix:
===
The 'THD::Cleanup' code invokes 'THD::log_events_and_free_tmp_shares' to look
for temporary tables and write appropriate DROP TABLE stmts for them. This
cleanup code provides a special flag named 'direct=TRUE' to the
Query_log_event constructor. Having 'direct=TRUE' means that this query
doesn't require any caching. Hence in this scenario the 'Query_log_event'
constructor should respect the 'direct' flag and simply skip the logic of
deciding the type of cache to be used for the statement. Hence the code will
not access the stale lex object.
---
.../rpl/r/rpl_drop_temp_table_invaid_lex.result | 8 +++
.../rpl/t/rpl_drop_temp_table_invaid_lex.test | 32 ++++++++++
sql/log_event.cc | 69 +++++++++++-----------
3 files changed, 76 insertions(+), 33 deletions(-)
diff --git a/mysql-test/suite/rpl/r/rpl_drop_temp_table_invaid_lex.result b/mysql-test/suite/rpl/r/rpl_drop_temp_table_invaid_lex.result
new file mode 100644
index 00000000000..8e75342c14e
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_drop_temp_table_invaid_lex.result
@@ -0,0 +1,8 @@
+include/master-slave.inc
+[connection master]
+connect con1,localhost,root,,;
+CREATE TEMPORARY TABLE tmp (a INT);
+CREATE TABLE non_existing_db.t SELECT SLEEP(2) AS b;
+disconnect con1;
+connection default;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_drop_temp_table_invaid_lex.test b/mysql-test/suite/rpl/t/rpl_drop_temp_table_invaid_lex.test
new file mode 100644
index 00000000000..04b2eaa4b4d
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_drop_temp_table_invaid_lex.test
@@ -0,0 +1,32 @@
+# ==== Purpose ====
+#
+# Test verifies that no ASAN issues are reported at the time of writing DROP
+# TEMPORARY TABLE statements to binary log as part of session cleanup.
+#
+# ==== Implementation ====
+#
+# Steps:
+# 1 - Create a new connection named 'con1'.
+# 2 - Create a temporary table named 'tmp' as part of connection 'con1'.
+# 3 - Try to disconnect the current session when a CREATE .. SELECT
+# statement is in the middle of execution.
+# 4 - Observe that no ASAN issue is reported.
+#
+# ==== References ====
+#
+# MDEV-19716: ASAN use-after-poison in Query_log_event::Query_log_event /
+# THD::log_events_and_free_tmp_shares
+
+--source include/have_log_bin.inc
+--source include/master-slave.inc
+
+--connect (con1,localhost,root,,)
+
+CREATE TEMPORARY TABLE tmp (a INT);
+
+--send CREATE TABLE non_existing_db.t SELECT SLEEP(2) AS b
+--disconnect con1
+
+--connection default
+
+--source include/rpl_end.inc
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 731cbf99060..bc125c5c7b9 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -4415,41 +4415,44 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, size_t que
bool trx_cache= FALSE;
cache_type= Log_event::EVENT_INVALID_CACHE;
- switch (lex->sql_command)
+ if (!direct)
{
- case SQLCOM_DROP_TABLE:
- case SQLCOM_DROP_SEQUENCE:
- use_cache= (lex->tmp_table() && thd->in_multi_stmt_transaction_mode());
- break;
+ switch (lex->sql_command)
+ {
+ case SQLCOM_DROP_TABLE:
+ case SQLCOM_DROP_SEQUENCE:
+ use_cache= (lex->tmp_table() && thd->in_multi_stmt_transaction_mode());
+ break;
- case SQLCOM_CREATE_TABLE:
- case SQLCOM_CREATE_SEQUENCE:
- /*
- If we are using CREATE ... SELECT or if we are a slave
- executing BEGIN...COMMIT (generated by CREATE...SELECT) we
- have to use the transactional cache to ensure we don't
- calculate any checksum for the CREATE part.
- */
- trx_cache= (lex->first_select_lex()->item_list.elements &&
- thd->is_current_stmt_binlog_format_row()) ||
- (thd->variables.option_bits & OPTION_GTID_BEGIN);
- use_cache= (lex->tmp_table() &&
- thd->in_multi_stmt_transaction_mode()) || trx_cache;
- break;
- case SQLCOM_SET_OPTION:
- if (lex->autocommit)
- use_cache= trx_cache= FALSE;
- else
- use_cache= TRUE;
- break;
- case SQLCOM_RELEASE_SAVEPOINT:
- case SQLCOM_ROLLBACK_TO_SAVEPOINT:
- case SQLCOM_SAVEPOINT:
- use_cache= trx_cache= TRUE;
- break;
- default:
- use_cache= sqlcom_can_generate_row_events(thd);
- break;
+ case SQLCOM_CREATE_TABLE:
+ case SQLCOM_CREATE_SEQUENCE:
+ /*
+ If we are using CREATE ... SELECT or if we are a slave
+ executing BEGIN...COMMIT (generated by CREATE...SELECT) we
+ have to use the transactional cache to ensure we don't
+ calculate any checksum for the CREATE part.
+ */
+ trx_cache= (lex->first_select_lex()->item_list.elements &&
+ thd->is_current_stmt_binlog_format_row()) ||
+ (thd->variables.option_bits & OPTION_GTID_BEGIN);
+ use_cache= (lex->tmp_table() &&
+ thd->in_multi_stmt_transaction_mode()) || trx_cache;
+ break;
+ case SQLCOM_SET_OPTION:
+ if (lex->autocommit)
+ use_cache= trx_cache= FALSE;
+ else
+ use_cache= TRUE;
+ break;
+ case SQLCOM_RELEASE_SAVEPOINT:
+ case SQLCOM_ROLLBACK_TO_SAVEPOINT:
+ case SQLCOM_SAVEPOINT:
+ use_cache= trx_cache= TRUE;
+ break;
+ default:
+ use_cache= sqlcom_can_generate_row_events(thd);
+ break;
+ }
}
if (!use_cache || direct)
1
0

[Commits] 167da05: MDEV-19790 Wrong result for query with outer join and IS NOT TRUE predicate
by IgorBabaev 17 Jun '19
by IgorBabaev 17 Jun '19
17 Jun '19
revision-id: 167da05f554dbe27d16373f6f0b02408ee76dc94 (mariadb-5.5.64-20-g167da05)
parent(s): 039b8782d4794f34c5f0219d8a8d21f6e21d74f1
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-06-17 14:23:10 -0700
message:
MDEV-19790 Wrong result for query with outer join and IS NOT TRUE predicate
in where clause
The classes Item_func_isnottrue and Item_func_isnotfalse inherited the
implementation of the eval_not_null_tables method from the Item_func
class. As a result the not_null_tables_cache was set incorrectly for
the objects of these classes. It led to improper conversion of outer
joins to inner joins when the where clause of the processed query
contained IS NOT TRUE or IS NOT FALSE predicates. The coverted query
in many cases produced a wrong result set.
---
mysql-test/r/join_outer.result | 36 +++++++++++++++++++++++++++++++++++-
mysql-test/r/join_outer_jcl6.result | 36 +++++++++++++++++++++++++++++++++++-
mysql-test/t/join_outer.test | 25 ++++++++++++++++++++++++-
sql/item_cmpfunc.h | 4 ++++
4 files changed, 98 insertions(+), 3 deletions(-)
diff --git a/mysql-test/r/join_outer.result b/mysql-test/r/join_outer.result
index d55f11c..c976701 100644
--- a/mysql-test/r/join_outer.result
+++ b/mysql-test/r/join_outer.result
@@ -2353,7 +2353,7 @@ t1.b1+'0' t2.b2 + '0'
0 0
1 1
DROP TABLE t1, t2;
-set @join_cache_level= @save_join_cache_level;
+set @@join_cache_level= @save_join_cache_level;
#
# MDEV-14779: using left join causes incorrect results with materialization and derived tables
#
@@ -2418,5 +2418,39 @@ WHERE tb1.pk = 40
ORDER BY tb1.i1;
v2
DROP TABLE t1,t2;
+#
+# MDEV-19790 : IS NOT TRUE / IS NOT FALSE predicates over
+# inner tables of outer joins
+#
+create table t1 (a int);
+create table t2 (b int);
+insert into t1 values (3), (7), (1);
+insert into t2 values (7), (4), (3);
+select * from t1 left join t2 on a=b;
+a b
+3 3
+7 7
+1 NULL
+select * from t1 left join t2 on a=b where (b > 3) is not true;
+a b
+3 3
+1 NULL
+explain extended select * from t1 left join t2 on a=b where (b > 3) is not true;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00
+1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 Using where
+Warnings:
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t1` left join `test`.`t2` on((`test`.`t2`.`b` = `test`.`t1`.`a`)) where ((`test`.`t2`.`b` > 3) is not true)
+select * from t1 left join t2 on a=b where (b > 3) is not false;
+a b
+7 7
+1 NULL
+explain extended select * from t1 left join t2 on a=b where (b > 3) is not false;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00
+1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 Using where
+Warnings:
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t1` left join `test`.`t2` on((`test`.`t2`.`b` = `test`.`t1`.`a`)) where ((`test`.`t2`.`b` > 3) is not false)
+drop table t1,t2;
# end of 5.5 tests
SET optimizer_switch=@save_optimizer_switch;
diff --git a/mysql-test/r/join_outer_jcl6.result b/mysql-test/r/join_outer_jcl6.result
index 8a9b395..5c5581f 100644
--- a/mysql-test/r/join_outer_jcl6.result
+++ b/mysql-test/r/join_outer_jcl6.result
@@ -2364,7 +2364,7 @@ t1.b1+'0' t2.b2 + '0'
0 0
1 1
DROP TABLE t1, t2;
-set @join_cache_level= @save_join_cache_level;
+set @@join_cache_level= @save_join_cache_level;
#
# MDEV-14779: using left join causes incorrect results with materialization and derived tables
#
@@ -2429,6 +2429,40 @@ WHERE tb1.pk = 40
ORDER BY tb1.i1;
v2
DROP TABLE t1,t2;
+#
+# MDEV-19790 : IS NOT TRUE / IS NOT FALSE predicates over
+# inner tables of outer joins
+#
+create table t1 (a int);
+create table t2 (b int);
+insert into t1 values (3), (7), (1);
+insert into t2 values (7), (4), (3);
+select * from t1 left join t2 on a=b;
+a b
+7 7
+3 3
+1 NULL
+select * from t1 left join t2 on a=b where (b > 3) is not true;
+a b
+3 3
+1 NULL
+explain extended select * from t1 left join t2 on a=b where (b > 3) is not true;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00
+1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 Using where; Using join buffer (flat, BNL join)
+Warnings:
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t1` left join `test`.`t2` on((`test`.`t2`.`b` = `test`.`t1`.`a`)) where ((`test`.`t2`.`b` > 3) is not true)
+select * from t1 left join t2 on a=b where (b > 3) is not false;
+a b
+7 7
+1 NULL
+explain extended select * from t1 left join t2 on a=b where (b > 3) is not false;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00
+1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 Using where; Using join buffer (flat, BNL join)
+Warnings:
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t1` left join `test`.`t2` on((`test`.`t2`.`b` = `test`.`t1`.`a`)) where ((`test`.`t2`.`b` > 3) is not false)
+drop table t1,t2;
# end of 5.5 tests
SET optimizer_switch=@save_optimizer_switch;
set join_cache_level=default;
diff --git a/mysql-test/t/join_outer.test b/mysql-test/t/join_outer.test
index 305421c..bdd6149 100644
--- a/mysql-test/t/join_outer.test
+++ b/mysql-test/t/join_outer.test
@@ -1895,7 +1895,7 @@ set @save_join_cache_level= @@join_cache_level;
SET @@join_cache_level = 3;
SELECT t1.b1+'0' , t2.b2 + '0' FROM t1 LEFT JOIN t2 ON b1 = b2;
DROP TABLE t1, t2;
-set @join_cache_level= @save_join_cache_level;
+set @@join_cache_level= @save_join_cache_level;
--echo #
--echo # MDEV-14779: using left join causes incorrect results with materialization and derived tables
@@ -1959,6 +1959,29 @@ ORDER BY tb1.i1;
DROP TABLE t1,t2;
+--echo #
+--echo # MDEV-19790 : IS NOT TRUE / IS NOT FALSE predicates over
+--echo # inner tables of outer joins
+--echo #
+
+create table t1 (a int);
+create table t2 (b int);
+insert into t1 values (3), (7), (1);
+insert into t2 values (7), (4), (3);
+select * from t1 left join t2 on a=b;
+
+let $q=
+select * from t1 left join t2 on a=b where (b > 3) is not true;
+eval $q;
+eval explain extended $q;
+
+let $q=
+select * from t1 left join t2 on a=b where (b > 3) is not false;
+eval $q;
+eval explain extended $q;
+
+drop table t1,t2;
+
--echo # end of 5.5 tests
SET optimizer_switch=@save_optimizer_switch;
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index b10dc2b..2eafa0b 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -183,6 +183,8 @@ class Item_func_isnottrue : public Item_func_truth
Item_func_isnottrue(Item *a) : Item_func_truth(a, true, false) {}
~Item_func_isnottrue() {}
virtual const char* func_name() const { return "isnottrue"; }
+ bool eval_not_null_tables(uchar *opt_arg)
+ { not_null_tables_cache= 0; return false; }
};
@@ -209,6 +211,8 @@ class Item_func_isnotfalse : public Item_func_truth
Item_func_isnotfalse(Item *a) : Item_func_truth(a, false, false) {}
~Item_func_isnotfalse() {}
virtual const char* func_name() const { return "isnotfalse"; }
+ bool eval_not_null_tables(uchar *opt_arg)
+ { not_null_tables_cache= 0; return false; }
};
1
0

[Commits] 65e0c9b: MDEV-18661 loading the audit plugin causes performance regression.
by holyfootï¼ askmonty.org 14 Jun '19
by holyfootï¼ askmonty.org 14 Jun '19
14 Jun '19
revision-id: 65e0c9b91b46e2dfb4388c8c5c1bc76dd9f8fbd8 (mariadb-10.1.39-62-g65e0c9b)
parent(s): 5b65d61d9384a45ea1b8df79694493fbb1a14e4a
committer: Alexey Botchkov
timestamp: 2019-06-15 01:02:55 +0400
message:
MDEV-18661 loading the audit plugin causes performance regression.
Plugin fixed to not lock the LOCK_operations when not active.
Server fixed to lock the LOCK_plugin less - do it once per
thread and then only if a plugin was installed/uninstalled.
---
plugin/server_audit/server_audit.c | 112 ++++++++++++++++++++++---------------
sql/sql_audit.cc | 16 ++++++
sql/sql_audit.h | 1 +
sql/sql_class.cc | 4 +-
sql/sql_class.h | 1 +
sql/sql_connect.cc | 3 +-
sql/sql_plugin.cc | 3 +
sql/sql_plugin.h | 1 +
sql/threadpool_common.cc | 3 +-
9 files changed, 97 insertions(+), 47 deletions(-)
diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c
index c9e7e3a..3da5264 100644
--- a/plugin/server_audit/server_audit.c
+++ b/plugin/server_audit/server_audit.c
@@ -15,7 +15,7 @@
#define PLUGIN_VERSION 0x104
-#define PLUGIN_STR_VERSION "1.4.4"
+#define PLUGIN_STR_VERSION "1.4.7"
#define _my_thread_var loc_thread_var
@@ -295,7 +295,7 @@ static unsigned long long file_rotate_size;
static unsigned int rotations;
static my_bool rotate= TRUE;
static char logging;
-static int internal_stop_logging= 0;
+static volatile int internal_stop_logging= 0;
static char incl_user_buffer[1024];
static char excl_user_buffer[1024];
static char *big_buffer= NULL;
@@ -533,16 +533,20 @@ static struct st_mysql_show_var audit_status[]=
#if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI)
/* These belong to the service initialization */
static PSI_mutex_key key_LOCK_operations;
+static PSI_mutex_key key_LOCK_atomic;
static PSI_mutex_key key_LOCK_bigbuffer;
static PSI_mutex_info mutex_key_list[]=
{
{ &key_LOCK_operations, "SERVER_AUDIT_plugin::lock_operations",
PSI_FLAG_GLOBAL},
+ { &key_LOCK_atomic, "SERVER_AUDIT_plugin::lock_atomic",
+ PSI_FLAG_GLOBAL},
{ &key_LOCK_bigbuffer, "SERVER_AUDIT_plugin::lock_bigbuffer",
PSI_FLAG_GLOBAL}
};
#endif
static mysql_mutex_t lock_operations;
+static mysql_mutex_t lock_atomic;
static mysql_mutex_t lock_bigbuffer;
/* The Percona server and partly MySQL don't support */
@@ -553,6 +557,14 @@ static mysql_mutex_t lock_bigbuffer;
/* worths doing. */
#define CLIENT_ERROR if (!started_mysql) my_printf_error
+#define ADD_ATOMIC(x, a) \
+ do { \
+ flogger_mutex_lock(&lock_atomic); \
+ x+= a; \
+ flogger_mutex_unlock(&lock_atomic); \
+ } while (0)
+
+
static uchar *getkey_user(const char *entry, size_t *length,
my_bool nu __attribute__((unused)) )
{
@@ -731,20 +743,20 @@ static int user_coll_fill(struct user_coll *c, char *users,
if (cmp_user && take_over_cmp)
{
- internal_stop_logging= 1;
+ ADD_ATOMIC(internal_stop_logging, 1);
CLIENT_ERROR(1, "User '%.*s' was removed from the"
" server_audit_excl_users.",
MYF(ME_JUST_WARNING), (int) cmp_length, users);
- internal_stop_logging= 0;
+ ADD_ATOMIC(internal_stop_logging, -1);
blank_user(cmp_user);
refill_cmp_coll= 1;
}
else if (cmp_user)
{
- internal_stop_logging= 1;
+ ADD_ATOMIC(internal_stop_logging, 1);
CLIENT_ERROR(1, "User '%.*s' is in the server_audit_incl_users, "
"so wasn't added.", MYF(ME_JUST_WARNING), (int) cmp_length, users);
- internal_stop_logging= 0;
+ ADD_ATOMIC(internal_stop_logging, -1);
remove_user(users);
continue;
}
@@ -1252,23 +1264,30 @@ static void change_connection(struct connection_info *cn,
event->ip, event->ip_length);
}
-static int write_log(const char *message, int len)
+static int write_log(const char *message, size_t len, int take_lock)
{
+ int result= 0;
+ if (take_lock)
+ flogger_mutex_lock(&lock_operations);
+
if (output_type == OUTPUT_FILE)
{
if (logfile &&
- (is_active= (logger_write(logfile, message, len) == len)))
- return 0;
+ (is_active= (logger_write(logfile, message, len) == (int) len)))
+ goto exit;
++log_write_failures;
- return 1;
+ result= 1;
}
else if (output_type == OUTPUT_SYSLOG)
{
syslog(syslog_facility_codes[syslog_facility] |
syslog_priority_codes[syslog_priority],
- "%s %.*s", syslog_info, len, message);
+ "%s %.*s", syslog_info, (int) len, message);
}
- return 0;
+exit:
+ if (take_lock)
+ flogger_mutex_unlock(&lock_operations);
+ return result;
}
@@ -1327,7 +1346,7 @@ static int log_connection(const struct connection_info *cn,
csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize,
",%.*s,,%d", cn->db_length, cn->db, event->status);
message[csize]= '\n';
- return write_log(message, csize + 1);
+ return write_log(message, csize + 1, 1);
}
@@ -1348,7 +1367,7 @@ static int log_connection_event(const struct mysql_event_connection *event,
csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize,
",%.*s,,%d", event->database_length, event->database, event->status);
message[csize]= '\n';
- return write_log(message, csize + 1);
+ return write_log(message, csize + 1, 1);
}
@@ -1477,21 +1496,28 @@ static size_t escape_string_hide_passwords(const char *str, unsigned int len,
-static int do_log_user(const char *name)
+static int do_log_user(const char *name, int take_lock)
{
size_t len;
+ int result;
if (!name)
return 0;
len= strlen(name);
- if (incl_user_coll.n_users)
- return coll_search(&incl_user_coll, name, len) != 0;
+ if (take_lock)
+ flogger_mutex_lock(&lock_operations);
- if (excl_user_coll.n_users)
- return coll_search(&excl_user_coll, name, len) == 0;
+ if (incl_user_coll.n_users)
+ result= coll_search(&incl_user_coll, name, len) != 0;
+ else if (excl_user_coll.n_users)
+ result= coll_search(&excl_user_coll, name, len) == 0;
+ else
+ result= 1;
- return 1;
+ if (take_lock)
+ flogger_mutex_unlock(&lock_operations);
+ return result;
}
@@ -1588,7 +1614,7 @@ static int filter_query_type(const char *query, struct sa_keyword *kwd)
static int log_statement_ex(const struct connection_info *cn,
time_t ev_time, unsigned long thd_id,
const char *query, unsigned int query_len,
- int error_code, const char *type)
+ int error_code, const char *type, int take_lock)
{
size_t csize;
char message_loc[1024];
@@ -1736,7 +1762,7 @@ static int log_statement_ex(const struct connection_info *cn,
csize+= my_snprintf(message+csize, message_size - 1 - csize,
"\',%d", error_code);
message[csize]= '\n';
- result= write_log(message, csize + 1);
+ result= write_log(message, csize + 1, take_lock);
if (message == big_buffer)
flogger_mutex_unlock(&lock_bigbuffer);
@@ -1750,7 +1776,7 @@ static int log_statement(const struct connection_info *cn,
{
return log_statement_ex(cn, event->general_time, event->general_thread_id,
event->general_query, event->general_query_length,
- event->general_error_code, type);
+ event->general_error_code, type, 1);
}
@@ -1772,7 +1798,7 @@ static int log_table(const struct connection_info *cn,
",%.*s,%.*s,",event->database_length, event->database,
event->table_length, event->table);
message[csize]= '\n';
- return write_log(message, csize + 1);
+ return write_log(message, csize + 1, 1);
}
@@ -1796,7 +1822,7 @@ static int log_rename(const struct connection_info *cn,
event->new_database_length, event->new_database,
event->new_table_length, event->new_table);
message[csize]= '\n';
- return write_log(message, csize + 1);
+ return write_log(message, csize + 1, 1);
}
@@ -1988,8 +2014,6 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
if (!thd || internal_stop_logging)
return;
- flogger_mutex_lock(&lock_operations);
-
if (maria_55_started && debug_server_started &&
event_class == MYSQL_AUDIT_GENERAL_CLASS)
{
@@ -2024,7 +2048,7 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
goto exit_func;
if (event_class == MYSQL_AUDIT_GENERAL_CLASS && FILTER(EVENT_QUERY) &&
- cn && do_log_user(cn->user))
+ cn && do_log_user(cn->user, 1))
{
const struct mysql_event_general *event =
(const struct mysql_event_general *) ev;
@@ -2043,7 +2067,7 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
{
const struct mysql_event_table *event =
(const struct mysql_event_table *) ev;
- if (do_log_user(event->user))
+ if (do_log_user(event->user, 1))
{
switch (event->event_subclass)
{
@@ -2109,7 +2133,6 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
}
if (cn)
cn->log_always= 0;
- flogger_mutex_unlock(&lock_operations);
}
@@ -2377,6 +2400,7 @@ static int server_audit_init(void *p __attribute__((unused)))
PSI_server->register_mutex("server_audit", mutex_key_list, 1);
#endif
flogger_mutex_init(key_LOCK_operations, &lock_operations, MY_MUTEX_INIT_FAST);
+ flogger_mutex_init(key_LOCK_operations, &lock_atomic, MY_MUTEX_INIT_FAST);
flogger_mutex_init(key_LOCK_operations, &lock_bigbuffer, MY_MUTEX_INIT_FAST);
coll_init(&incl_user_coll);
@@ -2464,6 +2488,7 @@ static int server_audit_deinit(void *p __attribute__((unused)))
(void) free(big_buffer);
flogger_mutex_destroy(&lock_operations);
+ flogger_mutex_destroy(&lock_atomic);
flogger_mutex_destroy(&lock_bigbuffer);
error_header();
@@ -2553,10 +2578,10 @@ static void log_current_query(MYSQL_THD thd)
return;
cn= get_loc_info(thd);
if (!ci_needs_setup(cn) && cn->query_length &&
- FILTER(EVENT_QUERY) && do_log_user(cn->user))
+ FILTER(EVENT_QUERY) && do_log_user(cn->user, 0))
{
log_statement_ex(cn, cn->query_time, thd_get_thread_id(thd),
- cn->query, cn->query_length, 0, "QUERY");
+ cn->query, cn->query_length, 0, "QUERY", 0);
cn->log_always= 1;
}
}
@@ -2568,12 +2593,13 @@ static void update_file_path(MYSQL_THD thd,
{
char *new_name= (*(char **) save) ? *(char **) save : empty_str;
- if (!maria_55_started || !debug_server_started)
- flogger_mutex_lock(&lock_operations);
- internal_stop_logging= 1;
+ ADD_ATOMIC(internal_stop_logging, 1);
error_header();
fprintf(stderr, "Log file name was changed to '%s'.\n", new_name);
+ if (!maria_55_started || !debug_server_started)
+ flogger_mutex_lock(&lock_operations);
+
if (logging)
log_current_query(thd);
@@ -2582,7 +2608,6 @@ static void update_file_path(MYSQL_THD thd,
char *sav_path= file_path;
file_path= new_name;
- internal_stop_logging= 1;
stop_logging();
if (start_logging())
{
@@ -2598,16 +2623,15 @@ static void update_file_path(MYSQL_THD thd,
}
goto exit_func;
}
- internal_stop_logging= 0;
}
strncpy(path_buffer, new_name, sizeof(path_buffer)-1);
path_buffer[sizeof(path_buffer)-1]= 0;
file_path= path_buffer;
exit_func:
- internal_stop_logging= 0;
if (!maria_55_started || !debug_server_started)
flogger_mutex_unlock(&lock_operations);
+ ADD_ATOMIC(internal_stop_logging, -1);
}
@@ -2692,8 +2716,8 @@ static void update_output_type(MYSQL_THD thd,
if (output_type == new_output_type)
return;
+ ADD_ATOMIC(internal_stop_logging, 1);
flogger_mutex_lock(&lock_operations);
- internal_stop_logging= 1;
if (logging)
{
log_current_query(thd);
@@ -2707,8 +2731,8 @@ static void update_output_type(MYSQL_THD thd,
if (logging)
start_logging();
- internal_stop_logging= 0;
flogger_mutex_unlock(&lock_operations);
+ ADD_ATOMIC(internal_stop_logging, -1);
}
@@ -2756,9 +2780,9 @@ static void update_logging(MYSQL_THD thd,
if (new_logging == logging)
return;
+ ADD_ATOMIC(internal_stop_logging, 1);
if (!maria_55_started || !debug_server_started)
flogger_mutex_lock(&lock_operations);
- internal_stop_logging= 1;
if ((logging= new_logging))
{
start_logging();
@@ -2773,9 +2797,9 @@ static void update_logging(MYSQL_THD thd,
stop_logging();
}
- internal_stop_logging= 0;
if (!maria_55_started || !debug_server_started)
flogger_mutex_unlock(&lock_operations);
+ ADD_ATOMIC(internal_stop_logging, -1);
}
@@ -2787,16 +2811,16 @@ static void update_mode(MYSQL_THD thd __attribute__((unused)),
if (mode_readonly || new_mode == mode)
return;
+ ADD_ATOMIC(internal_stop_logging, 1);
if (!maria_55_started || !debug_server_started)
flogger_mutex_lock(&lock_operations);
- internal_stop_logging= 1;
mark_always_logged(thd);
error_header();
fprintf(stderr, "Logging mode was changed from %d to %d.\n", mode, new_mode);
mode= new_mode;
- internal_stop_logging= 0;
if (!maria_55_started || !debug_server_started)
flogger_mutex_unlock(&lock_operations);
+ ADD_ATOMIC(internal_stop_logging, -1);
}
diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc
index dd98e3c..cee0ac2 100644
--- a/sql/sql_audit.cc
+++ b/sql/sql_audit.cc
@@ -212,6 +212,7 @@ void mysql_audit_acquire_plugins(THD *thd, ulong *event_class_mask)
{
plugin_foreach(thd, acquire_plugins, MYSQL_AUDIT_PLUGIN, event_class_mask);
add_audit_mask(thd->audit_class_mask, event_class_mask);
+ thd->audit_plugin_version= global_plugin_version;
}
DBUG_VOID_RETURN;
}
@@ -242,6 +243,20 @@ void mysql_audit_notify(THD *thd, uint event_class, uint event_subtype, ...)
/**
+ Check if there were changes in the state of plugins
+ so we need to do the mysql_audit_release asap.
+
+ @param[in] thd
+
+*/
+
+my_bool mysql_audit_release_required(THD *thd)
+{
+ return thd && (thd->audit_plugin_version != global_plugin_version);
+}
+
+
+/**
Release any resources associated with the current thd.
@param[in] thd
@@ -276,6 +291,7 @@ void mysql_audit_release(THD *thd)
/* Reset the state of thread values */
reset_dynamic(&thd->audit_class_plugins);
bzero(thd->audit_class_mask, sizeof(thd->audit_class_mask));
+ thd->audit_plugin_version= -1;
}
diff --git a/sql/sql_audit.h b/sql/sql_audit.h
index 550b2a5..9a74675 100644
--- a/sql/sql_audit.h
+++ b/sql/sql_audit.h
@@ -60,6 +60,7 @@ static inline void mysql_audit_notify(THD *thd, uint event_class,
#define mysql_audit_connection_enabled() 0
#define mysql_audit_table_enabled() 0
#endif
+extern my_bool mysql_audit_release_required(THD *thd);
extern void mysql_audit_release(THD *thd);
#define MAX_USER_HOST_SIZE 512
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 8fabcd5..6bcff6d 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -776,6 +776,9 @@ THD::THD(bool is_wsrep_applier)
waiting_on_group_commit(FALSE), has_waiter(FALSE),
spcont(NULL),
m_parser_state(NULL),
+#ifndef EMBEDDED_LIBRARY
+ audit_plugin_version(-1),
+#endif
#if defined(ENABLED_DEBUG_SYNC)
debug_sync_control(0),
#endif /* defined(ENABLED_DEBUG_SYNC) */
@@ -1562,7 +1565,6 @@ THD::~THD()
mdl_context.destroy();
ha_close_connection(this);
- mysql_audit_release(this);
plugin_thdvar_cleanup(this);
main_security_ctx.destroy();
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 1cb516c..6392394 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -2978,6 +2978,7 @@ class THD :public Statement,
added to the list of audit plugins which are currently in use.
*/
unsigned long audit_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
+ int audit_plugin_version;
#endif
#if defined(ENABLED_DEBUG_SYNC)
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 4dbb53f..a6a01b1 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -1326,7 +1326,8 @@ void do_handle_one_connection(THD *thd_arg)
while (thd_is_connection_alive(thd))
{
- mysql_audit_release(thd);
+ if (mysql_audit_release_required(thd))
+ mysql_audit_release(thd);
if (do_command(thd))
break;
}
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 21093e3..48131b1 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -228,6 +228,7 @@ static DYNAMIC_ARRAY plugin_array;
static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM];
static MEM_ROOT plugin_mem_root;
static bool reap_needed= false;
+volatile int global_plugin_version= 1;
static bool initialized= 0;
ulong dlopen_count;
@@ -2181,6 +2182,7 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name,
reap_plugins();
}
err:
+ global_plugin_version++;
mysql_mutex_unlock(&LOCK_plugin);
if (argv)
free_defaults(argv);
@@ -2327,6 +2329,7 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
}
reap_plugins();
+ global_plugin_version++;
mysql_mutex_unlock(&LOCK_plugin);
DBUG_RETURN(error);
diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h
index 7f74114..3bde06a 100644
--- a/sql/sql_plugin.h
+++ b/sql/sql_plugin.h
@@ -37,6 +37,7 @@ enum enum_plugin_load_option { PLUGIN_OFF, PLUGIN_ON, PLUGIN_FORCE,
PLUGIN_FORCE_PLUS_PERMANENT };
extern const char *global_plugin_typelib_names[];
+extern volatile int global_plugin_version;
extern ulong dlopen_count;
#include <my_sys.h>
diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc
index b4066bd..b8be708 100644
--- a/sql/threadpool_common.cc
+++ b/sql/threadpool_common.cc
@@ -266,7 +266,8 @@ int threadpool_process_request(THD *thd)
{
Vio *vio;
thd->net.reading_or_writing= 0;
- mysql_audit_release(thd);
+ if (mysql_audit_release_required(thd))
+ mysql_audit_release(thd);
if ((retval= do_command(thd)) != 0)
goto end;
1
0

[Commits] 2762e16: MDEV-18661 loading the audit plugin causes performance regression.
by holyfootï¼ askmonty.org 14 Jun '19
by holyfootï¼ askmonty.org 14 Jun '19
14 Jun '19
revision-id: 2762e165083c35b1ae70069200372aec95f6fd79 (mariadb-10.1.39-62-g2762e16)
parent(s): 5b65d61d9384a45ea1b8df79694493fbb1a14e4a
committer: Alexey Botchkov
timestamp: 2019-06-14 11:32:30 +0400
message:
MDEV-18661 loading the audit plugin causes performance regression.
Plugin fixed to not lock the LOCK_operations when not active.
Server fixed to lock the LOCK_plugin less - do it once per thread
and then only if a plugin was installed/uninstalled.
---
plugin/server_audit/server_audit.c | 112 ++++++++++++++++++++++---------------
sql/sql_audit.cc | 16 ++++++
sql/sql_audit.h | 1 +
sql/sql_class.cc | 4 +-
sql/sql_class.h | 1 +
sql/sql_connect.cc | 3 +-
sql/sql_plugin.cc | 3 +
sql/sql_plugin.h | 1 +
sql/threadpool_common.cc | 3 +-
9 files changed, 97 insertions(+), 47 deletions(-)
diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c
index c9e7e3a..3da5264 100644
--- a/plugin/server_audit/server_audit.c
+++ b/plugin/server_audit/server_audit.c
@@ -15,7 +15,7 @@
#define PLUGIN_VERSION 0x104
-#define PLUGIN_STR_VERSION "1.4.4"
+#define PLUGIN_STR_VERSION "1.4.7"
#define _my_thread_var loc_thread_var
@@ -295,7 +295,7 @@ static unsigned long long file_rotate_size;
static unsigned int rotations;
static my_bool rotate= TRUE;
static char logging;
-static int internal_stop_logging= 0;
+static volatile int internal_stop_logging= 0;
static char incl_user_buffer[1024];
static char excl_user_buffer[1024];
static char *big_buffer= NULL;
@@ -533,16 +533,20 @@ static struct st_mysql_show_var audit_status[]=
#if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI)
/* These belong to the service initialization */
static PSI_mutex_key key_LOCK_operations;
+static PSI_mutex_key key_LOCK_atomic;
static PSI_mutex_key key_LOCK_bigbuffer;
static PSI_mutex_info mutex_key_list[]=
{
{ &key_LOCK_operations, "SERVER_AUDIT_plugin::lock_operations",
PSI_FLAG_GLOBAL},
+ { &key_LOCK_atomic, "SERVER_AUDIT_plugin::lock_atomic",
+ PSI_FLAG_GLOBAL},
{ &key_LOCK_bigbuffer, "SERVER_AUDIT_plugin::lock_bigbuffer",
PSI_FLAG_GLOBAL}
};
#endif
static mysql_mutex_t lock_operations;
+static mysql_mutex_t lock_atomic;
static mysql_mutex_t lock_bigbuffer;
/* The Percona server and partly MySQL don't support */
@@ -553,6 +557,14 @@ static mysql_mutex_t lock_bigbuffer;
/* worths doing. */
#define CLIENT_ERROR if (!started_mysql) my_printf_error
+#define ADD_ATOMIC(x, a) \
+ do { \
+ flogger_mutex_lock(&lock_atomic); \
+ x+= a; \
+ flogger_mutex_unlock(&lock_atomic); \
+ } while (0)
+
+
static uchar *getkey_user(const char *entry, size_t *length,
my_bool nu __attribute__((unused)) )
{
@@ -731,20 +743,20 @@ static int user_coll_fill(struct user_coll *c, char *users,
if (cmp_user && take_over_cmp)
{
- internal_stop_logging= 1;
+ ADD_ATOMIC(internal_stop_logging, 1);
CLIENT_ERROR(1, "User '%.*s' was removed from the"
" server_audit_excl_users.",
MYF(ME_JUST_WARNING), (int) cmp_length, users);
- internal_stop_logging= 0;
+ ADD_ATOMIC(internal_stop_logging, -1);
blank_user(cmp_user);
refill_cmp_coll= 1;
}
else if (cmp_user)
{
- internal_stop_logging= 1;
+ ADD_ATOMIC(internal_stop_logging, 1);
CLIENT_ERROR(1, "User '%.*s' is in the server_audit_incl_users, "
"so wasn't added.", MYF(ME_JUST_WARNING), (int) cmp_length, users);
- internal_stop_logging= 0;
+ ADD_ATOMIC(internal_stop_logging, -1);
remove_user(users);
continue;
}
@@ -1252,23 +1264,30 @@ static void change_connection(struct connection_info *cn,
event->ip, event->ip_length);
}
-static int write_log(const char *message, int len)
+static int write_log(const char *message, size_t len, int take_lock)
{
+ int result= 0;
+ if (take_lock)
+ flogger_mutex_lock(&lock_operations);
+
if (output_type == OUTPUT_FILE)
{
if (logfile &&
- (is_active= (logger_write(logfile, message, len) == len)))
- return 0;
+ (is_active= (logger_write(logfile, message, len) == (int) len)))
+ goto exit;
++log_write_failures;
- return 1;
+ result= 1;
}
else if (output_type == OUTPUT_SYSLOG)
{
syslog(syslog_facility_codes[syslog_facility] |
syslog_priority_codes[syslog_priority],
- "%s %.*s", syslog_info, len, message);
+ "%s %.*s", syslog_info, (int) len, message);
}
- return 0;
+exit:
+ if (take_lock)
+ flogger_mutex_unlock(&lock_operations);
+ return result;
}
@@ -1327,7 +1346,7 @@ static int log_connection(const struct connection_info *cn,
csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize,
",%.*s,,%d", cn->db_length, cn->db, event->status);
message[csize]= '\n';
- return write_log(message, csize + 1);
+ return write_log(message, csize + 1, 1);
}
@@ -1348,7 +1367,7 @@ static int log_connection_event(const struct mysql_event_connection *event,
csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize,
",%.*s,,%d", event->database_length, event->database, event->status);
message[csize]= '\n';
- return write_log(message, csize + 1);
+ return write_log(message, csize + 1, 1);
}
@@ -1477,21 +1496,28 @@ static size_t escape_string_hide_passwords(const char *str, unsigned int len,
-static int do_log_user(const char *name)
+static int do_log_user(const char *name, int take_lock)
{
size_t len;
+ int result;
if (!name)
return 0;
len= strlen(name);
- if (incl_user_coll.n_users)
- return coll_search(&incl_user_coll, name, len) != 0;
+ if (take_lock)
+ flogger_mutex_lock(&lock_operations);
- if (excl_user_coll.n_users)
- return coll_search(&excl_user_coll, name, len) == 0;
+ if (incl_user_coll.n_users)
+ result= coll_search(&incl_user_coll, name, len) != 0;
+ else if (excl_user_coll.n_users)
+ result= coll_search(&excl_user_coll, name, len) == 0;
+ else
+ result= 1;
- return 1;
+ if (take_lock)
+ flogger_mutex_unlock(&lock_operations);
+ return result;
}
@@ -1588,7 +1614,7 @@ static int filter_query_type(const char *query, struct sa_keyword *kwd)
static int log_statement_ex(const struct connection_info *cn,
time_t ev_time, unsigned long thd_id,
const char *query, unsigned int query_len,
- int error_code, const char *type)
+ int error_code, const char *type, int take_lock)
{
size_t csize;
char message_loc[1024];
@@ -1736,7 +1762,7 @@ static int log_statement_ex(const struct connection_info *cn,
csize+= my_snprintf(message+csize, message_size - 1 - csize,
"\',%d", error_code);
message[csize]= '\n';
- result= write_log(message, csize + 1);
+ result= write_log(message, csize + 1, take_lock);
if (message == big_buffer)
flogger_mutex_unlock(&lock_bigbuffer);
@@ -1750,7 +1776,7 @@ static int log_statement(const struct connection_info *cn,
{
return log_statement_ex(cn, event->general_time, event->general_thread_id,
event->general_query, event->general_query_length,
- event->general_error_code, type);
+ event->general_error_code, type, 1);
}
@@ -1772,7 +1798,7 @@ static int log_table(const struct connection_info *cn,
",%.*s,%.*s,",event->database_length, event->database,
event->table_length, event->table);
message[csize]= '\n';
- return write_log(message, csize + 1);
+ return write_log(message, csize + 1, 1);
}
@@ -1796,7 +1822,7 @@ static int log_rename(const struct connection_info *cn,
event->new_database_length, event->new_database,
event->new_table_length, event->new_table);
message[csize]= '\n';
- return write_log(message, csize + 1);
+ return write_log(message, csize + 1, 1);
}
@@ -1988,8 +2014,6 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
if (!thd || internal_stop_logging)
return;
- flogger_mutex_lock(&lock_operations);
-
if (maria_55_started && debug_server_started &&
event_class == MYSQL_AUDIT_GENERAL_CLASS)
{
@@ -2024,7 +2048,7 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
goto exit_func;
if (event_class == MYSQL_AUDIT_GENERAL_CLASS && FILTER(EVENT_QUERY) &&
- cn && do_log_user(cn->user))
+ cn && do_log_user(cn->user, 1))
{
const struct mysql_event_general *event =
(const struct mysql_event_general *) ev;
@@ -2043,7 +2067,7 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
{
const struct mysql_event_table *event =
(const struct mysql_event_table *) ev;
- if (do_log_user(event->user))
+ if (do_log_user(event->user, 1))
{
switch (event->event_subclass)
{
@@ -2109,7 +2133,6 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
}
if (cn)
cn->log_always= 0;
- flogger_mutex_unlock(&lock_operations);
}
@@ -2377,6 +2400,7 @@ static int server_audit_init(void *p __attribute__((unused)))
PSI_server->register_mutex("server_audit", mutex_key_list, 1);
#endif
flogger_mutex_init(key_LOCK_operations, &lock_operations, MY_MUTEX_INIT_FAST);
+ flogger_mutex_init(key_LOCK_operations, &lock_atomic, MY_MUTEX_INIT_FAST);
flogger_mutex_init(key_LOCK_operations, &lock_bigbuffer, MY_MUTEX_INIT_FAST);
coll_init(&incl_user_coll);
@@ -2464,6 +2488,7 @@ static int server_audit_deinit(void *p __attribute__((unused)))
(void) free(big_buffer);
flogger_mutex_destroy(&lock_operations);
+ flogger_mutex_destroy(&lock_atomic);
flogger_mutex_destroy(&lock_bigbuffer);
error_header();
@@ -2553,10 +2578,10 @@ static void log_current_query(MYSQL_THD thd)
return;
cn= get_loc_info(thd);
if (!ci_needs_setup(cn) && cn->query_length &&
- FILTER(EVENT_QUERY) && do_log_user(cn->user))
+ FILTER(EVENT_QUERY) && do_log_user(cn->user, 0))
{
log_statement_ex(cn, cn->query_time, thd_get_thread_id(thd),
- cn->query, cn->query_length, 0, "QUERY");
+ cn->query, cn->query_length, 0, "QUERY", 0);
cn->log_always= 1;
}
}
@@ -2568,12 +2593,13 @@ static void update_file_path(MYSQL_THD thd,
{
char *new_name= (*(char **) save) ? *(char **) save : empty_str;
- if (!maria_55_started || !debug_server_started)
- flogger_mutex_lock(&lock_operations);
- internal_stop_logging= 1;
+ ADD_ATOMIC(internal_stop_logging, 1);
error_header();
fprintf(stderr, "Log file name was changed to '%s'.\n", new_name);
+ if (!maria_55_started || !debug_server_started)
+ flogger_mutex_lock(&lock_operations);
+
if (logging)
log_current_query(thd);
@@ -2582,7 +2608,6 @@ static void update_file_path(MYSQL_THD thd,
char *sav_path= file_path;
file_path= new_name;
- internal_stop_logging= 1;
stop_logging();
if (start_logging())
{
@@ -2598,16 +2623,15 @@ static void update_file_path(MYSQL_THD thd,
}
goto exit_func;
}
- internal_stop_logging= 0;
}
strncpy(path_buffer, new_name, sizeof(path_buffer)-1);
path_buffer[sizeof(path_buffer)-1]= 0;
file_path= path_buffer;
exit_func:
- internal_stop_logging= 0;
if (!maria_55_started || !debug_server_started)
flogger_mutex_unlock(&lock_operations);
+ ADD_ATOMIC(internal_stop_logging, -1);
}
@@ -2692,8 +2716,8 @@ static void update_output_type(MYSQL_THD thd,
if (output_type == new_output_type)
return;
+ ADD_ATOMIC(internal_stop_logging, 1);
flogger_mutex_lock(&lock_operations);
- internal_stop_logging= 1;
if (logging)
{
log_current_query(thd);
@@ -2707,8 +2731,8 @@ static void update_output_type(MYSQL_THD thd,
if (logging)
start_logging();
- internal_stop_logging= 0;
flogger_mutex_unlock(&lock_operations);
+ ADD_ATOMIC(internal_stop_logging, -1);
}
@@ -2756,9 +2780,9 @@ static void update_logging(MYSQL_THD thd,
if (new_logging == logging)
return;
+ ADD_ATOMIC(internal_stop_logging, 1);
if (!maria_55_started || !debug_server_started)
flogger_mutex_lock(&lock_operations);
- internal_stop_logging= 1;
if ((logging= new_logging))
{
start_logging();
@@ -2773,9 +2797,9 @@ static void update_logging(MYSQL_THD thd,
stop_logging();
}
- internal_stop_logging= 0;
if (!maria_55_started || !debug_server_started)
flogger_mutex_unlock(&lock_operations);
+ ADD_ATOMIC(internal_stop_logging, -1);
}
@@ -2787,16 +2811,16 @@ static void update_mode(MYSQL_THD thd __attribute__((unused)),
if (mode_readonly || new_mode == mode)
return;
+ ADD_ATOMIC(internal_stop_logging, 1);
if (!maria_55_started || !debug_server_started)
flogger_mutex_lock(&lock_operations);
- internal_stop_logging= 1;
mark_always_logged(thd);
error_header();
fprintf(stderr, "Logging mode was changed from %d to %d.\n", mode, new_mode);
mode= new_mode;
- internal_stop_logging= 0;
if (!maria_55_started || !debug_server_started)
flogger_mutex_unlock(&lock_operations);
+ ADD_ATOMIC(internal_stop_logging, -1);
}
diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc
index dd98e3c..cee0ac2 100644
--- a/sql/sql_audit.cc
+++ b/sql/sql_audit.cc
@@ -212,6 +212,7 @@ void mysql_audit_acquire_plugins(THD *thd, ulong *event_class_mask)
{
plugin_foreach(thd, acquire_plugins, MYSQL_AUDIT_PLUGIN, event_class_mask);
add_audit_mask(thd->audit_class_mask, event_class_mask);
+ thd->audit_plugin_version= global_plugin_version;
}
DBUG_VOID_RETURN;
}
@@ -242,6 +243,20 @@ void mysql_audit_notify(THD *thd, uint event_class, uint event_subtype, ...)
/**
+ Check if there were changes in the state of plugins
+ so we need to do the mysql_audit_release asap.
+
+ @param[in] thd
+
+*/
+
+my_bool mysql_audit_release_required(THD *thd)
+{
+ return thd && (thd->audit_plugin_version != global_plugin_version);
+}
+
+
+/**
Release any resources associated with the current thd.
@param[in] thd
@@ -276,6 +291,7 @@ void mysql_audit_release(THD *thd)
/* Reset the state of thread values */
reset_dynamic(&thd->audit_class_plugins);
bzero(thd->audit_class_mask, sizeof(thd->audit_class_mask));
+ thd->audit_plugin_version= -1;
}
diff --git a/sql/sql_audit.h b/sql/sql_audit.h
index 550b2a5..9a74675 100644
--- a/sql/sql_audit.h
+++ b/sql/sql_audit.h
@@ -60,6 +60,7 @@ static inline void mysql_audit_notify(THD *thd, uint event_class,
#define mysql_audit_connection_enabled() 0
#define mysql_audit_table_enabled() 0
#endif
+extern my_bool mysql_audit_release_required(THD *thd);
extern void mysql_audit_release(THD *thd);
#define MAX_USER_HOST_SIZE 512
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 8fabcd5..6bcff6d 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -776,6 +776,9 @@ THD::THD(bool is_wsrep_applier)
waiting_on_group_commit(FALSE), has_waiter(FALSE),
spcont(NULL),
m_parser_state(NULL),
+#ifndef EMBEDDED_LIBRARY
+ audit_plugin_version(-1),
+#endif
#if defined(ENABLED_DEBUG_SYNC)
debug_sync_control(0),
#endif /* defined(ENABLED_DEBUG_SYNC) */
@@ -1562,7 +1565,6 @@ THD::~THD()
mdl_context.destroy();
ha_close_connection(this);
- mysql_audit_release(this);
plugin_thdvar_cleanup(this);
main_security_ctx.destroy();
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 1cb516c..6392394 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -2978,6 +2978,7 @@ class THD :public Statement,
added to the list of audit plugins which are currently in use.
*/
unsigned long audit_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
+ int audit_plugin_version;
#endif
#if defined(ENABLED_DEBUG_SYNC)
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 4dbb53f..a6a01b1 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -1326,7 +1326,8 @@ void do_handle_one_connection(THD *thd_arg)
while (thd_is_connection_alive(thd))
{
- mysql_audit_release(thd);
+ if (mysql_audit_release_required(thd))
+ mysql_audit_release(thd);
if (do_command(thd))
break;
}
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 21093e3..48131b1 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -228,6 +228,7 @@ static DYNAMIC_ARRAY plugin_array;
static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM];
static MEM_ROOT plugin_mem_root;
static bool reap_needed= false;
+volatile int global_plugin_version= 1;
static bool initialized= 0;
ulong dlopen_count;
@@ -2181,6 +2182,7 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name,
reap_plugins();
}
err:
+ global_plugin_version++;
mysql_mutex_unlock(&LOCK_plugin);
if (argv)
free_defaults(argv);
@@ -2327,6 +2329,7 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
}
reap_plugins();
+ global_plugin_version++;
mysql_mutex_unlock(&LOCK_plugin);
DBUG_RETURN(error);
diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h
index 7f74114..3bde06a 100644
--- a/sql/sql_plugin.h
+++ b/sql/sql_plugin.h
@@ -37,6 +37,7 @@ enum enum_plugin_load_option { PLUGIN_OFF, PLUGIN_ON, PLUGIN_FORCE,
PLUGIN_FORCE_PLUS_PERMANENT };
extern const char *global_plugin_typelib_names[];
+extern volatile int global_plugin_version;
extern ulong dlopen_count;
#include <my_sys.h>
diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc
index b4066bd..b8be708 100644
--- a/sql/threadpool_common.cc
+++ b/sql/threadpool_common.cc
@@ -266,7 +266,8 @@ int threadpool_process_request(THD *thd)
{
Vio *vio;
thd->net.reading_or_writing= 0;
- mysql_audit_release(thd);
+ if (mysql_audit_release_required(thd))
+ mysql_audit_release(thd);
if ((retval= do_command(thd)) != 0)
goto end;
1
0

[Commits] 5bfc8128c83: MDEV-8874 Replication filters configured in my.cnf are ignored if slave reset and reconfigured
by sachin.setiyaï¼ mariadb.com 12 Jun '19
by sachin.setiyaï¼ mariadb.com 12 Jun '19
12 Jun '19
revision-id: 5bfc8128c8381e9347589aa0e27cd4fbd66ef19d (mariadb-10.1.39-57-g5bfc8128c83)
parent(s): 2e0fb93e6f3359ffe146f309e32c59922400b7c4
author: Sachin
committer: Sachin
timestamp: 2019-06-12 15:58:44 +0530
message:
MDEV-8874 Replication filters configured in my.cnf are ignored if slave reset and reconfigured
Don't delete the rpl_filter on RESET SLAVE.
---
mysql-test/lib/My/Config.pm | 8 +-
mysql-test/suite/multi_source/mdev-8874.cnf | 25 +++++
mysql-test/suite/multi_source/mdev-8874.result | 114 ++++++++++++++++++++
mysql-test/suite/multi_source/mdev-8874.test | 141 +++++++++++++++++++++++++
sql/rpl_mi.cc | 2 -
5 files changed, 287 insertions(+), 3 deletions(-)
diff --git a/mysql-test/lib/My/Config.pm b/mysql-test/lib/My/Config.pm
index 12647edf0a4..86bb7a5f961 100644
--- a/mysql-test/lib/My/Config.pm
+++ b/mysql-test/lib/My/Config.pm
@@ -327,7 +327,13 @@ sub new {
# Skip comment
next;
}
-
+ # Correctly process Replication Filter when they are defined
+ # with connection name.
+ elsif ( $line =~ /^([\w]+.[\w]+)\s*=\s*(.*)\s*/){
+ my $option= $1;
+ my $value= $2;
+ $self->insert($group_name, $option, $value);
+ }
else {
croak "Unexpected line '$line' found in '$path'";
}
diff --git a/mysql-test/suite/multi_source/mdev-8874.cnf b/mysql-test/suite/multi_source/mdev-8874.cnf
new file mode 100644
index 00000000000..dc89db72772
--- /dev/null
+++ b/mysql-test/suite/multi_source/mdev-8874.cnf
@@ -0,0 +1,25 @@
+!include my.cnf
+
+[mysqld.1]
+log-bin
+log-slave-updates
+
+[mysqld.2]
+log-bin
+log-slave-updates
+
+[mysqld.3]
+log-bin
+log-slave-updates
+
+[mysqld.4]
+server-id=4
+log-bin=server4-bin
+log-slave-updates
+m1.replicate_ignore_table='a.t1'
+m2.replicate_ignore_table='b.t1'
+replicate_ignore_table='c.t1'
+
+[ENV]
+SERVER_MYPORT_4= @mysqld.4.port
+SERVER_MYSOCK_4= @mysqld.4.socket
diff --git a/mysql-test/suite/multi_source/mdev-8874.result b/mysql-test/suite/multi_source/mdev-8874.result
new file mode 100644
index 00000000000..25185eed02a
--- /dev/null
+++ b/mysql-test/suite/multi_source/mdev-8874.result
@@ -0,0 +1,114 @@
+create database a;
+use a;
+create table t1(a int);
+insert into t1 values(1);
+create table t2(a int);
+insert into t2 values(1);
+create database b;
+use b;
+create table t1(a int);
+insert into t1 values(1);
+create table t2(a int);
+insert into t2 values(1);
+create database c;
+use c;
+create table t1(a int);
+insert into t1 values(1);
+create table t2(a int);
+insert into t2 values(1);
+change master 'm1' to master_port=MYPORT_1 , master_host='127.0.0.1', master_user='root';
+change master 'm2' to master_port=MYPORT_2 , master_host='127.0.0.1', master_user='root';
+change master to master_port=MYPORT_3 , master_host='127.0.0.1', master_user='root';
+start all slaves;
+set default_master_connection = 'm1';
+include/wait_for_slave_to_start.inc
+set default_master_connection = 'm2';
+include/wait_for_slave_to_start.inc
+set default_master_connection = '';
+include/wait_for_slave_to_start.inc
+select @@global.'m1'.replicate_ignore_table;
+@@global.'m1'.replicate_ignore_table
+a.t1
+select @@global.'m2'.replicate_ignore_table;
+@@global.'m2'.replicate_ignore_table
+b.t1
+select @@global.replicate_ignore_table;
+@@global.replicate_ignore_table
+c.t1
+use a;
+#No t1 table
+show tables;
+Tables_in_a
+t2
+use b;
+#No t1 table
+show tables;
+Tables_in_b
+t2
+use c;
+#No t1 table
+show tables;
+Tables_in_c
+t2
+#TEST
+STOP ALL SLAVES;
+Warnings:
+Note 1938 SLAVE 'm2' stopped
+Note 1938 SLAVE '' stopped
+Note 1938 SLAVE 'm1' stopped
+RESET SLAVE 'm1' ALL ;
+RESET SLAVE 'm2' ALL ;
+RESET SLAVE ALL ;
+drop database a;
+drop database b;
+drop database c;
+change master 'm1' to master_port=MYPORT_1 , master_host='127.0.0.1', master_user='root';
+change master 'm2' to master_port=MYPORT_2 , master_host='127.0.0.1', master_user='root';
+change master to master_port=MYPORT_3 , master_host='127.0.0.1', master_user='root';
+start all slaves;
+Warnings:
+Note 1937 SLAVE 'm2' started
+Note 1937 SLAVE '' started
+Note 1937 SLAVE 'm1' started
+set default_master_connection = 'm1';
+include/wait_for_slave_to_start.inc
+set default_master_connection = 'm2';
+include/wait_for_slave_to_start.inc
+set default_master_connection = '';
+include/wait_for_slave_to_start.inc
+#Replication Filter should be intact (t1 still not replicated)
+select @@global.'m1'.replicate_ignore_table;
+@@global.'m1'.replicate_ignore_table
+a.t1
+select @@global.'m2'.replicate_ignore_table;
+@@global.'m2'.replicate_ignore_table
+b.t1
+select @@global.replicate_ignore_table;
+@@global.replicate_ignore_table
+c.t1
+use a;
+#No t1 table
+show tables;
+Tables_in_a
+t2
+use b;
+#No t1 table
+show tables;
+Tables_in_b
+t2
+use c;
+#No t1 table
+show tables;
+Tables_in_c
+t2
+#CleanUp
+drop database a;
+drop database b;
+drop database c;
+stop all slaves;
+SET default_master_connection = "m1";
+include/wait_for_slave_to_stop.inc
+SET default_master_connection = "m2";
+include/wait_for_slave_to_stop.inc
+SET default_master_connection = "";
+include/wait_for_slave_to_stop.inc
diff --git a/mysql-test/suite/multi_source/mdev-8874.test b/mysql-test/suite/multi_source/mdev-8874.test
new file mode 100644
index 00000000000..d03c255b911
--- /dev/null
+++ b/mysql-test/suite/multi_source/mdev-8874.test
@@ -0,0 +1,141 @@
+--source include/not_embedded.inc
+--source include/have_innodb.inc
+--source include/have_debug.inc
+# MDEV-8874
+# In Named Master slave connection if we do reset slave 'connection_name' ALL and then
+# if we reconnect slave, replication filters are ignored.
+# This patch fixes this issue.
+--connect (server_1,127.0.0.1,root,,,$SERVER_MYPORT_1)
+--connect (server_2,127.0.0.1,root,,,$SERVER_MYPORT_2)
+--connect (server_3,127.0.0.1,root,,,$SERVER_MYPORT_3)
+--connect (server_4,127.0.0.1,root,,,$SERVER_MYPORT_4)
+
+--connection server_1
+create database a;
+use a;
+create table t1(a int);
+insert into t1 values(1);
+create table t2(a int);
+insert into t2 values(1);
+--save_master_pos
+
+--connection server_2
+create database b;
+use b;
+create table t1(a int);
+insert into t1 values(1);
+create table t2(a int);
+insert into t2 values(1);
+--save_master_pos
+
+--connection server_3
+create database c;
+use c;
+create table t1(a int);
+insert into t1 values(1);
+create table t2(a int);
+insert into t2 values(1);
+--save_master_pos
+
+--connection server_4
+--disable_warnings
+--replace_result $SERVER_MYPORT_1 MYPORT_1
+eval change master 'm1' to master_port=$SERVER_MYPORT_1 , master_host='127.0.0.1', master_user='root';
+--replace_result $SERVER_MYPORT_2 MYPORT_2
+eval change master 'm2' to master_port=$SERVER_MYPORT_2 , master_host='127.0.0.1', master_user='root';
+--replace_result $SERVER_MYPORT_3 MYPORT_3
+eval change master to master_port=$SERVER_MYPORT_3 , master_host='127.0.0.1', master_user='root';
+start all slaves;
+set default_master_connection = 'm1';
+--source include/wait_for_slave_to_start.inc
+set default_master_connection = 'm2';
+--source include/wait_for_slave_to_start.inc
+set default_master_connection = '';
+--source include/wait_for_slave_to_start.inc
+select @@global.'m1'.replicate_ignore_table;
+select @@global.'m2'.replicate_ignore_table;
+select @@global.replicate_ignore_table;
+
+--enable_warnings
+--sync_with_master 0,'m1'
+--sync_with_master 0,'m2'
+--sync_with_master 0,''
+use a;
+--echo #No t1 table
+show tables;
+use b;
+--echo #No t1 table
+show tables;
+use c;
+--echo #No t1 table
+show tables;
+--echo #TEST
+STOP ALL SLAVES;
+RESET SLAVE 'm1' ALL ;
+RESET SLAVE 'm2' ALL ;
+RESET SLAVE ALL ;
+drop database a;
+drop database b;
+drop database c;
+--replace_result $SERVER_MYPORT_1 MYPORT_1
+eval change master 'm1' to master_port=$SERVER_MYPORT_1 , master_host='127.0.0.1', master_user='root';
+--replace_result $SERVER_MYPORT_2 MYPORT_2
+eval change master 'm2' to master_port=$SERVER_MYPORT_2 , master_host='127.0.0.1', master_user='root';
+--replace_result $SERVER_MYPORT_3 MYPORT_3
+eval change master to master_port=$SERVER_MYPORT_3 , master_host='127.0.0.1', master_user='root';
+start all slaves;
+set default_master_connection = 'm1';
+--source include/wait_for_slave_to_start.inc
+set default_master_connection = 'm2';
+--source include/wait_for_slave_to_start.inc
+set default_master_connection = '';
+--source include/wait_for_slave_to_start.inc
+--sync_with_master 0,'m1'
+--sync_with_master 0,'m2'
+--sync_with_master 0,''
+
+--echo #Replication Filter should be intact (t1 still not replicated)
+select @@global.'m1'.replicate_ignore_table;
+select @@global.'m2'.replicate_ignore_table;
+select @@global.replicate_ignore_table;
+use a;
+--echo #No t1 table
+show tables;
+use b;
+--echo #No t1 table
+show tables;
+use c;
+--echo #No t1 table
+show tables;
+
+
+#--echo #restart the server
+#--source include/restart_mysqld.inc
+
+
+--echo #CleanUp
+--connection server_1
+drop database a;
+--save_master_pos
+
+--connection server_2
+drop database b;
+--save_master_pos
+
+--connection server_3
+drop database c;
+--save_master_pos
+
+--connection server_4
+--sync_with_master 0,'m1'
+--sync_with_master 0,'m2'
+--sync_with_master 0,''
+--disable_warnings
+stop all slaves;
+--enable_warnings
+SET default_master_connection = "m1";
+--source include/wait_for_slave_to_stop.inc
+SET default_master_connection = "m2";
+--source include/wait_for_slave_to_stop.inc
+SET default_master_connection = "";
+--source include/wait_for_slave_to_stop.inc
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index 70e60b1d4ad..7aea89337a7 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -122,8 +122,6 @@ Master_info::~Master_info()
*/
if (strncmp(connection_name.str, STRING_WITH_LEN("wsrep")))
#endif
- rpl_filters.delete_element(connection_name.str, connection_name.length,
- (void (*)(const char*, uchar*)) free_rpl_filter);
my_free(connection_name.str);
delete_dynamic(&ignore_server_ids);
mysql_mutex_destroy(&run_lock);
2
1

[Commits] 321a43d9ac5: MDEV-8874 Replication filters configured in my.cnf are ignored if slave reset and reconfigured
by sachin.setiyaï¼ mariadb.com 12 Jun '19
by sachin.setiyaï¼ mariadb.com 12 Jun '19
12 Jun '19
revision-id: 321a43d9ac586034790407dc519c72e90b3bff81 (mariadb-10.1.39-54-g321a43d9ac5)
parent(s): b003b0c934cf6b59358e31144c4f69cf34622fb8
author: Sachin
committer: Sachin
timestamp: 2019-06-06 12:03:45 +0530
message:
MDEV-8874 Replication filters configured in my.cnf are ignored if slave reset and reconfigured
Don't delete the rpl_filter on RESET SLAVE.
---
mysql-test/lib/My/Config.pm | 8 +-
mysql-test/suite/multi_source/mdev-8874.cnf | 25 +++++
mysql-test/suite/multi_source/mdev-8874.result | 93 +++++++++++++++++++
mysql-test/suite/multi_source/mdev-8874.test | 123 +++++++++++++++++++++++++
sql/rpl_mi.cc | 2 -
5 files changed, 248 insertions(+), 3 deletions(-)
diff --git a/mysql-test/lib/My/Config.pm b/mysql-test/lib/My/Config.pm
index 12647edf0a4..349a397ed6d 100644
--- a/mysql-test/lib/My/Config.pm
+++ b/mysql-test/lib/My/Config.pm
@@ -327,7 +327,13 @@ sub new {
# Skip comment
next;
}
-
+
+ # Replication Filter
+ elsif ( $line =~ /^([\w]+.[\w]+)\s*=\s*(.*)\s*/){
+ my $option= $1;
+ my $value= $2;
+ $self->insert($group_name, $option, $value);
+ }
else {
croak "Unexpected line '$line' found in '$path'";
}
diff --git a/mysql-test/suite/multi_source/mdev-8874.cnf b/mysql-test/suite/multi_source/mdev-8874.cnf
new file mode 100644
index 00000000000..01da70cbaa0
--- /dev/null
+++ b/mysql-test/suite/multi_source/mdev-8874.cnf
@@ -0,0 +1,25 @@
+!include my.cnf
+
+[mysqld.1]
+log-bin
+log-slave-updates
+
+[mysqld.2]
+log-bin
+log-slave-updates
+
+[mysqld.3]
+log-bin
+log-slave-updates
+
+[mysqld.4]
+server-id=4
+log-bin=server4-bin
+log-slave-updates
+m1.replicate_ignore_table='a.t1'
+m2.replicate_ignore_table='b.t1'
+replicate_ignore_table='c.t1'
+
+[ENV]
+SERVER_MYPORT_4= @mysqld.4.port
+SERVER_MYSOCK_4= @mysqld.4.socket
diff --git a/mysql-test/suite/multi_source/mdev-8874.result b/mysql-test/suite/multi_source/mdev-8874.result
new file mode 100644
index 00000000000..6fb364a3d2d
--- /dev/null
+++ b/mysql-test/suite/multi_source/mdev-8874.result
@@ -0,0 +1,93 @@
+create database a;
+use a;
+create table t1(a int);
+insert into t1 values(1);
+create table t2(a int);
+insert into t2 values(1);
+create database b;
+use b;
+create table t1(a int);
+insert into t1 values(1);
+create table t2(a int);
+insert into t2 values(1);
+create database c;
+use c;
+create table t1(a int);
+insert into t1 values(1);
+create table t2(a int);
+insert into t2 values(1);
+change master 'm1' to master_port=MYPORT_1 , master_host='127.0.0.1', master_user='root';
+change master 'm2' to master_port=MYPORT_2 , master_host='127.0.0.1', master_user='root';
+change master to master_port=MYPORT_3 , master_host='127.0.0.1', master_user='root';
+start all slaves;
+set default_master_connection = 'm1';
+include/wait_for_slave_to_start.inc
+set default_master_connection = 'm2';
+include/wait_for_slave_to_start.inc
+set default_master_connection = '';
+include/wait_for_slave_to_start.inc
+use a;
+show tables;
+Tables_in_a
+t2
+use b;
+show tables;
+Tables_in_b
+t2
+use c;
+show tables;
+Tables_in_c
+t2
+#TEST
+STOP ALL SLAVES;
+Warnings:
+Note 1938 SLAVE 'm2' stopped
+Note 1938 SLAVE '' stopped
+Note 1938 SLAVE 'm1' stopped
+RESET SLAVE 'm1' ALL ;
+RESET SLAVE 'm2' ALL ;
+RESET SLAVE ALL ;
+drop database a;
+drop database b;
+drop database c;
+change master 'm1' to master_port=MYPORT_1 , master_host='127.0.0.1', master_user='root';
+change master 'm2' to master_port=MYPORT_2 , master_host='127.0.0.1', master_user='root';
+change master to master_port=MYPORT_3 , master_host='127.0.0.1', master_user='root';
+start all slaves;
+Warnings:
+Note 1937 SLAVE 'm2' started
+Note 1937 SLAVE '' started
+Note 1937 SLAVE 'm1' started
+set default_master_connection = 'm1';
+include/wait_for_slave_to_start.inc
+set default_master_connection = 'm2';
+include/wait_for_slave_to_start.inc
+set default_master_connection = '';
+include/wait_for_slave_to_start.inc
+use a;
+show tables;
+Tables_in_a
+t2
+use b;
+show tables;
+Tables_in_b
+t2
+use c;
+show tables;
+Tables_in_c
+t2
+#CleanUp
+drop database a;
+drop database b;
+drop database c;
+stop all slaves;
+Warnings:
+Note 1938 SLAVE 'm2' stopped
+Note 1938 SLAVE '' stopped
+Note 1938 SLAVE 'm1' stopped
+SET default_master_connection = "m1";
+include/wait_for_slave_to_stop.inc
+SET default_master_connection = "m2";
+include/wait_for_slave_to_stop.inc
+SET default_master_connection = "";
+include/wait_for_slave_to_stop.inc
diff --git a/mysql-test/suite/multi_source/mdev-8874.test b/mysql-test/suite/multi_source/mdev-8874.test
new file mode 100644
index 00000000000..b6d1aafe139
--- /dev/null
+++ b/mysql-test/suite/multi_source/mdev-8874.test
@@ -0,0 +1,123 @@
+--source include/not_embedded.inc
+--source include/have_innodb.inc
+--source include/have_debug.inc
+
+--connect (server_1,127.0.0.1,root,,,$SERVER_MYPORT_1)
+--connect (server_2,127.0.0.1,root,,,$SERVER_MYPORT_2)
+--connect (server_3,127.0.0.1,root,,,$SERVER_MYPORT_3)
+--connect (server_4,127.0.0.1,root,,,$SERVER_MYPORT_4)
+
+--connection server_1
+create database a;
+use a;
+create table t1(a int);
+insert into t1 values(1);
+create table t2(a int);
+insert into t2 values(1);
+--save_master_pos
+
+--connection server_2
+create database b;
+use b;
+create table t1(a int);
+insert into t1 values(1);
+create table t2(a int);
+insert into t2 values(1);
+--save_master_pos
+
+--connection server_3
+create database c;
+use c;
+create table t1(a int);
+insert into t1 values(1);
+create table t2(a int);
+insert into t2 values(1);
+--save_master_pos
+
+--connection server_4
+--disable_warnings
+--replace_result $SERVER_MYPORT_1 MYPORT_1
+eval change master 'm1' to master_port=$SERVER_MYPORT_1 , master_host='127.0.0.1', master_user='root';
+--replace_result $SERVER_MYPORT_2 MYPORT_2
+eval change master 'm2' to master_port=$SERVER_MYPORT_2 , master_host='127.0.0.1', master_user='root';
+--replace_result $SERVER_MYPORT_3 MYPORT_3
+eval change master to master_port=$SERVER_MYPORT_3 , master_host='127.0.0.1', master_user='root';
+start all slaves;
+set default_master_connection = 'm1';
+--source include/wait_for_slave_to_start.inc
+set default_master_connection = 'm2';
+--source include/wait_for_slave_to_start.inc
+set default_master_connection = '';
+--source include/wait_for_slave_to_start.inc
+
+--enable_warnings
+--sync_with_master 0,'m1'
+--sync_with_master 0,'m2'
+--sync_with_master 0,''
+use a;
+show tables;
+use b;
+show tables;
+use c;
+show tables;
+--echo #TEST
+STOP ALL SLAVES;
+RESET SLAVE 'm1' ALL ;
+RESET SLAVE 'm2' ALL ;
+RESET SLAVE ALL ;
+drop database a;
+drop database b;
+drop database c;
+--replace_result $SERVER_MYPORT_1 MYPORT_1
+eval change master 'm1' to master_port=$SERVER_MYPORT_1 , master_host='127.0.0.1', master_user='root';
+--replace_result $SERVER_MYPORT_2 MYPORT_2
+eval change master 'm2' to master_port=$SERVER_MYPORT_2 , master_host='127.0.0.1', master_user='root';
+--replace_result $SERVER_MYPORT_3 MYPORT_3
+eval change master to master_port=$SERVER_MYPORT_3 , master_host='127.0.0.1', master_user='root';
+start all slaves;
+set default_master_connection = 'm1';
+--source include/wait_for_slave_to_start.inc
+set default_master_connection = 'm2';
+--source include/wait_for_slave_to_start.inc
+set default_master_connection = '';
+--source include/wait_for_slave_to_start.inc
+--sync_with_master 0,'m1'
+--sync_with_master 0,'m2'
+--sync_with_master 0,''
+
+use a;
+show tables;
+use b;
+show tables;
+use c;
+show tables;
+
+
+#--echo #restart the server
+#--source include/restart_mysqld.inc
+
+
+--echo #CleanUp
+--connection server_1
+drop database a;
+--save_master_pos
+
+--connection server_2
+drop database b;
+--save_master_pos
+
+--connection server_3
+drop database c;
+--save_master_pos
+
+--connection server_4
+--sync_with_master 0,'m1'
+--sync_with_master 0,'m2'
+--sync_with_master 0,''
+stop all slaves;
+SET default_master_connection = "m1";
+--source include/wait_for_slave_to_stop.inc
+SET default_master_connection = "m2";
+--source include/wait_for_slave_to_stop.inc
+SET default_master_connection = "";
+--source include/wait_for_slave_to_stop.inc
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index 70e60b1d4ad..7aea89337a7 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -122,8 +122,6 @@ Master_info::~Master_info()
*/
if (strncmp(connection_name.str, STRING_WITH_LEN("wsrep")))
#endif
- rpl_filters.delete_element(connection_name.str, connection_name.length,
- (void (*)(const char*, uchar*)) free_rpl_filter);
my_free(connection_name.str);
delete_dynamic(&ignore_server_ids);
mysql_mutex_destroy(&run_lock);
3
2

[Commits] 9ca54bd686f: MDEV-19705: Assertion `tmp >= 0' failed in best_access_path
by Varun 12 Jun '19
by Varun 12 Jun '19
12 Jun '19
revision-id: 9ca54bd686f22000116df952841821a7831318f5 (mariadb-10.4.5-43-g9ca54bd686f)
parent(s): 2fd82471aba9447e5490b24da5da89c33a21525e
author: Varun
committer: Varun
timestamp: 2019-06-12 13:56:32 +0530
message:
MDEV-19705: Assertion `tmp >= 0' failed in best_access_path
The reason for hitting the assert is that rec_per_key estimates have some garbage value.
So the solution to fix this would be for long unique keys to use use rec_per_key for only 1 keypart,
that means rec_per_key[0] would have the estimate.
---
mysql-test/main/long_unique.result | 17 ++++++++++++++++-
mysql-test/main/long_unique.test | 14 ++++++++++++++
sql/table.cc | 4 +++-
3 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/mysql-test/main/long_unique.result b/mysql-test/main/long_unique.result
index 3843ff4aff0..03a63d8e7b5 100644
--- a/mysql-test/main/long_unique.result
+++ b/mysql-test/main/long_unique.result
@@ -1140,7 +1140,7 @@ t1 0 a 1 a A NULL NULL NULL YES HASH
t1 0 a 2 c A NULL NULL NULL YES HASH
t1 0 b 1 b A NULL NULL NULL YES HASH
t1 0 b 2 d A NULL NULL NULL YES HASH
-t1 0 e 1 e A 0 NULL NULL YES BTREE
+t1 0 e 1 e A NULL NULL NULL YES BTREE
drop table t1;
#visibility of db_row_hash
create table t1 (a blob unique , b blob unique);
@@ -1462,4 +1462,19 @@ t1 CREATE TABLE `t1` (
KEY `pk` (`pk`,`a`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
+#
+# MDEV-19705: Assertion `tmp >= 0' failed in best_access_path
+#
+CREATE TABLE t1 (d varchar(10)) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('a'),('q');
+CREATE TABLE t2 (f varchar(10), a2 datetime, b int, a1 varchar(1024), pk int NOT NULL, PRIMARY KEY (pk), UNIQUE KEY (f,a1,a2), KEY f2 (f(4),a2)) ENGINE=MyISAM;
+INSERT INTO t2 VALUES ('aaa','1985-09-06',-163,'s',1),('bbb','1995-01-05',3,'pucaz',2),('ccc','0000-00-00',NULL,'help',3),('ddd',NULL,618,'v',4),('eee','1995-12-20',410,'m',5),('ffq','1976-06-12 20:02:56',NULL,'POKNC',6),('dddd','0000-00-00',-328,'hgsu',7);
+explain
+SELECT t2.b FROM t1 JOIN t2 ON t1.d = t2.f WHERE t2.pk >= 20;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 range PRIMARY,f,f2 PRIMARY 4 NULL 1 Using index condition
+1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (flat, BNL join)
+SELECT t2.b FROM t1 JOIN t2 ON t1.d = t2.f WHERE t2.pk >= 20;
+b
+drop table t1,t2;
set @@GLOBAL.max_allowed_packet= @allowed_packet;
diff --git a/mysql-test/main/long_unique.test b/mysql-test/main/long_unique.test
index a6bc68f54dc..c0bd77ca5c9 100644
--- a/mysql-test/main/long_unique.test
+++ b/mysql-test/main/long_unique.test
@@ -542,4 +542,18 @@ alter table t1 modify a varchar(1000);
show create table t1;
drop table t1;
+--echo #
+--echo # MDEV-19705: Assertion `tmp >= 0' failed in best_access_path
+--echo #
+
+CREATE TABLE t1 (d varchar(10)) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('a'),('q');
+
+CREATE TABLE t2 (f varchar(10), a2 datetime, b int, a1 varchar(1024), pk int NOT NULL, PRIMARY KEY (pk), UNIQUE KEY (f,a1,a2), KEY f2 (f(4),a2)) ENGINE=MyISAM;
+INSERT INTO t2 VALUES ('aaa','1985-09-06',-163,'s',1),('bbb','1995-01-05',3,'pucaz',2),('ccc','0000-00-00',NULL,'help',3),('ddd',NULL,618,'v',4),('eee','1995-12-20',410,'m',5),('ffq','1976-06-12 20:02:56',NULL,'POKNC',6),('dddd','0000-00-00',-328,'hgsu',7);
+explain
+SELECT t2.b FROM t1 JOIN t2 ON t1.d = t2.f WHERE t2.pk >= 20;
+SELECT t2.b FROM t1 JOIN t2 ON t1.d = t2.f WHERE t2.pk >= 20;
+drop table t1,t2;
+
set @@GLOBAL.max_allowed_packet= @allowed_packet;
diff --git a/sql/table.cc b/sql/table.cc
index 699102885c2..d8739b75af5 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -800,7 +800,8 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
{
if (strpos + (new_frm_ver >= 1 ? 9 : 7) >= frm_image_end)
return 1;
- *rec_per_key++=0;
+ if (!(keyinfo->algorithm == HA_KEY_ALG_LONG_HASH))
+ *rec_per_key++=0;
key_part->fieldnr= (uint16) (uint2korr(strpos) & FIELD_NR_MASK);
key_part->offset= (uint) uint2korr(strpos+2)-1;
key_part->key_type= (uint) uint2korr(strpos+5);
@@ -828,6 +829,7 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
{
keyinfo->key_length= HA_HASH_KEY_LENGTH_WITHOUT_NULL;
key_part++; // reserved for the hash value
+ *rec_per_key++=0;
}
/*
1
0

[Commits] 2e0fb93e6f3: MDEV-17614 INSERT on dup key update is replication unsafe
by sachin.setiyaï¼ mariadb.com 12 Jun '19
by sachin.setiyaï¼ mariadb.com 12 Jun '19
12 Jun '19
revision-id: 2e0fb93e6f3359ffe146f309e32c59922400b7c4 (mariadb-10.1.39-56-g2e0fb93e6f3)
parent(s): e7695f95ae714f3168ce953fd022ddfb40f03e67
author: Sachin
committer: Sachin
timestamp: 2019-06-12 11:47:26 +0530
message:
MDEV-17614 INSERT on dup key update is replication unsafe
Make INSERT...ON DUPLICATE KEY UPDATE unsafe while using stmt or mixed format
When there is more then one unique key.
Although there is two exception.
1. Auto Increment key is not counted because Innodb will get gap lock for
failed Insert and concurrent insert will get a next increment value. But if
user supplies auto inc value it can be unsafe.
2. Count only unique keys for which insertion is performed.
So this patch also addresses the bug id #72921
---
.../suite/rpl/r/rpl_known_bugs_detection.result | 20 ----
mysql-test/suite/rpl/r/rpl_mdev_17614.result | 98 +++++++++++++++++
.../suite/rpl/t/rpl_known_bugs_detection.test | 39 -------
mysql-test/suite/rpl/t/rpl_mdev_17614.test | 121 +++++++++++++++++++++
sql/sql_class.h | 28 ++---
sql/sql_insert.cc | 38 +++++++
6 files changed, 271 insertions(+), 73 deletions(-)
diff --git a/mysql-test/suite/rpl/r/rpl_known_bugs_detection.result b/mysql-test/suite/rpl/r/rpl_known_bugs_detection.result
index ea738b710fd..adef091ea3e 100644
--- a/mysql-test/suite/rpl/r/rpl_known_bugs_detection.result
+++ b/mysql-test/suite/rpl/r/rpl_known_bugs_detection.result
@@ -1,26 +1,6 @@
call mtr.add_suppression("Unsafe statement written to the binary log using statement format");
include/master-slave.inc
[connection master]
-call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.");
-CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT,
-UNIQUE(b));
-INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10;
-SELECT * FROM t1;
-a b
-1 10
-2 2
-call mtr.add_suppression("Slave SQL.*suffer.*http:..bugs.mysql.com.bug.php.id=24432");
-include/wait_for_slave_sql_error.inc [errno=1105]
-Last_SQL_Error = 'Error 'master may suffer from http://bugs.mysql.com/bug.php?id=24432 so slave stops; check error log on slave for more info' on query. Default database: 'test'. Query: 'INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10''
-SELECT * FROM t1;
-a b
-stop slave;
-include/wait_for_slave_to_stop.inc
-reset slave;
-reset master;
-drop table t1;
-start slave;
-include/wait_for_slave_to_start.inc
CREATE TABLE t1 (
id bigint(20) unsigned NOT NULL auto_increment,
field_1 int(10) unsigned NOT NULL,
diff --git a/mysql-test/suite/rpl/r/rpl_mdev_17614.result b/mysql-test/suite/rpl/r/rpl_mdev_17614.result
new file mode 100644
index 00000000000..28de23e28c9
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_mdev_17614.result
@@ -0,0 +1,98 @@
+include/master-slave.inc
+[connection master]
+call mtr.add_suppression("Unsafe statement written to the binary log using statement format");
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY , b INT,
+UNIQUE(b), c int) engine=innodb;
+INSERT INTO t1 VALUES (1, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+Warnings:
+Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
+INSERT INTO t1 VALUES(2, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+Warnings:
+Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
+COMMIT;
+SELECT * FROM t1;
+a b c
+1 1 2
+2 2 3
+include/wait_for_slave_sql_error.inc [errno=1062]
+Last_SQL_Error = 'Error 'Duplicate entry '1' for key 'b'' on query. Default database: 'test'. Query: 'INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c)''
+#Different value from server
+SELECT * FROM t1;
+a b c
+1 1 1
+2 2 3
+stop slave;
+include/wait_for_slave_to_stop.inc
+reset slave;
+reset master;
+drop table t1;
+start slave;
+include/wait_for_slave_to_start.inc
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
+UNIQUE(b), c int) engine=innodb;
+INSERT INTO t1 VALUES (default, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (default, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+INSERT INTO t1 VALUES(default, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+COMMIT;
+SELECT * FROM t1;
+a b c
+1 1 2
+3 2 3
+#same data as master
+SELECT * FROM t1;
+a b c
+1 1 2
+3 2 3
+drop table t1;
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT,
+UNIQUE(b), c int, d int ) engine=innodb;
+INSERT INTO t1 VALUES (1, 1, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (2, NULL, 2, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+Warnings:
+Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
+INSERT INTO t1 VALUES(3, NULL, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+Warnings:
+Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
+COMMIT;
+SELECT * FROM t1;
+a b c d
+1 1 1 1
+2 NULL 2 2
+3 NULL 2 3
+#same data as master
+SELECT * FROM t1;
+a b c d
+1 1 1 1
+2 NULL 2 2
+3 NULL 2 3
+drop table t1;
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
+UNIQUE(b), c int) engine=innodb;
+INSERT INTO t1 VALUES (1, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+INSERT INTO t1 VALUES(2, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+COMMIT;
+SELECT * FROM t1;
+a b c
+1 1 2
+2 2 3
+include/wait_for_slave_sql_error.inc [errno=1062]
+Last_SQL_Error = 'Error 'Duplicate entry '1' for key 'b'' on query. Default database: 'test'. Query: 'INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c)''
+#Different value from server
+SELECT * FROM t1;
+a b c
+1 1 1
+2 2 3
+stop slave;
+include/wait_for_slave_to_stop.inc
+reset slave;
+reset master;
+drop table t1;
+start slave;
+include/wait_for_slave_to_start.inc
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_known_bugs_detection.test b/mysql-test/suite/rpl/t/rpl_known_bugs_detection.test
index ab263ece407..5ea056d5f14 100644
--- a/mysql-test/suite/rpl/t/rpl_known_bugs_detection.test
+++ b/mysql-test/suite/rpl/t/rpl_known_bugs_detection.test
@@ -14,45 +14,6 @@ source include/have_binlog_checksum_off.inc;
source include/master-slave.inc;
-call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.");
-
-#
-# This is to test that slave properly detects if
-# master may suffer from:
-# BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values"
-# (i.e. on master, INSERT ON DUPLICATE KEY UPDATE is used and manipulates
-# an auto_increment column, and is binlogged statement-based).
-#
-
-# testcase with INSERT VALUES
-CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT,
-UNIQUE(b));
-sync_slave_with_master;
-connection master;
-INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10;
-SELECT * FROM t1;
-connection slave;
-
-# show the error message
-#1105 = ER_UNKNOWN_ERROR
---let $slave_sql_errno= 1105
---let $show_slave_sql_error= 1
-call mtr.add_suppression("Slave SQL.*suffer.*http:..bugs.mysql.com.bug.php.id=24432");
---source include/wait_for_slave_sql_error.inc
-# show that it was not replicated
-SELECT * FROM t1;
-
-# restart replication for the next testcase
-stop slave;
---source include/wait_for_slave_to_stop.inc
-reset slave;
-connection master;
-reset master;
-drop table t1;
-connection slave;
-start slave;
---source include/wait_for_slave_to_start.inc
-
# testcase with INSERT SELECT
connection master;
CREATE TABLE t1 (
diff --git a/mysql-test/suite/rpl/t/rpl_mdev_17614.test b/mysql-test/suite/rpl/t/rpl_mdev_17614.test
new file mode 100644
index 00000000000..9b86c8c15b5
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_mdev_17614.test
@@ -0,0 +1,121 @@
+source include/have_debug.inc;
+source include/have_innodb.inc;
+-- source include/have_binlog_format_statement.inc
+source include/master-slave.inc;
+# MDEV-17614
+# INSERT on dup key update is replication unsafe
+# There can be three case
+# 1. 2 unique key, Replication is unsafe.
+# 2. 2 unique key , with one auto increment key, Safe to replicate because Innodb will acquire gap lock
+# 3. n no of unique keys (n>1) but insert is only in 1 unique key
+# 4. 2 unique key , with one auto increment key(but user gives auto inc value), unsafe to replicate
+
+# Case 1
+call mtr.add_suppression("Unsafe statement written to the binary log using statement format");
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY , b INT,
+UNIQUE(b), c int) engine=innodb;
+sync_slave_with_master;
+connection master;
+INSERT INTO t1 VALUES (1, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+ --connection master1
+ INSERT INTO t1 VALUES(2, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+--connection master
+COMMIT;
+SELECT * FROM t1;
+--connection slave
+# show the error message
+--let $slave_sql_errno= 1062
+--let $show_slave_sql_error= 1
+--source include/wait_for_slave_sql_error.inc
+--echo #Different value from server
+SELECT * FROM t1;
+
+# restart replication for the next testcase
+stop slave;
+--source include/wait_for_slave_to_stop.inc
+reset slave;
+connection master;
+reset master;
+drop table t1;
+connection slave;
+start slave;
+--source include/wait_for_slave_to_start.inc
+# Case 2
+--connection master
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
+UNIQUE(b), c int) engine=innodb;
+sync_slave_with_master;
+connection master;
+INSERT INTO t1 VALUES (default, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (default, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+ --connection master1
+ INSERT INTO t1 VALUES(default, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+--connection master
+COMMIT;
+SELECT * FROM t1;
+--sync_slave_with_master
+--echo #same data as master
+SELECT * FROM t1;
+
+connection master;
+drop table t1;
+--sync_slave_with_master
+
+# Case 3
+--connection master
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT,
+UNIQUE(b), c int, d int ) engine=innodb;
+sync_slave_with_master;
+connection master;
+INSERT INTO t1 VALUES (1, 1, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (2, NULL, 2, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+ --connection master1
+ INSERT INTO t1 VALUES(3, NULL, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+--connection master
+COMMIT;
+SELECT * FROM t1;
+--sync_slave_with_master
+--echo #same data as master
+SELECT * FROM t1;
+connection master;
+drop table t1;
+--sync_slave_with_master
+
+# Case 4
+--connection master
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
+UNIQUE(b), c int) engine=innodb;
+sync_slave_with_master;
+connection master;
+INSERT INTO t1 VALUES (1, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+ --connection master1
+ INSERT INTO t1 VALUES(2, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+--connection master
+COMMIT;
+SELECT * FROM t1;
+--connection slave
+# show the error message
+--let $slave_sql_errno= 1062
+--let $show_slave_sql_error= 1
+--source include/wait_for_slave_sql_error.inc
+--echo #Different value from server
+SELECT * FROM t1;
+
+# restart replication for the next testcase
+stop slave;
+--source include/wait_for_slave_to_stop.inc
+reset slave;
+connection master;
+reset master;
+drop table t1;
+connection slave;
+start slave;
+--source include/wait_for_slave_to_start.inc
+
+--source include/rpl_end.inc
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 1cb516c0656..24545e2e140 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -2208,6 +2208,20 @@ class THD :public Statement,
/* container for handler's private per-connection data */
Ha_data ha_data[MAX_HA];
+ /**
+ Bit field for the state of binlog warnings.
+
+ The first Lex::BINLOG_STMT_UNSAFE_COUNT bits list all types of
+ unsafeness that the current statement has.
+
+ This must be a member of THD and not of LEX, because warnings are
+ detected and issued in different places (@c
+ decide_logging_format() and @c binlog_query(), respectively).
+ Between these calls, the THD->lex object may change; e.g., if a
+ stored routine is invoked. Only THD persists between the calls.
+ */
+ uint32 binlog_unsafe_warning_flags;
+
#ifndef MYSQL_CLIENT
binlog_cache_mngr * binlog_setup_trx_data();
@@ -2317,20 +2331,6 @@ class THD :public Statement,
*/
enum_binlog_format current_stmt_binlog_format;
- /**
- Bit field for the state of binlog warnings.
-
- The first Lex::BINLOG_STMT_UNSAFE_COUNT bits list all types of
- unsafeness that the current statement has.
-
- This must be a member of THD and not of LEX, because warnings are
- detected and issued in different places (@c
- decide_logging_format() and @c binlog_query(), respectively).
- Between these calls, the THD->lex object may change; e.g., if a
- stored routine is invoked. Only THD persists between the calls.
- */
- uint32 binlog_unsafe_warning_flags;
-
/*
Number of outstanding table maps, i.e., table maps in the
transaction cache.
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 0be8abf4842..fa1b455f187 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1025,6 +1025,44 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
error= 1;
break;
}
+ /*
+ INSERT...ON DUPLICATE KEY UPDATE on a table with more than one unique keys
+ can be unsafe.
+ */
+ if(thd->wsrep_binlog_format() <= BINLOG_FORMAT_STMT &&
+ !thd->is_current_stmt_binlog_format_row() &&
+ !thd->lex->is_stmt_unsafe() &&
+ thd->lex->sql_command == SQLCOM_INSERT &&
+ thd->lex->duplicates == DUP_UPDATE)
+ {
+ uint unique_keys= 0;
+ uint keys= table->s->keys, i= 0;
+ Field *field;
+ for (KEY* keyinfo= table->s->key_info;
+ i < keys && unique_keys <= 1; i++, keyinfo++)
+ if (keyinfo->flags & HA_NOSAME &&
+ !(keyinfo->key_part->field->flags & AUTO_INCREMENT_FLAG &&
+ //User given auto inc can be unsafe
+ !keyinfo->key_part->field->val_int()))
+ {
+ for (uint j= 0; j < keyinfo->user_defined_key_parts; j++)
+ {
+ field= keyinfo->key_part[j].field;
+ if(!bitmap_is_set(table->write_set,field->field_index))
+ goto exit;
+ }
+ unique_keys++;
+exit:;
+ }
+
+ if (unique_keys > 1)
+ {
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS);
+ thd->binlog_unsafe_warning_flags|= thd->lex->get_stmt_unsafe_flags();
+ thd->set_current_stmt_binlog_format_row_if_mixed();
+ }
+
+ }
#ifndef EMBEDDED_LIBRARY
if (lock_type == TL_WRITE_DELAYED)
{
1
0

2c50b269ac2: MDEV-17603 REPLACE and INSERT…ON DUPLICATE KEY UPDATE are deadlock-prone in statement-based replication
by sachin.setiyaï¼ mariadb.com 12 Jun '19
by sachin.setiyaï¼ mariadb.com 12 Jun '19
12 Jun '19
revision-id: 2c50b269ac247a0c7d86b0bec80bf38fb5499024 (mariadb-10.1.39-56-g2c50b269ac2)
parent(s): e7695f95ae714f3168ce953fd022ddfb40f03e67
author: Sachin
committer: Sachin
timestamp: 2019-06-12 11:45:43 +0530
message:
MDEV-17603 REPLACE and INSERT…ON DUPLICATE KEY UPDATE are deadlock-prone in statement-based replication
Make INSERT...ON DUPLICATE KEY UPDATE unsafe while using stmt or mixed format
When there is more then one unique key.
Although there is two exception.
1. Auto Increment key is not counted because Innodb will get gap lock for
failed Insert and concurrent insert will get a next increment value. But if
user supplies auto inc value it can be unsafe.
2. Count only unique keys for which insertion is performed.
So this patch also addresses the bug id #72921
---
.../suite/rpl/r/rpl_known_bugs_detection.result | 20 ----
mysql-test/suite/rpl/r/rpl_mdev_17614.result | 98 +++++++++++++++++
.../suite/rpl/t/rpl_known_bugs_detection.test | 39 -------
mysql-test/suite/rpl/t/rpl_mdev_17614.test | 121 +++++++++++++++++++++
sql/sql_class.h | 28 ++---
sql/sql_insert.cc | 38 +++++++
6 files changed, 271 insertions(+), 73 deletions(-)
diff --git a/mysql-test/suite/rpl/r/rpl_known_bugs_detection.result b/mysql-test/suite/rpl/r/rpl_known_bugs_detection.result
index ea738b710fd..adef091ea3e 100644
--- a/mysql-test/suite/rpl/r/rpl_known_bugs_detection.result
+++ b/mysql-test/suite/rpl/r/rpl_known_bugs_detection.result
@@ -1,26 +1,6 @@
call mtr.add_suppression("Unsafe statement written to the binary log using statement format");
include/master-slave.inc
[connection master]
-call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.");
-CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT,
-UNIQUE(b));
-INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10;
-SELECT * FROM t1;
-a b
-1 10
-2 2
-call mtr.add_suppression("Slave SQL.*suffer.*http:..bugs.mysql.com.bug.php.id=24432");
-include/wait_for_slave_sql_error.inc [errno=1105]
-Last_SQL_Error = 'Error 'master may suffer from http://bugs.mysql.com/bug.php?id=24432 so slave stops; check error log on slave for more info' on query. Default database: 'test'. Query: 'INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10''
-SELECT * FROM t1;
-a b
-stop slave;
-include/wait_for_slave_to_stop.inc
-reset slave;
-reset master;
-drop table t1;
-start slave;
-include/wait_for_slave_to_start.inc
CREATE TABLE t1 (
id bigint(20) unsigned NOT NULL auto_increment,
field_1 int(10) unsigned NOT NULL,
diff --git a/mysql-test/suite/rpl/r/rpl_mdev_17614.result b/mysql-test/suite/rpl/r/rpl_mdev_17614.result
new file mode 100644
index 00000000000..28de23e28c9
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_mdev_17614.result
@@ -0,0 +1,98 @@
+include/master-slave.inc
+[connection master]
+call mtr.add_suppression("Unsafe statement written to the binary log using statement format");
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY , b INT,
+UNIQUE(b), c int) engine=innodb;
+INSERT INTO t1 VALUES (1, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+Warnings:
+Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
+INSERT INTO t1 VALUES(2, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+Warnings:
+Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
+COMMIT;
+SELECT * FROM t1;
+a b c
+1 1 2
+2 2 3
+include/wait_for_slave_sql_error.inc [errno=1062]
+Last_SQL_Error = 'Error 'Duplicate entry '1' for key 'b'' on query. Default database: 'test'. Query: 'INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c)''
+#Different value from server
+SELECT * FROM t1;
+a b c
+1 1 1
+2 2 3
+stop slave;
+include/wait_for_slave_to_stop.inc
+reset slave;
+reset master;
+drop table t1;
+start slave;
+include/wait_for_slave_to_start.inc
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
+UNIQUE(b), c int) engine=innodb;
+INSERT INTO t1 VALUES (default, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (default, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+INSERT INTO t1 VALUES(default, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+COMMIT;
+SELECT * FROM t1;
+a b c
+1 1 2
+3 2 3
+#same data as master
+SELECT * FROM t1;
+a b c
+1 1 2
+3 2 3
+drop table t1;
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT,
+UNIQUE(b), c int, d int ) engine=innodb;
+INSERT INTO t1 VALUES (1, 1, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (2, NULL, 2, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+Warnings:
+Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
+INSERT INTO t1 VALUES(3, NULL, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+Warnings:
+Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
+COMMIT;
+SELECT * FROM t1;
+a b c d
+1 1 1 1
+2 NULL 2 2
+3 NULL 2 3
+#same data as master
+SELECT * FROM t1;
+a b c d
+1 1 1 1
+2 NULL 2 2
+3 NULL 2 3
+drop table t1;
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
+UNIQUE(b), c int) engine=innodb;
+INSERT INTO t1 VALUES (1, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+INSERT INTO t1 VALUES(2, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+COMMIT;
+SELECT * FROM t1;
+a b c
+1 1 2
+2 2 3
+include/wait_for_slave_sql_error.inc [errno=1062]
+Last_SQL_Error = 'Error 'Duplicate entry '1' for key 'b'' on query. Default database: 'test'. Query: 'INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c)''
+#Different value from server
+SELECT * FROM t1;
+a b c
+1 1 1
+2 2 3
+stop slave;
+include/wait_for_slave_to_stop.inc
+reset slave;
+reset master;
+drop table t1;
+start slave;
+include/wait_for_slave_to_start.inc
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_known_bugs_detection.test b/mysql-test/suite/rpl/t/rpl_known_bugs_detection.test
index ab263ece407..5ea056d5f14 100644
--- a/mysql-test/suite/rpl/t/rpl_known_bugs_detection.test
+++ b/mysql-test/suite/rpl/t/rpl_known_bugs_detection.test
@@ -14,45 +14,6 @@ source include/have_binlog_checksum_off.inc;
source include/master-slave.inc;
-call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.");
-
-#
-# This is to test that slave properly detects if
-# master may suffer from:
-# BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values"
-# (i.e. on master, INSERT ON DUPLICATE KEY UPDATE is used and manipulates
-# an auto_increment column, and is binlogged statement-based).
-#
-
-# testcase with INSERT VALUES
-CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT,
-UNIQUE(b));
-sync_slave_with_master;
-connection master;
-INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10;
-SELECT * FROM t1;
-connection slave;
-
-# show the error message
-#1105 = ER_UNKNOWN_ERROR
---let $slave_sql_errno= 1105
---let $show_slave_sql_error= 1
-call mtr.add_suppression("Slave SQL.*suffer.*http:..bugs.mysql.com.bug.php.id=24432");
---source include/wait_for_slave_sql_error.inc
-# show that it was not replicated
-SELECT * FROM t1;
-
-# restart replication for the next testcase
-stop slave;
---source include/wait_for_slave_to_stop.inc
-reset slave;
-connection master;
-reset master;
-drop table t1;
-connection slave;
-start slave;
---source include/wait_for_slave_to_start.inc
-
# testcase with INSERT SELECT
connection master;
CREATE TABLE t1 (
diff --git a/mysql-test/suite/rpl/t/rpl_mdev_17614.test b/mysql-test/suite/rpl/t/rpl_mdev_17614.test
new file mode 100644
index 00000000000..9b86c8c15b5
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_mdev_17614.test
@@ -0,0 +1,121 @@
+source include/have_debug.inc;
+source include/have_innodb.inc;
+-- source include/have_binlog_format_statement.inc
+source include/master-slave.inc;
+# MDEV-17614
+# INSERT on dup key update is replication unsafe
+# There can be three case
+# 1. 2 unique key, Replication is unsafe.
+# 2. 2 unique key , with one auto increment key, Safe to replicate because Innodb will acquire gap lock
+# 3. n no of unique keys (n>1) but insert is only in 1 unique key
+# 4. 2 unique key , with one auto increment key(but user gives auto inc value), unsafe to replicate
+
+# Case 1
+call mtr.add_suppression("Unsafe statement written to the binary log using statement format");
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY , b INT,
+UNIQUE(b), c int) engine=innodb;
+sync_slave_with_master;
+connection master;
+INSERT INTO t1 VALUES (1, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+ --connection master1
+ INSERT INTO t1 VALUES(2, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+--connection master
+COMMIT;
+SELECT * FROM t1;
+--connection slave
+# show the error message
+--let $slave_sql_errno= 1062
+--let $show_slave_sql_error= 1
+--source include/wait_for_slave_sql_error.inc
+--echo #Different value from server
+SELECT * FROM t1;
+
+# restart replication for the next testcase
+stop slave;
+--source include/wait_for_slave_to_stop.inc
+reset slave;
+connection master;
+reset master;
+drop table t1;
+connection slave;
+start slave;
+--source include/wait_for_slave_to_start.inc
+# Case 2
+--connection master
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
+UNIQUE(b), c int) engine=innodb;
+sync_slave_with_master;
+connection master;
+INSERT INTO t1 VALUES (default, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (default, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+ --connection master1
+ INSERT INTO t1 VALUES(default, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+--connection master
+COMMIT;
+SELECT * FROM t1;
+--sync_slave_with_master
+--echo #same data as master
+SELECT * FROM t1;
+
+connection master;
+drop table t1;
+--sync_slave_with_master
+
+# Case 3
+--connection master
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT,
+UNIQUE(b), c int, d int ) engine=innodb;
+sync_slave_with_master;
+connection master;
+INSERT INTO t1 VALUES (1, 1, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (2, NULL, 2, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+ --connection master1
+ INSERT INTO t1 VALUES(3, NULL, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+--connection master
+COMMIT;
+SELECT * FROM t1;
+--sync_slave_with_master
+--echo #same data as master
+SELECT * FROM t1;
+connection master;
+drop table t1;
+--sync_slave_with_master
+
+# Case 4
+--connection master
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
+UNIQUE(b), c int) engine=innodb;
+sync_slave_with_master;
+connection master;
+INSERT INTO t1 VALUES (1, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+ --connection master1
+ INSERT INTO t1 VALUES(2, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+--connection master
+COMMIT;
+SELECT * FROM t1;
+--connection slave
+# show the error message
+--let $slave_sql_errno= 1062
+--let $show_slave_sql_error= 1
+--source include/wait_for_slave_sql_error.inc
+--echo #Different value from server
+SELECT * FROM t1;
+
+# restart replication for the next testcase
+stop slave;
+--source include/wait_for_slave_to_stop.inc
+reset slave;
+connection master;
+reset master;
+drop table t1;
+connection slave;
+start slave;
+--source include/wait_for_slave_to_start.inc
+
+--source include/rpl_end.inc
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 1cb516c0656..24545e2e140 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -2208,6 +2208,20 @@ class THD :public Statement,
/* container for handler's private per-connection data */
Ha_data ha_data[MAX_HA];
+ /**
+ Bit field for the state of binlog warnings.
+
+ The first Lex::BINLOG_STMT_UNSAFE_COUNT bits list all types of
+ unsafeness that the current statement has.
+
+ This must be a member of THD and not of LEX, because warnings are
+ detected and issued in different places (@c
+ decide_logging_format() and @c binlog_query(), respectively).
+ Between these calls, the THD->lex object may change; e.g., if a
+ stored routine is invoked. Only THD persists between the calls.
+ */
+ uint32 binlog_unsafe_warning_flags;
+
#ifndef MYSQL_CLIENT
binlog_cache_mngr * binlog_setup_trx_data();
@@ -2317,20 +2331,6 @@ class THD :public Statement,
*/
enum_binlog_format current_stmt_binlog_format;
- /**
- Bit field for the state of binlog warnings.
-
- The first Lex::BINLOG_STMT_UNSAFE_COUNT bits list all types of
- unsafeness that the current statement has.
-
- This must be a member of THD and not of LEX, because warnings are
- detected and issued in different places (@c
- decide_logging_format() and @c binlog_query(), respectively).
- Between these calls, the THD->lex object may change; e.g., if a
- stored routine is invoked. Only THD persists between the calls.
- */
- uint32 binlog_unsafe_warning_flags;
-
/*
Number of outstanding table maps, i.e., table maps in the
transaction cache.
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 0be8abf4842..fa1b455f187 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1025,6 +1025,44 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
error= 1;
break;
}
+ /*
+ INSERT...ON DUPLICATE KEY UPDATE on a table with more than one unique keys
+ can be unsafe.
+ */
+ if(thd->wsrep_binlog_format() <= BINLOG_FORMAT_STMT &&
+ !thd->is_current_stmt_binlog_format_row() &&
+ !thd->lex->is_stmt_unsafe() &&
+ thd->lex->sql_command == SQLCOM_INSERT &&
+ thd->lex->duplicates == DUP_UPDATE)
+ {
+ uint unique_keys= 0;
+ uint keys= table->s->keys, i= 0;
+ Field *field;
+ for (KEY* keyinfo= table->s->key_info;
+ i < keys && unique_keys <= 1; i++, keyinfo++)
+ if (keyinfo->flags & HA_NOSAME &&
+ !(keyinfo->key_part->field->flags & AUTO_INCREMENT_FLAG &&
+ //User given auto inc can be unsafe
+ !keyinfo->key_part->field->val_int()))
+ {
+ for (uint j= 0; j < keyinfo->user_defined_key_parts; j++)
+ {
+ field= keyinfo->key_part[j].field;
+ if(!bitmap_is_set(table->write_set,field->field_index))
+ goto exit;
+ }
+ unique_keys++;
+exit:;
+ }
+
+ if (unique_keys > 1)
+ {
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS);
+ thd->binlog_unsafe_warning_flags|= thd->lex->get_stmt_unsafe_flags();
+ thd->set_current_stmt_binlog_format_row_if_mixed();
+ }
+
+ }
#ifndef EMBEDDED_LIBRARY
if (lock_type == TL_WRITE_DELAYED)
{
1
0