diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a4442653839..d63a61e0ab1 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4030,26 +4030,6 @@ end_with_restore_list: /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */ thd->variables.option_bits|= OPTION_KEEP_LOG; } -#ifdef WITH_WSREP - bool has_tmp_tables= false; - for (TABLE_LIST *table= all_tables; table; table= table->next_global) - { - if (lex->drop_temporary || find_temporary_table(thd, table)) - { - has_tmp_tables= true; - break; - } - } - if (has_tmp_tables) - { - wsrep_replicate_drop_query(thd, first_table, lex->check_exists, - lex->drop_temporary, false); - } - else - { - WSREP_TO_ISOLATION_BEGIN(NULL, NULL, all_tables); - } -#endif /* WITH_WSREP */ /* If we are a slave, we should add IF EXISTS if the query executed on the master without an error. This will help a slave to @@ -4060,6 +4040,7 @@ end_with_restore_list: slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT) lex->check_exists= 1; + WSREP_TO_ISOLATION_BEGIN(NULL, NULL, all_tables); /* DDL and binlog write order are protected by metadata locks. */ res= mysql_rm_table(thd, first_table, lex->check_exists, lex->drop_temporary); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 95a12abf986..e2b16d402bf 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2156,94 +2156,6 @@ static uint32 comment_length(THD *thd, uint32 comment_pos, return 0; } -#ifdef WITH_WSREP -static void -wsrep_append_name(THD *thd, String *packet, const char *name, uint length, - const CHARSET_INFO *from_cs, const CHARSET_INFO *to_cs) -{ - const char *to_name= name; - size_t to_length= length; - String to_string(name,length, from_cs); - if (from_cs != NULL && to_cs != NULL && from_cs != to_cs) - thd->convert_string(&to_string, from_cs, to_cs); - - if (to_cs != NULL) - { - to_name= to_string.c_ptr(); - to_length= to_string.length(); - } - packet->append(to_name, to_length, packet->charset()); -} - -int wsrep_replicate_drop_query(THD *thd, TABLE_LIST *tables, bool if_exists, - bool drop_temporary, bool dont_log_query) -{ - TABLE_LIST *table; - int error= 0; - String built_query; - bool non_tmp_table_deleted= FALSE; - - DBUG_ENTER("wsrep_build_drop_query"); - - if (!dont_log_query) - { - if (!drop_temporary) - { - built_query.set_charset(system_charset_info); - if (if_exists) - built_query.append("DROP TABLE IF EXISTS "); - else - built_query.append("DROP TABLE "); - } - } - - for (table= tables; table; table= table->next_local) - { - char *db=table->db; - int db_len= table->db_length; - - DBUG_PRINT("table", ("table_l: '%s'.'%s' table: 0x%lx s: 0x%lx", - table->db, table->table_name, (long) table->table, - table->table ? (long) table->table->s : (long) -1)); - - if (!find_temporary_table(thd, table)) - { - non_tmp_table_deleted= TRUE; - - if (thd->db == NULL || strcmp(db,thd->db) != 0) - { - wsrep_append_name(thd, &built_query, db, db_len, - system_charset_info, thd->charset()); - built_query.append("."); - } - - thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE; - wsrep_append_name(thd, &built_query, table->table_name, - strlen(table->table_name), system_charset_info, - thd->charset()); - built_query.append(","); - } - } - - if (non_tmp_table_deleted) - { - /* Chop of the last comma */ - built_query.chop(); - built_query.append(" /* generated by server */"); - WSREP_DEBUG("TOI for %s", built_query.ptr()); - if (WSREP_TO_ISOLATION_BEGIN_QUERY(built_query.ptr(), NULL, NULL, tables)) - { - WSREP_DEBUG("TOI failed for DROP TABLE: %s", WSREP_QUERY(thd)); - error= 1; - goto end; - } - } - -end: - DBUG_RETURN(error); -} - -#endif /* WITH_WSREP */ /** Execute the drop of a normal or temporary table. diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 6d0c5e75592..fa8991709db 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -2,7 +2,7 @@ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. + the Free Software Foundation; version 2 of the License.x1 This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -16,6 +16,7 @@ #include #include #include +#include /* find_temporary_table() */ #include "wsrep_priv.h" #include "wsrep_thd.h" #include "wsrep_sst.h" @@ -948,86 +949,66 @@ static bool wsrep_prepare_keys_for_isolation(THD* thd, const TABLE_LIST* table_list, wsrep_key_arr_t* ka) { - ka->keys= 0; - ka->keys_len= 0; + ka->keys= 0; + ka->keys_len= 0; - extern TABLE* find_temporary_table(THD*, const TABLE_LIST*); - - if (db || table) + if (db || table) + { + if (!(ka->keys= (wsrep_key_t*)my_malloc(sizeof(wsrep_key_t), MYF(0)))) { - TABLE_LIST tmp_table; - MDL_request mdl_request; - - memset(&tmp_table, 0, sizeof(tmp_table)); - tmp_table.table_name= (char*)table; - tmp_table.db= (char*)db; - tmp_table.mdl_request.init(MDL_key::GLOBAL, (db) ? db : "", - (table) ? table : "", - MDL_INTENTION_EXCLUSIVE, MDL_STATEMENT); - - if (!table || !find_temporary_table(thd, &tmp_table)) - { - if (!(ka->keys= (wsrep_key_t*)my_malloc(sizeof(wsrep_key_t), MYF(0)))) - { - 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; - } - 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"); - goto err; - } - } + WSREP_ERROR("Can't allocate memory for key_array"); + goto err; } - - for (const TABLE_LIST* table= table_list; table; table= table->next_global) + ka->keys_len= 1; + if (!(ka->keys[0].key_parts= (wsrep_buf_t*) + my_malloc(sizeof(wsrep_buf_t)*2, MYF(0)))) { - if (!find_temporary_table(thd, table)) - { - wsrep_key_t* tmp; - tmp= (wsrep_key_t*)my_realloc( - ka->keys, (ka->keys_len + 1) * sizeof(wsrep_key_t), - MYF(MY_ALLOW_ZERO_PTR)); - - if (!tmp) - { - WSREP_ERROR("Can't allocate memory for key_array"); - goto err; - } - 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"); - 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"); - goto err; - } - } + WSREP_ERROR("Can't allocate memory for key_parts"); + 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; } - return true; + } + + for (const TABLE_LIST* table= table_list; table; table= table->next_global) + { + 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"); + goto err; + } + 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"); + 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; + } + } + return 0; err: wsrep_keys_free(ka); - return false; + return 1; } @@ -1205,6 +1186,144 @@ create_view_query(THD *thd, uchar** buf, size_t* buf_len) return wsrep_to_buf_helper(thd, buff.ptr(), buff.length(), buf, buf_len); } +/* + Rewrite DROP TABLE for TOI. Temporary tables are eliminated from + the query as they are visible only to client connection. + + TODO: See comments for sql_base.cc:drop_temporary_table() and refine + the function to deal with transactional locked tables. + */ +static int wsrep_drop_table_query(THD* thd, uchar** buf, size_t* buf_len) +{ + + LEX* lex= thd->lex; + SELECT_LEX* select_lex= &lex->select_lex; + TABLE_LIST* first_table= select_lex->table_list.first; + String buff; + + DBUG_ASSERT(!lex->drop_temporary); + + bool found_temp_table= false; + for (TABLE_LIST* table= first_table; table; table= table->next_global) + { + if (find_temporary_table(thd, table->db, table->table_name)) + { + found_temp_table= true; + break; + } + } + + if (found_temp_table) + { + buff.append("DROP TABLE "); + if (lex->check_exists) + buff.append("IF EXISTS "); + + for (TABLE_LIST* table= first_table; table; table= table->next_global) + { + if (!find_temporary_table(thd, table->db, table->table_name)) + { + append_identifier(thd, &buff, table->db, strlen(table->db)); + buff.append("."); + append_identifier(thd, &buff, table->table_name, + strlen(table->table_name)); + buff.append(","); + } + } + + /* Chop the last comma */ + buff.chop(); + buff.append(" /* generated by wsrep */"); + + WSREP_DEBUG("Rewrote '%s' as '%s'", thd->query(), buff.ptr()); + + return wsrep_to_buf_helper(thd, buff.ptr(), buff.length(), buf, buf_len); + } + else + { + return wsrep_to_buf_helper(thd, thd->query(), thd->query_length(), + buf, buf_len); + } +} + +/* + Decide if statement should run in TOI. + + Look if table or table_list contain temporary tables. If the + statement affects only temporary tables, statement should not run + in TOI. If the table list contains mix of regular and temporary tables + (DROP TABLE, OPTIMIZE, ANALYZE), statement should be run in TOI but + should be rewritten at later time for replication to contain only + non-temporary tables. + */ +static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, + const TABLE_LIST *table_list) +{ + DBUG_ASSERT(!table || db); + DBUG_ASSERT(table_list || db); + + LEX* lex= thd->lex; + SELECT_LEX* select_lex= &lex->select_lex; + TABLE_LIST* first_table= select_lex->table_list.first; + + switch (lex->sql_command) + { + case SQLCOM_CREATE_TABLE: + DBUG_ASSERT(!table_list); + if (thd->lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) + { + return false; + } + return true; + + case SQLCOM_CREATE_VIEW: + + DBUG_ASSERT(!table_list); + DBUG_ASSERT(first_table); /* First table is view name */ + /* + If any of the remaining tables refer to temporary table error + is returned to client, so TOI can be skipped + */ + for (TABLE_LIST* it= first_table->next_global; it; it= it->next_global) + { + if (find_temporary_table(thd, it)) + { + return false; + } + } + return true; + + case SQLCOM_CREATE_TRIGGER: + + DBUG_ASSERT(!table_list); + DBUG_ASSERT(first_table); + + if (find_temporary_table(thd, first_table)) + { + return false; + } + return true; + + default: + if (table && !find_temporary_table(thd, db, table)) + { + return true; + } + + if (table_list) + { + for (TABLE_LIST* table= first_table; table; table= table->next_global) + { + if (!find_temporary_table(thd, table->db, table->table_name)) + { + return true; + } + } + } + return !(table || table_list); + } +} + /* returns: 0: statement was replicated as TOI @@ -1220,6 +1339,12 @@ static int wsrep_TOI_begin(THD *thd, const char *query, char *db_, char *table_, int buf_err; int rc= 0; + if (wsrep_can_run_in_toi(thd, db_, table_, table_list) == false) + { + WSREP_DEBUG("No TOI for %s", WSREP_QUERY(thd)); + return 1; + } + WSREP_DEBUG("TO BEGIN: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd), thd->wsrep_exec_mode, thd->query() ); switch (thd->lex->sql_command) @@ -1255,9 +1380,9 @@ static int wsrep_TOI_begin(THD *thd, const char *query, char *db_, char *table_, wsrep_key_arr_t key_arr= {0, 0}; struct wsrep_buf buff = { buf, buf_len }; - if (!buf_err && - wsrep_prepare_keys_for_isolation(thd, db_, table_, table_list, &key_arr)&& - key_arr.keys_len > 0 && + if (!buf_err && + !wsrep_prepare_keys_for_isolation(thd, db_, table_, table_list, &key_arr) && + key_arr.keys_len > 0 && WSREP_OK == (ret = wsrep->to_execute_start(wsrep, thd->thread_id, key_arr.keys, key_arr.keys_len, &buff, 1, @@ -1453,9 +1578,12 @@ int wsrep_to_isolation_begin(THD *thd, const char *query, char *db_, char *table if (thd->variables.wsrep_on && thd->wsrep_exec_mode==LOCAL_STATE) { switch (thd->variables.wsrep_OSU_method) { - case WSREP_OSU_TOI: ret = wsrep_TOI_begin(thd, query, db_, table_, - table_list); break; - case WSREP_OSU_RSU: ret = wsrep_RSU_begin(thd, db_, table_); break; + case WSREP_OSU_TOI: + ret = wsrep_TOI_begin(thd, query, db_, table_, table_list); + break; + case WSREP_OSU_RSU: + ret = wsrep_RSU_begin(thd, db_, table_); + break; default: WSREP_ERROR("Unsupported OSU method: %lu", thd->variables.wsrep_OSU_method); diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index fa069723021..ea7e7df9e10 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -337,9 +337,6 @@ void wsrep_init_sidno(const wsrep_uuid_t&); bool wsrep_node_is_donor(); bool wsrep_node_is_synced(); -int wsrep_replicate_drop_query(THD *thd, TABLE_LIST *tables, bool if_exists, - bool drop_temporary, bool dont_log_query); - #define WSREP_MYSQL_DB (char *)"mysql" #define WSREP_TO_ISOLATION_BEGIN(db_, table_, table_list_) \