216
246
my_casedn_str(files_charset_info, p);
219
uint32_t length= unpack_filename(buff, buff);
249
uint length= unpack_filename(buff, buff);
250
DBUG_PRINT("exit", ("buff: '%s'", buff));
255
--------------------------------------------------------------------------
260
This module is used to ensure that we can recover from crashes that occur
261
in the middle of a meta-data operation in MySQL. E.g. DROP TABLE t1, t2;
262
We need to ensure that both t1 and t2 are dropped and not only t1 and
263
also that each table drop is entirely done and not "half-baked".
265
To support this we create log entries for each meta-data statement in the
266
ddl log while we are executing. These entries are dropped when the
267
operation is completed.
269
At recovery those entries that were not completed will be executed.
271
There is only one ddl log in the system and it is protected by a mutex
272
and there is a global struct that contains information about its current
276
First version written in 2006 by Mikael Ronstrom
277
--------------------------------------------------------------------------
281
struct st_global_ddl_log
284
We need to adjust buffer size to be able to handle downgrades/upgrades
285
where IO_SIZE has changed. We'll set the buffer size such that we can
286
handle that the buffer size was upto 4 times bigger in the version
287
that wrote the DDL log.
289
char file_entry_buf[4*IO_SIZE];
290
char file_name_str[FN_REFLEN];
292
DDL_LOG_MEMORY_ENTRY *first_free;
293
DDL_LOG_MEMORY_ENTRY *first_used;
301
st_global_ddl_log() : inited(false), do_release(false) {}
304
st_global_ddl_log global_ddl_log;
306
pthread_mutex_t LOCK_gdl;
308
#define DDL_LOG_ENTRY_TYPE_POS 0
309
#define DDL_LOG_ACTION_TYPE_POS 1
310
#define DDL_LOG_PHASE_POS 2
311
#define DDL_LOG_NEXT_ENTRY_POS 4
312
#define DDL_LOG_NAME_POS 8
314
#define DDL_LOG_NUM_ENTRY_POS 0
315
#define DDL_LOG_NAME_LEN_POS 4
316
#define DDL_LOG_IO_SIZE_POS 8
319
Read one entry from ddl log file
321
read_ddl_log_file_entry()
322
entry_no Entry number to read
328
static bool read_ddl_log_file_entry(uint entry_no)
331
File file_id= global_ddl_log.file_id;
332
uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf;
333
ssize_t io_size= (ssize_t)global_ddl_log.io_size;
334
DBUG_ENTER("read_ddl_log_file_entry");
336
if (pread(file_id, file_entry_buf, io_size, io_size * entry_no) != io_size)
343
Write one entry from ddl log file
345
write_ddl_log_file_entry()
346
entry_no Entry number to read
352
static bool write_ddl_log_file_entry(uint entry_no)
355
File file_id= global_ddl_log.file_id;
356
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
357
DBUG_ENTER("write_ddl_log_file_entry");
359
if (pwrite(file_id, (uchar*)file_entry_buf,
360
IO_SIZE, IO_SIZE * entry_no) != IO_SIZE)
369
write_ddl_log_header()
375
static bool write_ddl_log_header()
379
DBUG_ENTER("write_ddl_log_header");
381
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS],
382
global_ddl_log.num_entries);
384
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS],
387
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS],
389
if (write_ddl_log_file_entry(0UL))
391
sql_print_error("Error writing ddl log header");
394
VOID(sync_ddl_log());
400
Create ddl log file name
402
create_ddl_log_file_name()
403
file_name Filename setup
408
static inline void create_ddl_log_file_name(char *file_name)
410
strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS);
415
Read header of ddl log file
417
read_ddl_log_header()
419
> 0 Last entry in ddl log
420
0 No entries in ddl log
422
When we read the ddl log header we get information about maximum sizes
423
of names in the ddl log and we also get information about the number
424
of entries in the ddl log.
427
static uint read_ddl_log_header()
429
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
430
char file_name[FN_REFLEN];
432
bool successful_open= false;
433
DBUG_ENTER("read_ddl_log_header");
435
create_ddl_log_file_name(file_name);
436
if ((global_ddl_log.file_id= my_open(file_name,
437
O_RDWR | O_BINARY, MYF(0))) >= 0)
439
if (read_ddl_log_file_entry(0UL))
441
/* Write message into error log */
442
sql_print_error("Failed to read ddl log file in recovery");
445
successful_open= true;
447
entry_no= uint4korr(&file_entry_buf[DDL_LOG_NUM_ENTRY_POS]);
448
global_ddl_log.name_len= uint4korr(&file_entry_buf[DDL_LOG_NAME_LEN_POS]);
451
global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]);
452
DBUG_ASSERT(global_ddl_log.io_size <=
453
sizeof(global_ddl_log.file_entry_buf));
459
global_ddl_log.first_free= NULL;
460
global_ddl_log.first_used= NULL;
461
global_ddl_log.num_entries= 0;
462
VOID(pthread_mutex_init(&LOCK_gdl, MY_MUTEX_INIT_FAST));
463
global_ddl_log.do_release= true;
464
DBUG_RETURN(entry_no);
472
read_entry Number of entry to read
473
out:entry_info Information from entry
478
Read a specified entry in the ddl log
481
bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry)
483
char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf;
486
DBUG_ENTER("read_ddl_log_entry");
488
if (read_ddl_log_file_entry(read_entry))
492
ddl_log_entry->entry_pos= read_entry;
493
single_char= file_entry_buf[DDL_LOG_ENTRY_TYPE_POS];
494
ddl_log_entry->entry_type= (enum ddl_log_entry_code)single_char;
495
single_char= file_entry_buf[DDL_LOG_ACTION_TYPE_POS];
496
ddl_log_entry->action_type= (enum ddl_log_action_code)single_char;
497
ddl_log_entry->phase= file_entry_buf[DDL_LOG_PHASE_POS];
498
ddl_log_entry->next_entry= uint4korr(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS]);
499
ddl_log_entry->name= &file_entry_buf[DDL_LOG_NAME_POS];
500
inx= DDL_LOG_NAME_POS + global_ddl_log.name_len;
501
ddl_log_entry->from_name= &file_entry_buf[inx];
502
inx+= global_ddl_log.name_len;
503
ddl_log_entry->handler_name= &file_entry_buf[inx];
514
Write the header of the ddl log file and length of names. Also set
515
number of entries to zero.
522
static bool init_ddl_log()
524
char file_name[FN_REFLEN];
525
DBUG_ENTER("init_ddl_log");
527
if (global_ddl_log.inited)
530
global_ddl_log.io_size= IO_SIZE;
531
create_ddl_log_file_name(file_name);
532
if ((global_ddl_log.file_id= my_create(file_name,
534
O_RDWR | O_TRUNC | O_BINARY,
537
/* Couldn't create ddl log file, this is serious error */
538
sql_print_error("Failed to open ddl log file");
541
global_ddl_log.inited= true;
542
if (write_ddl_log_header())
544
VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
545
global_ddl_log.inited= false;
555
Execute one action in a ddl log entry
557
execute_ddl_log_action()
558
ddl_log_entry Information in action entry to execute
564
static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
566
bool frm_action= false;
567
LEX_STRING handler_name;
571
char to_path[FN_REFLEN];
572
char from_path[FN_REFLEN];
574
DBUG_ENTER("execute_ddl_log_action");
576
if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE)
580
handler_name.str= (char*)ddl_log_entry->handler_name;
581
handler_name.length= strlen(ddl_log_entry->handler_name);
582
init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
583
if (!strcmp(ddl_log_entry->handler_name, reg_ext))
587
plugin_ref plugin= ha_resolve_by_name(thd, &handler_name);
590
my_error(ER_ILLEGAL_HA, MYF(0), ddl_log_entry->handler_name);
593
hton= plugin_data(plugin, handlerton*);
594
file= get_new_handler((TABLE_SHARE*)0, &mem_root, hton);
597
my_error(ER_OUTOFMEMORY, MYF(0), sizeof(handler));
601
switch (ddl_log_entry->action_type)
603
case DDL_LOG_REPLACE_ACTION:
604
case DDL_LOG_DELETE_ACTION:
606
if (ddl_log_entry->phase == 0)
610
strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
611
if ((error= my_delete(to_path, MYF(MY_WME))))
613
if (my_errno != ENOENT)
619
if ((error= file->ha_delete_table(ddl_log_entry->name)))
621
if (error != ENOENT && error != HA_ERR_NO_SUCH_TABLE)
625
if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
627
VOID(sync_ddl_log());
629
if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION)
632
DBUG_ASSERT(ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION);
634
Fall through and perform the rename action of the replace
635
action. We have already indicated the success of the delete
636
action in the log entry by stepping up the phase.
639
case DDL_LOG_RENAME_ACTION:
644
strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
645
strxmov(from_path, ddl_log_entry->from_name, reg_ext, NullS);
646
if (my_rename(from_path, to_path, MYF(MY_WME)))
651
if (file->ha_rename_table(ddl_log_entry->from_name,
652
ddl_log_entry->name))
655
if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
657
VOID(sync_ddl_log());
667
free_root(&mem_root, MYF(0));
673
Get a free entry in the ddl log
675
get_free_ddl_log_entry()
676
out:active_entry A ddl log memory entry returned
682
static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
685
DDL_LOG_MEMORY_ENTRY *used_entry;
686
DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used;
687
DBUG_ENTER("get_free_ddl_log_entry");
689
if (global_ddl_log.first_free == NULL)
691
if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(
692
sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME))))
694
sql_print_error("Failed to allocate memory for ddl log free list");
697
global_ddl_log.num_entries++;
698
used_entry->entry_pos= global_ddl_log.num_entries;
703
used_entry= global_ddl_log.first_free;
704
global_ddl_log.first_free= used_entry->next_log_entry;
705
*write_header= false;
708
Move from free list to used list
710
used_entry->next_log_entry= first_used;
711
used_entry->prev_log_entry= NULL;
712
global_ddl_log.first_used= used_entry;
714
first_used->prev_log_entry= used_entry;
716
*active_entry= used_entry;
722
External interface methods for the DDL log Module
723
---------------------------------------------------
728
write_ddl_log_entry()
729
ddl_log_entry Information about log entry
730
out:entry_written Entry information written into
737
A careful write of the ddl log is performed to ensure that we can
738
handle crashes occurring during CREATE and ALTER TABLE processing.
741
bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
742
DDL_LOG_MEMORY_ENTRY **active_entry)
744
bool error, write_header;
745
DBUG_ENTER("write_ddl_log_entry");
751
global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]=
752
(char)DDL_LOG_ENTRY_CODE;
753
global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]=
754
(char)ddl_log_entry->action_type;
755
global_ddl_log.file_entry_buf[DDL_LOG_PHASE_POS]= 0;
756
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NEXT_ENTRY_POS],
757
ddl_log_entry->next_entry);
758
DBUG_ASSERT(strlen(ddl_log_entry->name) < FN_LEN);
759
strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS],
760
ddl_log_entry->name, FN_LEN - 1);
761
if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION ||
762
ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION)
764
DBUG_ASSERT(strlen(ddl_log_entry->from_name) < FN_LEN);
765
strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN],
766
ddl_log_entry->from_name, FN_LEN - 1);
769
global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
770
DBUG_ASSERT(strlen(ddl_log_entry->handler_name) < FN_LEN);
771
strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_LEN)],
772
ddl_log_entry->handler_name, FN_LEN - 1);
773
if (get_free_ddl_log_entry(active_entry, &write_header))
778
if (write_ddl_log_file_entry((*active_entry)->entry_pos))
781
sql_print_error("Failed to write entry_no = %u",
782
(*active_entry)->entry_pos);
784
if (write_header && !error)
786
VOID(sync_ddl_log());
787
if (write_ddl_log_header())
791
release_ddl_log_memory_entry(*active_entry);
797
Write final entry in the ddl log
799
write_execute_ddl_log_entry()
800
first_entry First entry in linked list of entries
801
to execute, if 0 = NULL it means that
802
the entry is removed and the entries
803
are put into the free list.
804
complete Flag indicating we are simply writing
805
info about that entry has been completed
806
in:out:active_entry Entry to execute, 0 = NULL if the entry
807
is written first time and needs to be
808
returned. In this case the entry written
809
is returned in this parameter
815
This is the last write in the ddl log. The previous log entries have
816
already been written but not yet synched to disk.
817
We write a couple of log entries that describes action to perform.
818
This entries are set-up in a linked list, however only when a first
819
execute entry is put as the first entry these will be executed.
820
This routine writes this first
823
bool write_execute_ddl_log_entry(uint first_entry,
825
DDL_LOG_MEMORY_ENTRY **active_entry)
827
bool write_header= false;
828
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
829
DBUG_ENTER("write_execute_ddl_log_entry");
838
We haven't synched the log entries yet, we synch them now before
839
writing the execute entry. If complete is true we haven't written
840
any log entries before, we are only here to write the execute
841
entry to indicate it is done.
843
VOID(sync_ddl_log());
844
file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_LOG_EXECUTE_CODE;
847
file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_IGNORE_LOG_ENTRY_CODE;
848
file_entry_buf[DDL_LOG_ACTION_TYPE_POS]= 0; /* Ignored for execute entries */
849
file_entry_buf[DDL_LOG_PHASE_POS]= 0;
850
int4store(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS], first_entry);
851
file_entry_buf[DDL_LOG_NAME_POS]= 0;
852
file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
853
file_entry_buf[DDL_LOG_NAME_POS + 2*FN_LEN]= 0;
854
if (!(*active_entry))
856
if (get_free_ddl_log_entry(active_entry, &write_header))
861
if (write_ddl_log_file_entry((*active_entry)->entry_pos))
863
sql_print_error("Error writing execute entry in ddl log");
864
release_ddl_log_memory_entry(*active_entry);
867
VOID(sync_ddl_log());
870
if (write_ddl_log_header())
872
release_ddl_log_memory_entry(*active_entry);
881
For complex rename operations we need to deactivate individual entries.
883
deactivate_ddl_log_entry()
884
entry_no Entry position of record to change
889
During replace operations where we start with an existing table called
890
t1 and a replacement table called t1#temp or something else and where
891
we want to delete t1 and rename t1#temp to t1 this is not possible to
892
do in a safe manner unless the ddl log is informed of the phases in
895
Delete actions are 1-phase actions that can be ignored immediately after
897
Rename actions from x to y is also a 1-phase action since there is no
898
interaction with any other handlers named x and y.
899
Replace action where drop y and x -> y happens needs to be a two-phase
900
action. Thus the first phase will drop y and the second phase will
904
bool deactivate_ddl_log_entry(uint entry_no)
906
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
907
DBUG_ENTER("deactivate_ddl_log_entry");
909
if (!read_ddl_log_file_entry(entry_no))
911
if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE)
913
if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_DELETE_ACTION ||
914
file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_RENAME_ACTION ||
915
(file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION &&
916
file_entry_buf[DDL_LOG_PHASE_POS] == 1))
917
file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_IGNORE_LOG_ENTRY_CODE;
918
else if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION)
920
DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] == 0);
921
file_entry_buf[DDL_LOG_PHASE_POS]= 1;
927
if (write_ddl_log_file_entry(entry_no))
929
sql_print_error("Error in deactivating log entry. Position = %u",
937
sql_print_error("Failed in reading entry before deactivating it");
956
DBUG_ENTER("sync_ddl_log");
958
if ((!global_ddl_log.recovery_phase) &&
963
if (my_sync(global_ddl_log.file_id, MYF(0)))
965
/* Write to error log */
966
sql_print_error("Failed to sync ddl log");
974
Release a log memory entry
976
release_ddl_log_memory_entry()
977
log_memory_entry Log memory entry to release
982
void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
984
DDL_LOG_MEMORY_ENTRY *first_free= global_ddl_log.first_free;
985
DDL_LOG_MEMORY_ENTRY *next_log_entry= log_entry->next_log_entry;
986
DDL_LOG_MEMORY_ENTRY *prev_log_entry= log_entry->prev_log_entry;
987
DBUG_ENTER("release_ddl_log_memory_entry");
989
global_ddl_log.first_free= log_entry;
990
log_entry->next_log_entry= first_free;
993
prev_log_entry->next_log_entry= next_log_entry;
995
global_ddl_log.first_used= next_log_entry;
997
next_log_entry->prev_log_entry= prev_log_entry;
1003
Execute one entry in the ddl log. Executing an entry means executing
1004
a linked list of actions.
1006
execute_ddl_log_entry()
1007
first_entry Reference to first action in entry
1013
bool execute_ddl_log_entry(THD *thd, uint first_entry)
1015
DDL_LOG_ENTRY ddl_log_entry;
1016
uint read_entry= first_entry;
1017
DBUG_ENTER("execute_ddl_log_entry");
1019
pthread_mutex_lock(&LOCK_gdl);
1022
if (read_ddl_log_entry(read_entry, &ddl_log_entry))
1024
/* Write to error log and continue with next log entry */
1025
sql_print_error("Failed to read entry = %u from ddl log",
1029
DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE ||
1030
ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE);
1032
if (execute_ddl_log_action(thd, &ddl_log_entry))
1034
/* Write to error log and continue with next log entry */
1035
sql_print_error("Failed to execute action for entry = %u from ddl log",
1039
read_entry= ddl_log_entry.next_entry;
1040
} while (read_entry);
1041
pthread_mutex_unlock(&LOCK_gdl);
1054
static void close_ddl_log()
1056
DBUG_ENTER("close_ddl_log");
1057
if (global_ddl_log.file_id >= 0)
1059
VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
1060
global_ddl_log.file_id= (File) -1;
1067
Execute the ddl log at recovery of MySQL Server
1069
execute_ddl_log_recovery()
1074
void execute_ddl_log_recovery()
1076
uint num_entries, i;
1078
DDL_LOG_ENTRY ddl_log_entry;
1079
char file_name[FN_REFLEN];
1080
DBUG_ENTER("execute_ddl_log_recovery");
1083
Initialise global_ddl_log struct
1085
bzero(global_ddl_log.file_entry_buf, sizeof(global_ddl_log.file_entry_buf));
1086
global_ddl_log.inited= false;
1087
global_ddl_log.recovery_phase= true;
1088
global_ddl_log.io_size= IO_SIZE;
1089
global_ddl_log.file_id= (File) -1;
1092
To be able to run this from boot, we allocate a temporary THD
1096
thd->thread_stack= (char*) &thd;
1097
thd->store_globals();
1099
num_entries= read_ddl_log_header();
1100
for (i= 1; i < num_entries + 1; i++)
1102
if (read_ddl_log_entry(i, &ddl_log_entry))
1104
sql_print_error("Failed to read entry no = %u from ddl log",
1108
if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE)
1110
if (execute_ddl_log_entry(thd, ddl_log_entry.next_entry))
1112
/* Real unpleasant scenario but we continue anyways. */
1118
create_ddl_log_file_name(file_name);
1119
VOID(my_delete(file_name, MYF(0)));
1120
global_ddl_log.recovery_phase= false;
1122
/* Remember that we don't have a THD */
1123
my_pthread_setspecific_ptr(THR_THD, 0);
1129
Release all memory allocated to the ddl log
1136
void release_ddl_log()
1138
DDL_LOG_MEMORY_ENTRY *free_list= global_ddl_log.first_free;
1139
DDL_LOG_MEMORY_ENTRY *used_list= global_ddl_log.first_used;
1140
DBUG_ENTER("release_ddl_log");
1142
if (!global_ddl_log.do_release)
1145
pthread_mutex_lock(&LOCK_gdl);
1148
DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry;
1149
my_free(used_list, MYF(0));
1154
DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry;
1155
my_free(free_list, MYF(0));
1159
global_ddl_log.inited= 0;
1160
pthread_mutex_unlock(&LOCK_gdl);
1161
VOID(pthread_mutex_destroy(&LOCK_gdl));
1162
global_ddl_log.do_release= false;
1168
---------------------------------------------------------------------------
1171
--------------------
1173
---------------------------------------------------------------------------