diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/ddl_log.cc | 210 | ||||
-rw-r--r-- | sql/ddl_log.h | 7 | ||||
-rw-r--r-- | sql/handler.cc | 18 | ||||
-rw-r--r-- | sql/handler.h | 55 | ||||
-rw-r--r-- | sql/log.cc | 7 | ||||
-rw-r--r-- | sql/log_event.h | 4 | ||||
-rw-r--r-- | sql/sql_alter.cc | 1 | ||||
-rw-r--r-- | sql/sql_alter.h | 8 | ||||
-rw-r--r-- | sql/sql_class.h | 11 | ||||
-rw-r--r-- | sql/sql_db.cc | 5 | ||||
-rw-r--r-- | sql/sql_insert.cc | 309 | ||||
-rw-r--r-- | sql/sql_partition.cc | 2 | ||||
-rw-r--r-- | sql/sql_select.cc | 2 | ||||
-rw-r--r-- | sql/sql_show.cc | 3 | ||||
-rw-r--r-- | sql/sql_table.cc | 737 | ||||
-rw-r--r-- | sql/sql_table.h | 56 | ||||
-rw-r--r-- | sql/sql_trigger.cc | 19 | ||||
-rw-r--r-- | sql/sql_trigger.h | 5 | ||||
-rw-r--r-- | sql/table.cc | 2 |
19 files changed, 1014 insertions, 447 deletions
diff --git a/sql/ddl_log.cc b/sql/ddl_log.cc index 418b5f3b5b0..90ae31cadcd 100644 --- a/sql/ddl_log.cc +++ b/sql/ddl_log.cc @@ -135,6 +135,13 @@ public: char current_db[NAME_LEN]; uint execute_entry_pos; ulonglong xid; + void free() + { + drop_table.free(); + drop_view.free(); + query.free(); + db.free(); + } }; static st_global_ddl_log global_ddl_log; @@ -455,7 +462,7 @@ bool ddl_log_disable_execute_entry(DDL_LOG_MEMORY_ENTRY **active_entry) static bool is_execute_entry_active(uint entry_pos) { uchar buff[1]; - DBUG_ENTER("disable_execute_entry"); + DBUG_ENTER("is_execute_entry_active"); if (mysql_file_pread(global_ddl_log.file_id, buff, sizeof(buff), global_ddl_log.io_size * entry_pos + @@ -1081,15 +1088,15 @@ static handler *create_handler(THD *thd, MEM_ROOT *mem_root, like connect, needs the .frm file to exists to be able to do an rename. */ -static void execute_rename_table(DDL_LOG_ENTRY *ddl_log_entry, handler *file, - const LEX_CSTRING *from_db, - const LEX_CSTRING *from_table, - const LEX_CSTRING *to_db, - const LEX_CSTRING *to_table, - uint flags, - char *from_path, char *to_path) +static int execute_rename_table(DDL_LOG_ENTRY *ddl_log_entry, handler *file, + const LEX_CSTRING *from_db, + const LEX_CSTRING *from_table, + const LEX_CSTRING *to_db, + const LEX_CSTRING *to_table, uint flags, + char *from_path, char *to_path) { uint to_length=0, fr_length=0; + int err; DBUG_ENTER("execute_rename_table"); if (file->needs_lower_case_filenames()) @@ -1102,14 +1109,14 @@ static void execute_rename_table(DDL_LOG_ENTRY *ddl_log_entry, handler *file, } else { - fr_length= build_table_filename(from_path, FN_REFLEN, - from_db->str, from_table->str, "", - flags & FN_TO_IS_TMP); + fr_length= + build_table_filename(from_path, FN_REFLEN, from_db->str, + from_table->str, "", flags & FN_FROM_IS_TMP); to_length= build_table_filename(to_path, FN_REFLEN, to_db->str, to_table->str, "", flags & FN_TO_IS_TMP); } - file->ha_rename_table(from_path, to_path); + err= file->ha_rename_table(from_path, to_path); if (file->needs_lower_case_filenames()) { /* @@ -1130,7 +1137,7 @@ static void execute_rename_table(DDL_LOG_ENTRY *ddl_log_entry, handler *file, } if (!access(from_path, F_OK)) (void) mysql_file_rename(key_file_frm, from_path, to_path, MYF(MY_WME)); - DBUG_VOID_RETURN; + DBUG_RETURN(err); } @@ -1287,7 +1294,8 @@ static void rename_in_stat_tables(THD *thd, DDL_LOG_ENTRY *ddl_log_entry, */ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, - DDL_LOG_ENTRY *ddl_log_entry) + DDL_LOG_ENTRY *ddl_log_entry, + bool report_error) { LEX_CSTRING handler_name; handler *file= NULL; @@ -1295,15 +1303,15 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, handlerton *hton= 0; ddl_log_error_handler no_such_table_handler; uint entry_pos= ddl_log_entry->entry_pos; - int error; + int error= 0; + uint flags; bool frm_action= FALSE; DBUG_ENTER("ddl_log_execute_action"); mysql_mutex_assert_owner(&LOCK_gdl); DBUG_PRINT("ddl_log", - ("pos: %u=>%u->%u type: %u action: %u (%s) phase: %u " + ("pos: %u->%u type: %u action: %u (%s) phase: %u " "handler: '%s' name: '%s' from_name: '%s' tmp_name: '%s'", - recovery_state.execute_entry_pos, ddl_log_entry->entry_pos, ddl_log_entry->next_entry, (uint) ddl_log_entry->entry_type, @@ -1320,7 +1328,8 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, DBUG_RETURN(FALSE); handler_name= ddl_log_entry->handler_name; - thd->push_internal_handler(&no_such_table_handler); + if (!report_error) + thd->push_internal_handler(&no_such_table_handler); if (!strcmp(ddl_log_entry->handler_name.str, reg_ext)) frm_action= TRUE; @@ -1374,24 +1383,28 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, /* fall through */ case DDL_LOG_RENAME_ACTION: { - error= TRUE; if (frm_action) { strxmov(to_path, ddl_log_entry->name.str, reg_ext, NullS); strxmov(from_path, ddl_log_entry->from_name.str, reg_ext, NullS); - (void) mysql_file_rename(key_file_frm, from_path, to_path, MYF(MY_WME)); + error= mysql_file_rename(key_file_frm, from_path, to_path, MYF(MY_WME)); #ifdef WITH_PARTITION_STORAGE_ENGINE strxmov(to_path, ddl_log_entry->name.str, PAR_EXT, NullS); strxmov(from_path, ddl_log_entry->from_name.str, PAR_EXT, NullS); - (void) mysql_file_rename(key_file_partition_ddl_log, from_path, to_path, - MYF(MY_WME)); + int err2= mysql_file_rename(key_file_partition_ddl_log, from_path, + to_path, MYF(MY_WME)); + if (!error) + error= err2; #endif } else - (void) file->ha_rename_table(ddl_log_entry->from_name.str, + error= file->ha_rename_table(ddl_log_entry->from_name.str, ddl_log_entry->name.str); if (increment_phase(entry_pos)) + { + error= -1; break; + } break; } case DDL_LOG_EXCHANGE_ACTION: @@ -1459,11 +1472,11 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, /* fall through */ case DDL_RENAME_PHASE_TABLE: /* Restore frm and table to original names */ - execute_rename_table(ddl_log_entry, file, - &ddl_log_entry->db, &ddl_log_entry->name, - &ddl_log_entry->from_db, &ddl_log_entry->from_name, - 0, - from_path, to_path); + flags= report_error ? FN_FROM_IS_TMP : 0; + error= execute_rename_table(ddl_log_entry, file, + &ddl_log_entry->db, &ddl_log_entry->name, + &ddl_log_entry->from_db, &ddl_log_entry->from_name, + flags, from_path, to_path); if (ddl_log_entry->flags & DDL_LOG_FLAG_UPDATE_STAT) { @@ -1569,8 +1582,15 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, break; /* Fall through */ case DDL_DROP_PHASE_TRIGGER: + /* + Note: frm is already deleted and build_table_filename() will not detect + FN_IS_TMP flag. + */ + flags= (ddl_log_entry->flags & DDL_LOG_FLAG_TMP_TABLE) ? FN_IS_TMP : 0; + Table_triggers_list::drop_all_triggers(thd, &db, &table, - MYF(MY_WME | MY_IGNORE_ENOENT)); + MYF(MY_WME | MY_IGNORE_ENOENT), + flags); if (increment_phase(entry_pos)) break; /* Fall through */ @@ -1765,31 +1785,6 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, } strxnmov(to_path, sizeof(to_path)-1, path.str, reg_ext, NullS); mysql_file_delete(key_file_frm, to_path, MYF(MY_WME|MY_IGNORE_ENOENT)); - if (ddl_log_entry->phase == DDL_CREATE_TABLE_PHASE_LOG) - { - /* - The server logged CREATE TABLE ... SELECT into binary log - before crashing. As the commit failed and we have delete the - table above, we have now to log the DROP of the created table. - */ - - String *query= &recovery_state.drop_table; - query->length(0); - query->append(STRING_WITH_LEN("DROP TABLE IF EXISTS ")); - append_identifier(thd, query, &db); - query->append('.'); - append_identifier(thd, query, &table); - query->append(&end_comment); - - if (mysql_bin_log.is_open()) - { - mysql_mutex_unlock(&LOCK_gdl); - (void) thd->binlog_query(THD::STMT_QUERY_TYPE, - query->ptr(), query->length(), - TRUE, FALSE, FALSE, 0); - mysql_mutex_lock(&LOCK_gdl); - } - } (void) update_phase(entry_pos, DDL_LOG_FINAL_PHASE); error= 0; break; @@ -2296,11 +2291,28 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, } end: + if (report_error) + { + if (error && file) + { + TABLE_SHARE share; + bzero(&share, sizeof(share)); + share.db= ddl_log_entry->db; + share.table_name= ddl_log_entry->name; + share.normalized_path= ddl_log_entry->tmp_name; + /* TODO: make TABLE_SHARE-independent handler::print_error()? */ + file->change_table_ptr(NULL, &share); + file->print_error(error, MYF(0)); + } + } + else + { + /* We are only interested in errors that where not ignored */ + if ((error= (no_such_table_handler.unhandled_errors > 0))) + my_errno= no_such_table_handler.first_error; + thd->pop_internal_handler(); + } delete file; - /* We are only interested in errors that where not ignored */ - if ((error= (no_such_table_handler.unhandled_errors > 0))) - my_errno= no_such_table_handler.first_error; - thd->pop_internal_handler(); DBUG_RETURN(error); } @@ -2395,11 +2407,13 @@ void ddl_log_release_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry) @retval FALSE Success */ -static bool ddl_log_execute_entry_no_lock(THD *thd, uint first_entry) +static bool ddl_log_execute_entry_no_lock(THD *thd, uint first_entry, + bool report_error) { DDL_LOG_ENTRY ddl_log_entry; uint read_entry= first_entry; MEM_ROOT mem_root; + bool result= false; DBUG_ENTER("ddl_log_execute_entry_no_lock"); mysql_mutex_assert_owner(&LOCK_gdl); @@ -2415,24 +2429,29 @@ static bool ddl_log_execute_entry_no_lock(THD *thd, uint first_entry) DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE || ddl_log_entry.entry_type == DDL_LOG_IGNORE_ENTRY_CODE); - if (ddl_log_execute_action(thd, &mem_root, &ddl_log_entry)) + if (ddl_log_execute_action(thd, &mem_root, &ddl_log_entry, report_error)) { uint action_type= ddl_log_entry.action_type; if (action_type >= DDL_LOG_LAST_ACTION) action_type= 0; - /* Write to error log and continue with next log entry */ - sql_print_error("DDL_LOG: Got error %d when trying to execute action " - "for entry %u of type '%s'", - (int) my_errno, read_entry, - ddl_log_action_name[action_type]); + if (!report_error) + { + /* Write to error log and continue with next log entry */ + sql_print_error("DDL_LOG: Got error %d when trying to execute action " + "for entry %u of type '%s'", + (int) my_errno, read_entry, + ddl_log_action_name[action_type]); + } + result= true; break; } read_entry= ddl_log_entry.next_entry; } while (read_entry); + recovery_state.free(); // FIXME: is this correct? free_root(&mem_root, MYF(0)); - DBUG_RETURN(FALSE); + DBUG_RETURN(result); } @@ -2638,7 +2657,7 @@ bool ddl_log_execute_entry(THD *thd, uint first_entry) DBUG_ENTER("ddl_log_execute_entry"); mysql_mutex_lock(&LOCK_gdl); - error= ddl_log_execute_entry_no_lock(thd, first_entry); + error= ddl_log_execute_entry_no_lock(thd, first_entry, false); mysql_mutex_unlock(&LOCK_gdl); DBUG_RETURN(error); } @@ -2761,7 +2780,7 @@ int ddl_log_execute_recovery() if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE) { /* - Remeber information about executive ddl log entry, + Remember information about executive ddl log entry, used for binary logging during recovery */ recovery_state.execute_entry_pos= i; @@ -2792,7 +2811,7 @@ int ddl_log_execute_recovery() continue; } - if (ddl_log_execute_entry_no_lock(thd, ddl_log_entry.next_entry)) + if (ddl_log_execute_entry_no_lock(thd, ddl_log_entry.next_entry, false)) { /* Real unpleasant scenario but we have to continue anyway */ error= -1; @@ -2930,7 +2949,7 @@ void ddl_log_complete(DDL_LOG_STATE *state) This is called for failed rename table, create trigger or drop trigger. */ -bool ddl_log_revert(THD *thd, DDL_LOG_STATE *state) +bool ddl_log_revert(THD *thd, DDL_LOG_STATE *state, bool report_error) { bool res= 0; DBUG_ENTER("ddl_log_revert"); @@ -2941,7 +2960,8 @@ bool ddl_log_revert(THD *thd, DDL_LOG_STATE *state) mysql_mutex_lock(&LOCK_gdl); if (likely(state->execute_entry)) { - res= ddl_log_execute_entry_no_lock(thd, state->list->entry_pos); + res= ddl_log_execute_entry_no_lock(thd, state->list->entry_pos, + report_error); ddl_log_disable_execute_entry(&state->execute_entry); } ddl_log_release_entries(state); @@ -3042,6 +3062,7 @@ static bool ddl_log_write(DDL_LOG_STATE *ddl_state, mysql_mutex_lock(&LOCK_gdl); error= ((ddl_log_write_entry(ddl_log_entry, &log_entry)) || ddl_log_write_execute_entry(log_entry->entry_pos, + ddl_state->master_chain_pos, &ddl_state->execute_entry)); mysql_mutex_unlock(&LOCK_gdl); if (error) @@ -3121,16 +3142,16 @@ bool ddl_log_rename_view(THD *thd, DDL_LOG_STATE *ddl_state, */ static bool ddl_log_drop_init(THD *thd, DDL_LOG_STATE *ddl_state, - ddl_log_action_code action_code, const LEX_CSTRING *db, const LEX_CSTRING *comment) { DDL_LOG_ENTRY ddl_log_entry; - DBUG_ENTER("ddl_log_drop_file"); + DBUG_ENTER("ddl_log_drop_init"); bzero(&ddl_log_entry, sizeof(ddl_log_entry)); - ddl_log_entry.action_type= action_code; + ddl_log_entry.action_type= DDL_LOG_DROP_INIT_ACTION; + ddl_log_entry.next_entry= ddl_state->list ? ddl_state->list->entry_pos : 0; ddl_log_entry.from_db= *const_cast<LEX_CSTRING*>(db); ddl_log_entry.tmp_name= *const_cast<LEX_CSTRING*>(comment); @@ -3142,15 +3163,13 @@ bool ddl_log_drop_table_init(THD *thd, DDL_LOG_STATE *ddl_state, const LEX_CSTRING *db, const LEX_CSTRING *comment) { - return ddl_log_drop_init(thd, ddl_state, DDL_LOG_DROP_INIT_ACTION, - db, comment); + return ddl_log_drop_init(thd, ddl_state, db, comment); } bool ddl_log_drop_view_init(THD *thd, DDL_LOG_STATE *ddl_state, const LEX_CSTRING *db) { - return ddl_log_drop_init(thd, ddl_state, DDL_LOG_DROP_INIT_ACTION, - db, &empty_clex_str); + return ddl_log_drop_init(thd, ddl_state, db, &empty_clex_str); } @@ -3172,7 +3191,7 @@ static bool ddl_log_drop(THD *thd, DDL_LOG_STATE *ddl_state, const LEX_CSTRING *table) { DDL_LOG_ENTRY ddl_log_entry; - DDL_LOG_MEMORY_ENTRY *log_entry; + DDL_LOG_MEMORY_ENTRY *log_entry, *first_entry= NULL; DBUG_ENTER("ddl_log_drop"); DBUG_ASSERT(ddl_state->list); @@ -3187,6 +3206,25 @@ static bool ddl_log_drop(THD *thd, DDL_LOG_STATE *ddl_state, ddl_log_entry.tmp_name= *const_cast<LEX_CSTRING*>(path); ddl_log_entry.phase= (uchar) phase; + /* + Get first entry in the chain and if it is not DDL_LOG_DROP_INIT_ACTION + place it after this action. Required for logging rename before drop, + but replaying rename after drop. + */ + for (log_entry= ddl_state->list->next_active_log_entry; log_entry; + first_entry= log_entry, log_entry= log_entry->next_active_log_entry); + + if (first_entry) + { + DDL_LOG_ENTRY first; + mysql_mutex_lock(&LOCK_gdl); + (void) read_ddl_log_entry(first_entry->entry_pos, &first); + mysql_mutex_unlock(&LOCK_gdl); + if (first.action_type != DDL_LOG_DROP_INIT_ACTION) + ddl_log_entry.next_entry= + ddl_state->list->next_active_log_entry->entry_pos; + } + mysql_mutex_lock(&LOCK_gdl); if (ddl_log_write_entry(&ddl_log_entry, &log_entry)) goto error; @@ -3324,6 +3362,7 @@ bool ddl_log_create_table(THD *thd, DDL_LOG_STATE *ddl_state, ddl_log_entry.name= *const_cast<LEX_CSTRING*>(table); ddl_log_entry.tmp_name= *const_cast<LEX_CSTRING*>(path); ddl_log_entry.flags= only_frm ? DDL_LOG_FLAG_ONLY_FRM : 0; + ddl_log_entry.next_entry= ddl_state->list ? ddl_state->list->entry_pos : 0; DBUG_RETURN(ddl_log_write(ddl_state, &ddl_log_entry)); } @@ -3577,3 +3616,16 @@ bool ddl_log_delete_frm(DDL_LOG_STATE *ddl_state, const char *to_path) ddl_log_add_entry(ddl_state, log_entry); DBUG_RETURN(0); } + +/* + Link the ddl_log_state to another (master) chain. If the master + chain is active during DDL recovery, this event will not be executed. + + This is used for DROP TABLE of the original table when + CREATE OR REPLACE ... is used. +*/ +void ddl_log_link_chains(DDL_LOG_STATE *state, DDL_LOG_STATE *master_state) +{ + DBUG_ASSERT(master_state->execute_entry); + state->master_chain_pos= master_state->execute_entry->entry_pos; +} diff --git a/sql/ddl_log.h b/sql/ddl_log.h index 9960855a813..9f8a9beedee 100644 --- a/sql/ddl_log.h +++ b/sql/ddl_log.h @@ -126,7 +126,6 @@ enum enum_ddl_log_drop_db_phase { enum enum_ddl_log_create_table_phase { DDL_CREATE_TABLE_PHASE_INIT=0, - DDL_CREATE_TABLE_PHASE_LOG, DDL_CREATE_TABLE_PHASE_END }; @@ -173,6 +172,7 @@ enum enum_ddl_log_alter_table_phase { engine is not changed */ #define DDL_LOG_FLAG_ALTER_PARTITION (1 << 4) +#define DDL_LOG_FLAG_TMP_TABLE (1 << 5) /* Setting ddl_log_entry.phase to this has the same effect as setting @@ -248,6 +248,7 @@ typedef struct st_ddl_log_state */ DDL_LOG_MEMORY_ENTRY *main_entry; uint16 flags; /* Cache for flags */ + ulonglong master_chain_pos; bool is_active() { return list != 0; } } DDL_LOG_STATE; @@ -273,7 +274,8 @@ bool ddl_log_write_execute_entry(uint first_entry, bool ddl_log_disable_execute_entry(DDL_LOG_MEMORY_ENTRY **active_entry); void ddl_log_complete(DDL_LOG_STATE *ddl_log_state); -bool ddl_log_revert(THD *thd, DDL_LOG_STATE *ddl_log_state); +bool ddl_log_revert(THD *thd, DDL_LOG_STATE *ddl_log_state, + bool report_error= false); bool ddl_log_update_phase(DDL_LOG_STATE *entry, uchar phase); bool ddl_log_add_flag(DDL_LOG_STATE *entry, uint16 flag); @@ -356,5 +358,6 @@ bool ddl_log_alter_table(THD *thd, DDL_LOG_STATE *ddl_state, bool ddl_log_store_query(THD *thd, DDL_LOG_STATE *ddl_log_state, const char *query, size_t length); bool ddl_log_delete_frm(DDL_LOG_STATE *ddl_state, const char *to_path); +void ddl_log_link_chains(DDL_LOG_STATE *state, DDL_LOG_STATE *master_state); extern mysql_mutex_t LOCK_gdl; #endif /* DDL_LOG_INCLUDED */ diff --git a/sql/handler.cc b/sql/handler.cc index 57992d98c04..0b922a3cfc8 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6922,6 +6922,8 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat) RETURN VALUE 0 No binary logging in row format 1 Row needs to be logged + + TODO: remove needless proxy */ bool handler::check_table_binlog_row_based() @@ -6987,8 +6989,7 @@ int handler::binlog_log_row(TABLE *table, THD *thd= table->in_use; DBUG_ENTER("binlog_log_row"); - if (!thd->binlog_table_maps && - thd->binlog_write_table_maps()) + if (!thd->binlog_table_maps && thd->binlog_write_table_maps(table)) DBUG_RETURN(HA_ERR_RBR_LOGGING_FAILED); error= (*log_func)(thd, table, row_logging_has_trans, @@ -8317,7 +8318,7 @@ bool Table_scope_and_contents_source_st::vers_fix_system_fields( bool Table_scope_and_contents_source_st::vers_check_system_fields( THD *thd, Alter_info *alter_info, const Lex_table_name &table_name, - const Lex_table_name &db, int select_count) + const Lex_table_name &db) { if (!(options & HA_VERSIONED_TABLE)) return false; @@ -8337,7 +8338,7 @@ bool Table_scope_and_contents_source_st::vers_check_system_fields( SELECT go last there. */ bool is_dup= false; - if (fieldnr >= alter_info->create_list.elements - select_count) + if (fieldnr >= alter_info->field_count()) { List_iterator<Create_field> dup_it(alter_info->create_list); for (Create_field *dup= dup_it++; !is_dup && dup != f; dup= dup_it++) @@ -8745,12 +8746,11 @@ bool Table_period_info::check_field(const Create_field* f, } bool Table_scope_and_contents_source_st::check_fields( - THD *thd, Alter_info *alter_info, - const Lex_table_name &table_name, const Lex_table_name &db, int select_count) + THD *thd, Alter_info *alter_info, const Lex_table_name &table_name, + const Lex_table_name &db) { - return vers_check_system_fields(thd, alter_info, - table_name, db, select_count) || - check_period_fields(thd, alter_info); + return vers_check_system_fields(thd, alter_info, table_name, db) || + check_period_fields(thd, alter_info); } bool Table_scope_and_contents_source_st::check_period_fields( diff --git a/sql/handler.h b/sql/handler.h index fe61666bf20..1a2156e7c46 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -35,6 +35,7 @@ #include "sql_array.h" /* Dynamic_array<> */ #include "mdl.h" #include "vers_string.h" +#include "backup.h" #include "sql_analyze_stmt.h" // for Exec_time_tracker @@ -1827,6 +1828,12 @@ handlerton *ha_default_tmp_handlerton(THD *thd); */ #define HTON_REQUIRES_NOTIFY_TABLEDEF_CHANGED_AFTER_COMMIT (1 << 20) +/* + Indicates that rename table is expensive operation. + When set atomic CREATE OR REPLACE TABLE is not used. +*/ +#define HTON_EXPENSIVE_RENAME (1 << 21) + class Ha_trx_info; struct THD_TRANS @@ -2303,8 +2310,7 @@ struct Table_scope_and_contents_source_st: bool fix_period_fields(THD *thd, Alter_info *alter_info); bool check_fields(THD *thd, Alter_info *alter_info, const Lex_table_name &table_name, - const Lex_table_name &db, - int select_count= 0); + const Lex_table_name &db); bool check_period_fields(THD *thd, Alter_info *alter_info); bool vers_fix_system_fields(THD *thd, Alter_info *alter_info, @@ -2312,9 +2318,33 @@ struct Table_scope_and_contents_source_st: bool vers_check_system_fields(THD *thd, Alter_info *alter_info, const Lex_table_name &table_name, - const Lex_table_name &db, - int select_count= 0); + const Lex_table_name &db); +}; +typedef struct st_ddl_log_state DDL_LOG_STATE; + +struct Atomic_info +{ + TABLE_LIST *tmp_name; + DDL_LOG_STATE *ddl_log_state_create; + DDL_LOG_STATE *ddl_log_state_rm; + backup_log_info drop_entry; + + Atomic_info() : + tmp_name(NULL), + ddl_log_state_create(NULL), + ddl_log_state_rm(NULL) + { + bzero(&drop_entry, sizeof(drop_entry)); + } + + Atomic_info(DDL_LOG_STATE *ddl_log_state_rm) : + tmp_name(NULL), + ddl_log_state_create(NULL), + ddl_log_state_rm(ddl_log_state_rm) + { + bzero(&drop_entry, sizeof(drop_entry)); + } }; @@ -2324,7 +2354,8 @@ struct Table_scope_and_contents_source_st: parts are handled on the SQL level and are not needed on the handler level. */ struct HA_CREATE_INFO: public Table_scope_and_contents_source_st, - public Schema_specification_st + public Schema_specification_st, + public Atomic_info { /* TODO: remove after MDEV-20865 */ Alter_info *alter_info; @@ -2369,6 +2400,16 @@ struct HA_CREATE_INFO: public Table_scope_and_contents_source_st, else return table_options; } + bool ok_atomic_replace() const + { + return !tmp_table() && !sequence && + !(db_type->flags & HTON_EXPENSIVE_RENAME) && + !DBUG_IF("ddl_log_expensive_rename"); + } + bool handle_atomic_replace(THD *thd, const LEX_CSTRING &db, + const LEX_CSTRING &table_name, + const DDL_options_st options); + bool finalize_ddl(THD *thd); }; @@ -2401,6 +2442,10 @@ struct Table_specification_st: public HA_CREATE_INFO, HA_CREATE_INFO::options= 0; DDL_options_st::init(); } + bool is_atomic_replace() const + { + return or_replace() && ok_atomic_replace(); + } }; diff --git a/sql/log.cc b/sql/log.cc index 70ceecc66f8..5bdf23a17e4 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -5953,7 +5953,7 @@ bool THD::binlog_write_annotated_row(Log_event_writer *writer) THD::binlog_prepare_for_row_logging */ -bool THD::binlog_write_table_maps() +bool THD::binlog_write_table_maps(TABLE *cur_table) { bool with_annotate; MYSQL_LOCK *locks[2], **locks_end= locks; @@ -6006,6 +6006,11 @@ bool THD::binlog_write_table_maps() } } } + if (cur_table->s->tmp_table && cur_table->file->row_logging) + { + if (binlog_write_table_map(cur_table, with_annotate)) + DBUG_RETURN(1); + } binlog_table_maps= 1; // Table maps written DBUG_RETURN(0); } diff --git a/sql/log_event.h b/sql/log_event.h index 3adc7a26d93..09407f0ae0b 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -695,9 +695,11 @@ enum Log_event_type /* New MySQL/Sun events are to be added right above this comment */ MYSQL_EVENTS_END, - MARIA_EVENTS_BEGIN= 160, /* New Maria event numbers start from here */ ANNOTATE_ROWS_EVENT= 160, + /* Keep that here for GDB to display ANNOTATE_ROWS_EVENT */ + MARIA_EVENTS_BEGIN= 160, + /* Binlog checkpoint event. Used for XA crash recovery on the master, not used in replication. diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index 58902fc859f..4d13cac704a 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -27,6 +27,7 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root) key_list(rhs.key_list, mem_root), alter_rename_key_list(rhs.alter_rename_key_list, mem_root), create_list(rhs.create_list, mem_root), + select_field_count(rhs.select_field_count), alter_index_ignorability_list(rhs.alter_index_ignorability_list, mem_root), check_constraint_list(rhs.check_constraint_list, mem_root), flags(rhs.flags), partition_flags(rhs.partition_flags), diff --git a/sql/sql_alter.h b/sql/sql_alter.h index bf1edd4c964..142b71c725a 100644 --- a/sql/sql_alter.h +++ b/sql/sql_alter.h @@ -96,6 +96,7 @@ public: List<Alter_rename_key> alter_rename_key_list; // List of columns, used by both CREATE and ALTER TABLE. List<Create_field> create_list; + uint select_field_count; // Indexes whose ignorability needs to be changed. List<Alter_index_ignorability> alter_index_ignorability_list; List<Virtual_column_info> check_constraint_list; @@ -118,6 +119,7 @@ public: Alter_info() : + select_field_count(0), flags(0), partition_flags(0), keys_onoff(LEAVE_AS_IS), num_parts(0), @@ -134,6 +136,7 @@ public: create_list.empty(); alter_index_ignorability_list.empty(); check_constraint_list.empty(); + select_field_count= 0; flags= 0; partition_flags= 0; keys_onoff= LEAVE_AS_IS; @@ -234,6 +237,11 @@ public: */ enum_alter_table_algorithm algorithm(const THD *thd) const; + uint field_count() const + { + return create_list.elements - select_field_count; + } + private: Alter_info &operator=(const Alter_info &rhs); // not implemented Alter_info(const Alter_info &rhs); // not implemented diff --git a/sql/sql_class.h b/sql/sql_class.h index 0d02f74686f..2e445eebacd 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2886,7 +2886,7 @@ public: bool prepare_handlers_for_update(uint flag); bool binlog_write_annotated_row(Log_event_writer *writer); void binlog_prepare_for_row_logging(); - bool binlog_write_table_maps(); + bool binlog_write_table_maps(TABLE *cur_table); bool binlog_write_table_map(TABLE *table, bool with_annotate); static void binlog_prepare_row_images(TABLE* table); @@ -6021,6 +6021,9 @@ class select_insert :public select_result_interceptor { ulonglong autoinc_value_of_last_inserted_row; // autogenerated or not COPY_INFO info; bool insert_into_view; + bool binary_logged; + bool atomic_replace; + select_insert(THD *thd_arg, TABLE_LIST *table_list_par, TABLE *table_par, List<Item> *fields_par, List<Item> *update_fields, List<Item> *update_values, enum_duplicates duplic, @@ -6042,6 +6045,8 @@ class select_insert :public select_result_interceptor { class select_create: public select_insert { TABLE_LIST *create_table; + TABLE_LIST *orig_table; + TABLE_LIST new_table; Table_specification_st *create_info; TABLE_LIST *select_tables; Alter_info *alter_info; @@ -6063,6 +6068,7 @@ public: select_insert(thd_arg, table_arg, NULL, &select_fields, 0, 0, duplic, ignore, NULL), create_table(table_arg), + orig_table(table_arg), create_info(create_info_par), select_tables(select_tables_arg), alter_info(alter_info_arg), @@ -6071,6 +6077,9 @@ public: { bzero(&ddl_log_state_create, sizeof(ddl_log_state_create)); bzero(&ddl_log_state_rm, sizeof(ddl_log_state_rm)); + atomic_replace= create_info->is_atomic_replace(); + create_info->ddl_log_state_create= &ddl_log_state_create; + create_info->ddl_log_state_rm= &ddl_log_state_rm; } int prepare(List<Item> &list, SELECT_LEX_UNIT *u); diff --git a/sql/sql_db.cc b/sql/sql_db.cc index c5defc1959c..a053569f94d 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1044,6 +1044,7 @@ mysql_rm_db_internal(THD *thd, const LEX_CSTRING *db, bool if_exists, TABLE_LIST *tables= NULL; TABLE_LIST *table; DDL_LOG_STATE ddl_log_state; + Atomic_info atomic_replace_info(&ddl_log_state); Drop_table_error_handler err_handler; LEX_CSTRING rm_db; char db_tmp[SAFE_NAME_LEN+1]; @@ -1121,8 +1122,8 @@ mysql_rm_db_internal(THD *thd, const LEX_CSTRING *db, bool if_exists, thd->push_internal_handler(&err_handler); if (!thd->killed && !(tables && - mysql_rm_table_no_locks(thd, tables, &rm_db, &ddl_log_state, true, false, - true, false, true, false))) + mysql_rm_table_no_locks(thd, tables, &rm_db, &atomic_replace_info, + true, false, true, false, true, false))) { debug_crash_here("ddl_log_drop_after_drop_tables"); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 707b8a0d3bf..1f462009c15 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3834,7 +3834,8 @@ select_insert::select_insert(THD *thd_arg, TABLE_LIST *table_list_par, sel_result(result), table_list(table_list_par), table(table_par), fields(fields_par), autoinc_value_of_last_inserted_row(0), - insert_into_view(table_list_par && table_list_par->view != 0) + insert_into_view(table_list_par && table_list_par->view != 0), + binary_logged(false), atomic_replace(false) { bzero((char*) &info,sizeof(info)); info.handle_duplicates= duplic; @@ -4166,7 +4167,6 @@ bool select_insert::prepare_eof() int error; bool const trans_table= table->file->has_transactions_and_rollback(); bool changed; - bool binary_logged= 0; killed_state killed_status= thd->killed; DBUG_ENTER("select_insert::prepare_eof"); @@ -4185,8 +4185,8 @@ bool select_insert::prepare_eof() error= thd->get_stmt_da()->sql_errno(); if (info.ignore || info.handle_duplicates != DUP_ERROR) - if (table->file->ha_table_flags() & HA_DUPLICATE_POS) - table->file->ha_rnd_end(); + if (!atomic_replace && table->file->ha_table_flags() & HA_DUPLICATE_POS) + table->file->ha_rnd_end(); table->file->extra(HA_EXTRA_END_ALTER_COPY); table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); @@ -4231,6 +4231,11 @@ bool select_insert::prepare_eof() table->file->ha_release_auto_increment(); DBUG_RETURN(true); } + /* + FIXME: bad check !table->s->tmp_table in case of atomic_replace. + The better check is create_info->tmp_table(). The even better is to + update binary_logged in do_postlock() for RBR. + */ binary_logged= res == 0 || !table->s->tmp_table; } table->s->table_creation_was_logged|= binary_logged; @@ -4349,6 +4354,12 @@ void select_insert::abort_result_set() res= thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), transactional_table, FALSE, FALSE, errcode); + + /* + FIXME: bad check !table->s->tmp_table in case of atomic_replace. + The better check is create_info->tmp_table(). The even better is to + update binary_logged in do_postlock() for RBR. + */ binary_logged= res == 0 || !table->s->tmp_table; } if (changed) @@ -4423,11 +4434,13 @@ TABLE *select_create::create_table_from_items(THD *thd, List<Item> *items, TABLE tmp_table; // Used during 'Create_field()' TABLE_SHARE share; TABLE *table= 0; - uint select_field_count= items->elements; + alter_info->select_field_count= items->elements; /* Add selected items to field list */ List_iterator_fast<Item> it(*items); Item *item; bool save_table_creation_was_logged; + int create_table_mode= CREATE_ORDINARY; + LEX_CUSTRING frm= {0, 0}; DBUG_ENTER("select_create::create_table_from_items"); tmp_table.s= &share; @@ -4481,10 +4494,8 @@ TABLE *select_create::create_table_from_items(THD *thd, List<Item> *items, alter_info->create_list.push_back(cr_field, thd->mem_root); } - if (create_info->check_fields(thd, alter_info, - create_table->table_name, - create_table->db, - select_field_count)) + if (create_info->check_fields(thd, alter_info, create_table->table_name, + create_table->db)) DBUG_RETURN(NULL); DEBUG_SYNC(thd,"create_table_select_before_create"); @@ -4499,6 +4510,20 @@ TABLE *select_create::create_table_from_items(THD *thd, List<Item> *items, create_info->mdl_ticket= create_table->table->mdl_ticket; } + if (atomic_replace) + { + DBUG_ASSERT(!create_info->tmp_table()); // FIXME: test + if (make_tmp_name(thd, "create", create_table, &new_table)) + DBUG_RETURN(NULL); + create_table_mode|= CREATE_TMP_TABLE; + DBUG_ASSERT(!(create_info->options & HA_CREATE_TMP_ALTER)); + // FIXME: restore options? + create_info->options|= HA_CREATE_TMP_ALTER; + select_create::create_table= &new_table; + select_insert::table_list= &new_table; + create_info->tmp_name= &new_table; + } + /* Create and lock table. @@ -4516,11 +4541,14 @@ TABLE *select_create::create_table_from_items(THD *thd, List<Item> *items, open_table(). */ - if (!mysql_create_table_no_lock(thd, &ddl_log_state_create, &ddl_log_state_rm, + if (!mysql_create_table_no_lock(thd, + &orig_table->db, + &orig_table->table_name, &create_table->db, &create_table->table_name, create_info, alter_info, NULL, - select_field_count, create_table)) + create_table_mode, create_table, + atomic_replace ? &frm : NULL)) { DEBUG_SYNC(thd,"create_table_select_before_open"); @@ -4530,7 +4558,44 @@ TABLE *select_create::create_table_from_items(THD *thd, List<Item> *items, */ create_table->table= 0; - if (!create_info->tmp_table()) + if (atomic_replace) + { + char tmp_path[FN_REFLEN + 1]; + build_table_filename(tmp_path, sizeof(tmp_path) - 1, new_table.db.str, + new_table.table_name.str, "", FN_IS_TMP); + + create_table->table= + thd->create_and_open_tmp_table(&frm, tmp_path, orig_table->db.str, + orig_table->table_name.str, true); + /* NOTE: if create_and_open_tmp_table() fails the table is dropped by + * ddl_log_state_create */ + if (create_table->table) + { + /* + NOTE: Aria tables require table locking to work in transactional + mode. Since we don't lock our temporary table we get problems with + unproperly initialized transactional mode: seg-fault while accessing + uninitialized trn member (reproduced by + atomic.create_replace,aria,stmt). + + This hack disables logging for Aria table (that is not needed anyway + for a temporary table). + */ + bool on_save= thd->transaction->on; + thd->transaction->on= false; + if (create_table->table->file->ha_external_lock(thd, F_WRLCK)) + { + // FIXME: test + create_table->table= 0; + thd->transaction->on= on_save; + goto err; + } + + thd->transaction->on= on_save; + create_table->table->s->can_do_row_logging= 1; + } + } + else if (!create_info->tmp_table()) { Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN); TABLE_LIST::enum_open_strategy save_open_strategy; @@ -4571,13 +4636,20 @@ TABLE *select_create::create_table_from_items(THD *thd, List<Item> *items, } else create_table->table= 0; // Create failed - + +err: + DBUG_ASSERT(!create_table->table || frm.str || !atomic_replace); + my_free(const_cast<uchar *>(frm.str)); + if (unlikely(!(table= create_table->table))) { if (likely(!thd->is_error())) // CREATE ... IF NOT EXISTS my_ok(thd); // succeed, but did nothing + if (ddl_log_state_rm.is_active()) + (void) ddl_log_revert(thd, &ddl_log_state_create); + else + ddl_log_complete(&ddl_log_state_create); ddl_log_complete(&ddl_log_state_rm); - ddl_log_complete(&ddl_log_state_create); DBUG_RETURN(NULL); } @@ -4598,8 +4670,9 @@ TABLE *select_create::create_table_from_items(THD *thd, List<Item> *items, since it won't wait for the table lock (we have exclusive metadata lock on the table) and thus can't get aborted. */ - if (unlikely(!((*lock)= mysql_lock_tables(thd, &table, 1, 0)) || - hooks->postlock(&table, 1))) + if ((!atomic_replace && + unlikely(!((*lock)= mysql_lock_tables(thd, &table, 1, 0)))) || + hooks->postlock(&table, 1)) { /* purecov: begin tested */ /* @@ -4616,13 +4689,20 @@ TABLE *select_create::create_table_from_items(THD *thd, List<Item> *items, *lock= 0; } drop_open_table(thd, table, &create_table->db, &create_table->table_name); + if (ddl_log_state_rm.is_active()) + (void) ddl_log_revert(thd, &ddl_log_state_create); + else + ddl_log_complete(&ddl_log_state_create); ddl_log_complete(&ddl_log_state_rm); - ddl_log_complete(&ddl_log_state_create); DBUG_RETURN(NULL); /* purecov: end */ } + DBUG_ASSERT( + create_info->tmp_table() || + thd->mdl_context.is_lock_owner(MDL_key::TABLE, orig_table->db.str, + orig_table->table_name.str, MDL_SHARED)); table->s->table_creation_was_logged= save_table_creation_was_logged; - if (!table->s->tmp_table) + if (!create_info->tmp_table()) table->file->prepare_for_row_logging(); /* @@ -4677,6 +4757,11 @@ select_create::prepare(List<Item> &_values, SELECT_LEX_UNIT *u) private: virtual int do_postlock(TABLE **tables, uint count) { + /* + NOTE: for row format CREATE TABLE must be logged before row data. + + TODO: Remove creepy TABLEOP_HOOKS interface? + */ int error; THD *thd= const_cast<THD*>(ptr->get_thd()); TABLE_LIST *save_next_global= create_table->next_global; @@ -4690,10 +4775,23 @@ select_create::prepare(List<Item> &_values, SELECT_LEX_UNIT *u) if (unlikely(error)) return error; - TABLE const *const table = *tables; if (thd->is_current_stmt_binlog_format_row() && - !table->s->tmp_table) - return binlog_show_create_table(thd, *tables, ptr->create_info); + !ptr->create_info->tmp_table()) + { + thd->binlog_xid= thd->query_id; + /* + Remember xid's for the case of row based logging. Note that binary + log is not flushed until the end of statement, so it is OK to write + it now and if crash happens until we closed ddl_log_state_rm we + won't see CREATE OR REPLACE event in the binary log. + */ + ddl_log_update_xid(&ptr->ddl_log_state_create, thd->binlog_xid); + if (ptr->ddl_log_state_rm.is_active() && + !ptr->create_info->is_atomic_replace()) + ddl_log_update_xid(&ptr->ddl_log_state_rm, thd->binlog_xid); + error= binlog_show_create_table(thd, *tables, ptr->create_info); + return error; + } return 0; } select_create *ptr; @@ -4720,8 +4818,9 @@ select_create::prepare(List<Item> &_values, SELECT_LEX_UNIT *u) if (!(table= create_table_from_items(thd, &values, &extra_lock, hook_ptr))) { - if (create_info->or_replace()) + if (create_info->or_replace() && !atomic_replace) { + /* TODO: why create_info->table_was_deleted not used? */ /* Original table was deleted. We have to log it */ log_drop_table(thd, &create_table->db, &create_table->table_name, &create_info->org_storage_engine_name, @@ -4734,6 +4833,8 @@ select_create::prepare(List<Item> &_values, SELECT_LEX_UNIT *u) DBUG_RETURN(-1); } + DBUG_ASSERT(table == create_table->table); + if (create_info->tmp_table()) { /* @@ -4743,14 +4844,14 @@ select_create::prepare(List<Item> &_values, SELECT_LEX_UNIT *u) list to keep them inaccessible from inner statements. e.g. CREATE TEMPORARY TABLE `t1` AS SELECT * FROM `t1`; */ - saved_tmp_table_share= thd->save_tmp_table_share(create_table->table); + saved_tmp_table_share= thd->save_tmp_table_share(table); } if (extra_lock) { DBUG_ASSERT(m_plock == NULL); - if (create_info->tmp_table()) + if (table->s->tmp_table) m_plock= &m_lock; else m_plock= &thd->extra_lock; @@ -4845,6 +4946,10 @@ static int binlog_show_create_table(THD *thd, TABLE *table, create_info, WITH_DB_NAME); DBUG_ASSERT(result == 0); /* show_create_table() always return 0 */ + /* + TODO (optimization): why it does show_create_table() even if + !mysql_bin_log.is_open()? + */ if (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()) { int errcode= query_error_code(thd, thd->killed == NOT_KILLED); @@ -4966,28 +5071,42 @@ bool select_create::send_eof() is in select_insert::prepare_eof(). For that reason, we mark the flag at this point. */ - if (table->s->tmp_table) + if (create_info->tmp_table()) thd->transaction->stmt.mark_created_temp_table(); if (thd->slave_thread) thd->variables.binlog_annotate_row_events= 0; + if (atomic_replace) + { + DBUG_ASSERT(table->s->tmp_table); + + int result; + if (table->file->ha_index_or_rnd_end() || + table->file->ha_external_lock(thd, F_UNLCK)) + { + abort_result_set(); + DBUG_RETURN(true); + } + + create_info->table= orig_table->table; + if (create_table_handle_exists(thd, orig_table->db, orig_table->table_name, + *create_info, create_info, result)) + { + abort_result_set(); + DBUG_RETURN(true); + } + } + debug_crash_here("ddl_log_create_before_binlog"); - /* - In case of crash, we have to add DROP TABLE to the binary log as - the CREATE TABLE will already be logged if we are not using row based - replication. - */ - if (!thd->is_current_stmt_binlog_format_row()) + if (!thd->binlog_xid && !create_info->tmp_table()) { - if (ddl_log_state_create.is_active()) // Not temporary table - ddl_log_update_phase(&ddl_log_state_create, DDL_CREATE_TABLE_PHASE_LOG); - /* - We can ignore if we replaced an old table as ddl_log_state_create will - now handle the logging of the drop if needed. - */ - ddl_log_complete(&ddl_log_state_rm); + thd->binlog_xid= thd->query_id; + /* Remember xid's for the case of row based logging */ + ddl_log_update_xid(&ddl_log_state_create, thd->binlog_xid); + if (ddl_log_state_rm.is_active() && !atomic_replace) + ddl_log_update_xid(&ddl_log_state_rm, thd->binlog_xid); } if (prepare_eof()) @@ -4997,7 +5116,7 @@ bool select_create::send_eof() } debug_crash_here("ddl_log_create_after_prepare_eof"); - if (table->s->tmp_table) + if (create_info->tmp_table()) { /* Now is good time to add the new table to THD temporary tables list. @@ -5017,17 +5136,25 @@ bool select_create::send_eof() thd->restore_tmp_table_share(saved_tmp_table_share); } } + else if (atomic_replace) + { + create_table= orig_table; + create_info->table= NULL; + table->file->ha_reset(); + thd->drop_temporary_table(table, NULL, false); + table= NULL; + } /* Do an implicit commit at end of statement for non-temporary tables. This can fail, but we should unlock the table nevertheless. */ - if (!table->s->tmp_table) + if (!create_info->tmp_table()) { #ifdef WITH_WSREP if (WSREP(thd) && - table->file->ht->db_type == DB_TYPE_INNODB) + create_info->db_type->db_type == DB_TYPE_INNODB) { if (thd->wsrep_trx_id() == WSREP_UNDEFINED_TRX_ID) { @@ -5062,10 +5189,6 @@ bool select_create::send_eof() thd->get_stmt_da()->set_overwrite_status(true); } #endif /* WITH_WSREP */ - thd->binlog_xid= thd->query_id; - /* Remember xid's for the case of row based logging */ - ddl_log_update_xid(&ddl_log_state_create, thd->binlog_xid); - ddl_log_update_xid(&ddl_log_state_rm, thd->binlog_xid); trans_commit_stmt(thd); if (!(thd->variables.option_bits & OPTION_GTID_BEGIN)) trans_commit_implicit(thd); @@ -5102,6 +5225,11 @@ bool select_create::send_eof() ddl_log.org_database= create_table->db; ddl_log.org_table= create_table->table_name; ddl_log.org_table_id= create_info->tabledef_version; + if (create_info->drop_entry.query.length) + { + DBUG_ASSERT(atomic_replace); + backup_log_ddl(&create_info->drop_entry); + } backup_log_ddl(&ddl_log); } /* @@ -5110,9 +5238,17 @@ bool select_create::send_eof() (as the query was logged before commit!) */ debug_crash_here("ddl_log_create_after_binlog"); - ddl_log_complete(&ddl_log_state_rm); - ddl_log_complete(&ddl_log_state_create); - debug_crash_here("ddl_log_create_log_complete"); + if (create_info->finalize_ddl(thd)) + { + if (atomic_replace) + { + /* Now we have to log DROP_AFTER_CREATE */ + atomic_replace= false; + create_table= &new_table; + } + abort_result_set(); + DBUG_RETURN(true); + } /* exit_done must only be set after last potential call to @@ -5120,10 +5256,9 @@ bool select_create::send_eof() */ exit_done= 1; // Avoid double calls - send_ok_packet(); - if (m_plock) { + DBUG_ASSERT(!atomic_replace); MYSQL_LOCK *lock= *m_plock; *m_plock= NULL; m_plock= NULL; @@ -5142,11 +5277,37 @@ bool select_create::send_eof() create_info-> pos_in_locked_tables, table, lock)) + { + send_ok_packet(); DBUG_RETURN(false); // ok + } /* Fail. Continue without locking the table */ } mysql_unlock_tables(thd, lock); } + else if (atomic_replace && create_info->pos_in_locked_tables) + { + DBUG_ASSERT(thd->locked_tables_mode); + DBUG_ASSERT(thd->variables.option_bits & OPTION_TABLE_LOCK); + TABLE_LIST *pos_in_locked_tables= create_info->pos_in_locked_tables; + /* + Add back the deleted table and re-created table as a locked table + This should always work as we have a meta lock on the table. + */ + thd->locked_tables_list.add_back_last_deleted_lock(pos_in_locked_tables); + if (thd->locked_tables_list.reopen_tables(thd, false)) + { + thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); + DBUG_RETURN(true); + } + else + { + TABLE *table= pos_in_locked_tables->table; + table->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); + } + } + + send_ok_packet(); DBUG_RETURN(false); } @@ -5191,14 +5352,14 @@ void select_create::abort_result_set() if (table) { - bool tmp_table= table->s->tmp_table; - bool table_creation_was_logged= (!tmp_table || - table->s->table_creation_was_logged); + bool tmp_table= create_info->tmp_table(); if (tmp_table) { DBUG_ASSERT(saved_tmp_table_share); thd->restore_tmp_table_share(saved_tmp_table_share); } + else if (atomic_replace) + create_table= &new_table; if (table->file->inited && (info.ignore || info.handle_duplicates != DUP_ERROR) && @@ -5215,7 +5376,14 @@ void select_create::abort_result_set() m_plock= NULL; } - drop_open_table(thd, table, &create_table->db, &create_table->table_name); + if (atomic_replace) + { + (void) table->file->ha_external_lock(thd, F_UNLCK); + (void) thd->drop_temporary_table(table, NULL, true); + } + else + drop_open_table(thd, table, &create_table->db, + &create_table->table_name); table=0; // Safety if (thd->log_current_statement) { @@ -5223,39 +5391,30 @@ void select_create::abort_result_set() { /* Remove logging of drop, create + insert rows */ binlog_reset_cache(thd); - /* Original table was deleted. We have to log it */ - if (table_creation_was_logged) - { - thd->binlog_xid= thd->query_id; - ddl_log_update_xid(&ddl_log_state_create, thd->binlog_xid); - ddl_log_update_xid(&ddl_log_state_rm, thd->binlog_xid); - debug_crash_here("ddl_log_create_before_binlog"); - log_drop_table(thd, &create_table->db, &create_table->table_name, - &create_info->org_storage_engine_name, - create_info->db_type == partition_hton, - &create_info->tabledef_version, - tmp_table); - debug_crash_here("ddl_log_create_after_binlog"); - thd->binlog_xid= 0; - } } - else if (!tmp_table) + else if (!tmp_table && !atomic_replace) { backup_log_info ddl_log; bzero(&ddl_log, sizeof(ddl_log)); ddl_log.query= { C_STRING_WITH_LEN("DROP_AFTER_CREATE") }; ddl_log.org_partitioned= (create_info->db_type == partition_hton); ddl_log.org_storage_engine_name= create_info->org_storage_engine_name; - ddl_log.org_database= create_table->db; - ddl_log.org_table= create_table->table_name; + ddl_log.org_database= orig_table->db; + ddl_log.org_table= orig_table->table_name; ddl_log.org_table_id= create_info->tabledef_version; backup_log_ddl(&ddl_log); } } } - - ddl_log_complete(&ddl_log_state_rm); - ddl_log_complete(&ddl_log_state_create); + if (!binary_logged) + { + if (ddl_log_state_rm.is_active()) + (void) ddl_log_revert(thd, &ddl_log_state_create); + else + ddl_log_complete(&ddl_log_state_create); + ddl_log_complete(&ddl_log_state_rm); + } + thd->binlog_xid= 0; if (create_info->table_was_deleted) { diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index b75a318ab65..8f2ecdcfafc 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -7003,6 +7003,8 @@ static void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, { THD *thd= lpt->thd; partition_info *part_info= lpt->part_info->get_clone(thd); + /* TABLE is going to be released, we should not access old part_info anymore */ + lpt->part_info= part_info; TABLE *table= lpt->table; DBUG_ENTER("handle_alter_part_error"); DBUG_ASSERT(table->needs_reopen()); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a357d4f8c8a..955e76f537b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -55,6 +55,7 @@ #include "sql_window.h" #include "tztime.h" +#include "debug.h" #include "debug_sync.h" // DEBUG_SYNC #include <m_ctype.h> #include <my_bit.h> @@ -22308,6 +22309,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) // error < 0 => duplicate row join->duplicate_rows++; } + debug_crash_here("ddl_log_create_after_send_data"); } join->send_records++; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index d01f84fe7d1..a138b2d51e6 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2107,7 +2107,8 @@ int show_create_table_ex(THD *thd, TABLE_LIST *table_list, !create_info_arg->or_replace_slave_generated()) || create_info_arg->table_was_deleted)) packet->append(STRING_WITH_LEN("OR REPLACE ")); - if (share->tmp_table) + if (share->tmp_table && + !(create_info_arg && create_info_arg->is_atomic_replace())) packet->append(STRING_WITH_LEN("TEMPORARY ")); packet->append(STRING_WITH_LEN("TABLE ")); if (create_info_arg && create_info_arg->if_not_exists()) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index b07efb29bba..7e08ad7b801 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -742,7 +742,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) if (mysql_prepare_create_table(lpt->thd, lpt->create_info, lpt->alter_info, &lpt->db_options, lpt->table->file, &lpt->key_info_buffer, &lpt->key_count, - C_ALTER_TABLE, lpt->db, lpt->table_name)) + CREATE_TMP_TABLE, lpt->db, lpt->table_name)) { DBUG_RETURN(TRUE); } @@ -816,7 +816,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) if (mysql_prepare_create_table(thd, create_info, lpt->alter_info, &lpt->db_options, file, &lpt->key_info_buffer, &lpt->key_count, - C_ALTER_TABLE, alter_ctx->new_db, + CREATE_TMP_TABLE, alter_ctx->new_db, alter_ctx->new_name)) DBUG_RETURN(TRUE); @@ -1144,11 +1144,9 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, /* mark for close and remove all cached entries */ thd->push_internal_handler(&err_handler); - error= mysql_rm_table_no_locks(thd, tables, &thd->db, (DDL_LOG_STATE*) 0, - if_exists, - drop_temporary, - false, drop_sequence, dont_log_query, - false); + error= mysql_rm_table_no_locks(thd, tables, &thd->db, NULL, if_exists, + drop_temporary, false, drop_sequence, + dont_log_query, false); thd->pop_internal_handler(); if (unlikely(error)) @@ -1203,6 +1201,39 @@ static uint32 get_comment(THD *thd, uint32 comment_pos, return 0; } +bool make_tmp_name(THD *thd, const char *prefix, const TABLE_LIST *orig, + TABLE_LIST *res) +{ + char res_name[NAME_LEN + 1]; + char file_name[FN_REFLEN]; + LEX_CSTRING table_name; + + (void) tablename_to_filename(orig->table_name.str, file_name, + sizeof(file_name)); + size_t len= my_snprintf(res_name, sizeof(res_name) - 1, + tmp_file_prefix "-%s-%lx-%llx-%s", prefix, + current_pid, thd->thread_id, file_name); + + table_name.str= strmake_root(thd->mem_root, res_name, len); + if (!table_name.str) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return true; + } + + table_name.length= len; + + if (lower_case_table_names) + { + my_casedn_str(system_charset_info, res_name); + table_name.str= strmake_root(thd->mem_root, res_name, len); + } + + res->init_one_table(&orig->db, &table_name, NULL, orig->lock_type); + return false; +} + + /** Execute the drop of a sequence, view or table (normal or temporary). @@ -1243,7 +1274,7 @@ static uint32 get_comment(THD *thd, uint32 comment_pos, int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, const LEX_CSTRING *current_db, - DDL_LOG_STATE *ddl_log_state, + Atomic_info *atomic_info, bool if_exists, bool drop_temporary, bool drop_view, bool drop_sequence, @@ -1271,6 +1302,15 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, String built_trans_tmp_query, built_non_trans_tmp_query; DBUG_ENTER("mysql_rm_table_no_locks"); + DDL_LOG_STATE *ddl_log_state= NULL; + bool atomic_replace= false; + + if (atomic_info) + { + ddl_log_state= atomic_info->ddl_log_state_rm; + atomic_replace= (atomic_info->tmp_name != NULL); + } + if (!ddl_log_state) { ddl_log_state= &local_ddl_log_state; @@ -1456,12 +1496,13 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db.str, table_name.str, MDL_SHARED)); + /* NOTE: alias holds original table name, table_name holds lowercase name */ alias= (lower_case_table_names == 2) ? table->alias : table_name; /* remove .frm file and engine files */ path_length= build_table_filename(path, sizeof(path) - 1, db.str, alias.str, reg_ext, 0); path_end= path + path_length - reg_ext_length; - } + } /* if (!drop_temporary) */ DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table"); if (drop_temporary) @@ -1569,6 +1610,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, } debug_crash_here("ddl_log_drop_before_delete_table"); + if (atomic_replace) + goto report_error; error= ha_delete_table(thd, hton, path, &db, &table_name, enoent_warning); debug_crash_here("ddl_log_drop_after_delete_table"); @@ -1638,6 +1681,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, int ferror= 0; DBUG_ASSERT(!was_view); + /* We've already logged drop and we don't need that entry anymore. */ + ddl_log_disable_entry(ddl_log_state); if (ddl_log_drop_table(thd, ddl_log_state, 0, &cpath, &db, &table_name)) { @@ -1734,29 +1779,42 @@ report_error: if (if_exists && non_existing_table_error(error)) error= 0; - if (!error && table_dropped) + if (!error) { - PSI_CALL_drop_table_share(temporary_table_was_dropped, - db.str, (uint)db.length, - table_name.str, (uint)table_name.length); - mysql_audit_drop_table(thd, table); - if (!is_temporary) + if (table_dropped) + { + PSI_CALL_drop_table_share(temporary_table_was_dropped, db.str, + (uint) db.length, table_name.str, + (uint) table_name.length); + mysql_audit_drop_table(thd, table); + } + if (!is_temporary && (atomic_replace || table_dropped)) { backup_log_info ddl_log; - bzero(&ddl_log, sizeof(ddl_log)); - ddl_log.query= { C_STRING_WITH_LEN("DROP") }; - if ((ddl_log.org_partitioned= (partition_engine_name.str != 0))) - ddl_log.org_storage_engine_name= partition_engine_name; + backup_log_info *d; + DBUG_ASSERT(!atomic_replace || atomic_info); + DBUG_ASSERT(!(atomic_replace && table_dropped)); + if (atomic_replace) + d= &atomic_info->drop_entry; else - lex_string_set(&ddl_log.org_storage_engine_name, + { + d= &ddl_log; + bzero(d, sizeof(*d)); + } + d->query= { C_STRING_WITH_LEN("DROP") }; + if ((d->org_partitioned= (partition_engine_name.str != 0))) + d->org_storage_engine_name= partition_engine_name; + else + lex_string_set(&d->org_storage_engine_name, ha_resolve_storage_engine_name(hton)); - ddl_log.org_database= table->db; - ddl_log.org_table= table->table_name; - ddl_log.org_table_id= version; - backup_log_ddl(&ddl_log); + d->org_database= table->db; + d->org_table= table->table_name; + d->org_table_id= version; + if (table_dropped) + backup_log_ddl(d); } } - if (!was_view) + if (!was_view && !atomic_replace) ddl_log_update_phase(ddl_log_state, DDL_DROP_PHASE_BINLOG); if (!dont_log_query && @@ -2684,8 +2742,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, List_iterator_fast<Create_field> it(alter_info->create_list); List_iterator<Create_field> it2(alter_info->create_list); uint total_uneven_bit_length= 0; - int select_field_count= C_CREATE_SELECT(create_table_mode); - bool tmp_table= create_table_mode == C_ALTER_TABLE; + bool tmp_table= (create_table_mode & CREATE_TMP_TABLE); const bool create_simple= thd->lex->create_simple(); bool is_hash_field_needed= false; const Column_derived_attributes dattr(create_info->default_table_charset); @@ -2724,7 +2781,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, DBUG_RETURN(TRUE); } - select_field_pos= alter_info->create_list.elements - select_field_count; + select_field_pos= alter_info->field_count(); null_fields= 0; create_info->varchar= 0; max_key_length= file->max_key_length(); @@ -2899,7 +2956,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, from the select tables. This order may differ on master and slave. We therefore mark it as unsafe. */ - if (select_field_count > 0 && auto_increment) + if (alter_info->select_field_count > 0 && auto_increment) thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_CREATE_SELECT_AUTOINC); /* Create keys */ @@ -2935,6 +2992,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, ER_THD(thd, ER_KEY_REF_DO_NOT_MATCH_TABLE_REF)); DBUG_RETURN(TRUE); } + if (create_info->tmp_name && + !lex_string_cmp(table_alias_charset, &table_name, + &fk_key->ref_table)) + { + fk_key->ref_table= create_info->tmp_name->table_name; + } continue; } (*key_count)++; @@ -4246,6 +4309,155 @@ err: DBUG_RETURN(NULL); } +inline bool +HA_CREATE_INFO::handle_atomic_replace(THD *thd, const LEX_CSTRING &db, + const LEX_CSTRING &table_name, + const DDL_options_st options) +{ + DBUG_ASSERT(options.or_replace()); + DBUG_ASSERT(ok_atomic_replace()); + ddl_log_link_chains(ddl_log_state_rm, ddl_log_state_create); + if (ddl_log_rename_table(thd, ddl_log_state_rm, db_type, &db, &table_name, + &tmp_name->db, &tmp_name->table_name)) + return true; + debug_crash_here("ddl_log_create_after_log_rename"); + return false; +} + +bool HA_CREATE_INFO::finalize_ddl(THD *thd) +{ + bool result; + if (ddl_log_state_create->execute_entry) + { + DBUG_ASSERT(ddl_log_state_create->is_active()); + mysql_mutex_lock(&LOCK_gdl); + ddl_log_disable_execute_entry(&ddl_log_state_create->execute_entry); + mysql_mutex_unlock(&LOCK_gdl); + } + debug_crash_here("ddl_log_create_before_remove_backup"); + /* NOTE: holds "drop old table; rename tmp table" */ + result= ddl_log_revert(thd, ddl_log_state_rm, true); + if (result && ddl_log_state_create->is_active()) + { + /* In case roll forward fails we must roll back to drop tmp table */ + mysql_mutex_lock(&LOCK_gdl); + ddl_log_write_execute_entry(ddl_log_state_create->list->entry_pos, 0, + &ddl_log_state_create->execute_entry); + mysql_mutex_unlock(&LOCK_gdl); + (void) ddl_log_revert(thd, ddl_log_state_create); + } + else + { + mysql_mutex_lock(&LOCK_gdl); + ddl_log_release_entries(ddl_log_state_create); + mysql_mutex_unlock(&LOCK_gdl); + ddl_log_state_create->list= 0; + } + debug_crash_here("ddl_log_create_log_complete"); + return result; +} + +bool create_table_handle_exists(THD *thd, const LEX_CSTRING &db, + const LEX_CSTRING &table_name, + const DDL_options_st options, + HA_CREATE_INFO *create_info, int &error) +{ + handlerton *db_type; + const bool atomic_replace= create_info->tmp_name != NULL; + + if (!ha_table_exists(thd, &db, &table_name, + &create_info->org_tabledef_version, NULL, &db_type)) + { + if (atomic_replace && + create_info->handle_atomic_replace(thd, db, table_name, options)) + return true; + return false; + } + + if (ha_check_if_updates_are_ignored(thd, db_type, "CREATE")) + { + /* Don't create table. CREATE will still be logged in binary log */ + error= 0; + return true; + } + + if (options.or_replace()) + { + (void) delete_statistics_for_table(thd, &db, &table_name); + + TABLE_LIST table_list; + table_list.init_one_table(&db, &table_name, 0, TL_WRITE_ALLOW_WRITE); + table_list.table= create_info->table; + + if (check_if_log_table(&table_list, TRUE, "CREATE OR REPLACE")) + return true; + + if (atomic_replace) + { + /* + NOTE: we must log rename before drop! Otherwise we may recover into + drop, but not do rename. See next_active_log_entry handling in + ddl_log_drop(). + */ + if (create_info->handle_atomic_replace(thd, db, table_name, options)) + return true; + } + else + { + /* + Rollback the empty transaction started in mysql_create_table() + call to open_and_lock_tables() when we are using LOCK TABLES. + */ + (void) trans_rollback_stmt(thd); + } + /* Remove normal table without logging. Keep tables locked */ + if (mysql_rm_table_no_locks(thd, &table_list, &thd->db, create_info, 0, 0, + 0, 0, 1, 1)) + return true; + + debug_crash_here("ddl_log_create_after_drop"); + + /* + We have to log this query, even if it failed later to ensure the + drop is done. + */ + thd->variables.option_bits|= OPTION_KEEP_LOG; + thd->log_current_statement= 1; + if (!atomic_replace) + create_info->table_was_deleted= 1; + lex_string_set(&create_info->org_storage_engine_name, + ha_resolve_storage_engine_name(db_type)); + DBUG_EXECUTE_IF("send_kill_after_delete", thd->set_killed(KILL_QUERY);); + /* + Restart statement transactions for the case of CREATE ... SELECT. + */ + if (!atomic_replace && thd->lex->first_select_lex()->item_list.elements && + restart_trans_for_tables(thd, thd->lex->query_tables)) + return true; + } + else if (options.if_not_exists()) + { + /* + We never come here as part of normal create table as table existance + is checked in open_and_lock_tables(). We may come here as part of + ALTER TABLE when converting a table for a distributed engine to a + a local one. + */ + + /* Log CREATE IF NOT EXISTS on slave for distributed engines */ + if (thd->slave_thread && (db_type && db_type->flags & HTON_IGNORE_UPDATES)) + thd->log_current_statement= 1; + error= -1; + return true; + } + else + { + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name.str); + return true; + } + return false; +} + /** Create a table @@ -4286,8 +4498,6 @@ err: */ int create_table_impl(THD *thd, - DDL_LOG_STATE *ddl_log_state_create, - DDL_LOG_STATE *ddl_log_state_rm, const LEX_CSTRING &orig_db, const LEX_CSTRING &orig_table_name, const LEX_CSTRING &db, const LEX_CSTRING &table_name, @@ -4296,22 +4506,17 @@ int create_table_impl(THD *thd, int create_table_mode, bool *is_trans, KEY **key_info, uint *key_count, LEX_CUSTRING *frm) { - LEX_CSTRING *alias; + LEX_CSTRING *alias= const_cast<LEX_CSTRING*>(table_case_name(create_info, &table_name)); handler *file= 0; int error= 1; - bool frm_only= create_table_mode == C_ALTER_TABLE_FRM_ONLY; - bool internal_tmp_table= create_table_mode == C_ALTER_TABLE || frm_only; + bool frm_only= (create_table_mode & CREATE_FRM_ONLY); + bool internal_tmp_table= (create_table_mode & CREATE_TMP_TABLE) || frm_only; + /* Easy check for ddl logging if we are creating a temporary table */ + DDL_LOG_STATE *ddl_log_state_create= create_info->tmp_table() ? 0 : create_info->ddl_log_state_create; DBUG_ENTER("create_table_impl"); DBUG_PRINT("enter", ("db: '%s' table: '%s' tmp: %d path: %s", db.str, table_name.str, internal_tmp_table, path.str)); - /* Easy check for ddl logging if we are creating a temporary table */ - if (create_info->tmp_table()) - { - ddl_log_state_create= 0; - ddl_log_state_rm= 0; - } - if (fix_constraints_names(thd, &alter_info->check_constraint_list, create_info)) DBUG_RETURN(1); @@ -4340,8 +4545,6 @@ int create_table_impl(THD *thd, goto err; } - alias= const_cast<LEX_CSTRING*>(table_case_name(create_info, &table_name)); - /* Check if table exists */ if (create_info->tmp_table()) { @@ -4365,7 +4568,10 @@ int create_table_impl(THD *thd, goto err; } else if (options.if_not_exists()) - goto warn; + { + error= -1; + goto err; + } else { my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias->str); @@ -4396,81 +4602,10 @@ int create_table_impl(THD *thd, goto err; } - handlerton *db_type; if (!internal_tmp_table && - ha_table_exists(thd, &db, &table_name, - &create_info->org_tabledef_version, NULL, &db_type)) - { - if (ha_check_if_updates_are_ignored(thd, db_type, "CREATE")) - { - /* Don't create table. CREATE will still be logged in binary log */ - error= 0; - goto err; - } - - if (options.or_replace()) - { - (void) delete_statistics_for_table(thd, &db, &table_name); - - TABLE_LIST table_list; - table_list.init_one_table(&db, &table_name, 0, TL_WRITE_ALLOW_WRITE); - table_list.table= create_info->table; - - if (check_if_log_table(&table_list, TRUE, "CREATE OR REPLACE")) - goto err; - - /* - Rollback the empty transaction started in mysql_create_table() - call to open_and_lock_tables() when we are using LOCK TABLES. - */ - (void) trans_rollback_stmt(thd); - /* Remove normal table without logging. Keep tables locked */ - if (mysql_rm_table_no_locks(thd, &table_list, &thd->db, - ddl_log_state_rm, - 0, 0, 0, 0, 1, 1)) - goto err; - - debug_crash_here("ddl_log_create_after_drop"); - - /* - We have to log this query, even if it failed later to ensure the - drop is done. - */ - thd->variables.option_bits|= OPTION_KEEP_LOG; - thd->log_current_statement= 1; - create_info->table_was_deleted= 1; - lex_string_set(&create_info->org_storage_engine_name, - ha_resolve_storage_engine_name(db_type)); - DBUG_EXECUTE_IF("send_kill_after_delete", - thd->set_killed(KILL_QUERY);); - /* - Restart statement transactions for the case of CREATE ... SELECT. - */ - if (thd->lex->first_select_lex()->item_list.elements && - restart_trans_for_tables(thd, thd->lex->query_tables)) - goto err; - } - else if (options.if_not_exists()) - { - /* - We never come here as part of normal create table as table existance - is checked in open_and_lock_tables(). We may come here as part of - ALTER TABLE when converting a table for a distributed engine to a - a local one. - */ - - /* Log CREATE IF NOT EXISTS on slave for distributed engines */ - if (thd->slave_thread && (db_type && db_type->flags & - HTON_IGNORE_UPDATES)) - thd->log_current_statement= 1; - goto warn; - } - else - { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name.str); - goto err; - } - } + create_table_handle_exists(thd, db, table_name, options, create_info, + error)) + goto err; } THD_STAGE_INFO(thd, stage_creating_table); @@ -4478,7 +4613,7 @@ int create_table_impl(THD *thd, if (check_engine(thd, orig_db.str, orig_table_name.str, create_info)) goto err; - if (create_table_mode == C_ASSISTED_DISCOVERY) + if (create_table_mode & CREATE_ASSISTED) { /* check that it's used correctly */ DBUG_ASSERT(alter_info->create_list.elements == 0); @@ -4564,8 +4699,8 @@ int create_table_impl(THD *thd, if (!frm_only) { debug_crash_here("ddl_log_create_before_create_table"); - if (ha_create_table(thd, path.str, db.str, table_name.str, create_info, - frm, 0)) + if (ha_create_table(thd, path.str, orig_db.str, orig_table_name.str, + create_info, frm, 0)) { file->ha_create_partitioning_metadata(path.str, NULL, CHF_DELETE_FLAG); deletefrm(path.str); @@ -4597,6 +4732,12 @@ int create_table_impl(THD *thd, error= 0; err: + if (error == -1) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_TABLE_EXISTS_ERROR, + ER_THD(thd, ER_TABLE_EXISTS_ERROR), alias->str); + } if (unlikely(error) && ddl_log_state_create) { /* Table was never created, so we can ignore the ddl log entry */ @@ -4607,14 +4748,6 @@ err: delete file; DBUG_PRINT("exit", ("return: %d", error)); DBUG_RETURN(error); - -warn: - error= -1; - push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_TABLE_EXISTS_ERROR, - ER_THD(thd, ER_TABLE_EXISTS_ERROR), - alias->str); - goto err; } /** @@ -4626,16 +4759,18 @@ warn: 2 error; Don't log create statement 0 ok -1 Table was used with IF NOT EXISTS and table existed (warning, not error) + + TODO: orig_db, orig_table_name, db, table_name should be moved to create_info */ -int mysql_create_table_no_lock(THD *thd, - DDL_LOG_STATE *ddl_log_state_create, - DDL_LOG_STATE *ddl_log_state_rm, +int mysql_create_table_no_lock(THD *thd, const LEX_CSTRING *orig_db, + const LEX_CSTRING *orig_table_name, const LEX_CSTRING *db, const LEX_CSTRING *table_name, Table_specification_st *create_info, Alter_info *alter_info, bool *is_trans, - int create_table_mode, TABLE_LIST *table_list) + int create_table_mode, TABLE_LIST *table_list, + LEX_CUSTRING *frm) { KEY *not_used_1; uint not_used_2; @@ -4643,16 +4778,21 @@ int mysql_create_table_no_lock(THD *thd, uint path_length; char path[FN_REFLEN + 1]; LEX_CSTRING cpath; - LEX_CUSTRING frm= {0,0}; + LEX_CUSTRING frm_local; + if (!frm) + { + frm_local= {0, 0}; + frm= &frm_local; + } if (create_info->tmp_table()) path_length= build_tmptable_filename(thd, path, sizeof(path)); else { const LEX_CSTRING *alias= table_case_name(create_info, table_name); + uint flags= (create_info->options & HA_CREATE_TMP_ALTER) ? FN_IS_TMP : 0; path_length= build_table_filename(path, sizeof(path) - 1, db->str, - alias->str, - "", 0); + alias->str, "", flags); // Check if we hit FN_REFLEN bytes along with file extension. if (path_length+reg_ext_length > FN_REFLEN) { @@ -4663,12 +4803,12 @@ int mysql_create_table_no_lock(THD *thd, } lex_string_set3(&cpath, path, path_length); - res= create_table_impl(thd, ddl_log_state_create, ddl_log_state_rm, - *db, *table_name, *db, *table_name, cpath, - *create_info, create_info, - alter_info, create_table_mode, - is_trans, ¬_used_1, ¬_used_2, &frm); - my_free(const_cast<uchar*>(frm.str)); + res= create_table_impl(thd, *orig_db, *orig_table_name, *db, *table_name, + cpath, *create_info, create_info, alter_info, + create_table_mode, is_trans, ¬_used_1, ¬_used_2, + frm); + if (frm == &frm_local) + my_free(const_cast<uchar *>(frm_local.str)); if (!res && create_info->sequence) { @@ -4683,8 +4823,7 @@ int mysql_create_table_no_lock(THD *thd, DBUG_ASSERT(thd->is_error()); /* Drop the table as it wasn't completely done */ if (!mysql_rm_table_no_locks(thd, table_list, &thd->db, - (DDL_LOG_STATE*) 0, - 1, + NULL, 1, create_info->tmp_table(), false, true /* Sequence*/, true /* Don't log_query */, @@ -4723,13 +4862,20 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, int create_table_mode; uint save_thd_create_info_options; bool is_trans= FALSE; - bool result; + int result; + TABLE_LIST new_table; + TABLE_LIST *orig_table= create_table; + const bool atomic_replace= create_info->is_atomic_replace(); DBUG_ENTER("mysql_create_table"); DBUG_ASSERT(create_table == thd->lex->query_tables); bzero(&ddl_log_state_create, sizeof(ddl_log_state_create)); bzero(&ddl_log_state_rm, sizeof(ddl_log_state_rm)); + if (atomic_replace) + create_info->tmp_name= &new_table; + create_info->ddl_log_state_create= &ddl_log_state_create; + create_info->ddl_log_state_rm= &ddl_log_state_rm; /* Copy temporarily the statement flags to thd for lock_table_names() */ save_thd_create_info_options= thd->lex->create_info.options; @@ -4765,9 +4911,9 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, DEBUG_SYNC(thd, "locked_table_name"); if (alter_info->create_list.elements || alter_info->key_list.elements) - create_table_mode= C_ORDINARY_CREATE; + create_table_mode= CREATE_ORDINARY; else - create_table_mode= C_ASSISTED_DISCOVERY; + create_table_mode= CREATE_ASSISTED; if (!opt_explicit_defaults_for_timestamp) promote_first_timestamp_column(&alter_info->create_list); @@ -4775,7 +4921,23 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, /* We can abort create table for any table type */ thd->abort_on_warning= thd->is_strict_mode(); - if (mysql_create_table_no_lock(thd, &ddl_log_state_create, &ddl_log_state_rm, + if (atomic_replace) + { + if (make_tmp_name(thd, "create", create_table, &new_table)) + { + result= 1; + goto err; + } + create_table_mode|= CREATE_TMP_TABLE; + DBUG_ASSERT(!(create_info->options & HA_CREATE_TMP_ALTER)); + // FIXME: restore options? + create_info->options|= HA_CREATE_TMP_ALTER; + create_table= &new_table; + } + + if (mysql_create_table_no_lock(thd, + &orig_table->db, + &orig_table->table_name, &create_table->db, &create_table->table_name, create_info, alter_info, @@ -4786,30 +4948,17 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, goto err; } - /* - Check if we are doing CREATE OR REPLACE TABLE under LOCK TABLES - on a non temporary table - */ - if (thd->locked_tables_mode && pos_in_locked_tables && - create_info->or_replace()) + if (atomic_replace) { - DBUG_ASSERT(thd->variables.option_bits & OPTION_TABLE_LOCK); - /* - Add back the deleted table and re-created table as a locked table - This should always work as we have a meta lock on the table. - */ - thd->locked_tables_list.add_back_last_deleted_lock(pos_in_locked_tables); - if (thd->locked_tables_list.reopen_tables(thd, false)) + create_info->table= orig_table->table; + if (create_table_handle_exists(thd, orig_table->db, orig_table->table_name, + *create_info, create_info, result)) { - thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); result= 1; goto err; } - else - { - TABLE *table= pos_in_locked_tables->table; - table->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); - } + create_table= orig_table; + create_info->table= 0; } err: @@ -4845,12 +4994,15 @@ err: we should log a delete of it. If create_info->table was not set, it's a normal table and table_creation_was_logged will be set when the share is created. + + NOTE: this is only needed for non-atomic CREATE OR REPLACE */ + DBUG_ASSERT(!atomic_replace); create_info->table->s->table_creation_was_logged= 1; } thd->binlog_xid= thd->query_id; ddl_log_update_xid(&ddl_log_state_create, thd->binlog_xid); - if (ddl_log_state_rm.is_active()) + if (!atomic_replace) ddl_log_update_xid(&ddl_log_state_rm, thd->binlog_xid); debug_crash_here("ddl_log_create_before_binlog"); if (unlikely(write_bin_log(thd, result ? FALSE : TRUE, thd->query(), @@ -4869,11 +5021,48 @@ err: ddl_log.org_database= create_table->db; ddl_log.org_table= create_table->table_name; ddl_log.org_table_id= create_info->tabledef_version; + if (create_info->drop_entry.query.length) + { + DBUG_ASSERT(atomic_replace); + backup_log_ddl(&create_info->drop_entry); + } backup_log_ddl(&ddl_log); } } - ddl_log_complete(&ddl_log_state_rm); - ddl_log_complete(&ddl_log_state_create); + if (result) + { + (void) ddl_log_revert(thd, &ddl_log_state_create); + ddl_log_complete(&ddl_log_state_rm); + } + else + result= create_info->finalize_ddl(thd); + + /* + Check if we are doing CREATE OR REPLACE TABLE under LOCK TABLES + on a non temporary table + */ + if (thd->locked_tables_mode && pos_in_locked_tables && + create_info->or_replace()) + { + DBUG_ASSERT(thd->variables.option_bits & OPTION_TABLE_LOCK); + /* + Add back the deleted table and re-created table as a locked table + This should always work as we have a meta lock on the table. + */ + thd->locked_tables_list.add_back_last_deleted_lock(pos_in_locked_tables); + if (thd->locked_tables_list.reopen_tables(thd, false)) + { + thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); + result= 1; + goto err; + } + else + { + TABLE *table= pos_in_locked_tables->table; + table->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); + } + } + DBUG_RETURN(result); } @@ -5180,10 +5369,18 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, bool src_table_exists= FALSE; uint not_used; int create_res; + TABLE_LIST new_table; + TABLE_LIST *orig_table= table; + const bool atomic_replace= create_info->is_atomic_replace(); + int create_table_mode= CREATE_ORDINARY; DBUG_ENTER("mysql_create_like_table"); bzero(&ddl_log_state_create, sizeof(ddl_log_state_create)); bzero(&ddl_log_state_rm, sizeof(ddl_log_state_rm)); + if (atomic_replace) + local_create_info.tmp_name= &new_table; + local_create_info.ddl_log_state_create= &ddl_log_state_create; + local_create_info.ddl_log_state_rm= &ddl_log_state_rm; #ifdef WITH_WSREP if (WSREP(thd) && !thd->wsrep_applier && @@ -5276,60 +5473,46 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, /* The following is needed only in case of lock tables */ if ((local_create_info.table= thd->lex->query_tables->table)) - pos_in_locked_tables= local_create_info.table->pos_in_locked_tables; + pos_in_locked_tables= local_create_info.table->pos_in_locked_tables; + + if (atomic_replace) + { + if (make_tmp_name(thd, "create", table, &new_table)) + { + goto err; + } + create_table_mode|= CREATE_TMP_TABLE; + DBUG_ASSERT(!(create_info->options & HA_CREATE_TMP_ALTER)); + // FIXME: restore options? + local_create_info.options|= HA_CREATE_TMP_ALTER; + new_table.mdl_request.duration= MDL_EXPLICIT; + table= &new_table; + } res= ((create_res= mysql_create_table_no_lock(thd, - &ddl_log_state_create, &ddl_log_state_rm, + &orig_table->db, + &orig_table->table_name, &table->db, &table->table_name, &local_create_info, &local_alter_info, - &is_trans, C_ORDINARY_CREATE, + &is_trans, create_table_mode, table)) > 0); /* Remember to log if we deleted something */ do_logging= thd->log_current_statement; if (res) goto err; - /* - Check if we are doing CREATE OR REPLACE TABLE under LOCK TABLES - on a non temporary table - */ - if (thd->locked_tables_mode && pos_in_locked_tables && - create_info->or_replace()) - { - /* - Add back the deleted table and re-created table as a locked table - This should always work as we have a meta lock on the table. - */ - thd->locked_tables_list.add_back_last_deleted_lock(pos_in_locked_tables); - if (thd->locked_tables_list.reopen_tables(thd, false)) - { - thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); - res= 1; // We got an error - } - else - { - /* - Get pointer to the newly opened table. We need this to ensure we - don't reopen the table when doing statment logging below. - */ - table->table= pos_in_locked_tables->table; - table->table->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); - } - } - else + if (atomic_replace) { + local_create_info.table= orig_table->table; + if (create_table_handle_exists(thd, orig_table->db, orig_table->table_name, + local_create_info, &local_create_info, res)) + goto err; /* - Ensure that we have an exclusive lock on target table if we are creating - non-temporary table. We don't have or need the lock if the create failed - because of existing table when using "if exists". + NOTE: orig_table->table is reopened and now is the same share as + new_table. */ - DBUG_ASSERT((create_info->tmp_table()) || create_res < 0 || - thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db.str, - table->table_name.str, - MDL_EXCLUSIVE) || - (thd->locked_tables_mode && pos_in_locked_tables && - create_info->if_not_exists())); + local_create_info.table= 0; } DEBUG_SYNC(thd, "create_table_like_before_binlog"); @@ -5371,6 +5554,14 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, 5 any shared Generated statement if the table was created if engine changed ==== ========= ========= ============================== + + TODO: why this is in a separate branch? All logging should be done in + single branch (if (do_logging)), possibly moved out to a separate + function. Along with backup logging, XID update, etc. This branch is not + properly tested now, AFAICS this is tested only by + rpl.create_or_replace2. + + Why "generated statement" is needed? No explanation in this comment... */ if (!(create_info->tmp_table()) || force_generated_create) { @@ -5382,7 +5573,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, query.length(0); // Have to zero it since constructor doesn't Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN | MYSQL_OPEN_IGNORE_KILLED); - bool new_table= FALSE; // Whether newly created table is open. + bool opened_new_table= FALSE; // Whether newly created table is open. if (create_res != 0) { @@ -5401,6 +5592,19 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, save_open_strategy= table->open_strategy; table->open_strategy= TABLE_LIST::OPEN_NORMAL; + if (atomic_replace && + thd->mdl_context.acquire_lock(&table->mdl_request, + thd->variables.lock_wait_timeout)) + { + /* + NOTE: We acquire lock for temporary table just to make + close_thread_table() happy. We open it like a normal table + because it's too complex to open it like tmp_table here. + */ + res= 1; + goto err; + } + /* In order for show_create_table() to work we need to open destination table if it is not already open (i.e. if it @@ -5417,7 +5621,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, res= 1; goto err; } - new_table= TRUE; + opened_new_table= TRUE; } /* We have to re-test if the table was a view as the view may not @@ -5440,18 +5644,40 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, */ create_info->used_fields|= HA_CREATE_USED_ENGINE; + if (atomic_replace) + { + DBUG_ASSERT(!table->schema_table); + table->table->s->table_name.str= + strmake_root(&table->table->s->mem_root, + LEX_STRING_WITH_LEN(orig_table->table_name)); + table->table->s->table_name.length= orig_table->table_name.length; + table->table->alias.copy(LEX_STRING_WITH_LEN(orig_table->alias), + system_charset_info); + } + int result __attribute__((unused))= show_create_table(thd, table, &query, create_info, WITH_DB_NAME); DBUG_ASSERT(result == 0); // show_create_table() always return 0 do_logging= FALSE; + thd->binlog_xid= thd->query_id; + + thd->binlog_xid= thd->query_id; + ddl_log_update_xid(&ddl_log_state_create, thd->binlog_xid); + if (ddl_log_state_rm.is_active() && !atomic_replace) + ddl_log_update_xid(&ddl_log_state_rm, thd->binlog_xid); + debug_crash_here("ddl_log_create_before_binlog"); + if (write_bin_log(thd, TRUE, query.ptr(), query.length())) { res= 1; goto err; } - if (new_table) + debug_crash_here("ddl_log_create_after_binlog"); + thd->binlog_xid= 0; + + if (opened_new_table) { DBUG_ASSERT(thd->open_tables == table->table); /* @@ -5459,7 +5685,10 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, (MYSQL_OPEN_GET_NEW_TABLE). Now we can close the table without risking to close some locked table. */ + table->table->s->tdc->flushed= true; close_thread_table(thd, &thd->open_tables); + if (atomic_replace) + thd->mdl_context.release_lock(table->mdl_request.ticket); } } } @@ -5483,21 +5712,42 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, /* Remember that tmp table creation was logged so that we know if we should log a delete of it. + + NOTE: this is only needed for non-atomic CREATE OR REPLACE */ + DBUG_ASSERT(!atomic_replace); local_create_info.table->s->table_creation_was_logged= 1; } } do_logging= TRUE; } + if (!(thd->locked_tables_mode && pos_in_locked_tables && + create_info->or_replace())) + { + /* + Ensure that we have an exclusive lock on target table if we are creating + non-temporary table. We don't have or need the lock if the create failed + because of existing table when using "if exists". + */ + DBUG_ASSERT((create_info->tmp_table()) || create_res < 0 || + thd->mdl_context.is_lock_owner(MDL_key::TABLE, orig_table->db.str, + orig_table->table_name.str, + MDL_EXCLUSIVE) || + (thd->locked_tables_mode && pos_in_locked_tables && + create_info->if_not_exists())); + } + err: + table= orig_table; + if (do_logging) { thd->binlog_xid= thd->query_id; ddl_log_update_xid(&ddl_log_state_create, thd->binlog_xid); - if (ddl_log_state_rm.is_active()) + if (ddl_log_state_rm.is_active() && !atomic_replace) ddl_log_update_xid(&ddl_log_state_rm, thd->binlog_xid); - debug_crash_here("ddl_log_create_before_binlog"); + debug_crash_here("ddl_log_create_before_binlog"); if (res && create_info->table_was_deleted) { /* @@ -5505,6 +5755,7 @@ err: We have to log it. */ DBUG_ASSERT(ddl_log_state_rm.is_active()); + DBUG_ASSERT(!atomic_replace); log_drop_table(thd, &table->db, &table->table_name, &create_info->org_storage_engine_name, create_info->db_type == partition_hton, @@ -5530,11 +5781,56 @@ err: ddl_log.org_database= table->db; ddl_log.org_table= table->table_name; ddl_log.org_table_id= local_create_info.tabledef_version; + if (local_create_info.drop_entry.query.length) + { + DBUG_ASSERT(atomic_replace); + backup_log_ddl(&local_create_info.drop_entry); + } backup_log_ddl(&ddl_log); } - ddl_log_complete(&ddl_log_state_rm); - ddl_log_complete(&ddl_log_state_create); + if (res) + { + if (ddl_log_state_rm.is_active() && + ddl_log_revert(thd, &ddl_log_state_create)) + res= 1; + else + ddl_log_complete(&ddl_log_state_create); + ddl_log_complete(&ddl_log_state_rm); + } + else + { + res= local_create_info.finalize_ddl(thd); + } + + /* + Check if we are doing CREATE OR REPLACE TABLE under LOCK TABLES + on a non temporary table + */ + if (thd->locked_tables_mode && pos_in_locked_tables && + create_info->or_replace()) + { + /* + Add back the deleted table and re-created table as a locked table + This should always work as we have a meta lock on the table. + */ + thd->locked_tables_list.add_back_last_deleted_lock(pos_in_locked_tables); + if (thd->locked_tables_list.reopen_tables(thd, false)) + { + thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); + res= 1; // We got an error + } + else + { + /* + Get pointer to the newly opened table. We need this to ensure we + don't reopen the table when doing statment logging below. + */ + table->table= pos_in_locked_tables->table; + table->table->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); + } + } + DBUG_RETURN(res != 0); } @@ -7000,7 +7296,7 @@ bool mysql_compare_tables(TABLE *table, /* Create the prepared information. */ int create_table_mode= table->s->tmp_table == NO_TMP_TABLE ? - C_ORDINARY_CREATE : C_ALTER_TABLE; + CREATE_ORDINARY : CREATE_TMP_TABLE; if (mysql_prepare_create_table(thd, create_info, &tmp_alter_info, &db_options, table->file, &key_info_buffer, &key_count, create_table_mode, db, table_name)) @@ -10180,12 +10476,11 @@ do_continue:; No ddl logging needed as ddl_log_alter_query will take care of failed table creations. */ - error= create_table_impl(thd, (DDL_LOG_STATE*) 0, (DDL_LOG_STATE*) 0, - alter_ctx.db, alter_ctx.table_name, + error= create_table_impl(thd, alter_ctx.db, alter_ctx.table_name, alter_ctx.new_db, alter_ctx.tmp_name, alter_ctx.get_tmp_cstring_path(), thd->lex->create_info, create_info, alter_info, - C_ALTER_TABLE_FRM_ONLY, NULL, + CREATE_FRM_ONLY, NULL, &key_info, &key_count, &frm); reenable_binlog(thd); diff --git a/sql/sql_table.h b/sql/sql_table.h index eaa03bfaf8c..d29dc3945a2 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -96,50 +96,20 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, bool add_keyword_to_query(THD *thd, String *result, const LEX_CSTRING *keyword, const LEX_CSTRING *add); -/* - mysql_create_table_no_lock can be called in one of the following - mutually exclusive situations: - - - Just a normal ordinary CREATE TABLE statement that explicitly - defines the table structure. - - - CREATE TABLE ... SELECT. It is special, because only in this case, - the list of fields is allowed to have duplicates, as long as one of the - duplicates comes from the select list, and the other doesn't. For - example in - - CREATE TABLE t1 (a int(5) NOT NUL) SELECT b+10 as a FROM t2; - - the list in alter_info->create_list will have two fields `a`. - - - ALTER TABLE, that creates a temporary table #sql-xxx, which will be later - renamed to replace the original table. - - - ALTER TABLE as above, but which only modifies the frm file, it only - creates an frm file for the #sql-xxx, the table in the engine is not - created. - - - Assisted discovery, CREATE TABLE statement without the table structure. - - These situations are distinguished by the following "create table mode" - values, where a CREATE ... SELECT is denoted by any non-negative number - (which should be the number of fields in the SELECT ... part), and other - cases use constants as defined below. -*/ -#define C_CREATE_SELECT(X) ((X) > 0 ? (X) : 0) -#define C_ORDINARY_CREATE 0 -#define C_ALTER_TABLE -1 -#define C_ALTER_TABLE_FRM_ONLY -2 -#define C_ASSISTED_DISCOVERY -3 +#define CREATE_ORDINARY 0 +#define CREATE_TMP_TABLE 1 +#define CREATE_FRM_ONLY 2 +#define CREATE_ASSISTED 4 int mysql_create_table_no_lock(THD *thd, - DDL_LOG_STATE *ddl_log_state, - DDL_LOG_STATE *ddl_log_state_rm, + const LEX_CSTRING *orig_db, + const LEX_CSTRING *orig_table_name, const LEX_CSTRING *db, const LEX_CSTRING *table_name, Table_specification_st *create_info, Alter_info *alter_info, bool *is_trans, - int create_table_mode, TABLE_LIST *table); + int create_table_mode, TABLE_LIST *table, + LEX_CUSTRING *frm= NULL); handler *mysql_create_frm_image(THD *thd, const LEX_CSTRING &db, @@ -192,7 +162,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, bool dont_log_query); int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, const LEX_CSTRING *db, - DDL_LOG_STATE *ddl_log_state, + Atomic_info *atomic_info, bool if_exists, bool drop_temporary, bool drop_view, bool drop_sequence, @@ -226,4 +196,12 @@ extern MYSQL_PLUGIN_IMPORT const LEX_CSTRING primary_key_name; bool check_engine(THD *, const char *, const char *, HA_CREATE_INFO *); +bool make_tmp_name(THD *thd, const char *prefix, const TABLE_LIST *orig, + TABLE_LIST *res); + +bool create_table_handle_exists(THD *thd, const LEX_CSTRING &db, + const LEX_CSTRING &table_name, + const DDL_options_st options, + HA_CREATE_INFO *create_info, int &error); + #endif /* SQL_TABLE_INCLUDED */ diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 7b3db324db7..0a3ce76d35b 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -1174,9 +1174,11 @@ bool Trigger::add_to_file_list(void* param_arg) */ static bool rm_trigger_file(char *path, const LEX_CSTRING *db, - const LEX_CSTRING *table_name, myf MyFlags) + const LEX_CSTRING *table_name, myf MyFlags, + uint flags= 0) { - build_table_filename(path, FN_REFLEN-1, db->str, table_name->str, TRG_EXT, 0); + build_table_filename(path, FN_REFLEN - 1, db->str, table_name->str, TRG_EXT, + flags); return mysql_file_delete(key_file_trg, path, MyFlags); } @@ -1502,7 +1504,8 @@ bool Table_triggers_list::prepare_record_accessors(TABLE *table) bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name, TABLE *table, - bool names_only) + bool names_only, + uint flags) { char path_buff[FN_REFLEN]; LEX_CSTRING path; @@ -1511,7 +1514,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db, DBUG_ENTER("Table_triggers_list::check_n_load"); path.length= build_table_filename(path_buff, FN_REFLEN - 1, - db->str, table_name->str, TRG_EXT, 0); + db->str, table_name->str, TRG_EXT, flags); path.str= path_buff; // QQ: should we analyze errno somehow ? @@ -2001,7 +2004,7 @@ bool add_table_for_trigger(THD *thd, bool Table_triggers_list::drop_all_triggers(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *name, - myf MyFlags) + myf MyFlags, uint flags) { TABLE table; char path[FN_REFLEN]; @@ -2012,11 +2015,11 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, const LEX_CSTRING *db, init_sql_alloc(key_memory_Table_trigger_dispatcher, &table.mem_root, 8192, 0, MYF(MY_WME)); - if (Table_triggers_list::check_n_load(thd, db, name, &table, 1)) + if (Table_triggers_list::check_n_load(thd, db, name, &table, 1, flags)) { result= 1; /* We couldn't parse trigger file, best to just remove it */ - rm_trigger_file(path, db, name, MyFlags); + rm_trigger_file(path, db, name, MyFlags, flags); goto end; } if (table.triggers) @@ -2050,7 +2053,7 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, const LEX_CSTRING *db, } } } - if (rm_trigger_file(path, db, name, MyFlags)) + if (rm_trigger_file(path, db, name, MyFlags, flags)) result= 1; delete table.triggers; } diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 739669c86a5..61925c2eb73 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -258,9 +258,10 @@ public: bool save_trigger_file(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name); static bool check_n_load(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name, - TABLE *table, bool names_only); + TABLE *table, bool names_only, uint flags= 0); static bool drop_all_triggers(THD *thd, const LEX_CSTRING *db, - const LEX_CSTRING *table_name, myf MyFlags); + const LEX_CSTRING *table_name, myf MyFlags, + uint flags= 0); static bool prepare_for_rename(THD *thd, TRIGGER_RENAME_PARAM *param, const LEX_CSTRING *db, const LEX_CSTRING *old_alias, diff --git a/sql/table.cc b/sql/table.cc index b1a7b6bfe2b..3933b5220b1 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3509,7 +3509,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write, promote_first_timestamp_column(&thd->lex->alter_info.create_list); file= mysql_create_frm_image(thd, db, table_name, &thd->lex->create_info, &thd->lex->alter_info, - C_ORDINARY_CREATE, &unused1, &unused2, &frm); + CREATE_ORDINARY, &unused1, &unused2, &frm); error|= file == 0; delete file; |