revision-id: 9b29bda0d67ab15299659c9ad4046ab4f5ca7b6a (mariadb-galera-10.0.35-15-g9b29bda0d67) parent(s): c5a8583b3146613429d88c1c044de88a4c4c94b0 e88e26b4242e1c030bb138fca7ea2c916dbe6a76 author: Jan Lindström committer: Jan Lindström timestamp: 2018-08-02 13:13:21 +0300 message: Merge remote-tracking branch 'origin/5.5-galera' into 10.0-galera mysql-test/suite/galera/r/mysql-wsrep#332.result | 111 ++++++++++++ mysql-test/suite/galera/t/mysql-wsrep#332.test | 113 ++++++++++++ sql/sql_alter.cc | 17 +- sql/sql_parse.h | 5 + sql/wsrep_mysqld.cc | 217 +++++++++++++---------- sql/wsrep_mysqld.h | 4 +- 6 files changed, 369 insertions(+), 98 deletions(-) diff --cc sql/sql_alter.cc index a39f07ae35d,e343808cf77..1129b209775 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@@ -308,12 -102,17 +308,16 @@@ bool Sql_cmd_alter_table::execute(THD * #ifdef WITH_WSREP TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl); - if (WSREP(thd) && - (!thd->is_current_stmt_binlog_format_row() || + if ((!thd->is_current_stmt_binlog_format_row() || !find_temporary_table(thd, first_table))) { - WSREP_TO_ISOLATION_BEGIN(((lex->name.str) ? select_lex->db : NULL), - ((lex->name.str) ? lex->name.str : NULL), - first_table); + WSREP_TO_ISOLATION_BEGIN_ALTER(((lex->name.str) ? select_lex->db : NULL), + ((lex->name.str) ? lex->name.str : NULL), + first_table, + &alter_info); + + thd->variables.auto_increment_offset = 1; + thd->variables.auto_increment_increment = 1; } #endif /* WITH_WSREP */ result= mysql_alter_table(thd, select_lex->db, lex->name.str, @@@ -322,45 -121,14 +326,48 @@@ &alter_info, select_lex->order_list.elements, select_lex->order_list.first, - lex->ignore, lex->online); + lex->ignore); + + DBUG_RETURN(result); + DBUG_RETURN(result); #ifdef WITH_WSREP error: - WSREP_WARN("ALTER TABLE isolation failure"); - DBUG_RETURN(TRUE); + { + WSREP_WARN("ALTER TABLE isolation failure"); + DBUG_RETURN(TRUE); + } #endif /* WITH_WSREP */ } + +bool Sql_cmd_discard_import_tablespace::execute(THD *thd) +{ + /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ + SELECT_LEX *select_lex= &thd->lex->select_lex; + /* first table of first SELECT_LEX */ + TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first; + + if (check_access(thd, ALTER_ACL, table_list->db, + &table_list->grant.privilege, + &table_list->grant.m_internal, + 0, 0)) + return true; + + if (check_grant(thd, ALTER_ACL, table_list, false, UINT_MAX, false)) + return true; + + thd->enable_slow_log= opt_log_slow_admin_statements; + + /* + Check if we attempt to alter mysql.slow_log or + mysql.general_log table and return an error if + it is the case. + TODO: this design is obsolete and will be removed. + */ + if (check_if_log_table(table_list, TRUE, "ALTER")) + return true; + + return + mysql_discard_or_import_tablespace(thd, table_list, + m_tablespace_op == DISCARD_TABLESPACE); +} diff --cc sql/wsrep_mysqld.cc index 5b6575e7166,a7950754666..91dbe75408a --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@@ -894,49 -981,107 +894,107 @@@ static bool wsrep_prepare_key_for_isola wsrep_buf_t* key, size_t* key_len) { - if (*key_len < 2) return false; + if (*key_len < 2) return false; - switch (wsrep_protocol_version) - { - case 0: - *key_len= 0; - break; - case 1: - case 2: - case 3: + switch (wsrep_protocol_version) + { + case 0: + *key_len= 0; + break; + case 1: + case 2: + case 3: + { + *key_len= 0; + if (db) { - *key_len= 0; - if (db) - { - // sql_print_information("%s.%s", db, table); - if (db) - { - key[*key_len].ptr= db; - key[*key_len].len= strlen(db); - ++(*key_len); - if (table) - { - key[*key_len].ptr= table; - key[*key_len].len= strlen(table); - ++(*key_len); - } - } - } - break; + // sql_print_information("%s.%s", db, table); + key[*key_len].ptr= db; + key[*key_len].len= strlen(db); + ++(*key_len); + if (table) + { + key[*key_len].ptr= table; + key[*key_len].len= strlen(table); + ++(*key_len); + } } - 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_table->db.str; - const char *table_name= fk_key->ref_table->table.str; ++ 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 true; + } + 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) + + static bool wsrep_prepare_keys_for_isolation(THD* thd, + const char* db, + const char* table, + const TABLE_LIST* table_list, + Alter_info* alter_info, + wsrep_key_arr_t* ka) { ka->keys= 0; ka->keys_len= 0; @@@ -968,40 -1094,32 +1007,32 @@@ for (const TABLE_LIST* table= table_list; table; table= table->next_global) { - wsrep_key_t* tmp; - 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"); + if (!wsrep_prepare_key_for_isolation(table->db, table->table_name, ka)) 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)"); + } + - if (alter_info && (alter_info->flags & ALTER_FOREIGN_KEY)) ++ if (alter_info && (alter_info->flags & (Alter_info::ADD_FOREIGN_KEY))) + { + if (!wsrep_prepare_keys_for_alter_add_fk(table_list->db, alter_info, ka)) goto err; - } } - return 0; + + return false; + err: - wsrep_keys_free(ka); - return 1; + wsrep_keys_free(ka); + 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); } @@@ -1179,171 -1282,9 +1210,172 @@@ create_view_query(THD *thd, uchar** buf 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); + } +} + +static const char* wsrep_get_query_or_msg(const THD* thd) +{ + switch(thd->lex->sql_command) + { + case SQLCOM_CREATE_USER: + return "CREATE USER"; + case SQLCOM_GRANT: + return "GRANT"; + case SQLCOM_REVOKE: + return "REVOKE"; + case SQLCOM_SET_OPTION: + if (thd->lex->definer) + return "SET PASSWORD"; + /* fallthrough */ + default: + return thd->query(); + } +} + +/* + returns: + 0: statement was replicated as TOI + 1: TOI replication was skipped + -1: TOI replication failed + */ 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); uchar* buf(0); @@@ -1393,12 -1320,13 +1425,13 @@@ 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) && + !wsrep_prepare_keys_for_isolation(thd, db_, table_, + table_list, alter_info, &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, - &thd->wsrep_trx_meta))) + key_arr.keys, key_arr.keys_len, + &buff, 1, + &thd->wsrep_trx_meta))) { thd->wsrep_exec_mode= TOTAL_ORDER; wsrep_to_isolation++; @@@ -1589,28 -1494,17 +1622,28 @@@ int wsrep_to_isolation_begin(THD *thd, if (thd->variables.wsrep_on && thd->wsrep_exec_mode==LOCAL_STATE) { - switch (wsrep_OSU_method_options) { + switch (thd->variables.wsrep_OSU_method) { 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; case WSREP_OSU_RSU: - ret = wsrep_RSU_begin(thd, db_, table_); + ret= wsrep_RSU_begin(thd, db_, table_); break; + default: + WSREP_ERROR("Unsupported OSU method: %lu", + thd->variables.wsrep_OSU_method); + ret= -1; + break; } - if (!ret) - { - thd->wsrep_exec_mode= TOTAL_ORDER; + switch (ret) { + case 0: thd->wsrep_exec_mode= TOTAL_ORDER; break; + case 1: + /* TOI replication skipped, treat as success */ + ret = 0; + break; + case -1: + /* TOI replication failed, treat as error */ + break; } } return ret;