summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/ddl_log.cc210
-rw-r--r--sql/ddl_log.h7
-rw-r--r--sql/handler.cc18
-rw-r--r--sql/handler.h55
-rw-r--r--sql/log.cc7
-rw-r--r--sql/log_event.h4
-rw-r--r--sql/sql_alter.cc1
-rw-r--r--sql/sql_alter.h8
-rw-r--r--sql/sql_class.h11
-rw-r--r--sql/sql_db.cc5
-rw-r--r--sql/sql_insert.cc309
-rw-r--r--sql/sql_partition.cc2
-rw-r--r--sql/sql_select.cc2
-rw-r--r--sql/sql_show.cc3
-rw-r--r--sql/sql_table.cc737
-rw-r--r--sql/sql_table.h56
-rw-r--r--sql/sql_trigger.cc19
-rw-r--r--sql/sql_trigger.h5
-rw-r--r--sql/table.cc2
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, &not_used_1, &not_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, &not_used_1, &not_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;