1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

MDEV-23844 Atomic DROP TABLE (single table)

Logging logic:
- Log tables, just before they are dropped, to the ddl log
- After the last table for the statement is dropped, log an xid for the
  whole ddl log event

In case of crash:
- Remove first any active DROP TABLE events from the ddl log that matches
  xids found in binary log (this mean the drop was successful and was
  propery logged).
- Loop over all active DROP TABLE events
  - Ensure that the table is completely dropped
- Write a DROP TABLE entry to the binary log with the dropped tables.

Other things:
- Added code to ha_drop_table() to be able to tell the difference if
  a get_new_handler() failed because of out-of-memory or because the
  handler refused/was not able to create a a handler. This was needed
  to get sequences to work as sequences needs a share object to be passed
  to get_new_handler()
- TC_LOG_BINLOG::recover() was changed to always collect Xid's from the
  binary log and always call ddl_log_close_binlogged_events(). This was
  needed to be able to collect DROP TABLE events with embedded Xid's
  (used by ddl log).
- Added a new variable "$grep_script" to binlog filter to be able to find
  only rows that matches a regexp.
- Had to adjust some test that changed because drop statements are a bit
  larger in the binary log than before (as we have to store the xid)

Other things:
- MDEV-25588 Atomic DDL: Binlog query event written upon recovery is corrupt
  fixed (in the original commit).
This commit is contained in:
Monty
2020-12-04 18:23:40 +02:00
committed by Sergei Golubchik
parent 47010ccffa
commit e3cfb7c803
15 changed files with 1136 additions and 51 deletions

View File

@ -56,6 +56,7 @@
#include "tztime.h"
#include "sql_insert.h" // binlog_drop_table
#include "ddl_log.h"
#include "debug_sync.h" // debug_crash_here()
#include <algorithm>
#ifdef __WIN__
@ -1139,9 +1140,11 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
char path[FN_REFLEN + 1];
LEX_CSTRING alias= null_clex_str;
StringBuffer<160> unknown_tables(system_charset_info);
uint not_found_errors= 0;
DDL_LOG_STATE ddl_log_state;
const char *comment_start;
uint not_found_errors= 0, table_count= 0, non_temp_tables_count= 0;
int error= 0;
int non_temp_tables_count= 0;
uint32 comment_len;
bool trans_tmp_table_deleted= 0, non_trans_tmp_table_deleted= 0;
bool non_tmp_table_deleted= 0;
bool is_drop_tmp_if_exists_added= 0;
@ -1154,6 +1157,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
DBUG_ENTER("mysql_rm_table_no_locks");
unknown_tables.length(0);
comment_len= get_comment(thd, if_exists ? 17:9, &comment_start);
/*
Prepares the drop statements that will be written into the binary
log as follows:
@ -1204,6 +1209,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
built_non_trans_tmp_query.set_charset(system_charset_info);
built_non_trans_tmp_query.copy(built_trans_tmp_query);
}
bzero(&ddl_log_state, sizeof(ddl_log_state));
for (table= tables; table; table= table->next_local)
{
@ -1213,6 +1219,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
bool table_dropped= 0;
const LEX_CSTRING db= table->db;
const LEX_CSTRING table_name= table->table_name;
LEX_CSTRING cpath= {0,0};
handlerton *hton= 0;
Table_type table_type;
size_t path_length= 0;
@ -1347,6 +1354,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
continue;
}
lex_string_set3(&cpath, path, (size_t) (path_end - path));
{
char engine_buf[NAME_CHAR_LEN + 1];
LEX_CSTRING engine= { engine_buf, 0 };
@ -1363,6 +1372,17 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
thd->replication_flags= 0;
was_view= table_type == TABLE_TYPE_VIEW;
if (!table_count++)
{
LEX_CSTRING comment= {comment_start, (size_t) comment_len};
if (ddl_log_drop_table_init(thd, &ddl_log_state, &comment))
{
error= 1;
goto err;
}
}
if ((table_type == TABLE_TYPE_UNKNOWN) || (was_view && !drop_view) ||
(table_type != TABLE_TYPE_SEQUENCE && drop_sequence))
{
@ -1376,6 +1396,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
was_table|= wrong_drop_sequence;
error= table_type == TABLE_TYPE_UNKNOWN ? ENOENT : -1;
tdc_remove_table(thd, db.str, table_name.str);
if (wrong_drop_sequence)
goto report_error;
}
else
{
@ -1411,7 +1433,17 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
log_if_exists= 1;
bool enoent_warning= !dont_log_query && !(hton && hton->discover_table);
error= ha_delete_table(thd, hton, path, &db, &table_name, enoent_warning);
if (ddl_log_drop_table(thd, &ddl_log_state, hton, &cpath, &db,
&table_name))
{
error= -1;
goto err;
}
debug_crash_here("ddl_log_drop_before_delete_table");
error= ha_delete_table(thd, hton, path, &db, &table_name,
enoent_warning);
debug_crash_here("ddl_log_drop_after_delete_table");
if (!error)
table_dropped= 1;
@ -1473,11 +1505,18 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
scan all engines try to drop the table from there.
This is to ensure we don't have any partial table files left.
*/
if (non_existing_table_error(error) && !wrong_drop_sequence)
if (non_existing_table_error(error))
{
int ferror= 0;
DBUG_ASSERT(!was_view);
if (ddl_log_drop_table(thd, &ddl_log_state, hton, &cpath, &db,
&table_name))
{
error= -1;
goto err;
}
/* Remove extension for delete */
*path_end= '\0';
ferror= ha_delete_table_force(thd, path, &db, &table_name);
@ -1509,13 +1548,19 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
if (thd->replication_flags & OPTION_IF_EXISTS)
log_if_exists= 1;
debug_crash_here("ddl_log_drop_before_drop_trigger");
ddl_log_update_phase(&ddl_log_state, DDL_DROP_PHASE_TRIGGER);
debug_crash_here("ddl_log_drop_before_drop_trigger2");
if (likely(!error) || non_existing_table_error(error))
{
if (Table_triggers_list::drop_all_triggers(thd, &db, &table_name,
MYF(MY_WME | MY_IGNORE_ENOENT)))
error= error ? error : -1;
}
debug_crash_here("ddl_log_drop_after_drop_trigger");
report_error:
if (error)
{
StringBuffer<FN_REFLEN> tbl_name(system_charset_info);
@ -1565,6 +1610,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
table_name.str, (uint)table_name.length);
mysql_audit_drop_table(thd, table);
}
ddl_log_update_phase(&ddl_log_state, DDL_DROP_PHASE_BINLOG);
if (!dont_log_query &&
(!error || table_dropped || non_existing_table_error(error)))
@ -1621,6 +1667,7 @@ err:
query_cache_invalidate3(thd, tables, 0);
if (!dont_log_query && mysql_bin_log.is_open())
{
debug_crash_here("ddl_log_drop_before_binlog");
if (non_trans_tmp_table_deleted)
{
/* Chop of the last comma */
@ -1648,8 +1695,6 @@ err:
if (non_tmp_table_deleted)
{
String built_query;
const char *comment_start;
uint32 comment_len;
built_query.set_charset(thd->charset());
built_query.append(STRING_WITH_LEN("DROP "));
@ -1659,8 +1704,7 @@ err:
built_query.append(STRING_WITH_LEN("IF EXISTS "));
/* Preserve comment in original query */
if ((comment_len= get_comment(thd, if_exists ? 17:9,
&comment_start)))
if (comment_len)
{
built_query.append(comment_start, comment_len);
built_query.append(' ');
@ -1670,13 +1714,18 @@ err:
normal_tables.chop();
built_query.append(normal_tables.ptr(), normal_tables.length());
built_query.append(generated_by_server);
thd->binlog_xid= thd->query_id;
ddl_log_update_xid(&ddl_log_state, thd->binlog_xid);
error |= (thd->binlog_query(THD::STMT_QUERY_TYPE,
built_query.ptr(),
built_query.length(),
TRUE, FALSE, FALSE, 0) > 0);
thd->binlog_xid= 0;
}
debug_crash_here("ddl_log_drop_after_binlog");
}
}
ddl_log_complete(&ddl_log_state);
if (!drop_temporary)
{