diff options
author | unknown <guilhem@gbichot3.local> | 2006-09-01 17:53:10 +0200 |
---|---|---|
committer | unknown <guilhem@gbichot3.local> | 2006-09-01 17:53:10 +0200 |
commit | a1f25544d531b8909e58d36734ee6f65c1e189d5 (patch) | |
tree | f4a0d677568823d8784eda7d5d823fe417926a4a /storage/maria | |
parent | 8a2901a7584a87a326eee67b00ccb9eb8519ea03 (diff) | |
download | mariadb-git-a1f25544d531b8909e58d36734ee6f65c1e189d5.tar.gz |
WL#3234 "Maria - control file manager"
- fixes to the control file module
- unit test for it
- renames of all Maria files I created to start with ma_
storage/maria/ma_checkpoint.c:
Rename: storage/maria/checkpoint.c -> storage/maria/ma_checkpoint.c
storage/maria/ma_checkpoint.h:
Rename: storage/maria/checkpoint.h -> storage/maria/ma_checkpoint.h
storage/maria/ma_least_recently_dirtied.c:
Rename: storage/maria/least_recently_dirtied.c -> storage/maria/ma_least_recently_dirtied.c
storage/maria/ma_least_recently_dirtied.h:
Rename: storage/maria/least_recently_dirtied.h -> storage/maria/ma_least_recently_dirtied.h
storage/maria/ma_recovery.c:
Rename: storage/maria/recovery.c -> storage/maria/ma_recovery.c
storage/maria/ma_recovery.h:
Rename: storage/maria/recovery.h -> storage/maria/ma_recovery.h
storage/maria/Makefile.am:
control file module and its unit test program
storage/maria/ma_control_file.c:
DBUG_ tags. Fix for gcc warnings.
log_no -> logno (I felt "_no" sounded like a standalone "No" word).
ma_ prefix for some functions.
last_checkpoint_lsn_at_startup -> last_checkpoint_lsn (no need
to make special vars for the values at startup). Same for last_logno.
ma_control_file_write_and_force() now updates last_checkpoint_lsn
and last_logno, the idea being that they belong to the module,
others should not update them.
And thus when the module shuts down, it zeroes those vars.
storage/maria/ma_control_file.h:
importing structs from Sanja to get the control file module to compile;
we'll remove that when Sanja pushes the log handler.
CONTROL_FILE_IMPOSSIBLE_LOGNO is 0, not FFFFFFFF.
storage/maria/ma_control_file_test.c:
Unit test program for the Maria control file module.
Modelled after other ma_test* files in this directory (so, does
not follow the unit test framework recently introduced with libtap;
TODO as a task on all ma_test* programs).
We test that writing to the control file works, and re-reading from it
too, we check (by reading the file by ourselves) that its content
on disk is correct, and check that a corrupted control file is detected.
Diffstat (limited to 'storage/maria')
-rw-r--r-- | storage/maria/Makefile.am | 7 | ||||
-rw-r--r-- | storage/maria/control_file.h | 46 | ||||
-rw-r--r-- | storage/maria/ma_checkpoint.c (renamed from storage/maria/checkpoint.c) | 0 | ||||
-rw-r--r-- | storage/maria/ma_checkpoint.h (renamed from storage/maria/checkpoint.h) | 0 | ||||
-rw-r--r-- | storage/maria/ma_control_file.c (renamed from storage/maria/control_file.c) | 105 | ||||
-rw-r--r-- | storage/maria/ma_control_file.h | 75 | ||||
-rw-r--r-- | storage/maria/ma_control_file_test.c | 290 | ||||
-rw-r--r-- | storage/maria/ma_least_recently_dirtied.c (renamed from storage/maria/least_recently_dirtied.c) | 0 | ||||
-rw-r--r-- | storage/maria/ma_least_recently_dirtied.h (renamed from storage/maria/least_recently_dirtied.h) | 0 | ||||
-rw-r--r-- | storage/maria/ma_recovery.c (renamed from storage/maria/recovery.c) | 0 | ||||
-rw-r--r-- | storage/maria/ma_recovery.h (renamed from storage/maria/recovery.h) | 0 |
11 files changed, 433 insertions, 90 deletions
diff --git a/storage/maria/Makefile.am b/storage/maria/Makefile.am index d4315b4d446..e2689698d62 100644 --- a/storage/maria/Makefile.am +++ b/storage/maria/Makefile.am @@ -27,7 +27,7 @@ pkglib_LIBRARIES = libmaria.a bin_PROGRAMS = maria_chk maria_pack maria_ftdump maria_chk_DEPENDENCIES= $(LIBRARIES) maria_pack_DEPENDENCIES=$(LIBRARIES) -noinst_PROGRAMS = ma_test1 ma_test2 ma_test3 ma_rt_test ma_sp_test +noinst_PROGRAMS = ma_test1 ma_test2 ma_test3 ma_rt_test ma_sp_test ma_control_file_test noinst_HEADERS = maria_def.h ma_rt_index.h ma_rt_key.h ma_rt_mbr.h ma_sp_defs.h ma_fulltext.h ma_ftdefs.h ma_ft_test1.h ma_ft_eval.h ma_test1_DEPENDENCIES= $(LIBRARIES) ma_test2_DEPENDENCIES= $(LIBRARIES) @@ -53,8 +53,9 @@ libmaria_a_SOURCES = ma_init.c ma_open.c ma_extra.c ma_info.c ma_rkey.c \ ma_ft_update.c ma_ft_boolean_search.c \ ma_ft_nlq_search.c ft_maria.c ma_sort.c \ ma_rt_index.c ma_rt_key.c ma_rt_mbr.c ma_rt_split.c \ - ma_sp_key.c -CLEANFILES = test?.MA? FT?.MA? isam.log ma_test_all ma_rt_test.MA? sp_test.MA? + ma_sp_key.c \ + ma_control_file.c +CLEANFILES = test?.MA? FT?.MA? isam.log ma_test_all ma_rt_test.MA? sp_test.MA? maria_control DEFS = SUFFIXES = .sh diff --git a/storage/maria/control_file.h b/storage/maria/control_file.h deleted file mode 100644 index 66a1f225cd8..00000000000 --- a/storage/maria/control_file.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - WL#3234 Maria control file - First version written by Guilhem Bichot on 2006-04-27. - Does not compile yet. -*/ - -#ifndef _control_file_h -#define _control_file_h - -/* indicate absence of the log file number */ -#define CONTROL_FILE_IMPOSSIBLE_LOGNO 0xFFFFFFFF - -/* Here is the interface of this module */ - -/* - LSN of the last checkoint - (if last_checkpoint_lsn_at_startup.file_no == CONTROL_FILE_IMPOSSIBLE_LOGNO - then there was never a checkpoint) -*/ -extern LSN last_checkpoint_lsn_at_startup; -/* - Last log number at startup time (if last_logno_at_startup == - CONTROL_FILE_IMPOSSIBLE_LOGNO then there is no log file yet) -*/ -extern uint32 last_logno_at_startup; - -/* - Looks for the control file. If absent, it's a fresh start, create file. - If present, read it to find out last checkpoint's LSN and last log. - Called at engine's start. -*/ -int control_file_create_or_open(); - -/* - Write information durably to the control file. - Called when we have created a new log (after syncing this log's creation) - and when we have written a checkpoint (after syncing this log record). -*/ -int control_file_write_and_force(LSN *checkpoint_lsn, uint32 log_no, - uint objs_to_write); - - -/* Free resources taken by control file subsystem */ -void control_file_end(); - -#endif diff --git a/storage/maria/checkpoint.c b/storage/maria/ma_checkpoint.c index 22e7b93d2f4..22e7b93d2f4 100644 --- a/storage/maria/checkpoint.c +++ b/storage/maria/ma_checkpoint.c diff --git a/storage/maria/checkpoint.h b/storage/maria/ma_checkpoint.h index a9de18c695f..a9de18c695f 100644 --- a/storage/maria/checkpoint.h +++ b/storage/maria/ma_checkpoint.h diff --git a/storage/maria/control_file.c b/storage/maria/ma_control_file.c index 70eb62c645b..d36e1c04c0c 100644 --- a/storage/maria/control_file.c +++ b/storage/maria/ma_control_file.c @@ -5,7 +5,7 @@ */ #include "maria_def.h" - +#include "ma_control_file.h" /* Here is the implementation of this module */ @@ -17,16 +17,22 @@ /* total size should be < sector size for atomic write operation */ #define CONTROL_FILE_MAGIC_STRING "MACF" #define CONTROL_FILE_MAGIC_STRING_OFFSET 0 -#define CONTROL_FILE_MAGIC_STRING_SIZE sizeof(CONTROL_FILE_MAGIC_STRING) +#define CONTROL_FILE_MAGIC_STRING_SIZE 4 #define CONTROL_FILE_LSN_OFFSET (CONTROL_FILE_MAGIC_STRING_OFFSET + CONTROL_FILE_MAGIC_STRING_SIZE) #define CONTROL_FILE_LSN_SIZE (4+4) #define CONTROL_FILE_FILENO_OFFSET (CONTROL_FILE_LSN_OFFSET + CONTROL_FILE_LSN_SIZE) #define CONTROL_FILE_FILENO_SIZE 4 #define CONTROL_FILE_MAX_SIZE (CONTROL_FILE_FILENO_OFFSET + CONTROL_FILE_FILENO_SIZE) - -LSN last_checkpoint_lsn_at_startup; -uint32 last_logno_at_startup; +/* + This module owns these two vars. + uint32 is always atomically updated, but LSN is 8 bytes, we will need + provisions to ensure that it's updated atomically in + ma_control_file_write_and_force(). Probably the log mutex could be + used. TODO. +*/ +LSN last_checkpoint_lsn; +uint32 last_logno; /* @@ -35,7 +41,7 @@ uint32 last_logno_at_startup; */ static int control_file_fd; -static void lsn8store(char *buffer, LSN *lsn) +static void lsn8store(char *buffer, const LSN *lsn) { int4store(buffer, lsn->file_no); int4store(buffer + CONTROL_FILE_FILENO_SIZE, lsn->rec_offset); @@ -53,21 +59,23 @@ static LSN lsn8korr(char *buffer) Initialize control file subsystem SYNOPSIS - control_file_create_or_open() + ma_control_file_create_or_open() - Looks for the control file. If absent, it's a fresh start, create file. - If present, read it to find out last checkpoint's LSN and last log. + Looks for the control file. If absent, it's a fresh start, creates file. + If present, reads it to find out last checkpoint's LSN and last log, updates + the last_checkpoint_lsn and last_logno global variables. Called at engine's start. RETURN 0 - OK 1 - Error */ -int control_file_create_or_open() +int ma_control_file_create_or_open() { char buffer[CONTROL_FILE_MAX_SIZE]; char name[FN_REFLEN]; MY_STAT stat_buff; + DBUG_ENTER("ma_control_file_create_or_open"); /* If you change sizes in the #defines, you at least have to change the @@ -79,12 +87,12 @@ int control_file_create_or_open() /* name is concatenation of Maria's home dir and "control" */ if (fn_format(name, "control", maria_data_root, "", MYF(MY_WME)) == NullS) - return 1; + DBUG_RETURN(1); if ((control_file_fd= my_open(name, O_CREAT | O_BINARY | /*O_DIRECT |*/ O_RDWR, MYF(MY_WME))) < 0) - return 1; + DBUG_RETURN(1); /* TODO: from "man fsync" on Linux: @@ -96,16 +104,14 @@ int control_file_create_or_open() */ if (my_stat(name, &stat_buff, MYF(MY_WME)) == NULL) - return 1; + DBUG_RETURN(1); - if (stat_buff.st_size < CONTROL_FILE_MAX_SIZE) + if ((uint)stat_buff.st_size < CONTROL_FILE_MAX_SIZE) { /* File shorter than expected (either we just created it, or a previous run crashed between creation and first write); do first write. - */ - char buffer[CONTROL_FILE_MAX_SIZE]; - /* + To be safer we should make sure that there are no logs or data/index files around (indeed it could be that the control file alone was deleted or not restored, and we should not go on with life at this point). @@ -117,38 +123,41 @@ int control_file_create_or_open() directory of logs, finding the newest log, reading it to find last checkpoint... Slow but can save your db. */ - last_checkpoint_lsn_at_startup.file_no= CONTROL_FILE_IMPOSSIBLE_FILENO; - last_checkpoint_lsn_at_startup.rec_offset= 0; - last_logno_at_startup= CONTROL_FILE_IMPOSSIBLE_FILENO; + LSN imposs_lsn= CONTROL_FILE_IMPOSSIBLE_LSN; + uint32 imposs_logno= CONTROL_FILE_IMPOSSIBLE_FILENO; /* init the file with these "undefined" values */ - return control_file_write_and_force(last_checkpoint_lsn_at_startup, - last_logno_at_startup, - CONTROL_FILE_WRITE_ALL); + DBUG_RETURN(ma_control_file_write_and_force(&imposs_lsn, imposs_logno, + CONTROL_FILE_WRITE_ALL)); } /* Already existing file, read it */ if (my_read(control_file_fd, buffer, CONTROL_FILE_MAX_SIZE, MYF(MY_FNABP | MY_WME))) - return 1; + DBUG_RETURN(1); if (memcmp(buffer + CONTROL_FILE_MAGIC_STRING_OFFSET, CONTROL_FILE_MAGIC_STRING, CONTROL_FILE_MAGIC_STRING_SIZE)) - return 1; - last_checkpoint_lsn_at_startup= lsn8korr(buffer + CONTROL_FILE_LSN_OFFSET); - last_logno_at_startup= uint4korr(buffer + CONTROL_FILE_FILENO_OFFSET); - return 0; + { + /* + TODO: what is the good way to report the error? Knowing that this + happens at startup, probably stderr. + */ + DBUG_PRINT("error", ("bad magic string")); + DBUG_RETURN(1); + } + last_checkpoint_lsn= lsn8korr(buffer + CONTROL_FILE_LSN_OFFSET); + last_logno= uint4korr(buffer + CONTROL_FILE_FILENO_OFFSET); + DBUG_RETURN(0); } -#define CONTROL_FILE_WRITE_ALL 0 /* write all 3 objects */ -#define CONTROL_FILE_WRITE_ONLY_LSN 1 -#define CONTROL_FILE_WRITE_ONLY_LOGNO 2 /* - Write information durably to the control file. + Write information durably to the control file; stores this information into + the last_checkpoint_lsn and last_logno global variables. SYNOPSIS - control_file_write_and_force() + ma_control_file_write_and_force() checkpoint_lsn LSN of last checkpoint - log_no last log file number + logno last log file number objs_to_write what we should write Called when we have created a new log (after syncing this log's creation) @@ -163,38 +172,44 @@ int control_file_create_or_open() 1 - Error */ -int control_file_write_and_force(LSN *checkpoint_lsn, uint32 log_no, +int ma_control_file_write_and_force(const LSN *checkpoint_lsn, uint32 logno, uint objs_to_write) { char buffer[CONTROL_FILE_MAX_SIZE]; uint start, size; + DBUG_ENTER("ma_control_file_write_and_force"); + memcpy(buffer + CONTROL_FILE_MAGIC_STRING_OFFSET, CONTROL_FILE_MAGIC_STRING, CONTROL_FILE_MAGIC_STRING_SIZE); /* write checkpoint LSN */ if (checkpoint_lsn) lsn8store(buffer + CONTROL_FILE_LSN_OFFSET, checkpoint_lsn); /* write logno */ - int4store(buffer + CONTROL_FILE_FILENO_OFFSET, log_no); + int4store(buffer + CONTROL_FILE_FILENO_OFFSET, logno); if (objs_to_write == CONTROL_FILE_WRITE_ALL) { start= CONTROL_FILE_MAGIC_STRING_OFFSET; size= CONTROL_FILE_MAX_SIZE; + last_checkpoint_lsn= *checkpoint_lsn; + last_logno= logno; } else if (objs_to_write == CONTROL_FILE_WRITE_ONLY_LSN) { start= CONTROL_FILE_LSN_OFFSET; size= CONTROL_FILE_LSN_SIZE; + last_checkpoint_lsn= *checkpoint_lsn; } else if (objs_to_write == CONTROL_FILE_WRITE_ONLY_LOGNO) { start= CONTROL_FILE_FILENO_OFFSET; size= CONTROL_FILE_FILENO_SIZE; + last_logno= logno; } else /* incorrect value of objs_to_write */ DBUG_ASSERT(0); - return (my_pwrite(control_file_fd, buffer + start, size, - start, MYF(MY_FNABP | MY_WME)) || - my_sync(control_file_fd, MYF(MY_WME))); + DBUG_RETURN(my_pwrite(control_file_fd, buffer + start, size, + start, MYF(MY_FNABP | MY_WME)) || + my_sync(control_file_fd, MYF(MY_WME))); } @@ -202,10 +217,18 @@ int control_file_write_and_force(LSN *checkpoint_lsn, uint32 log_no, Free resources taken by control file subsystem SYNOPSIS - control_file_end() + ma_control_file_end() */ -void control_file_end() +void ma_control_file_end() { + DBUG_ENTER("ma_control_file_end"); my_close(control_file_fd, MYF(MY_WME)); + /* + As this module owns these variables, closing the module forbids access to + them (just a safety): + */ + last_checkpoint_lsn= CONTROL_FILE_IMPOSSIBLE_LSN; + last_logno= CONTROL_FILE_IMPOSSIBLE_FILENO; + DBUG_VOID_RETURN; } diff --git a/storage/maria/ma_control_file.h b/storage/maria/ma_control_file.h new file mode 100644 index 00000000000..d081718b919 --- /dev/null +++ b/storage/maria/ma_control_file.h @@ -0,0 +1,75 @@ +/* + WL#3234 Maria control file + First version written by Guilhem Bichot on 2006-04-27. + Does not compile yet. +*/ + +#ifndef _control_file_h +#define _control_file_h + +/* + Not everybody needs to call the control file that's why control_file.h is + not in maria_def.h. However, policy or habit may want to change this. +*/ + +#ifndef REMOVE_WHEN_SANJA_PUSHES_LOG_HANDLER +/* + this is to get the control file to compile, until Sanja pushes the log + handler which will supersede those definitions. +*/ +typedef struct st_lsn { + uint32 file_no; + uint32 rec_offset; +} LSN; +#define maria_data_root "." +#endif + +/* + indicate absence of the log file number; first log is always number 1, 0 is + impossible. +*/ +#define CONTROL_FILE_IMPOSSIBLE_FILENO 0 +/* logs always have a header */ +#define CONTROL_FILE_IMPOSSIBLE_LOG_OFFSET 0 +/* + indicate absence of LSN. +*/ +#define CONTROL_FILE_IMPOSSIBLE_LSN ((LSN){CONTROL_FILE_IMPOSSIBLE_FILENO,CONTROL_FILE_IMPOSSIBLE_LOG_OFFSET}) + +/* Here is the interface of this module */ + +/* + LSN of the last checkoint + (if last_checkpoint_lsn.file_no == CONTROL_FILE_IMPOSSIBLE_FILENO + then there was never a checkpoint) +*/ +extern LSN last_checkpoint_lsn; +/* + Last log number (if last_logno == + CONTROL_FILE_IMPOSSIBLE_FILENO then there is no log file yet) +*/ +extern uint32 last_logno; + +/* + Looks for the control file. If absent, it's a fresh start, create file. + If present, read it to find out last checkpoint's LSN and last log. + Called at engine's start. +*/ +int ma_control_file_create_or_open(); + +/* + Write information durably to the control file. + Called when we have created a new log (after syncing this log's creation) + and when we have written a checkpoint (after syncing this log record). +*/ +#define CONTROL_FILE_WRITE_ALL 0 /* write all 3 objects */ +#define CONTROL_FILE_WRITE_ONLY_LSN 1 +#define CONTROL_FILE_WRITE_ONLY_LOGNO 2 +int ma_control_file_write_and_force(const LSN *checkpoint_lsn, uint32 logno, + uint objs_to_write); + + +/* Free resources taken by control file subsystem */ +void ma_control_file_end(); + +#endif diff --git a/storage/maria/ma_control_file_test.c b/storage/maria/ma_control_file_test.c new file mode 100644 index 00000000000..b3ba27c8e4b --- /dev/null +++ b/storage/maria/ma_control_file_test.c @@ -0,0 +1,290 @@ +/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Unit test of the control file module of the Maria engine */ + +/* TODO: make it fit the mytap framework */ + +/* + Note that it is not possible to test the durability of the write (can't + pull the plug programmatically :) +*/ + +#include "maria.h" +#include "ma_control_file.h" +#include <my_getopt.h> + +char file_name[FN_REFLEN]; +int fd= -1; + +static void clean_files(); +static void run_test_normal(); +static void run_test_abnormal(); +static void usage(); +static void get_options(int argc, char *argv[]); + +int main(int argc,char *argv[]) +{ + MY_INIT(argv[0]); + + get_options(argc,argv); + + clean_files(); + run_test_normal(); + run_test_abnormal(); + + exit(0); /* all ok, if some test failed, we will have aborted */ +} + +/* + Abort unless given expression is non-zero. + + SYNOPSIS + DIE_UNLESS(expr) + + DESCRIPTION + We can't use any kind of system assert as we need to + preserve tested invariants in release builds as well. + + NOTE + This is infamous copy-paste from mysql_client_test.c; + we should instead put it in some include in one single place. +*/ + +#define DIE_UNLESS(expr) \ + ((void) ((expr) ? 0 : (die(__FILE__, __LINE__, #expr), 0))) +#define DIE_IF(expr) \ + ((void) (!(expr) ? 0 : (die(__FILE__, __LINE__, #expr), 0))) +#define DIE(expr) \ + die(__FILE__, __LINE__, #expr) + +void die(const char *file, int line, const char *expr) +{ + fprintf(stderr, "%s:%d: check failed: '%s'\n", file, line, expr); + abort(); +} + + +static void clean_files() +{ + DIE_IF(fn_format(file_name, "control", maria_data_root, "", MYF(MY_WME)) == + NullS); + my_delete(file_name, MYF(0)); /* maybe file does not exist, ignore error */ +} + + +static void run_test_normal() +{ + LSN checkpoint_lsn; + uint32 logno; + uint objs_to_write; + uint i; + char buffer[4]; + + /* TEST0: Instance starts from scratch (control file does not exist) */ + DIE_UNLESS(ma_control_file_create_or_open() == 0); + /* Check that the module reports no information */ + DIE_UNLESS(last_logno == CONTROL_FILE_IMPOSSIBLE_FILENO); + DIE_UNLESS(last_checkpoint_lsn.file_no == CONTROL_FILE_IMPOSSIBLE_FILENO); + DIE_UNLESS(last_checkpoint_lsn.rec_offset == CONTROL_FILE_IMPOSSIBLE_LOG_OFFSET); + + /* TEST1: Simulate creation of one log */ + + objs_to_write= CONTROL_FILE_WRITE_ONLY_LOGNO; + logno= 123; + DIE_UNLESS(ma_control_file_write_and_force(NULL, logno, + objs_to_write) == 0); + /* Check that last_logno was updated */ + DIE_UNLESS(last_logno == logno); + /* Simulate shutdown */ + ma_control_file_end(); + /* Verify amnesia */ + DIE_UNLESS(last_logno == CONTROL_FILE_IMPOSSIBLE_FILENO); + DIE_UNLESS(last_checkpoint_lsn.file_no == CONTROL_FILE_IMPOSSIBLE_FILENO); + DIE_UNLESS(last_checkpoint_lsn.rec_offset == CONTROL_FILE_IMPOSSIBLE_LOG_OFFSET); + /* And restart */ + DIE_UNLESS(ma_control_file_create_or_open() == 0); + DIE_UNLESS(last_logno == logno); + + /* TEST2: Simulate creation of 5 logs */ + + objs_to_write= CONTROL_FILE_WRITE_ONLY_LOGNO; + logno= 100; + for (i= 0; i<5; i++) + { + logno*= 3; + DIE_UNLESS(ma_control_file_write_and_force(NULL, logno, + objs_to_write) == 0); + } + ma_control_file_end(); + DIE_UNLESS(last_logno == CONTROL_FILE_IMPOSSIBLE_FILENO); + DIE_UNLESS(last_checkpoint_lsn.file_no == CONTROL_FILE_IMPOSSIBLE_FILENO); + DIE_UNLESS(last_checkpoint_lsn.rec_offset == CONTROL_FILE_IMPOSSIBLE_LOG_OFFSET); + DIE_UNLESS(ma_control_file_create_or_open() == 0); + DIE_UNLESS(last_logno == logno); + + /* + TEST3: Simulate one checkpoint, one log creation, two checkpoints, one + log creation. + */ + + objs_to_write= CONTROL_FILE_WRITE_ONLY_LSN; + checkpoint_lsn= (LSN){5, 10000}; + logno= 10; + DIE_UNLESS(ma_control_file_write_and_force(&checkpoint_lsn, logno, + objs_to_write) == 0); + /* check that last_logno was not updated */ + DIE_UNLESS(last_logno != logno); + /* Check that last_checkpoint_lsn was updated */ + DIE_UNLESS(last_checkpoint_lsn.file_no == checkpoint_lsn.file_no); + DIE_UNLESS(last_checkpoint_lsn.rec_offset == checkpoint_lsn.rec_offset); + + objs_to_write= CONTROL_FILE_WRITE_ONLY_LOGNO; + checkpoint_lsn= (LSN){5, 20000}; + logno= 17; + DIE_UNLESS(ma_control_file_write_and_force(&checkpoint_lsn, logno, + objs_to_write) == 0); + /* Check that checkpoint LSN was not updated */ + DIE_UNLESS(last_checkpoint_lsn.rec_offset != checkpoint_lsn.rec_offset); + objs_to_write= CONTROL_FILE_WRITE_ONLY_LSN; + checkpoint_lsn= (LSN){17, 20000}; + DIE_UNLESS(ma_control_file_write_and_force(&checkpoint_lsn, logno, + objs_to_write) == 0); + objs_to_write= CONTROL_FILE_WRITE_ONLY_LSN; + checkpoint_lsn= (LSN){17, 45000}; + DIE_UNLESS(ma_control_file_write_and_force(&checkpoint_lsn, logno, + objs_to_write) == 0); + objs_to_write= CONTROL_FILE_WRITE_ONLY_LOGNO; + logno= 19; + DIE_UNLESS(ma_control_file_write_and_force(&checkpoint_lsn, logno, + objs_to_write) == 0); + + ma_control_file_end(); + DIE_UNLESS(last_logno == CONTROL_FILE_IMPOSSIBLE_FILENO); + DIE_UNLESS(last_checkpoint_lsn.file_no == CONTROL_FILE_IMPOSSIBLE_FILENO); + DIE_UNLESS(last_checkpoint_lsn.rec_offset == CONTROL_FILE_IMPOSSIBLE_LOG_OFFSET); + DIE_UNLESS(ma_control_file_create_or_open() == 0); + DIE_UNLESS(last_logno == logno); + DIE_UNLESS(last_checkpoint_lsn.file_no == checkpoint_lsn.file_no); + DIE_UNLESS(last_checkpoint_lsn.rec_offset == checkpoint_lsn.rec_offset); + + /* + TEST4: actually check by ourselves the content of the file. + Note that constants (offsets) are hard-coded here, precisely to prevent + someone from changing them in the control file module and breaking + backward-compatibility. + */ + + DIE_IF((fd= my_open(file_name, + O_BINARY | O_RDWR, + MYF(MY_WME))) < 0); + DIE_IF(my_read(fd, buffer, 16, MYF(MY_FNABP | MY_WME)) != 0); + DIE_IF(my_close(fd, MYF(MY_WME)) != 0); + i= uint4korr(buffer+4); + DIE_UNLESS(i == last_checkpoint_lsn.file_no); + i= uint4korr(buffer+8); + DIE_UNLESS(i == last_checkpoint_lsn.rec_offset); + i= uint4korr(buffer+12); + DIE_UNLESS(i == last_logno); + + + /* TEST5: Simulate stop/start/nothing/stop/start */ + + ma_control_file_end(); + DIE_UNLESS(last_logno == CONTROL_FILE_IMPOSSIBLE_FILENO); + DIE_UNLESS(ma_control_file_create_or_open() == 0); + ma_control_file_end(); + DIE_UNLESS(last_logno == CONTROL_FILE_IMPOSSIBLE_FILENO); + DIE_UNLESS(ma_control_file_create_or_open() == 0); + DIE_UNLESS(last_logno == logno); + DIE_UNLESS(last_checkpoint_lsn.file_no == checkpoint_lsn.file_no); + DIE_UNLESS(last_checkpoint_lsn.rec_offset == checkpoint_lsn.rec_offset); + +} + +static void run_test_abnormal() +{ + /* Corrupt the control file */ + DIE_IF((fd= my_open(file_name, + O_BINARY | O_RDWR, + MYF(MY_WME))) < 0); + DIE_IF(my_write(fd, "papa", 4, MYF(MY_FNABP | MY_WME)) != 0); + DIE_IF(my_close(fd, MYF(MY_WME)) != 0); + + /* Check that control file module sees the problem */ + DIE_IF(ma_control_file_create_or_open() == 0); +} + + +static struct my_option my_long_options[] = +{ +#ifndef DBUG_OFF + {"debug", '#', "Debug log.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#endif + {"help", '?', "Display help and exit", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"version", 'V', "Print version number and exit", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} +}; + + +static void version() +{ + printf("ma_control_file_test: unit test for the control file " + "module of the Maria storage engine. Ver 1.0 \n"); +} + +static my_bool +get_one_option(int optid, const struct my_option *opt __attribute__((unused)), + char *argument) +{ + switch(optid) { + case 'V': + version(); + exit(0); + case '#': + DBUG_PUSH (argument); + break; + case '?': + version(); + usage(); + exit(0); + } + return 0; +} + + +/* Read options */ + +static void get_options(int argc, char *argv[]) +{ + int ho_error; + + if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option))) + exit(ho_error); + + return; +} /* get options */ + + +static void usage() +{ + printf("Usage: %s [options]\n\n", my_progname); + my_print_help(my_long_options); + my_print_variables(my_long_options); +} diff --git a/storage/maria/least_recently_dirtied.c b/storage/maria/ma_least_recently_dirtied.c index c6285fe47cd..c6285fe47cd 100644 --- a/storage/maria/least_recently_dirtied.c +++ b/storage/maria/ma_least_recently_dirtied.c diff --git a/storage/maria/least_recently_dirtied.h b/storage/maria/ma_least_recently_dirtied.h index 6a30db4b5f0..6a30db4b5f0 100644 --- a/storage/maria/least_recently_dirtied.h +++ b/storage/maria/ma_least_recently_dirtied.h diff --git a/storage/maria/recovery.c b/storage/maria/ma_recovery.c index babf7507ef1..babf7507ef1 100644 --- a/storage/maria/recovery.c +++ b/storage/maria/ma_recovery.c diff --git a/storage/maria/recovery.h b/storage/maria/ma_recovery.h index b85ffdeef59..b85ffdeef59 100644 --- a/storage/maria/recovery.h +++ b/storage/maria/ma_recovery.h |