228
--------------------------------------------------------------------------
233
This module is used to ensure that we can recover from crashes that occur
234
in the middle of a meta-data operation in MySQL. E.g. DROP TABLE t1, t2;
235
We need to ensure that both t1 and t2 are dropped and not only t1 and
236
also that each table drop is entirely done and not "half-baked".
238
To support this we create log entries for each meta-data statement in the
239
ddl log while we are executing. These entries are dropped when the
240
operation is completed.
242
At recovery those entries that were not completed will be executed.
244
There is only one ddl log in the system and it is protected by a mutex
245
and there is a global struct that contains information about its current
249
First version written in 2006 by Mikael Ronstrom
250
--------------------------------------------------------------------------
254
struct st_global_ddl_log
257
We need to adjust buffer size to be able to handle downgrades/upgrades
258
where IO_SIZE has changed. We'll set the buffer size such that we can
259
handle that the buffer size was upto 4 times bigger in the version
260
that wrote the DDL log.
262
char file_entry_buf[4*IO_SIZE];
263
char file_name_str[FN_REFLEN];
265
DDL_LOG_MEMORY_ENTRY *first_free;
266
DDL_LOG_MEMORY_ENTRY *first_used;
274
st_global_ddl_log() : inited(false), do_release(false) {}
277
st_global_ddl_log global_ddl_log;
279
pthread_mutex_t LOCK_gdl;
281
#define DDL_LOG_ENTRY_TYPE_POS 0
282
#define DDL_LOG_ACTION_TYPE_POS 1
283
#define DDL_LOG_PHASE_POS 2
284
#define DDL_LOG_NEXT_ENTRY_POS 4
285
#define DDL_LOG_NAME_POS 8
287
#define DDL_LOG_NUM_ENTRY_POS 0
288
#define DDL_LOG_NAME_LEN_POS 4
289
#define DDL_LOG_IO_SIZE_POS 8
292
Read one entry from ddl log file
294
read_ddl_log_file_entry()
295
entry_no Entry number to read
301
static bool read_ddl_log_file_entry(uint entry_no)
304
File file_id= global_ddl_log.file_id;
305
uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf;
306
ssize_t io_size= (ssize_t)global_ddl_log.io_size;
308
if (pread(file_id, file_entry_buf, io_size, io_size * entry_no) != io_size)
315
Write one entry from ddl log file
317
write_ddl_log_file_entry()
318
entry_no Entry number to read
324
static bool write_ddl_log_file_entry(uint entry_no)
327
File file_id= global_ddl_log.file_id;
328
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
330
if (pwrite(file_id, (uchar*)file_entry_buf,
331
IO_SIZE, IO_SIZE * entry_no) != IO_SIZE)
340
write_ddl_log_header()
346
static bool write_ddl_log_header()
351
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS],
352
global_ddl_log.num_entries);
354
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS],
357
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS],
359
if (write_ddl_log_file_entry(0UL))
361
sql_print_error("Error writing ddl log header");
364
VOID(sync_ddl_log());
370
Create ddl log file name
372
create_ddl_log_file_name()
373
file_name Filename setup
378
static inline void create_ddl_log_file_name(char *file_name)
380
strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS);
385
Read header of ddl log file
387
read_ddl_log_header()
389
> 0 Last entry in ddl log
390
0 No entries in ddl log
392
When we read the ddl log header we get information about maximum sizes
393
of names in the ddl log and we also get information about the number
394
of entries in the ddl log.
397
static uint read_ddl_log_header()
399
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
400
char file_name[FN_REFLEN];
402
bool successful_open= false;
404
create_ddl_log_file_name(file_name);
405
if ((global_ddl_log.file_id= my_open(file_name,
406
O_RDWR | O_BINARY, MYF(0))) >= 0)
408
if (read_ddl_log_file_entry(0UL))
410
/* Write message into error log */
411
sql_print_error("Failed to read ddl log file in recovery");
414
successful_open= true;
416
entry_no= uint4korr(&file_entry_buf[DDL_LOG_NUM_ENTRY_POS]);
417
global_ddl_log.name_len= uint4korr(&file_entry_buf[DDL_LOG_NAME_LEN_POS]);
420
global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]);
421
assert(global_ddl_log.io_size <=
422
sizeof(global_ddl_log.file_entry_buf));
428
global_ddl_log.first_free= NULL;
429
global_ddl_log.first_used= NULL;
430
global_ddl_log.num_entries= 0;
431
VOID(pthread_mutex_init(&LOCK_gdl, MY_MUTEX_INIT_FAST));
432
global_ddl_log.do_release= true;
441
read_entry Number of entry to read
442
out:entry_info Information from entry
447
Read a specified entry in the ddl log
450
bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry)
452
char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf;
456
if (read_ddl_log_file_entry(read_entry))
460
ddl_log_entry->entry_pos= read_entry;
461
single_char= file_entry_buf[DDL_LOG_ENTRY_TYPE_POS];
462
ddl_log_entry->entry_type= (enum ddl_log_entry_code)single_char;
463
single_char= file_entry_buf[DDL_LOG_ACTION_TYPE_POS];
464
ddl_log_entry->action_type= (enum ddl_log_action_code)single_char;
465
ddl_log_entry->phase= file_entry_buf[DDL_LOG_PHASE_POS];
466
ddl_log_entry->next_entry= uint4korr(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS]);
467
ddl_log_entry->name= &file_entry_buf[DDL_LOG_NAME_POS];
468
inx= DDL_LOG_NAME_POS + global_ddl_log.name_len;
469
ddl_log_entry->from_name= &file_entry_buf[inx];
470
inx+= global_ddl_log.name_len;
471
ddl_log_entry->handler_name= &file_entry_buf[inx];
482
Write the header of the ddl log file and length of names. Also set
483
number of entries to zero.
490
static bool init_ddl_log()
492
char file_name[FN_REFLEN];
494
if (global_ddl_log.inited)
497
global_ddl_log.io_size= IO_SIZE;
498
create_ddl_log_file_name(file_name);
499
if ((global_ddl_log.file_id= my_create(file_name,
501
O_RDWR | O_TRUNC | O_BINARY,
504
/* Couldn't create ddl log file, this is serious error */
505
sql_print_error("Failed to open ddl log file");
508
global_ddl_log.inited= true;
509
if (write_ddl_log_header())
511
VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
512
global_ddl_log.inited= false;
522
Execute one action in a ddl log entry
524
execute_ddl_log_action()
525
ddl_log_entry Information in action entry to execute
531
static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
533
bool frm_action= false;
534
LEX_STRING handler_name;
538
char to_path[FN_REFLEN];
539
char from_path[FN_REFLEN];
542
if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE)
546
handler_name.str= (char*)ddl_log_entry->handler_name;
547
handler_name.length= strlen(ddl_log_entry->handler_name);
548
init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
549
if (!strcmp(ddl_log_entry->handler_name, reg_ext))
553
plugin_ref plugin= ha_resolve_by_name(thd, &handler_name);
556
my_error(ER_ILLEGAL_HA, MYF(0), ddl_log_entry->handler_name);
559
hton= plugin_data(plugin, handlerton*);
560
file= get_new_handler((TABLE_SHARE*)0, &mem_root, hton);
563
my_error(ER_OUTOFMEMORY, MYF(0), sizeof(handler));
567
switch (ddl_log_entry->action_type)
569
case DDL_LOG_REPLACE_ACTION:
570
case DDL_LOG_DELETE_ACTION:
572
if (ddl_log_entry->phase == 0)
576
strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
577
if ((error= my_delete(to_path, MYF(MY_WME))))
579
if (my_errno != ENOENT)
585
if ((error= file->ha_delete_table(ddl_log_entry->name)))
587
if (error != ENOENT && error != HA_ERR_NO_SUCH_TABLE)
591
if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
593
VOID(sync_ddl_log());
595
if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION)
598
assert(ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION);
600
Fall through and perform the rename action of the replace
601
action. We have already indicated the success of the delete
602
action in the log entry by stepping up the phase.
605
case DDL_LOG_RENAME_ACTION:
610
strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
611
strxmov(from_path, ddl_log_entry->from_name, reg_ext, NullS);
612
if (my_rename(from_path, to_path, MYF(MY_WME)))
617
if (file->ha_rename_table(ddl_log_entry->from_name,
618
ddl_log_entry->name))
621
if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
623
VOID(sync_ddl_log());
633
free_root(&mem_root, MYF(0));
639
Get a free entry in the ddl log
641
get_free_ddl_log_entry()
642
out:active_entry A ddl log memory entry returned
648
static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
651
DDL_LOG_MEMORY_ENTRY *used_entry;
652
DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used;
654
if (global_ddl_log.first_free == NULL)
656
if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(
657
sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME))))
659
sql_print_error("Failed to allocate memory for ddl log free list");
662
global_ddl_log.num_entries++;
663
used_entry->entry_pos= global_ddl_log.num_entries;
668
used_entry= global_ddl_log.first_free;
669
global_ddl_log.first_free= used_entry->next_log_entry;
670
*write_header= false;
673
Move from free list to used list
675
used_entry->next_log_entry= first_used;
676
used_entry->prev_log_entry= NULL;
677
global_ddl_log.first_used= used_entry;
679
first_used->prev_log_entry= used_entry;
681
*active_entry= used_entry;
687
External interface methods for the DDL log Module
688
---------------------------------------------------
693
write_ddl_log_entry()
694
ddl_log_entry Information about log entry
695
out:entry_written Entry information written into
702
A careful write of the ddl log is performed to ensure that we can
703
handle crashes occurring during CREATE and ALTER TABLE processing.
706
bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
707
DDL_LOG_MEMORY_ENTRY **active_entry)
709
bool error, write_header;
715
global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]=
716
(char)DDL_LOG_ENTRY_CODE;
717
global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]=
718
(char)ddl_log_entry->action_type;
719
global_ddl_log.file_entry_buf[DDL_LOG_PHASE_POS]= 0;
720
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NEXT_ENTRY_POS],
721
ddl_log_entry->next_entry);
722
assert(strlen(ddl_log_entry->name) < FN_LEN);
723
strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS],
724
ddl_log_entry->name, FN_LEN - 1);
725
if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION ||
726
ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION)
728
assert(strlen(ddl_log_entry->from_name) < FN_LEN);
729
strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN],
730
ddl_log_entry->from_name, FN_LEN - 1);
733
global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
734
assert(strlen(ddl_log_entry->handler_name) < FN_LEN);
735
strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_LEN)],
736
ddl_log_entry->handler_name, FN_LEN - 1);
737
if (get_free_ddl_log_entry(active_entry, &write_header))
742
if (write_ddl_log_file_entry((*active_entry)->entry_pos))
745
sql_print_error("Failed to write entry_no = %u",
746
(*active_entry)->entry_pos);
748
if (write_header && !error)
750
VOID(sync_ddl_log());
751
if (write_ddl_log_header())
755
release_ddl_log_memory_entry(*active_entry);
761
Write final entry in the ddl log
763
write_execute_ddl_log_entry()
764
first_entry First entry in linked list of entries
765
to execute, if 0 = NULL it means that
766
the entry is removed and the entries
767
are put into the free list.
768
complete Flag indicating we are simply writing
769
info about that entry has been completed
770
in:out:active_entry Entry to execute, 0 = NULL if the entry
771
is written first time and needs to be
772
returned. In this case the entry written
773
is returned in this parameter
779
This is the last write in the ddl log. The previous log entries have
780
already been written but not yet synched to disk.
781
We write a couple of log entries that describes action to perform.
782
This entries are set-up in a linked list, however only when a first
783
execute entry is put as the first entry these will be executed.
784
This routine writes this first
787
bool write_execute_ddl_log_entry(uint first_entry,
789
DDL_LOG_MEMORY_ENTRY **active_entry)
791
bool write_header= false;
792
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
801
We haven't synched the log entries yet, we synch them now before
802
writing the execute entry. If complete is true we haven't written
803
any log entries before, we are only here to write the execute
804
entry to indicate it is done.
806
VOID(sync_ddl_log());
807
file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_LOG_EXECUTE_CODE;
810
file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_IGNORE_LOG_ENTRY_CODE;
811
file_entry_buf[DDL_LOG_ACTION_TYPE_POS]= 0; /* Ignored for execute entries */
812
file_entry_buf[DDL_LOG_PHASE_POS]= 0;
813
int4store(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS], first_entry);
814
file_entry_buf[DDL_LOG_NAME_POS]= 0;
815
file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
816
file_entry_buf[DDL_LOG_NAME_POS + 2*FN_LEN]= 0;
817
if (!(*active_entry))
819
if (get_free_ddl_log_entry(active_entry, &write_header))
824
if (write_ddl_log_file_entry((*active_entry)->entry_pos))
826
sql_print_error("Error writing execute entry in ddl log");
827
release_ddl_log_memory_entry(*active_entry);
830
VOID(sync_ddl_log());
833
if (write_ddl_log_header())
835
release_ddl_log_memory_entry(*active_entry);
844
For complex rename operations we need to deactivate individual entries.
846
deactivate_ddl_log_entry()
847
entry_no Entry position of record to change
852
During replace operations where we start with an existing table called
853
t1 and a replacement table called t1#temp or something else and where
854
we want to delete t1 and rename t1#temp to t1 this is not possible to
855
do in a safe manner unless the ddl log is informed of the phases in
858
Delete actions are 1-phase actions that can be ignored immediately after
860
Rename actions from x to y is also a 1-phase action since there is no
861
interaction with any other handlers named x and y.
862
Replace action where drop y and x -> y happens needs to be a two-phase
863
action. Thus the first phase will drop y and the second phase will
867
bool deactivate_ddl_log_entry(uint entry_no)
869
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
871
if (!read_ddl_log_file_entry(entry_no))
873
if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE)
875
if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_DELETE_ACTION ||
876
file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_RENAME_ACTION ||
877
(file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION &&
878
file_entry_buf[DDL_LOG_PHASE_POS] == 1))
879
file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_IGNORE_LOG_ENTRY_CODE;
880
else if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION)
882
assert(file_entry_buf[DDL_LOG_PHASE_POS] == 0);
883
file_entry_buf[DDL_LOG_PHASE_POS]= 1;
889
if (write_ddl_log_file_entry(entry_no))
891
sql_print_error("Error in deactivating log entry. Position = %u",
899
sql_print_error("Failed in reading entry before deactivating it");
919
if ((!global_ddl_log.recovery_phase) &&
924
if (my_sync(global_ddl_log.file_id, MYF(0)))
926
/* Write to error log */
927
sql_print_error("Failed to sync ddl log");
935
Release a log memory entry
937
release_ddl_log_memory_entry()
938
log_memory_entry Log memory entry to release
943
void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
945
DDL_LOG_MEMORY_ENTRY *first_free= global_ddl_log.first_free;
946
DDL_LOG_MEMORY_ENTRY *next_log_entry= log_entry->next_log_entry;
947
DDL_LOG_MEMORY_ENTRY *prev_log_entry= log_entry->prev_log_entry;
949
global_ddl_log.first_free= log_entry;
950
log_entry->next_log_entry= first_free;
953
prev_log_entry->next_log_entry= next_log_entry;
955
global_ddl_log.first_used= next_log_entry;
957
next_log_entry->prev_log_entry= prev_log_entry;
963
Execute one entry in the ddl log. Executing an entry means executing
964
a linked list of actions.
966
execute_ddl_log_entry()
967
first_entry Reference to first action in entry
973
bool execute_ddl_log_entry(THD *thd, uint first_entry)
975
DDL_LOG_ENTRY ddl_log_entry;
976
uint read_entry= first_entry;
978
pthread_mutex_lock(&LOCK_gdl);
981
if (read_ddl_log_entry(read_entry, &ddl_log_entry))
983
/* Write to error log and continue with next log entry */
984
sql_print_error("Failed to read entry = %u from ddl log",
988
assert(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE ||
989
ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE);
991
if (execute_ddl_log_action(thd, &ddl_log_entry))
993
/* Write to error log and continue with next log entry */
994
sql_print_error("Failed to execute action for entry = %u from ddl log",
998
read_entry= ddl_log_entry.next_entry;
999
} while (read_entry);
1000
pthread_mutex_unlock(&LOCK_gdl);
1013
static void close_ddl_log()
1015
if (global_ddl_log.file_id >= 0)
1017
VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
1018
global_ddl_log.file_id= (File) -1;
1025
Execute the ddl log at recovery of MySQL Server
1027
execute_ddl_log_recovery()
1032
void execute_ddl_log_recovery()
1034
uint num_entries, i;
1036
DDL_LOG_ENTRY ddl_log_entry;
1037
char file_name[FN_REFLEN];
1040
Initialise global_ddl_log struct
1042
bzero(global_ddl_log.file_entry_buf, sizeof(global_ddl_log.file_entry_buf));
1043
global_ddl_log.inited= false;
1044
global_ddl_log.recovery_phase= true;
1045
global_ddl_log.io_size= IO_SIZE;
1046
global_ddl_log.file_id= (File) -1;
1049
To be able to run this from boot, we allocate a temporary THD
1053
thd->thread_stack= (char*) &thd;
1054
thd->store_globals();
1056
num_entries= read_ddl_log_header();
1057
for (i= 1; i < num_entries + 1; i++)
1059
if (read_ddl_log_entry(i, &ddl_log_entry))
1061
sql_print_error("Failed to read entry no = %u from ddl log",
1065
if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE)
1067
if (execute_ddl_log_entry(thd, ddl_log_entry.next_entry))
1069
/* Real unpleasant scenario but we continue anyways. */
1075
create_ddl_log_file_name(file_name);
1076
VOID(my_delete(file_name, MYF(0)));
1077
global_ddl_log.recovery_phase= false;
1079
/* Remember that we don't have a THD */
1080
my_pthread_setspecific_ptr(THR_THD, 0);
1086
Release all memory allocated to the ddl log
1093
void release_ddl_log()
1095
DDL_LOG_MEMORY_ENTRY *free_list= global_ddl_log.first_free;
1096
DDL_LOG_MEMORY_ENTRY *used_list= global_ddl_log.first_used;
1098
if (!global_ddl_log.do_release)
1101
pthread_mutex_lock(&LOCK_gdl);
1104
DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry;
1105
my_free(used_list, MYF(0));
1110
DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry;
1111
my_free(free_list, MYF(0));
1115
global_ddl_log.inited= 0;
1116
pthread_mutex_unlock(&LOCK_gdl);
1117
VOID(pthread_mutex_destroy(&LOCK_gdl));
1118
global_ddl_log.do_release= false;
1124
---------------------------------------------------------------------------
1127
--------------------
1129
---------------------------------------------------------------------------
1136
230
thd Thread object