216
235
my_casedn_str(files_charset_info, p);
219
uint32_t length= unpack_filename(buff, buff);
238
uint length= unpack_filename(buff, buff);
243
--------------------------------------------------------------------------
248
This module is used to ensure that we can recover from crashes that occur
249
in the middle of a meta-data operation in MySQL. E.g. DROP TABLE t1, t2;
250
We need to ensure that both t1 and t2 are dropped and not only t1 and
251
also that each table drop is entirely done and not "half-baked".
253
To support this we create log entries for each meta-data statement in the
254
ddl log while we are executing. These entries are dropped when the
255
operation is completed.
257
At recovery those entries that were not completed will be executed.
259
There is only one ddl log in the system and it is protected by a mutex
260
and there is a global struct that contains information about its current
264
First version written in 2006 by Mikael Ronstrom
265
--------------------------------------------------------------------------
269
struct st_global_ddl_log
272
We need to adjust buffer size to be able to handle downgrades/upgrades
273
where IO_SIZE has changed. We'll set the buffer size such that we can
274
handle that the buffer size was upto 4 times bigger in the version
275
that wrote the DDL log.
277
char file_entry_buf[4*IO_SIZE];
278
char file_name_str[FN_REFLEN];
280
DDL_LOG_MEMORY_ENTRY *first_free;
281
DDL_LOG_MEMORY_ENTRY *first_used;
289
st_global_ddl_log() : inited(false), do_release(false) {}
292
st_global_ddl_log global_ddl_log;
294
pthread_mutex_t LOCK_gdl;
296
#define DDL_LOG_ENTRY_TYPE_POS 0
297
#define DDL_LOG_ACTION_TYPE_POS 1
298
#define DDL_LOG_PHASE_POS 2
299
#define DDL_LOG_NEXT_ENTRY_POS 4
300
#define DDL_LOG_NAME_POS 8
302
#define DDL_LOG_NUM_ENTRY_POS 0
303
#define DDL_LOG_NAME_LEN_POS 4
304
#define DDL_LOG_IO_SIZE_POS 8
307
Read one entry from ddl log file
309
read_ddl_log_file_entry()
310
entry_no Entry number to read
316
static bool read_ddl_log_file_entry(uint entry_no)
319
File file_id= global_ddl_log.file_id;
320
uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf;
321
ssize_t io_size= (ssize_t)global_ddl_log.io_size;
323
if (pread(file_id, file_entry_buf, io_size, io_size * entry_no) != io_size)
330
Write one entry from ddl log file
332
write_ddl_log_file_entry()
333
entry_no Entry number to read
339
static bool write_ddl_log_file_entry(uint entry_no)
342
File file_id= global_ddl_log.file_id;
343
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
345
if (pwrite(file_id, (uchar*)file_entry_buf,
346
IO_SIZE, IO_SIZE * entry_no) != IO_SIZE)
355
write_ddl_log_header()
361
static bool write_ddl_log_header()
366
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS],
367
global_ddl_log.num_entries);
369
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS],
372
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS],
374
if (write_ddl_log_file_entry(0UL))
376
sql_print_error("Error writing ddl log header");
379
VOID(sync_ddl_log());
385
Create ddl log file name
387
create_ddl_log_file_name()
388
file_name Filename setup
393
static inline void create_ddl_log_file_name(char *file_name)
395
strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS);
400
Read header of ddl log file
402
read_ddl_log_header()
404
> 0 Last entry in ddl log
405
0 No entries in ddl log
407
When we read the ddl log header we get information about maximum sizes
408
of names in the ddl log and we also get information about the number
409
of entries in the ddl log.
412
static uint read_ddl_log_header()
414
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
415
char file_name[FN_REFLEN];
417
bool successful_open= false;
419
create_ddl_log_file_name(file_name);
420
if ((global_ddl_log.file_id= my_open(file_name,
421
O_RDWR | O_BINARY, MYF(0))) >= 0)
423
if (read_ddl_log_file_entry(0UL))
425
/* Write message into error log */
426
sql_print_error("Failed to read ddl log file in recovery");
429
successful_open= true;
431
entry_no= uint4korr(&file_entry_buf[DDL_LOG_NUM_ENTRY_POS]);
432
global_ddl_log.name_len= uint4korr(&file_entry_buf[DDL_LOG_NAME_LEN_POS]);
435
global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]);
436
assert(global_ddl_log.io_size <=
437
sizeof(global_ddl_log.file_entry_buf));
443
global_ddl_log.first_free= NULL;
444
global_ddl_log.first_used= NULL;
445
global_ddl_log.num_entries= 0;
446
VOID(pthread_mutex_init(&LOCK_gdl, MY_MUTEX_INIT_FAST));
447
global_ddl_log.do_release= true;
456
read_entry Number of entry to read
457
out:entry_info Information from entry
462
Read a specified entry in the ddl log
465
bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry)
467
char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf;
471
if (read_ddl_log_file_entry(read_entry))
475
ddl_log_entry->entry_pos= read_entry;
476
single_char= file_entry_buf[DDL_LOG_ENTRY_TYPE_POS];
477
ddl_log_entry->entry_type= (enum ddl_log_entry_code)single_char;
478
single_char= file_entry_buf[DDL_LOG_ACTION_TYPE_POS];
479
ddl_log_entry->action_type= (enum ddl_log_action_code)single_char;
480
ddl_log_entry->phase= file_entry_buf[DDL_LOG_PHASE_POS];
481
ddl_log_entry->next_entry= uint4korr(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS]);
482
ddl_log_entry->name= &file_entry_buf[DDL_LOG_NAME_POS];
483
inx= DDL_LOG_NAME_POS + global_ddl_log.name_len;
484
ddl_log_entry->from_name= &file_entry_buf[inx];
485
inx+= global_ddl_log.name_len;
486
ddl_log_entry->handler_name= &file_entry_buf[inx];
497
Write the header of the ddl log file and length of names. Also set
498
number of entries to zero.
505
static bool init_ddl_log()
507
char file_name[FN_REFLEN];
509
if (global_ddl_log.inited)
512
global_ddl_log.io_size= IO_SIZE;
513
create_ddl_log_file_name(file_name);
514
if ((global_ddl_log.file_id= my_create(file_name,
516
O_RDWR | O_TRUNC | O_BINARY,
519
/* Couldn't create ddl log file, this is serious error */
520
sql_print_error("Failed to open ddl log file");
523
global_ddl_log.inited= true;
524
if (write_ddl_log_header())
526
VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
527
global_ddl_log.inited= false;
537
Execute one action in a ddl log entry
539
execute_ddl_log_action()
540
ddl_log_entry Information in action entry to execute
546
static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
548
bool frm_action= false;
549
LEX_STRING handler_name;
553
char to_path[FN_REFLEN];
554
char from_path[FN_REFLEN];
557
if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE)
561
handler_name.str= (char*)ddl_log_entry->handler_name;
562
handler_name.length= strlen(ddl_log_entry->handler_name);
563
init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
564
if (!strcmp(ddl_log_entry->handler_name, reg_ext))
568
plugin_ref plugin= ha_resolve_by_name(thd, &handler_name);
571
my_error(ER_ILLEGAL_HA, MYF(0), ddl_log_entry->handler_name);
574
hton= plugin_data(plugin, handlerton*);
575
file= get_new_handler((TABLE_SHARE*)0, &mem_root, hton);
578
my_error(ER_OUTOFMEMORY, MYF(0), sizeof(handler));
582
switch (ddl_log_entry->action_type)
584
case DDL_LOG_REPLACE_ACTION:
585
case DDL_LOG_DELETE_ACTION:
587
if (ddl_log_entry->phase == 0)
591
strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
592
if ((error= my_delete(to_path, MYF(MY_WME))))
594
if (my_errno != ENOENT)
600
if ((error= file->ha_delete_table(ddl_log_entry->name)))
602
if (error != ENOENT && error != HA_ERR_NO_SUCH_TABLE)
606
if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
608
VOID(sync_ddl_log());
610
if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION)
613
assert(ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION);
615
Fall through and perform the rename action of the replace
616
action. We have already indicated the success of the delete
617
action in the log entry by stepping up the phase.
620
case DDL_LOG_RENAME_ACTION:
625
strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
626
strxmov(from_path, ddl_log_entry->from_name, reg_ext, NullS);
627
if (my_rename(from_path, to_path, MYF(MY_WME)))
632
if (file->ha_rename_table(ddl_log_entry->from_name,
633
ddl_log_entry->name))
636
if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
638
VOID(sync_ddl_log());
648
free_root(&mem_root, MYF(0));
654
Get a free entry in the ddl log
656
get_free_ddl_log_entry()
657
out:active_entry A ddl log memory entry returned
663
static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
666
DDL_LOG_MEMORY_ENTRY *used_entry;
667
DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used;
669
if (global_ddl_log.first_free == NULL)
671
if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(
672
sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME))))
674
sql_print_error("Failed to allocate memory for ddl log free list");
677
global_ddl_log.num_entries++;
678
used_entry->entry_pos= global_ddl_log.num_entries;
683
used_entry= global_ddl_log.first_free;
684
global_ddl_log.first_free= used_entry->next_log_entry;
685
*write_header= false;
688
Move from free list to used list
690
used_entry->next_log_entry= first_used;
691
used_entry->prev_log_entry= NULL;
692
global_ddl_log.first_used= used_entry;
694
first_used->prev_log_entry= used_entry;
696
*active_entry= used_entry;
702
External interface methods for the DDL log Module
703
---------------------------------------------------
708
write_ddl_log_entry()
709
ddl_log_entry Information about log entry
710
out:entry_written Entry information written into
717
A careful write of the ddl log is performed to ensure that we can
718
handle crashes occurring during CREATE and ALTER TABLE processing.
721
bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
722
DDL_LOG_MEMORY_ENTRY **active_entry)
724
bool error, write_header;
730
global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]=
731
(char)DDL_LOG_ENTRY_CODE;
732
global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]=
733
(char)ddl_log_entry->action_type;
734
global_ddl_log.file_entry_buf[DDL_LOG_PHASE_POS]= 0;
735
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NEXT_ENTRY_POS],
736
ddl_log_entry->next_entry);
737
assert(strlen(ddl_log_entry->name) < FN_LEN);
738
strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS],
739
ddl_log_entry->name, FN_LEN - 1);
740
if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION ||
741
ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION)
743
assert(strlen(ddl_log_entry->from_name) < FN_LEN);
744
strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN],
745
ddl_log_entry->from_name, FN_LEN - 1);
748
global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
749
assert(strlen(ddl_log_entry->handler_name) < FN_LEN);
750
strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_LEN)],
751
ddl_log_entry->handler_name, FN_LEN - 1);
752
if (get_free_ddl_log_entry(active_entry, &write_header))
757
if (write_ddl_log_file_entry((*active_entry)->entry_pos))
760
sql_print_error("Failed to write entry_no = %u",
761
(*active_entry)->entry_pos);
763
if (write_header && !error)
765
VOID(sync_ddl_log());
766
if (write_ddl_log_header())
770
release_ddl_log_memory_entry(*active_entry);
776
Write final entry in the ddl log
778
write_execute_ddl_log_entry()
779
first_entry First entry in linked list of entries
780
to execute, if 0 = NULL it means that
781
the entry is removed and the entries
782
are put into the free list.
783
complete Flag indicating we are simply writing
784
info about that entry has been completed
785
in:out:active_entry Entry to execute, 0 = NULL if the entry
786
is written first time and needs to be
787
returned. In this case the entry written
788
is returned in this parameter
794
This is the last write in the ddl log. The previous log entries have
795
already been written but not yet synched to disk.
796
We write a couple of log entries that describes action to perform.
797
This entries are set-up in a linked list, however only when a first
798
execute entry is put as the first entry these will be executed.
799
This routine writes this first
802
bool write_execute_ddl_log_entry(uint first_entry,
804
DDL_LOG_MEMORY_ENTRY **active_entry)
806
bool write_header= false;
807
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
816
We haven't synched the log entries yet, we synch them now before
817
writing the execute entry. If complete is true we haven't written
818
any log entries before, we are only here to write the execute
819
entry to indicate it is done.
821
VOID(sync_ddl_log());
822
file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_LOG_EXECUTE_CODE;
825
file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_IGNORE_LOG_ENTRY_CODE;
826
file_entry_buf[DDL_LOG_ACTION_TYPE_POS]= 0; /* Ignored for execute entries */
827
file_entry_buf[DDL_LOG_PHASE_POS]= 0;
828
int4store(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS], first_entry);
829
file_entry_buf[DDL_LOG_NAME_POS]= 0;
830
file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
831
file_entry_buf[DDL_LOG_NAME_POS + 2*FN_LEN]= 0;
832
if (!(*active_entry))
834
if (get_free_ddl_log_entry(active_entry, &write_header))
839
if (write_ddl_log_file_entry((*active_entry)->entry_pos))
841
sql_print_error("Error writing execute entry in ddl log");
842
release_ddl_log_memory_entry(*active_entry);
845
VOID(sync_ddl_log());
848
if (write_ddl_log_header())
850
release_ddl_log_memory_entry(*active_entry);
859
For complex rename operations we need to deactivate individual entries.
861
deactivate_ddl_log_entry()
862
entry_no Entry position of record to change
867
During replace operations where we start with an existing table called
868
t1 and a replacement table called t1#temp or something else and where
869
we want to delete t1 and rename t1#temp to t1 this is not possible to
870
do in a safe manner unless the ddl log is informed of the phases in
873
Delete actions are 1-phase actions that can be ignored immediately after
875
Rename actions from x to y is also a 1-phase action since there is no
876
interaction with any other handlers named x and y.
877
Replace action where drop y and x -> y happens needs to be a two-phase
878
action. Thus the first phase will drop y and the second phase will
882
bool deactivate_ddl_log_entry(uint entry_no)
884
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
886
if (!read_ddl_log_file_entry(entry_no))
888
if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE)
890
if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_DELETE_ACTION ||
891
file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_RENAME_ACTION ||
892
(file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION &&
893
file_entry_buf[DDL_LOG_PHASE_POS] == 1))
894
file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_IGNORE_LOG_ENTRY_CODE;
895
else if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION)
897
assert(file_entry_buf[DDL_LOG_PHASE_POS] == 0);
898
file_entry_buf[DDL_LOG_PHASE_POS]= 1;
904
if (write_ddl_log_file_entry(entry_no))
906
sql_print_error("Error in deactivating log entry. Position = %u",
914
sql_print_error("Failed in reading entry before deactivating it");
934
if ((!global_ddl_log.recovery_phase) &&
939
if (my_sync(global_ddl_log.file_id, MYF(0)))
941
/* Write to error log */
942
sql_print_error("Failed to sync ddl log");
950
Release a log memory entry
952
release_ddl_log_memory_entry()
953
log_memory_entry Log memory entry to release
958
void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
960
DDL_LOG_MEMORY_ENTRY *first_free= global_ddl_log.first_free;
961
DDL_LOG_MEMORY_ENTRY *next_log_entry= log_entry->next_log_entry;
962
DDL_LOG_MEMORY_ENTRY *prev_log_entry= log_entry->prev_log_entry;
964
global_ddl_log.first_free= log_entry;
965
log_entry->next_log_entry= first_free;
968
prev_log_entry->next_log_entry= next_log_entry;
970
global_ddl_log.first_used= next_log_entry;
972
next_log_entry->prev_log_entry= prev_log_entry;
978
Execute one entry in the ddl log. Executing an entry means executing
979
a linked list of actions.
981
execute_ddl_log_entry()
982
first_entry Reference to first action in entry
988
bool execute_ddl_log_entry(THD *thd, uint first_entry)
990
DDL_LOG_ENTRY ddl_log_entry;
991
uint read_entry= first_entry;
993
pthread_mutex_lock(&LOCK_gdl);
996
if (read_ddl_log_entry(read_entry, &ddl_log_entry))
998
/* Write to error log and continue with next log entry */
999
sql_print_error("Failed to read entry = %u from ddl log",
1003
assert(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE ||
1004
ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE);
1006
if (execute_ddl_log_action(thd, &ddl_log_entry))
1008
/* Write to error log and continue with next log entry */
1009
sql_print_error("Failed to execute action for entry = %u from ddl log",
1013
read_entry= ddl_log_entry.next_entry;
1014
} while (read_entry);
1015
pthread_mutex_unlock(&LOCK_gdl);
1028
static void close_ddl_log()
1030
if (global_ddl_log.file_id >= 0)
1032
VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
1033
global_ddl_log.file_id= (File) -1;
1040
Execute the ddl log at recovery of MySQL Server
1042
execute_ddl_log_recovery()
1047
void execute_ddl_log_recovery()
1049
uint num_entries, i;
1051
DDL_LOG_ENTRY ddl_log_entry;
1052
char file_name[FN_REFLEN];
1055
Initialise global_ddl_log struct
1057
bzero(global_ddl_log.file_entry_buf, sizeof(global_ddl_log.file_entry_buf));
1058
global_ddl_log.inited= false;
1059
global_ddl_log.recovery_phase= true;
1060
global_ddl_log.io_size= IO_SIZE;
1061
global_ddl_log.file_id= (File) -1;
1064
To be able to run this from boot, we allocate a temporary THD
1068
thd->thread_stack= (char*) &thd;
1069
thd->store_globals();
1071
num_entries= read_ddl_log_header();
1072
for (i= 1; i < num_entries + 1; i++)
1074
if (read_ddl_log_entry(i, &ddl_log_entry))
1076
sql_print_error("Failed to read entry no = %u from ddl log",
1080
if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE)
1082
if (execute_ddl_log_entry(thd, ddl_log_entry.next_entry))
1084
/* Real unpleasant scenario but we continue anyways. */
1090
create_ddl_log_file_name(file_name);
1091
VOID(my_delete(file_name, MYF(0)));
1092
global_ddl_log.recovery_phase= false;
1094
/* Remember that we don't have a THD */
1095
my_pthread_setspecific_ptr(THR_THD, 0);
1101
Release all memory allocated to the ddl log
1108
void release_ddl_log()
1110
DDL_LOG_MEMORY_ENTRY *free_list= global_ddl_log.first_free;
1111
DDL_LOG_MEMORY_ENTRY *used_list= global_ddl_log.first_used;
1113
if (!global_ddl_log.do_release)
1116
pthread_mutex_lock(&LOCK_gdl);
1119
DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry;
1120
my_free(used_list, MYF(0));
1125
DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry;
1126
my_free(free_list, MYF(0));
1130
global_ddl_log.inited= 0;
1131
pthread_mutex_unlock(&LOCK_gdl);
1132
VOID(pthread_mutex_destroy(&LOCK_gdl));
1133
global_ddl_log.do_release= false;
1139
---------------------------------------------------------------------------
1142
--------------------
1144
---------------------------------------------------------------------------
226
1151
thd Thread object