1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

MDEV-24576 Atomic CREATE TABLE

There are a few different cases to consider

Logging of CREATE TABLE and CREATE TABLE ... LIKE
- If REPLACE is used and there was an existing table, DDL log the drop of
  the table.
- If discovery of table is to be done
    - DDL LOG create table
  else
    - DDL log create table (with engine type)
    - create the table
- If table was created
  - Log entry to binary log with xid
  - Mark DDL log completed

Crash recovery:
- If query was in binary log do nothing and exit
- If discoverted table
   - Delete the .frm file
-else
   - Drop created table and frm file
- If table was dropped, write a DROP TABLE statement in binary log

CREATE TABLE ... SELECT required a little more work as when one is using
statement logging the query is written to the binary log before commit is
done.
This was fixed by adding a DROP TABLE to the binary log during crash
recovery if the ddl log entry was not closed. In this case the binary log
will contain:
CREATE TABLE xxx ... SELECT ....
DROP TABLE xxx;

Other things:
- Added debug_crash_here() functionality to Aria to be able to test
  crash in create table between the creation of the .MAI and the .MAD files.
This commit is contained in:
Monty
2021-01-17 16:06:43 +02:00
committed by Sergei Golubchik
parent 7a588c30b1
commit 6aa9a552c2
28 changed files with 1239 additions and 314 deletions

View File

@ -4375,7 +4375,8 @@ Field *Item::create_field_for_create_select(MEM_ROOT *root, TABLE *table)
*/
TABLE *select_create::create_table_from_items(THD *thd, List<Item> *items,
MYSQL_LOCK **lock, TABLEOP_HOOKS *hooks)
MYSQL_LOCK **lock,
TABLEOP_HOOKS *hooks)
{
TABLE tmp_table; // Used during 'Create_field()'
TABLE_SHARE share;
@ -4473,7 +4474,8 @@ TABLE *select_create::create_table_from_items(THD *thd, List<Item> *items,
open_table().
*/
if (!mysql_create_table_no_lock(thd, &create_table->db,
if (!mysql_create_table_no_lock(thd, &ddl_log_state_create, &ddl_log_state_rm,
&create_table->db,
&create_table->table_name,
create_info, alter_info, NULL,
select_field_count, create_table))
@ -4532,6 +4534,8 @@ TABLE *select_create::create_table_from_items(THD *thd, List<Item> *items,
{
if (likely(!thd->is_error())) // CREATE ... IF NOT EXISTS
my_ok(thd); // succeed, but did nothing
ddl_log_complete(&ddl_log_state_rm);
ddl_log_complete(&ddl_log_state_create);
DBUG_RETURN(NULL);
}
@ -4570,7 +4574,9 @@ 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);
DBUG_RETURN(0);
ddl_log_complete(&ddl_log_state_rm);
ddl_log_complete(&ddl_log_state_create);
DBUG_RETURN(NULL);
/* purecov: end */
}
table->s->table_creation_was_logged= save_table_creation_was_logged;
@ -4912,11 +4918,30 @@ bool select_create::send_eof()
if (thd->slave_thread)
thd->variables.binlog_annotate_row_events= 0;
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 (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);
}
if (prepare_eof())
{
abort_result_set();
DBUG_RETURN(true);
}
debug_crash_here("ddl_log_create_after_prepare_eof");
if (table->s->tmp_table)
{
@ -4983,9 +5008,15 @@ 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);
thd->binlog_xid= 0;
#ifdef WITH_WSREP
if (WSREP(thd))
{
@ -5004,6 +5035,15 @@ bool select_create::send_eof()
}
#endif /* WITH_WSREP */
}
/*
If are using statement based replication the table will be deleted here
in case of a crash as we can't use xid to check if the query was logged
(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");
/*
exit_done must only be set after last potential call to
@ -5120,9 +5160,19 @@ void select_create::abort_result_set()
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,
tmp_table);
debug_crash_here("ddl_log_create_after_binlog");
thd->binlog_xid= 0;
}
}
}
ddl_log_complete(&ddl_log_state_rm);
ddl_log_complete(&ddl_log_state_create);
DBUG_VOID_RETURN;
}