Commit 9b29bda0 authored by Jan Lindström's avatar Jan Lindström

Merge remote-tracking branch 'origin/5.5-galera' into 10.0-galera

parents c5a8583b e88e26b4
CREATE TABLE p (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE c (f1 INTEGER PRIMARY KEY, p_id INTEGER) ENGINE=INNODB;
INSERT INTO p VALUES (1, 0);
INSERT INTO p VALUES (2, 0);
INSERT INTO c VALUES (1, 1);
INSERT INTO c VALUES (2, 2);
SET AUTOCOMMIT=ON;
START TRANSACTION;
UPDATE p SET f1 = f1 + 100;
SET SESSION wsrep_sync_wait = 0;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
ALTER TABLE c ADD FOREIGN KEY (p_id) REFERENCES p(f1);
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'dbug=';
SET GLOBAL wsrep_provider_options = 'dbug=d,local_monitor_enter_sync';
COMMIT;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
SET GLOBAL wsrep_provider_options = 'signal=local_monitor_enter_sync';
SET GLOBAL wsrep_provider_options = 'dbug=';
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
SELECT * FROM p;
f1 f2
1 0
2 0
SELECT * FROM c;
f1 p_id
1 1
2 2
DROP TABLE c;
DROP TABLE p;
CREATE TABLE p1 (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE p2 (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE c (f1 INTEGER PRIMARY KEY, p_id1 INTEGER, p_id2 INTEGER) ENGINE=INNODB;
INSERT INTO p1 VALUES (1, 0), (2, 0);
INSERT INTO p2 VALUES (1, 0), (2, 0);
INSERT INTO c VALUES (1, 1, 1);
INSERT INTO c VALUES (2, 2, 2);
SET AUTOCOMMIT=ON;
START TRANSACTION;
UPDATE p1 SET f1 = f1 + 100;
SET SESSION wsrep_sync_wait = 0;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
ALTER TABLE c ADD FOREIGN KEY (p_id1) REFERENCES p1(f1), ADD FOREIGN KEY (p_id2) REFERENCES p2(f1);
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'dbug=';
SET GLOBAL wsrep_provider_options = 'dbug=d,local_monitor_enter_sync';
COMMIT;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
SET GLOBAL wsrep_provider_options = 'signal=local_monitor_enter_sync';
SET GLOBAL wsrep_provider_options = 'dbug=';
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
SELECT * FROM p1;
f1 f2
1 0
2 0
SELECT * FROM p2;
f1 f2
1 0
2 0
SELECT * FROM c;
f1 p_id1 p_id2
1 1 1
2 2 2
DROP TABLE c;
DROP TABLE p1;
DROP TABLE p2;
CREATE TABLE p1 (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE p2 (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE c (f1 INTEGER PRIMARY KEY, p_id1 INTEGER, p_id2 INTEGER) ENGINE=INNODB;
INSERT INTO p1 VALUES (1, 0), (2, 0);
INSERT INTO p2 VALUES (1, 0), (2, 0);
INSERT INTO c VALUES (1, 1, 1);
INSERT INTO c VALUES (2, 2, 2);
SET AUTOCOMMIT=ON;
START TRANSACTION;
UPDATE p2 SET f1 = f1 + 100;
SET SESSION wsrep_sync_wait = 0;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
ALTER TABLE c ADD FOREIGN KEY (p_id1) REFERENCES p1(f1), ADD FOREIGN KEY (p_id2) REFERENCES p2(f1);
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'dbug=';
SET GLOBAL wsrep_provider_options = 'dbug=d,local_monitor_enter_sync';
COMMIT;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
SET GLOBAL wsrep_provider_options = 'signal=local_monitor_enter_sync';
SET GLOBAL wsrep_provider_options = 'dbug=';
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
SELECT * FROM p1;
f1 f2
1 0
2 0
SELECT * FROM p2;
f1 f2
1 0
2 0
SELECT * FROM c;
f1 p_id1 p_id2
1 1 1
2 2 2
DROP TABLE c;
DROP TABLE p1;
DROP TABLE p2;
--source include/galera_cluster.inc
--source include/have_innodb.inc
--source include/have_debug_sync.inc
--source suite/galera/include/galera_have_debug_sync.inc
# Open connection node_1a here, MW-369.inc will use it later
--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1
#
# Test the scenario where a foreign key is added to an existing child table, and
# concurrently UPDATE the parent table so that it violates the constraint.
#
# We expect that ALTER TABLE ADD FOREIGN KEY adds a table level key on both
# parent and child table. And therefore we also expect the UPDATE to fail
# certification.
#
--connection node_1
CREATE TABLE p (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE c (f1 INTEGER PRIMARY KEY, p_id INTEGER) ENGINE=INNODB;
INSERT INTO p VALUES (1, 0);
INSERT INTO p VALUES (2, 0);
INSERT INTO c VALUES (1, 1);
INSERT INTO c VALUES (2, 2);
--let $mw_369_parent_query = UPDATE p SET f1 = f1 + 100
--let $mw_369_child_query = ALTER TABLE c ADD FOREIGN KEY (p_id) REFERENCES p(f1)
--source MW-369.inc
# Expect certification failure
--connection node_1
--error ER_LOCK_DEADLOCK
--reap
--connection node_2
SELECT * FROM p;
SELECT * FROM c;
DROP TABLE c;
DROP TABLE p;
#
# Same as above, except that two foreign keys pointing to different parent
# tables are added, p1 and p2. Concurrently UPDATE p1.
#
# Expect certification error on UPDATE.
#
--connection node_1
CREATE TABLE p1 (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE p2 (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE c (f1 INTEGER PRIMARY KEY, p_id1 INTEGER, p_id2 INTEGER) ENGINE=INNODB;
INSERT INTO p1 VALUES (1, 0), (2, 0);
INSERT INTO p2 VALUES (1, 0), (2, 0);
INSERT INTO c VALUES (1, 1, 1);
INSERT INTO c VALUES (2, 2, 2);
--let $mw_369_parent_query = UPDATE p1 SET f1 = f1 + 100
--let $mw_369_child_query = ALTER TABLE c ADD FOREIGN KEY (p_id1) REFERENCES p1(f1), ADD FOREIGN KEY (p_id2) REFERENCES p2(f1)
--source MW-369.inc
# Expect certification failure
--connection node_1
--error ER_LOCK_DEADLOCK
--reap
--connection node_2
SELECT * FROM p1;
SELECT * FROM p2;
SELECT * FROM c;
DROP TABLE c;
DROP TABLE p1;
DROP TABLE p2;
#
# Same as above, except that UPDATE is on p2.
#
--connection node_1
CREATE TABLE p1 (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE p2 (f1 INTEGER PRIMARY KEY, f2 INTEGER) ENGINE=INNODB;
CREATE TABLE c (f1 INTEGER PRIMARY KEY, p_id1 INTEGER, p_id2 INTEGER) ENGINE=INNODB;
INSERT INTO p1 VALUES (1, 0), (2, 0);
INSERT INTO p2 VALUES (1, 0), (2, 0);
INSERT INTO c VALUES (1, 1, 1);
INSERT INTO c VALUES (2, 2, 2);
--let $mw_369_parent_query = UPDATE p2 SET f1 = f1 + 100
--let $mw_369_child_query = ALTER TABLE c ADD FOREIGN KEY (p_id1) REFERENCES p1(f1), ADD FOREIGN KEY (p_id2) REFERENCES p2(f1)
--source MW-369.inc
# Expect certification failure
--connection node_1
--error ER_LOCK_DEADLOCK
--reap
--connection node_2
SELECT * FROM p1;
SELECT * FROM p2;
SELECT * FROM c;
DROP TABLE c;
DROP TABLE p1;
DROP TABLE p2;
...@@ -311,9 +311,13 @@ bool Sql_cmd_alter_table::execute(THD *thd) ...@@ -311,9 +311,13 @@ bool Sql_cmd_alter_table::execute(THD *thd)
if ((!thd->is_current_stmt_binlog_format_row() || if ((!thd->is_current_stmt_binlog_format_row() ||
!find_temporary_table(thd, first_table))) !find_temporary_table(thd, first_table)))
{ {
WSREP_TO_ISOLATION_BEGIN(((lex->name.str) ? select_lex->db : NULL), WSREP_TO_ISOLATION_BEGIN_ALTER(((lex->name.str) ? select_lex->db : NULL),
((lex->name.str) ? lex->name.str : NULL), ((lex->name.str) ? lex->name.str : NULL),
first_table); first_table,
&alter_info);
thd->variables.auto_increment_offset = 1;
thd->variables.auto_increment_increment = 1;
} }
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
result= mysql_alter_table(thd, select_lex->db, lex->name.str, result= mysql_alter_table(thd, select_lex->db, lex->name.str,
...@@ -326,10 +330,13 @@ bool Sql_cmd_alter_table::execute(THD *thd) ...@@ -326,10 +330,13 @@ bool Sql_cmd_alter_table::execute(THD *thd)
DBUG_RETURN(result); DBUG_RETURN(result);
DBUG_RETURN(result);
#ifdef WITH_WSREP #ifdef WITH_WSREP
error: error:
WSREP_WARN("ALTER TABLE isolation failure"); {
DBUG_RETURN(TRUE); WSREP_WARN("ALTER TABLE isolation failure");
DBUG_RETURN(TRUE);
}
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
} }
......
...@@ -215,6 +215,11 @@ inline bool is_supported_parser_charset(CHARSET_INFO *cs) ...@@ -215,6 +215,11 @@ inline bool is_supported_parser_charset(CHARSET_INFO *cs)
#define WSREP_TO_ISOLATION_BEGIN(db_, table_, table_list_) \ #define WSREP_TO_ISOLATION_BEGIN(db_, table_, table_list_) \
if (WSREP(thd) && wsrep_to_isolation_begin(thd, db_, table_, table_list_)) goto error; if (WSREP(thd) && wsrep_to_isolation_begin(thd, db_, table_, table_list_)) goto error;
#define WSREP_TO_ISOLATION_BEGIN_ALTER(db_, table_, table_list_, alter_info_) \
if (WSREP(thd) && wsrep_to_isolation_begin(thd, db_, table_, \
table_list_, alter_info_)) \
goto error;
#define WSREP_TO_ISOLATION_END \ #define WSREP_TO_ISOLATION_END \
if (WSREP(thd) || (thd && thd->wsrep_exec_mode==TOTAL_ORDER)) \ if (WSREP(thd) || (thd && thd->wsrep_exec_mode==TOTAL_ORDER)) \
wsrep_to_isolation_end(thd); wsrep_to_isolation_end(thd);
......
...@@ -894,114 +894,145 @@ static bool wsrep_prepare_key_for_isolation(const char* db, ...@@ -894,114 +894,145 @@ static bool wsrep_prepare_key_for_isolation(const char* db,
wsrep_buf_t* key, wsrep_buf_t* key,
size_t* key_len) size_t* key_len)
{ {
if (*key_len < 2) return false; if (*key_len < 2) return false;
switch (wsrep_protocol_version) switch (wsrep_protocol_version)
{ {
case 0: case 0:
*key_len= 0; *key_len= 0;
break; break;
case 1: case 1:
case 2: case 2:
case 3: case 3:
{
*key_len= 0;
if (db)
{ {
*key_len= 0; // sql_print_information("%s.%s", db, table);
if (db) key[*key_len].ptr= db;
{ key[*key_len].len= strlen(db);
// sql_print_information("%s.%s", db, table); ++(*key_len);
if (db) if (table)
{ {
key[*key_len].ptr= db; key[*key_len].ptr= table;
key[*key_len].len= strlen(db); key[*key_len].len= strlen(table);
++(*key_len); ++(*key_len);
if (table) }
{
key[*key_len].ptr= table;
key[*key_len].len= strlen(table);
++(*key_len);
}
}
}
break;
} }
default: break;
}
default:
return false;
}
return true;
}
static bool wsrep_prepare_key_for_isolation(const char* db,
const char* table,
wsrep_key_arr_t* ka)
{
wsrep_key_t* tmp;
tmp= (wsrep_key_t*)my_realloc(ka->keys,
(ka->keys_len + 1) * sizeof(wsrep_key_t),
MYF(0));
if (!tmp)
{
WSREP_ERROR("Can't allocate memory for key_array");
return false;
}
ka->keys= tmp;
if (!(ka->keys[ka->keys_len].key_parts= (wsrep_buf_t*)
my_malloc(sizeof(wsrep_buf_t)*2, MYF(0))))
{
WSREP_ERROR("Can't allocate memory for key_parts");
return false;
}
ka->keys[ka->keys_len].key_parts_num= 2;
++ka->keys_len;
if (!wsrep_prepare_key_for_isolation(db, table,
(wsrep_buf_t*)ka->keys[ka->keys_len - 1].key_parts,
&ka->keys[ka->keys_len - 1].key_parts_num))
{
WSREP_ERROR("Preparing keys for isolation failed");
return false;
}
return true;
}
static bool wsrep_prepare_keys_for_alter_add_fk(char* child_table_db,
Alter_info* alter_info,
wsrep_key_arr_t* ka)
{
Key *key;
List_iterator<Key> key_iterator(alter_info->key_list);
while ((key= key_iterator++))
{
if (key->type == Key::FOREIGN_KEY)
{
Foreign_key *fk_key= (Foreign_key *)key;
const char *db_name= fk_key->ref_db.str;
const char *table_name= fk_key->ref_table.str;
if (!db_name)
{
db_name= child_table_db;
}
if (!wsrep_prepare_key_for_isolation(db_name, table_name, ka))
{
return false; return false;
}
} }
}
return true; return true;
} }
/* Prepare key list from db/table and table_list */
bool wsrep_prepare_keys_for_isolation(THD* thd, static bool wsrep_prepare_keys_for_isolation(THD* thd,
const char* db, const char* db,
const char* table, const char* table,
const TABLE_LIST* table_list, const TABLE_LIST* table_list,
wsrep_key_arr_t* ka) Alter_info* alter_info,
wsrep_key_arr_t* ka)
{ {
ka->keys= 0; ka->keys= 0;
ka->keys_len= 0; ka->keys_len= 0;
if (db || table) if (db || table)
{ {
if (!(ka->keys= (wsrep_key_t*)my_malloc(sizeof(wsrep_key_t), MYF(0)))) if (!wsrep_prepare_key_for_isolation(db, table, ka))
{
WSREP_ERROR("Can't allocate memory for key_array");
goto err;
}
ka->keys_len= 1;
if (!(ka->keys[0].key_parts= (wsrep_buf_t*)
my_malloc(sizeof(wsrep_buf_t)*2, MYF(0))))
{
WSREP_ERROR("Can't allocate memory for key_parts");
goto err; goto err;
}
ka->keys[0].key_parts_num= 2;
if (!wsrep_prepare_key_for_isolation(
db, table,
(wsrep_buf_t*)ka->keys[0].key_parts,
&ka->keys[0].key_parts_num))
{
WSREP_ERROR("Preparing keys for isolation failed (1)");
goto err;
}
} }
for (const TABLE_LIST* table= table_list; table; table= table->next_global) for (const TABLE_LIST* table= table_list; table; table= table->next_global)
{ {
wsrep_key_t* tmp; if (!wsrep_prepare_key_for_isolation(table->db, table->table_name, ka))
if (ka->keys)
tmp= (wsrep_key_t*)my_realloc(ka->keys,
(ka->keys_len + 1) * sizeof(wsrep_key_t),
MYF(0));
else
tmp= (wsrep_key_t*)my_malloc((ka->keys_len + 1) * sizeof(wsrep_key_t), MYF(0));
if (!tmp)
{
WSREP_ERROR("Can't allocate memory for key_array");
goto err; goto err;
} }
ka->keys= tmp;
if (!(ka->keys[ka->keys_len].key_parts= (wsrep_buf_t*) if (alter_info && (alter_info->flags & (Alter_info::ADD_FOREIGN_KEY)))
my_malloc(sizeof(wsrep_buf_t)*2, MYF(0)))) {
{ if (!wsrep_prepare_keys_for_alter_add_fk(table_list->db, alter_info, ka))
WSREP_ERROR("Can't allocate memory for key_parts");
goto err;
}
ka->keys[ka->keys_len].key_parts_num= 2;
++ka->keys_len;
if (!wsrep_prepare_key_for_isolation(table->db, table->table_name,
(wsrep_buf_t*)ka->keys[ka->keys_len - 1].key_parts,
&ka->keys[ka->keys_len - 1].key_parts_num))
{
WSREP_ERROR("Preparing keys for isolation failed (2)");
goto err; goto err;
}
} }
return 0;
return false;
err: err:
wsrep_keys_free(ka); wsrep_keys_free(ka);
return 1; return true;
}
/* Prepare key list from db/table and table_list */
bool wsrep_prepare_keys_for_isolation(THD* thd,
const char* db,
const char* table,
const TABLE_LIST* table_list,
wsrep_key_arr_t* ka)
{
return wsrep_prepare_keys_for_isolation(thd, db, table, table_list, NULL, ka);
} }
...@@ -1343,7 +1374,8 @@ static const char* wsrep_get_query_or_msg(const THD* thd) ...@@ -1343,7 +1374,8 @@ static const char* wsrep_get_query_or_msg(const THD* thd)
-1: TOI replication failed -1: TOI replication failed
*/ */
static int wsrep_TOI_begin(THD *thd, char *db_, char *table_, static int wsrep_TOI_begin(THD *thd, char *db_, char *table_,
const TABLE_LIST* table_list) const TABLE_LIST* table_list,
Alter_info* alter_info)
{ {
wsrep_status_t ret(WSREP_WARNING); wsrep_status_t ret(WSREP_WARNING);
uchar* buf(0); uchar* buf(0);
...@@ -1393,7 +1425,8 @@ static int wsrep_TOI_begin(THD *thd, char *db_, char *table_, ...@@ -1393,7 +1425,8 @@ static int wsrep_TOI_begin(THD *thd, char *db_, char *table_,
wsrep_key_arr_t key_arr= {0, 0}; wsrep_key_arr_t key_arr= {0, 0};
struct wsrep_buf buff = { buf, buf_len }; struct wsrep_buf buff = { buf, buf_len };
if (!buf_err && if (!buf_err &&
!wsrep_prepare_keys_for_isolation(thd, db_, table_, table_list, &key_arr) && !wsrep_prepare_keys_for_isolation(thd, db_, table_,
table_list, alter_info, &key_arr) &&
key_arr.keys_len > 0 && key_arr.keys_len > 0 &&
WSREP_OK == (ret = wsrep->to_execute_start(wsrep, thd->thread_id, WSREP_OK == (ret = wsrep->to_execute_start(wsrep, thd->thread_id,
key_arr.keys, key_arr.keys_len, key_arr.keys, key_arr.keys_len,
...@@ -1536,9 +1569,9 @@ static void wsrep_RSU_end(THD *thd) ...@@ -1536,9 +1569,9 @@ static void wsrep_RSU_end(THD *thd)
} }
int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_, int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_,
const TABLE_LIST* table_list) const TABLE_LIST* table_list,
Alter_info* alter_info)
{ {
/* /*
No isolation for applier or replaying threads. No isolation for applier or replaying threads.
*/ */
...@@ -1591,10 +1624,10 @@ int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_, ...@@ -1591,10 +1624,10 @@ int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_,
{ {
switch (thd->variables.wsrep_OSU_method) { switch (thd->variables.wsrep_OSU_method) {
case WSREP_OSU_TOI: case WSREP_OSU_TOI:
ret = wsrep_TOI_begin(thd, db_, table_, table_list); ret= wsrep_TOI_begin(thd, db_, table_, table_list, alter_info);
break; break;
case WSREP_OSU_RSU: case WSREP_OSU_RSU:
ret = wsrep_RSU_begin(thd, db_, table_); ret= wsrep_RSU_begin(thd, db_, table_);
break; break;
default: default:
WSREP_ERROR("Unsupported OSU method: %lu", WSREP_ERROR("Unsupported OSU method: %lu",
......
...@@ -326,8 +326,10 @@ extern PSI_mutex_key key_LOCK_wsrep_slave_threads; ...@@ -326,8 +326,10 @@ extern PSI_mutex_key key_LOCK_wsrep_slave_threads;
extern PSI_mutex_key key_LOCK_wsrep_desync; extern PSI_mutex_key key_LOCK_wsrep_desync;
#endif /* HAVE_PSI_INTERFACE */ #endif /* HAVE_PSI_INTERFACE */
struct TABLE_LIST; struct TABLE_LIST;
class Alter_info;
int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_, int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_,
const TABLE_LIST* table_list); const TABLE_LIST* table_list,
Alter_info* alter_info = NULL);
void wsrep_to_isolation_end(THD *thd); void wsrep_to_isolation_end(THD *thd);
void wsrep_cleanup_transaction(THD *thd); void wsrep_cleanup_transaction(THD *thd);
int wsrep_to_buf_helper( int wsrep_to_buf_helper(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment