summaryrefslogtreecommitdiff
path: root/sql/ddl_log.cc
diff options
context:
space:
mode:
authorMonty <monty@mariadb.org>2021-01-31 18:43:50 +0200
committerSergei Golubchik <serg@mariadb.org>2021-05-19 22:54:13 +0200
commitffe7f19fa6019c03f3ff190dc5c2d0e8de60bf14 (patch)
tree82d5167fae20dbbd32257204bb044217728e6707 /sql/ddl_log.cc
parentd494abd175d45aa114efbe3c35e2e765441b65b5 (diff)
downloadmariadb-git-ffe7f19fa6019c03f3ff190dc5c2d0e8de60bf14.tar.gz
MDEV-24746 Atomic CREATE TRIGGER
The purpose of this task is to ensure that CREATE TRIGGER is atomic When a trigger is created, we first create a trigger_name.TRN file and then create or update the table_name.TRG files. This is done by creating .TRN~ and .TRG~ files and replacing (or creating) the result files. The new logic is - Log CREATE TRIGGER to DDL log, with a marker if old trigger existsted - If old .TRN or .TRG files exists, make backup copies of these - Create the new .TRN and .TRG files as before - Remove the backups Crash recovery - If query has been logged to binary log: - delete any left over backup files - else - Delete any old .TRN~ or .TRG~ files - If there was orignally some triggers (old .TRG file existed) - If we crashed before creating all backup files - Delete existing backup files - else - Restore backup files - end - Delete .TRN and .TRG file (as there was no triggers before One benefit of the new code is that CREATE OR REPLACE TRIGGER is now totally atomic even if there existed an old trigger: Either the old trigger will be replaced or the old one will be left untouched. Other things: - If sql_create_definition_file() would fail, there could be memory leaks in CREATE TRIGGER, DROP TRIGGER or CREATE OR REPLACE TRIGGER. This is now fixed.
Diffstat (limited to 'sql/ddl_log.cc')
-rw-r--r--sql/ddl_log.cc117
1 files changed, 114 insertions, 3 deletions
diff --git a/sql/ddl_log.cc b/sql/ddl_log.cc
index 49fdb861f8d..7eae243771b 100644
--- a/sql/ddl_log.cc
+++ b/sql/ddl_log.cc
@@ -90,7 +90,7 @@ const char *ddl_log_action_name[DDL_LOG_LAST_ACTION]=
"rename table", "rename view",
"initialize drop table", "drop table",
"drop view", "drop trigger", "drop db", "create table", "create view",
- "delete tmp file",
+ "delete tmp file", "create trigger",
};
/* Number of phases per entry */
@@ -100,7 +100,7 @@ const uchar ddl_log_entry_phases[DDL_LOG_LAST_ACTION]=
(uchar) EXCH_PHASE_END, (uchar) DDL_RENAME_PHASE_END, 1, 1,
(uchar) DDL_DROP_PHASE_END, 1, 1,
(uchar) DDL_DROP_DB_PHASE_END, (uchar) DDL_CREATE_TABLE_PHASE_END,
- (uchar) DDL_CREATE_VIEW_PHASE_END, 0,
+ (uchar) DDL_CREATE_VIEW_PHASE_END, 0, (uchar) DDL_CREATE_TRIGGER_PHASE_END,
};
@@ -1637,7 +1637,96 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root,
(void) update_phase(entry_pos, DDL_LOG_FINAL_PHASE);
break;
}
- break;
+ case DDL_LOG_CREATE_TRIGGER_ACTION:
+ {
+ LEX_CSTRING db, table, trigger;
+ db= ddl_log_entry->db;
+ table= ddl_log_entry->name;
+ trigger= ddl_log_entry->tmp_name;
+
+ /* Delete backup .TRG (trigger file) if it exists */
+ (void) build_filename_and_delete_tmp_file(to_path, sizeof(to_path) - 1,
+ &db, &table,
+ TRG_EXT,
+ key_file_fileparser);
+ (void) build_filename_and_delete_tmp_file(to_path, sizeof(to_path) - 1,
+ &db, &trigger,
+ TRN_EXT,
+ key_file_fileparser);
+ switch (ddl_log_entry->phase) {
+ case DDL_CREATE_TRIGGER_PHASE_DELETE_COPY:
+ {
+ size_t length;
+ /* Delete copy of .TRN and .TRG files */
+ length= build_table_filename(to_path, sizeof(to_path) - 1,
+ db.str, table.str, TRG_EXT, 0);
+ to_path[length]= '-';
+ to_path[length+1]= 0;
+ mysql_file_delete(key_file_fileparser, to_path,
+ MYF(MY_WME|MY_IGNORE_ENOENT));
+
+ length= build_table_filename(to_path, sizeof(to_path) - 1,
+ db.str, trigger.str, TRN_EXT, 0);
+ to_path[length]= '-';
+ to_path[length+1]= 0;
+ mysql_file_delete(key_file_fileparser, to_path,
+ MYF(MY_WME|MY_IGNORE_ENOENT));
+ }
+ /* Nothing else to do */
+ (void) update_phase(entry_pos, DDL_LOG_FINAL_PHASE);
+ break;
+ case DDL_CREATE_TRIGGER_PHASE_OLD_COPIED:
+ {
+ LEX_CSTRING path= {to_path, 0};
+ size_t length;
+ /* Restore old version if the .TRN and .TRG files */
+ length= build_table_filename(to_path, sizeof(to_path) - 1,
+ db.str, table.str, TRG_EXT, 0);
+ to_path[length]='-';
+ to_path[length+1]= 0;
+ path.length= length+1;
+ /* an old TRN file only exist in the case if REPLACE was used */
+ if (!access(to_path, F_OK))
+ sql_restore_definition_file(&path);
+
+ length= build_table_filename(to_path, sizeof(to_path) - 1,
+ db.str, trigger.str, TRN_EXT, 0);
+ to_path[length]='-';
+ to_path[length+1]= 0;
+ path.length= length+1;
+ if (!access(to_path, F_OK))
+ sql_restore_definition_file(&path);
+ else
+ {
+ /*
+ There was originally no .TRN for this trigger.
+ Delete the newly created one.
+ */
+ to_path[length]= 0;
+ mysql_file_delete(key_file_fileparser, to_path,
+ MYF(MY_WME|MY_IGNORE_ENOENT));
+ }
+ (void) update_phase(entry_pos, DDL_LOG_FINAL_PHASE);
+ break;
+ }
+ case DDL_CREATE_TRIGGER_PHASE_NO_OLD_TRIGGER:
+ {
+ /* No old trigger existed. We can just delete the .TRN and .TRG files */
+ build_table_filename(to_path, sizeof(to_path) - 1,
+ db.str, table.str, TRG_EXT, 0);
+ mysql_file_delete(key_file_fileparser, to_path,
+ MYF(MY_WME|MY_IGNORE_ENOENT));
+ build_table_filename(to_path, sizeof(to_path) - 1,
+ db.str, trigger.str, TRN_EXT, 0);
+ mysql_file_delete(key_file_fileparser, to_path,
+ MYF(MY_WME|MY_IGNORE_ENOENT));
+ (void) update_phase(entry_pos, DDL_LOG_FINAL_PHASE);
+ break;
+ }
+ }
+ break;
+ }
+ }
default:
DBUG_ASSERT(0);
break;
@@ -2633,3 +2722,25 @@ bool ddl_log_delete_tmp_file(THD *thd, DDL_LOG_STATE *ddl_state,
ddl_log_entry.unique_id= depending_state->execute_entry->entry_pos;
DBUG_RETURN(ddl_log_write(ddl_state, &ddl_log_entry));
}
+
+
+/**
+ Log CREATE TRIGGER
+*/
+
+bool ddl_log_create_trigger(THD *thd, DDL_LOG_STATE *ddl_state,
+ const LEX_CSTRING *db, const LEX_CSTRING *table,
+ const LEX_CSTRING *trigger_name,
+ enum_ddl_log_create_trigger_phase phase)
+{
+ DDL_LOG_ENTRY ddl_log_entry;
+ DBUG_ENTER("ddl_log_create_view");
+
+ bzero(&ddl_log_entry, sizeof(ddl_log_entry));
+ ddl_log_entry.action_type= DDL_LOG_CREATE_TRIGGER_ACTION;
+ ddl_log_entry.db= *const_cast<LEX_CSTRING*>(db);
+ ddl_log_entry.name= *const_cast<LEX_CSTRING*>(table);
+ ddl_log_entry.tmp_name= *const_cast<LEX_CSTRING*>(trigger_name);
+ ddl_log_entry.phase= (uchar) phase;
+ DBUG_RETURN(ddl_log_write(ddl_state, &ddl_log_entry));
+}