Commit 722a8ebe authored by unknown's avatar unknown

Store maximum transaction id into control file at clean shutdown.

This can serve to maria_chk to check that trids found in rows and keys
are not too big. Also used by Recovery when logs are lost.
Options --require-control-file, --datadir, --log-dir (yes, the dashes are
inconsistent but I imitated mysqld --datadir and --maria-log-dir) for
maria_chk.
Lock control file _before_ reading its content.


storage/maria/ha_maria.cc:
  new prototype
storage/maria/ma_check.c:
  A function to find the max trid in the system (consults transaction
  manager and control file), to check tables.
storage/maria/ma_checkpoint.c:
  new prototype
storage/maria/ma_control_file.c:
  Store max trid into control file, in a backward-compatible way
  (can still read old control files).
  Parameter to ma_control_file_open(), to not create the log if it's
  missing (maria_chk needs that).
  Lock control file _before_ reading its content.
  Fix for a segfault when reading an old control file (bzero() with a
  negative second argument)
storage/maria/ma_control_file.h:
  changes to the control file module's API
storage/maria/ma_init.c:
  When Maria shuts down cleanly, store max trid into control file.
storage/maria/ma_loghandler.c:
  new prototype
storage/maria/ma_recovery.c:
  During recovery, consult max trid stored in control file, in case it is
  bigger than what we found in log (case of logs manually removed by user).
storage/maria/ma_test1.c:
  new prototype
storage/maria/ma_test2.c:
  new prototype
storage/maria/maria_chk.c:
  New option --require-control-file (abort if control file not found),
  --datadir (path for control file (and for logs if --log-dir not specified)),
  --log-dir (path for logs).
  Try to open control file when maria_chk starts.
storage/maria/maria_read_log.c:
  new prototype
storage/maria/trnman.c:
  A new function to know max trid in transaction manager
storage/maria/trnman_public.h:
  New function
storage/maria/unittest/ma_control_file-t.c:
  new prototypes. Testing storing and retrieving the max trid to/from
  control file
storage/maria/unittest/ma_test_loghandler-t.c:
  new prototype
storage/maria/unittest/ma_test_loghandler_first_lsn-t.c:
  new prototype
storage/maria/unittest/ma_test_loghandler_max_lsn-t.c:
  new prototype
storage/maria/unittest/ma_test_loghandler_multigroup-t.c:
  new prototype
storage/maria/unittest/ma_test_loghandler_multithread-t.c:
  new prototype
storage/maria/unittest/ma_test_loghandler_noflush-t.c:
  new prototype
storage/maria/unittest/ma_test_loghandler_nologs-t.c:
  new prototype
storage/maria/unittest/ma_test_loghandler_pagecache-t.c:
  new prototype
storage/maria/unittest/ma_test_loghandler_purge-t.c:
  new prototype
parent 42f970de
...@@ -2768,7 +2768,7 @@ static int ha_maria_init(void *p) ...@@ -2768,7 +2768,7 @@ static int ha_maria_init(void *p)
maria_hton->flags= HTON_CAN_RECREATE | HTON_SUPPORT_LOG_TABLES; maria_hton->flags= HTON_CAN_RECREATE | HTON_SUPPORT_LOG_TABLES;
bzero(maria_log_pagecache, sizeof(*maria_log_pagecache)); bzero(maria_log_pagecache, sizeof(*maria_log_pagecache));
maria_tmpdir= &mysql_tmpdir_list; /* For REDO */ maria_tmpdir= &mysql_tmpdir_list; /* For REDO */
res= maria_init() || ma_control_file_create_or_open() || res= maria_init() || ma_control_file_open(TRUE) ||
!init_pagecache(maria_pagecache, !init_pagecache(maria_pagecache,
(size_t) pagecache_buffer_size, pagecache_division_limit, (size_t) pagecache_buffer_size, pagecache_division_limit,
pagecache_age_threshold, maria_block_size, 0) || pagecache_age_threshold, maria_block_size, 0) ||
......
...@@ -98,6 +98,7 @@ static void report_keypage_fault(HA_CHECK *param, MARIA_HA *info, ...@@ -98,6 +98,7 @@ static void report_keypage_fault(HA_CHECK *param, MARIA_HA *info,
static my_bool create_new_data_handle(MARIA_SORT_PARAM *param, File new_file); static my_bool create_new_data_handle(MARIA_SORT_PARAM *param, File new_file);
static my_bool _ma_flush_table_files_before_swap(HA_CHECK *param, static my_bool _ma_flush_table_files_before_swap(HA_CHECK *param,
MARIA_HA *info); MARIA_HA *info);
static TrID max_trid_in_system(void);
void maria_chk_init(HA_CHECK *param) void maria_chk_init(HA_CHECK *param)
...@@ -6444,3 +6445,21 @@ static void report_keypage_fault(HA_CHECK *param, MARIA_HA *info, ...@@ -6444,3 +6445,21 @@ static void report_keypage_fault(HA_CHECK *param, MARIA_HA *info,
"error: %d", "error: %d",
llstr(position / block_size, buff), my_errno); llstr(position / block_size, buff), my_errno);
} }
/**
When we want to check a table, we verify that the transaction ids of rows
and keys are not bigger than the biggest id generated by Maria so far, which
is returned by the function below.
@note If control file is not open, 0 may be returned; to not confuse
this with a valid max trid of 0, the caller should notice that it failed to
open the control file (ma_control_file_inited() can serve for that).
*/
static TrID max_trid_in_system(void)
{
TrID id= trnman_get_max_trid(); /* 0 if transac manager not initialized */
/* 'id' may be far bigger, if last shutdown is old */
return max(id, max_trid_in_control_file);
}
...@@ -244,8 +244,8 @@ static int really_execute_checkpoint(void) ...@@ -244,8 +244,8 @@ static int really_execute_checkpoint(void)
such hook would be called before translog_flush (and we must be sure such hook would be called before translog_flush (and we must be sure
that log was flushed before we write to the control file). that log was flushed before we write to the control file).
*/ */
if (unlikely(ma_control_file_write_and_force(lsn, FILENO_IMPOSSIBLE, if (unlikely(ma_control_file_write_and_force(lsn, last_logno,
CONTROL_FILE_UPDATE_ONLY_LSN))) max_trid_in_control_file)))
{ {
translog_unlock(); translog_unlock();
goto err; goto err;
......
...@@ -77,8 +77,9 @@ one should increment the control file version number. ...@@ -77,8 +77,9 @@ one should increment the control file version number.
#define CF_LSN_SIZE LSN_STORE_SIZE #define CF_LSN_SIZE LSN_STORE_SIZE
#define CF_FILENO_OFFSET (CF_LSN_OFFSET + CF_LSN_SIZE) #define CF_FILENO_OFFSET (CF_LSN_OFFSET + CF_LSN_SIZE)
#define CF_FILENO_SIZE 4 #define CF_FILENO_SIZE 4
#define CF_MAX_TRID_OFFSET (CF_FILENO_OFFSET + CF_FILENO_SIZE)
#define CF_CHANGEABLE_TOTAL_SIZE (CF_FILENO_OFFSET + CF_FILENO_SIZE) #define CF_MAX_TRID_SIZE TRANSID_SIZE
#define CF_CHANGEABLE_TOTAL_SIZE (CF_MAX_TRID_OFFSET + CF_MAX_TRID_SIZE)
/* /*
The following values should not be changed, except when changing version The following values should not be changed, except when changing version
...@@ -100,6 +101,11 @@ one should increment the control file version number. ...@@ -100,6 +101,11 @@ one should increment the control file version number.
*/ */
LSN last_checkpoint_lsn= LSN_IMPOSSIBLE; LSN last_checkpoint_lsn= LSN_IMPOSSIBLE;
uint32 last_logno= FILENO_IMPOSSIBLE; uint32 last_logno= FILENO_IMPOSSIBLE;
/**
The maximum transaction id given to a transaction. It is only updated at
clean shutdown (in case of crash, logs have better information).
*/
TrID max_trid_in_control_file= 0;
/** /**
@brief If log's lock should be asserted when writing to control file. @brief If log's lock should be asserted when writing to control file.
...@@ -131,9 +137,6 @@ static CONTROL_FILE_ERROR create_control_file(const char *name, ...@@ -131,9 +137,6 @@ static CONTROL_FILE_ERROR create_control_file(const char *name,
uchar buffer[CF_CREATE_TIME_TOTAL_SIZE]; uchar buffer[CF_CREATE_TIME_TOTAL_SIZE];
DBUG_ENTER("maria_create_control_file"); DBUG_ENTER("maria_create_control_file");
/* in a recovery, we expect to find a control file */
if (maria_in_recovery)
DBUG_RETURN(CONTROL_FILE_MISSING);
if ((control_file_fd= my_create(name, 0, if ((control_file_fd= my_create(name, 0,
open_flags, open_flags,
MYF(MY_SYNC_DIR | MY_WME))) < 0) MYF(MY_SYNC_DIR | MY_WME))) < 0)
...@@ -164,7 +167,7 @@ static CONTROL_FILE_ERROR create_control_file(const char *name, ...@@ -164,7 +167,7 @@ static CONTROL_FILE_ERROR create_control_file(const char *name,
if (my_pwrite(control_file_fd, buffer, cf_create_time_size, if (my_pwrite(control_file_fd, buffer, cf_create_time_size,
0, MYF(MY_FNABP | MY_WME))) 0, MYF(MY_FNABP | MY_WME)))
DBUG_RETURN(1); DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
/* /*
To be safer we should make sure that there are no logs or data/index To be safer we should make sure that there are no logs or data/index
...@@ -184,10 +187,50 @@ static CONTROL_FILE_ERROR create_control_file(const char *name, ...@@ -184,10 +187,50 @@ static CONTROL_FILE_ERROR create_control_file(const char *name,
/* init the file with these "undefined" values */ /* init the file with these "undefined" values */
DBUG_RETURN(ma_control_file_write_and_force(LSN_IMPOSSIBLE, DBUG_RETURN(ma_control_file_write_and_force(LSN_IMPOSSIBLE,
FILENO_IMPOSSIBLE, FILENO_IMPOSSIBLE, 0));
CONTROL_FILE_UPDATE_ALL)); }
/**
Locks control file exclusively. This is kept for the duration of the engine
process, to prevent another Maria instance to write to our logs or control
file.
*/
static int lock_control_file(const char *name)
{
uint retry= 0;
/*
On Windows, my_lock() uses locking() which is mandatory locking and so
prevents maria-recovery.test from copying the control file. And in case of
crash, it may take a while for Windows to unlock file, causing downtime.
*/
/**
@todo BUG We should explore my_sopen(_SH_DENYWRD) to open or create the
file under Windows.
*/
#ifndef __WIN__
/*
We can't here use the automatic wait in my_lock() as the alarm thread
may not yet exists.
*/
while (my_lock(control_file_fd, F_WRLCK, 0L, F_TO_EOF,
MYF(MY_SEEK_NOT_DONE | MY_FORCE_LOCK | MY_NO_WAIT)))
{
if (retry == 0)
my_printf_error(HA_ERR_INITIALIZATION,
"Can't lock maria control file '%s' for exclusive use, "
"error: %d. Will retry for %d seconds", 0,
name, my_errno, MARIA_MAX_CONTROL_FILE_LOCK_RETRY);
if (retry++ > MARIA_MAX_CONTROL_FILE_LOCK_RETRY)
return 1;
sleep(1);
}
#endif
return 0;
} }
/* /*
@brief Initialize control file subsystem @brief Initialize control file subsystem
...@@ -200,24 +243,24 @@ static CONTROL_FILE_ERROR create_control_file(const char *name, ...@@ -200,24 +243,24 @@ static CONTROL_FILE_ERROR create_control_file(const char *name,
The format of the control file is defined in the comments and defines The format of the control file is defined in the comments and defines
at the start of this file. at the start of this file.
@note If in recovery, file is not created @param create_if_missing create file if not found
@return Operation status @return Operation status
@retval 0 OK @retval 0 OK
@retval 1 Error (in which case the file is left closed) @retval 1 Error (in which case the file is left closed)
*/ */
CONTROL_FILE_ERROR ma_control_file_create_or_open() CONTROL_FILE_ERROR ma_control_file_open(my_bool create_if_missing)
{ {
uchar buffer[CF_MAX_SIZE]; uchar buffer[CF_MAX_SIZE];
char name[FN_REFLEN], errmsg_buff[256]; char name[FN_REFLEN], errmsg_buff[256];
const char *errmsg; const char *errmsg, *lock_failed_errmsg= "Could not get an exclusive lock;"
" file is probably in use by another process";
uint new_cf_create_time_size, new_cf_changeable_size, new_block_size; uint new_cf_create_time_size, new_cf_changeable_size, new_block_size;
uint retry;
my_off_t file_size; my_off_t file_size;
int open_flags= O_BINARY | /*O_DIRECT |*/ O_RDWR; int open_flags= O_BINARY | /*O_DIRECT |*/ O_RDWR;
int error= CONTROL_FILE_UNKNOWN_ERROR; int error= CONTROL_FILE_UNKNOWN_ERROR;
DBUG_ENTER("ma_control_file_create_or_open"); DBUG_ENTER("ma_control_file_open");
/* /*
If you change sizes in the #defines, you at least have to change the If you change sizes in the #defines, you at least have to change the
...@@ -236,12 +279,25 @@ CONTROL_FILE_ERROR ma_control_file_create_or_open() ...@@ -236,12 +279,25 @@ CONTROL_FILE_ERROR ma_control_file_create_or_open()
if (my_access(name,F_OK)) if (my_access(name,F_OK))
{ {
if (create_control_file(name, open_flags)) CONTROL_FILE_ERROR create_error;
if (!create_if_missing)
{ {
error= CONTROL_FILE_MISSING;
errmsg= "Can't find file";
goto err;
}
if ((create_error= create_control_file(name, open_flags)))
{
error= create_error;
errmsg= "Can't create file"; errmsg= "Can't create file";
goto err; goto err;
} }
goto lock_file; if (lock_control_file(name))
{
errmsg= lock_failed_errmsg;
goto err;
}
goto ok;
} }
/* Otherwise, file exists */ /* Otherwise, file exists */
...@@ -252,6 +308,12 @@ CONTROL_FILE_ERROR ma_control_file_create_or_open() ...@@ -252,6 +308,12 @@ CONTROL_FILE_ERROR ma_control_file_create_or_open()
goto err; goto err;
} }
if (lock_control_file(name)) /* lock it before reading content */
{
errmsg= lock_failed_errmsg;
goto err;
}
file_size= my_seek(control_file_fd, 0, SEEK_END, MYF(MY_WME)); file_size= my_seek(control_file_fd, 0, SEEK_END, MYF(MY_WME));
if (file_size == MY_FILEPOS_ERROR) if (file_size == MY_FILEPOS_ERROR)
{ {
...@@ -343,7 +405,7 @@ CONTROL_FILE_ERROR ma_control_file_create_or_open() ...@@ -343,7 +405,7 @@ CONTROL_FILE_ERROR ma_control_file_create_or_open()
uint4korr(buffer + new_cf_create_time_size)) uint4korr(buffer + new_cf_create_time_size))
{ {
error= CONTROL_FILE_BAD_CHECKSUM; error= CONTROL_FILE_BAD_CHECKSUM;
errmsg= "Changeable part (end of control file) checksum missmatch"; errmsg= "Changeable part (end of control file) checksum mismatch";
goto err; goto err;
} }
...@@ -353,57 +415,29 @@ CONTROL_FILE_ERROR ma_control_file_create_or_open() ...@@ -353,57 +415,29 @@ CONTROL_FILE_ERROR ma_control_file_create_or_open()
last_checkpoint_lsn= lsn_korr(buffer + new_cf_create_time_size + last_checkpoint_lsn= lsn_korr(buffer + new_cf_create_time_size +
CF_LSN_OFFSET); CF_LSN_OFFSET);
last_logno= uint4korr(buffer + new_cf_create_time_size + CF_FILENO_OFFSET); last_logno= uint4korr(buffer + new_cf_create_time_size + CF_FILENO_OFFSET);
if (new_cf_changeable_size >= (CF_MAX_TRID_OFFSET + CF_MAX_TRID_SIZE))
max_trid_in_control_file=
transid_korr(buffer + new_cf_create_time_size + CF_MAX_TRID_OFFSET);
lock_file: ok:
retry= 0;
/*
On Windows, my_lock() uses locking() which is mandatory locking and so
prevents maria-recovery.test from copying the control file. And in case of
crash, it may take a while for Windows to unlock file, causing downtime.
*/
/**
@todo BUG We should explore my_sopen(_SH_DENYWRD) to open or create the
file under Windows.
*/
#ifndef __WIN__
/*
We can't here use the automatic wait in my_lock() as the alarm thread
may not yet exists.
*/
while (my_lock(control_file_fd, F_WRLCK, 0L, F_TO_EOF,
MYF(MY_SEEK_NOT_DONE | MY_FORCE_LOCK | MY_NO_WAIT)))
{
if (retry == 0)
my_printf_error(HA_ERR_INITIALIZATION,
"Can't lock maria control file '%s' for exclusive use, "
"error: %d. Will retry for %d seconds", 0,
name, my_errno, MARIA_MAX_CONTROL_FILE_LOCK_RETRY);
if (retry++ > MARIA_MAX_CONTROL_FILE_LOCK_RETRY)
{
errmsg= "Could not get an exclusive lock; file is probably in use by another process";
goto err;
}
sleep(1);
}
#endif
DBUG_RETURN(0); DBUG_RETURN(0);
err: err:
my_printf_error(HA_ERR_INITIALIZATION, my_printf_error(HA_ERR_INITIALIZATION,
"Error when trying to use maria control file '%s': %s", 0, "Error when trying to use maria control file '%s': %s", 0,
name, errmsg); name, errmsg);
ma_control_file_end(); ma_control_file_end(); /* will unlock file if needed */
DBUG_RETURN(error); DBUG_RETURN(error);
} }
/* /*
Write information durably to the control file; stores this information into Write information durably to the control file; stores this information into
the last_checkpoint_lsn and last_logno global variables. the last_checkpoint_lsn, last_logno, max_trid_in_control_file global
Called when we have created a new log (after syncing this log's creation) variables.
and when we have written a checkpoint (after syncing this log record). Called when we have created a new log (after syncing this log's creation),
when we have written a checkpoint (after syncing this log record), and at
shutdown (for storing trid in case logs are soon removed by user).
Variables last_checkpoint_lsn and last_logno must be protected by caller Variables last_checkpoint_lsn and last_logno must be protected by caller
using log's lock, unless this function is called at startup. using log's lock, unless this function is called at startup.
...@@ -411,13 +445,7 @@ CONTROL_FILE_ERROR ma_control_file_create_or_open() ...@@ -411,13 +445,7 @@ CONTROL_FILE_ERROR ma_control_file_create_or_open()
ma_control_file_write_and_force() ma_control_file_write_and_force()
checkpoint_lsn LSN of last checkpoint checkpoint_lsn LSN of last checkpoint
logno last log file number logno last log file number
objs_to_write which of the arguments should be used as new values trid maximum transaction longid.
(for example, CF_UPDATE_ONLY_LSN will not
write the logno argument to the control file and will
not update the last_logno global variable); can be:
CF_UPDATE_ALL
CF_UPDATE_ONLY_LSN
CF_UPDATE_ONLY_LOGNO.
NOTE NOTE
We always want to do one single my_pwrite() here to be as atomic as We always want to do one single my_pwrite() here to be as atomic as
...@@ -428,11 +456,10 @@ CONTROL_FILE_ERROR ma_control_file_create_or_open() ...@@ -428,11 +456,10 @@ CONTROL_FILE_ERROR ma_control_file_create_or_open()
1 - Error 1 - Error
*/ */
int ma_control_file_write_and_force(const LSN checkpoint_lsn, uint32 logno, int ma_control_file_write_and_force(LSN checkpoint_lsn, uint32 logno,
uint objs_to_write) TrID trid)
{ {
char buffer[CF_MAX_SIZE]; uchar buffer[CF_MAX_SIZE];
my_bool update_checkpoint_lsn= FALSE, update_logno= FALSE;
uint32 sum; uint32 sum;
DBUG_ENTER("ma_control_file_write_and_force"); DBUG_ENTER("ma_control_file_write_and_force");
...@@ -442,48 +469,38 @@ int ma_control_file_write_and_force(const LSN checkpoint_lsn, uint32 logno, ...@@ -442,48 +469,38 @@ int ma_control_file_write_and_force(const LSN checkpoint_lsn, uint32 logno,
translog_lock_handler_assert_owner(); translog_lock_handler_assert_owner();
#endif #endif
if (objs_to_write == CONTROL_FILE_UPDATE_ONLY_LSN) lsn_store(buffer + CF_LSN_OFFSET, checkpoint_lsn);
update_checkpoint_lsn= TRUE; int4store(buffer + CF_FILENO_OFFSET, logno);
else if (objs_to_write == CONTROL_FILE_UPDATE_ONLY_LOGNO) transid_store(buffer + CF_MAX_TRID_OFFSET, trid);
update_logno= TRUE;
else if (objs_to_write == CONTROL_FILE_UPDATE_ALL)
update_checkpoint_lsn= update_logno= TRUE;
else /* incorrect value of objs_to_write */
DBUG_ASSERT(0);
if (update_checkpoint_lsn)
lsn_store(buffer + CF_LSN_OFFSET, checkpoint_lsn);
else /* store old value == change nothing */
lsn_store(buffer + CF_LSN_OFFSET, last_checkpoint_lsn);
if (update_logno)
int4store(buffer + CF_FILENO_OFFSET, logno);
else
int4store(buffer + CF_FILENO_OFFSET, last_logno);
/* /*
Clear unknown part of changeable part. Clear unknown part of changeable part, if bigger than ours.
Other option would be to remember the original values in the file Other option would be to remember the original values in the file
and copy them here, but this should be safer. and copy them here, but this should be safer.
*/ */
bzero(buffer + CF_CHANGEABLE_TOTAL_SIZE, if (cf_changeable_size > CF_CHANGEABLE_TOTAL_SIZE)
cf_changeable_size - CF_CHANGEABLE_TOTAL_SIZE); bzero(buffer + CF_CHANGEABLE_TOTAL_SIZE,
cf_changeable_size - CF_CHANGEABLE_TOTAL_SIZE);
/* Checksum is stored first */ /* Checksum is stored first */
compile_time_assert(CF_CHECKSUM_OFFSET == 0); compile_time_assert(CF_CHECKSUM_OFFSET == 0);
sum= my_checksum(0, (const uchar *) buffer + CF_CHECKSUM_SIZE, sum= my_checksum(0, buffer + CF_CHECKSUM_SIZE,
cf_changeable_size - CF_CHECKSUM_SIZE); cf_changeable_size - CF_CHECKSUM_SIZE);
int4store(buffer, sum); int4store(buffer, sum);
if (my_pwrite(control_file_fd, (uchar *) buffer, cf_changeable_size, /**
@todo BUG by reusing the cf_changeable_size of the old control file (from
an old server), it does not write the new parts featured by the running
server (like max_trid), is it expected?
*/
if (my_pwrite(control_file_fd, buffer, cf_changeable_size,
cf_create_time_size, MYF(MY_FNABP | MY_WME)) || cf_create_time_size, MYF(MY_FNABP | MY_WME)) ||
my_sync(control_file_fd, MYF(MY_WME))) my_sync(control_file_fd, MYF(MY_WME)))
DBUG_RETURN(1); DBUG_RETURN(1);
if (update_checkpoint_lsn) last_checkpoint_lsn= checkpoint_lsn;
last_checkpoint_lsn= checkpoint_lsn; last_logno= logno;
if (update_logno) max_trid_in_control_file= trid;
last_logno= logno;
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -496,7 +513,7 @@ int ma_control_file_write_and_force(const LSN checkpoint_lsn, uint32 logno, ...@@ -496,7 +513,7 @@ int ma_control_file_write_and_force(const LSN checkpoint_lsn, uint32 logno,
ma_control_file_end() ma_control_file_end()
*/ */
int ma_control_file_end() int ma_control_file_end(void)
{ {
int close_error; int close_error;
DBUG_ENTER("ma_control_file_end"); DBUG_ENTER("ma_control_file_end");
...@@ -521,8 +538,19 @@ int ma_control_file_end() ...@@ -521,8 +538,19 @@ int ma_control_file_end()
*/ */
last_checkpoint_lsn= LSN_IMPOSSIBLE; last_checkpoint_lsn= LSN_IMPOSSIBLE;
last_logno= FILENO_IMPOSSIBLE; last_logno= FILENO_IMPOSSIBLE;
max_trid_in_control_file= 0;
DBUG_RETURN(close_error); DBUG_RETURN(close_error);
} }
/**
Tells if control file is initialized.
*/
my_bool ma_control_file_inited(void)
{
return (control_file_fd >= 0);
}
#endif /* EXTRACT_DEFINITIONS */ #endif /* EXTRACT_DEFINITIONS */
...@@ -42,6 +42,8 @@ extern LSN last_checkpoint_lsn; ...@@ -42,6 +42,8 @@ extern LSN last_checkpoint_lsn;
*/ */
extern uint32 last_logno; extern uint32 last_logno;
extern TrID max_trid_in_control_file;
extern my_bool maria_multi_threaded, maria_in_recovery; extern my_bool maria_multi_threaded, maria_in_recovery;
typedef enum enum_control_file_error { typedef enum enum_control_file_error {
...@@ -58,33 +60,10 @@ typedef enum enum_control_file_error { ...@@ -58,33 +60,10 @@ typedef enum enum_control_file_error {
CONTROL_FILE_UNKNOWN_ERROR /* any other error */ CONTROL_FILE_UNKNOWN_ERROR /* any other error */
} CONTROL_FILE_ERROR; } CONTROL_FILE_ERROR;
#define CONTROL_FILE_UPDATE_ALL 0 C_MODE_START
#define CONTROL_FILE_UPDATE_ONLY_LSN 1 CONTROL_FILE_ERROR ma_control_file_open(my_bool create_if_missing);
#define CONTROL_FILE_UPDATE_ONLY_LOGNO 2 int ma_control_file_write_and_force(LSN checkpoint_lsn, uint32 logno, TrID trid);
int ma_control_file_end(void);
#ifdef __cplusplus my_bool ma_control_file_inited(void);
extern "C" { C_MODE_END
#endif
/*
Looks for the control file. If none and creation was requested, creates file.
If present, reads it to find out last checkpoint's LSN and last log.
Called at engine's start.
*/
CONTROL_FILE_ERROR 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).
*/
int ma_control_file_write_and_force(const LSN checkpoint_lsn, uint32 logno,
uint objs_to_write);
/* Free resources taken by control file subsystem */
int ma_control_file_end();
#ifdef __cplusplus
}
#endif
#endif #endif
...@@ -52,9 +52,20 @@ void maria_end(void) ...@@ -52,9 +52,20 @@ void maria_end(void)
{ {
if (maria_inited) if (maria_inited)
{ {
TrID trid;
maria_inited= maria_multi_threaded= FALSE; maria_inited= maria_multi_threaded= FALSE;
ft_free_stopwords(); ft_free_stopwords();
ma_checkpoint_end(); ma_checkpoint_end();
if (ma_control_file_inited() &&
((trid= trnman_get_max_trid()) > max_trid_in_control_file))
{
/*
Store max transaction id into control file, in case logs are removed
by user, or maria_chk wants to check tables (it cannot access max trid
from the log, as it cannot process REDOs).
*/
ma_control_file_write_and_force(last_checkpoint_lsn, last_logno, trid);
}
trnman_destroy(); trnman_destroy();
if (translog_status == TRANSLOG_OK) if (translog_status == TRANSLOG_OK)
translog_destroy(); translog_destroy();
......
...@@ -1519,8 +1519,8 @@ static my_bool translog_create_new_file() ...@@ -1519,8 +1519,8 @@ static my_bool translog_create_new_file()
if (translog_write_file_header()) if (translog_write_file_header())
DBUG_RETURN(1); DBUG_RETURN(1);
if (ma_control_file_write_and_force(LSN_IMPOSSIBLE, file_no, if (ma_control_file_write_and_force(last_checkpoint_lsn, file_no,
CONTROL_FILE_UPDATE_ONLY_LOGNO)) max_trid_in_control_file))
{ {
translog_stop_writing(); translog_stop_writing();
DBUG_RETURN(1); DBUG_RETURN(1);
...@@ -3697,7 +3697,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3697,7 +3697,7 @@ my_bool translog_init_with_table(const char *directory,
log_descriptor.open_files.elements); log_descriptor.open_files.elements);
if (ma_control_file_write_and_force(checkpoint_lsn, start_file_num, if (ma_control_file_write_and_force(checkpoint_lsn, start_file_num,
CONTROL_FILE_UPDATE_ALL)) max_trid_in_control_file))
DBUG_RETURN(1); DBUG_RETURN(1);
/* assign buffer 0 */ /* assign buffer 0 */
translog_start_buffer(log_descriptor.buffers, &log_descriptor.bc, 0); translog_start_buffer(log_descriptor.buffers, &log_descriptor.bc, 0);
......
...@@ -2509,6 +2509,14 @@ static uint end_of_redo_phase(my_bool prepare_for_undo_phase) ...@@ -2509,6 +2509,14 @@ static uint end_of_redo_phase(my_bool prepare_for_undo_phase)
llstr(max_long_trid, llbuf); llstr(max_long_trid, llbuf);
tprint(tracef, "Maximum transaction long id seen: %s\n", llbuf); tprint(tracef, "Maximum transaction long id seen: %s\n", llbuf);
llstr(max_trid_in_control_file, llbuf);
tprint(tracef, "Maximum transaction long id seen in control file: %s\n",
llbuf);
/*
If logs were deleted, or lost, trid in control file is needed to set
trnman's generator:
*/
set_if_bigger(max_long_trid, max_trid_in_control_file);
if (prepare_for_undo_phase && trnman_init(max_long_trid)) if (prepare_for_undo_phase && trnman_init(max_long_trid))
return -1; return -1;
......
...@@ -77,7 +77,7 @@ int main(int argc,char *argv[]) ...@@ -77,7 +77,7 @@ int main(int argc,char *argv[])
if (maria_init() || if (maria_init() ||
(init_pagecache(maria_pagecache, maria_block_size * 16, 0, 0, (init_pagecache(maria_pagecache, maria_block_size * 16, 0, 0,
maria_block_size, MY_WME) == 0) || maria_block_size, MY_WME) == 0) ||
ma_control_file_create_or_open() || ma_control_file_open(TRUE) ||
(init_pagecache(maria_log_pagecache, (init_pagecache(maria_log_pagecache,
TRANSLOG_PAGECACHE_SIZE, 0, 0, TRANSLOG_PAGECACHE_SIZE, 0, 0,
TRANSLOG_PAGE_SIZE, MY_WME) == 0) || TRANSLOG_PAGE_SIZE, MY_WME) == 0) ||
......
...@@ -83,7 +83,7 @@ int main(int argc, char *argv[]) ...@@ -83,7 +83,7 @@ int main(int argc, char *argv[])
if (maria_init() || if (maria_init() ||
(init_pagecache(maria_pagecache, pagecache_size, 0, 0, (init_pagecache(maria_pagecache, pagecache_size, 0, 0,
maria_block_size, MY_WME) == 0) || maria_block_size, MY_WME) == 0) ||
ma_control_file_create_or_open() || ma_control_file_open(TRUE) ||
(init_pagecache(maria_log_pagecache, (init_pagecache(maria_log_pagecache,
TRANSLOG_PAGECACHE_SIZE, 0, 0, TRANSLOG_PAGECACHE_SIZE, 0, 0,
TRANSLOG_PAGE_SIZE, MY_WME) == 0) || TRANSLOG_PAGE_SIZE, MY_WME) == 0) ||
......
...@@ -37,11 +37,11 @@ SET_STACK_SIZE(9000) /* Minimum stack size for program */ ...@@ -37,11 +37,11 @@ SET_STACK_SIZE(9000) /* Minimum stack size for program */
static uint decode_bits; static uint decode_bits;
static char **default_argv; static char **default_argv;
static const char *load_default_groups[]= { "maria_chk", 0 }; static const char *load_default_groups[]= { "maria_chk", 0 };
static const char *set_collation_name, *opt_tmpdir; static const char *set_collation_name, *opt_tmpdir, *opt_log_dir;
static CHARSET_INFO *set_collation; static CHARSET_INFO *set_collation;
static int stopwords_inited= 0; static int stopwords_inited= 0;
static MY_TMPDIR maria_chk_tmpdir; static MY_TMPDIR maria_chk_tmpdir;
static my_bool opt_transaction_logging, opt_debug; static my_bool opt_transaction_logging, opt_debug, opt_require_control_file;
static const char *type_names[]= static const char *type_names[]=
{ {
...@@ -97,7 +97,7 @@ int main(int argc, char **argv) ...@@ -97,7 +97,7 @@ int main(int argc, char **argv)
int error; int error;
MY_INIT(argv[0]); MY_INIT(argv[0]);
maria_data_root= (char *)"."; opt_log_dir= maria_data_root= (char *)".";
maria_chk_init(&check_param); maria_chk_init(&check_param);
check_param.opt_lock_memory= 1; /* Lock memory if possible */ check_param.opt_lock_memory= 1; /* Lock memory if possible */
check_param.using_global_keycache = 0; check_param.using_global_keycache = 0;
...@@ -110,20 +110,30 @@ int main(int argc, char **argv) ...@@ -110,20 +110,30 @@ int main(int argc, char **argv)
If we are doing a repair, user may want to store this repair into the log If we are doing a repair, user may want to store this repair into the log
so that the log has a complete history and can be used to replay. so that the log has a complete history and can be used to replay.
*/ */
if (opt_transaction_logging && (check_param.testflag & T_REP_ANY) && if (opt_transaction_logging && (check_param.testflag & T_REP_ANY))
(ma_control_file_create_or_open() ||
init_pagecache(maria_log_pagecache,
TRANSLOG_PAGECACHE_SIZE, 0, 0,
TRANSLOG_PAGE_SIZE, MY_WME) == 0 ||
translog_init(maria_data_root, TRANSLOG_FILE_SIZE,
0, 0, maria_log_pagecache,
TRANSLOG_DEFAULT_FLAGS, 0)))
{ {
_ma_check_print_error(&check_param, if (ma_control_file_open(FALSE) ||
"Can't initialize transaction logging. Run " init_pagecache(maria_log_pagecache,
"recovery with switch --skip-transaction-log"); TRANSLOG_PAGECACHE_SIZE, 0, 0,
error= 1; TRANSLOG_PAGE_SIZE, MY_WME) == 0 ||
argc= 1; /* Force loop out */ translog_init(opt_log_dir, TRANSLOG_FILE_SIZE,
0, 0, maria_log_pagecache,
TRANSLOG_DEFAULT_FLAGS, 0))
{
_ma_check_print_error(&check_param,
"Can't initialize transaction logging. Run "
"recovery with switch --skip-transaction-log");
error= 1;
goto end;
}
}
else
{
if (ma_control_file_open(FALSE) && opt_require_control_file)
{
error= 1;
goto end;
}
} }
while (--argc >= 0) while (--argc >= 0)
...@@ -156,6 +166,7 @@ int main(int argc, char **argv) ...@@ -156,6 +166,7 @@ int main(int argc, char **argv)
VOID(fflush(stdout)); VOID(fflush(stdout));
} }
} }
end:
if (check_param.total_files > 1) if (check_param.total_files > 1)
{ /* Only if descript */ { /* Only if descript */
char buff[22],buff2[22]; char buff[22],buff2[22];
...@@ -183,7 +194,8 @@ enum options_mc { ...@@ -183,7 +194,8 @@ enum options_mc {
OPT_SORT_KEY_BLOCKS, OPT_DECODE_BITS, OPT_FT_MIN_WORD_LEN, OPT_SORT_KEY_BLOCKS, OPT_DECODE_BITS, OPT_FT_MIN_WORD_LEN,
OPT_FT_MAX_WORD_LEN, OPT_FT_STOPWORD_FILE, OPT_FT_MAX_WORD_LEN, OPT_FT_STOPWORD_FILE,
OPT_MAX_RECORD_LENGTH, OPT_AUTO_CLOSE, OPT_STATS_METHOD, OPT_TRANSACTION_LOG, OPT_MAX_RECORD_LENGTH, OPT_AUTO_CLOSE, OPT_STATS_METHOD, OPT_TRANSACTION_LOG,
OPT_SKIP_SAFEMALLOC, OPT_ZEROFILL_KEEP_LSN OPT_SKIP_SAFEMALLOC, OPT_ZEROFILL_KEEP_LSN, OPT_REQUIRE_CONTROL_FILE,
OPT_LOG_DIR, OPT_DATADIR
}; };
static struct my_option my_long_options[] = static struct my_option my_long_options[] =
...@@ -249,6 +261,13 @@ static struct my_option my_long_options[] = ...@@ -249,6 +261,13 @@ static struct my_option my_long_options[] =
(uchar**) &check_param.keys_in_use, (uchar**) &check_param.keys_in_use,
(uchar**) &check_param.keys_in_use, (uchar**) &check_param.keys_in_use,
0, GET_ULL, REQUIRED_ARG, -1, 0, 0, 0, 0, 0}, 0, GET_ULL, REQUIRED_ARG, -1, 0, 0, 0, 0, 0},
{"datadir", OPT_DATADIR,
"Path for control file (and logs if --log-dir not used).",
(uchar**) &maria_data_root, 0, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0},
{"log-dir", OPT_LOG_DIR,
"Path for log files.",
(uchar**) &opt_log_dir, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"max-record-length", OPT_MAX_RECORD_LENGTH, {"max-record-length", OPT_MAX_RECORD_LENGTH,
"Skip rows bigger than this if maria_chk can't allocate memory to hold it", "Skip rows bigger than this if maria_chk can't allocate memory to hold it",
(uchar**) &check_param.max_record_length, (uchar**) &check_param.max_record_length,
...@@ -274,6 +293,10 @@ static struct my_option my_long_options[] = ...@@ -274,6 +293,10 @@ static struct my_option my_long_options[] =
{"sort-recover", 'n', {"sort-recover", 'n',
"Force recovering with sorting even if the temporary file was very big.", "Force recovering with sorting even if the temporary file was very big.",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{ "require-control-file", OPT_REQUIRE_CONTROL_FILE,
"Abort if cannot find control file",
(uchar**)&opt_require_control_file, 0, 0, GET_BOOL, NO_ARG,
0, 0, 0, 0, 0, 0},
#ifdef DEBUG #ifdef DEBUG
{"start-check-pos", OPT_START_CHECK_POS, {"start-check-pos", OPT_START_CHECK_POS,
"No help available.", "No help available.",
......
...@@ -56,7 +56,7 @@ int main(int argc, char **argv) ...@@ -56,7 +56,7 @@ int main(int argc, char **argv)
goto err; goto err;
} }
/* we don't want to create a control file, it MUST exist */ /* we don't want to create a control file, it MUST exist */
if (ma_control_file_create_or_open()) if (ma_control_file_open(FALSE))
{ {
fprintf(stderr, "Can't open control file (%d)\n", errno); fprintf(stderr, "Can't open control file (%d)\n", errno);
goto err; goto err;
......
...@@ -745,3 +745,18 @@ TRN *trnman_get_any_trn() ...@@ -745,3 +745,18 @@ TRN *trnman_get_any_trn()
TRN *trn= active_list_min.next; TRN *trn= active_list_min.next;
return (trn != &active_list_max) ? trn : NULL; return (trn != &active_list_max) ? trn : NULL;
} }
/**
Returns maximum transaction id given to a transaction so far.
*/
TrID trnman_get_max_trid()
{
TrID id;
if (short_trid_to_active_trn == NULL)
return 0;
pthread_mutex_lock(&LOCK_trn_list);
id= global_trid_generator;
pthread_mutex_unlock(&LOCK_trn_list);
return id;
}
...@@ -54,7 +54,8 @@ uint trnman_decrement_locked_tables(TRN *trn); ...@@ -54,7 +54,8 @@ uint trnman_decrement_locked_tables(TRN *trn);
uint trnman_has_locked_tables(TRN *trn); uint trnman_has_locked_tables(TRN *trn);
void trnman_reset_locked_tables(TRN *trn, uint locked_tables); void trnman_reset_locked_tables(TRN *trn, uint locked_tables);
TRN *trnman_recreate_trn_from_recovery(uint16 shortid, TrID longid); TRN *trnman_recreate_trn_from_recovery(uint16 shortid, TrID longid);
TRN *trnman_get_any_trn(); TRN *trnman_get_any_trn(void);
TrID trnman_get_max_trid(void);
#define TRANSID_SIZE 6 #define TRANSID_SIZE 6
#define transid_store(dst, id) int6store(dst,id) #define transid_store(dst, id) int6store(dst,id)
#define transid_korr(P) uint6korr(P) #define transid_korr(P) uint6korr(P)
......
...@@ -42,36 +42,39 @@ ...@@ -42,36 +42,39 @@
char file_name[FN_REFLEN]; char file_name[FN_REFLEN];
/* The values we'll set and expect the control file module to return */ /* The values we'll set and expect the control file module to return */
LSN expect_checkpoint_lsn; LSN expect_checkpoint_lsn;
uint32 expect_logno; uint32 expect_logno;
TrID expect_max_trid;
static int delete_file(myf my_flags); static int delete_file(myf my_flags);
/* /*
Those are test-specific wrappers around the module's API functions: after Those are test-specific wrappers around the module's API functions: after
calling the module's API functions they perform checks on the result. calling the module's API functions they perform checks on the result.
*/ */
static int close_file(); /* wraps ma_control_file_end */ static int close_file(void); /* wraps ma_control_file_end */
static int create_or_open_file(); /* wraps ma_control_file_open_or_create */ /* wraps ma_control_file_open_or_create */
static int write_file(); /* wraps ma_control_file_write_and_force */ static int open_file(void);
/* wraps ma_control_file_write_and_force */
static int write_file(LSN checkpoint_lsn, uint32 logno, TrID trid);
/* Tests */ /* Tests */
static int test_one_log(); static int test_one_log(void);
static int test_five_logs(); static int test_five_logs_and_max_trid(void);
static int test_3_checkpoints_and_2_logs(); static int test_3_checkpoints_and_2_logs(void);
static int test_binary_content(); static int test_binary_content(void);
static int test_start_stop(); static int test_start_stop(void);
static int test_2_open_and_2_close(); static int test_2_open_and_2_close(void);
static int test_bad_magic_string(); static int test_bad_magic_string(void);
static int test_bad_checksum(); static int test_bad_checksum(void);
static int test_bad_hchecksum(); static int test_bad_hchecksum(void);
static int test_future_size(); static int test_future_size(void);
static int test_bad_blocksize(); static int test_bad_blocksize(void);
static int test_bad_size(); static int test_bad_size(void);
/* Utility */ /* Utility */
static int verify_module_values_match_expected(); static int verify_module_values_match_expected(void);
static int verify_module_values_are_impossible(); static int verify_module_values_are_impossible(void);
static void usage(); static void usage(void);
static void get_options(int argc, char *argv[]); static void get_options(int argc, char *argv[]);
/* /*
...@@ -83,10 +86,10 @@ static void get_options(int argc, char *argv[]); ...@@ -83,10 +86,10 @@ static void get_options(int argc, char *argv[]);
*/ */
#define RET_ERR_UNLESS(expr) \ #define RET_ERR_UNLESS(expr) \
{if (!(expr)) {diag("line %d: failure: '%s'", __LINE__, #expr); return 1;}} {if (!(expr)) {diag("line %d: failure: '%s'", __LINE__, #expr); assert(0);return 1;}}
/* Used to ignore error messages from ma_control_file_create_or_open */ /* Used to ignore error messages from ma_control_file_open() */
static int my_ignore_message(uint error __attribute__((unused)), static int my_ignore_message(uint error __attribute__((unused)),
const char *str __attribute__((unused)), const char *str __attribute__((unused)),
...@@ -101,13 +104,13 @@ int (*default_error_handler_hook)(uint my_err, const char *str, ...@@ -101,13 +104,13 @@ int (*default_error_handler_hook)(uint my_err, const char *str,
myf MyFlags) = 0; myf MyFlags) = 0;
/* like ma_control_file_create_or_open(), but without error messages */ /* like ma_control_file_open(), but without error messages */
static CONTROL_FILE_ERROR local_ma_control_file_create_or_open(void) static CONTROL_FILE_ERROR local_ma_control_file_open(void)
{ {
CONTROL_FILE_ERROR error; CONTROL_FILE_ERROR error;
error_handler_hook= my_ignore_message; error_handler_hook= my_ignore_message;
error= ma_control_file_create_or_open(); error= ma_control_file_open(TRUE);
error_handler_hook= default_error_handler_hook; error_handler_hook= default_error_handler_hook;
return error; return error;
} }
...@@ -133,7 +136,8 @@ int main(int argc,char *argv[]) ...@@ -133,7 +136,8 @@ int main(int argc,char *argv[])
diag("Tests of normal conditions"); diag("Tests of normal conditions");
ok(0 == test_one_log(), "test of creating one log"); ok(0 == test_one_log(), "test of creating one log");
ok(0 == test_five_logs(), "test of creating five logs"); ok(0 == test_five_logs_and_max_trid(),
"test of creating five logs and many transactions");
ok(0 == test_3_checkpoints_and_2_logs(), ok(0 == test_3_checkpoints_and_2_logs(),
"test of creating three checkpoints and two logs"); "test of creating three checkpoints and two logs");
ok(0 == test_binary_content(), "test of the binary content of the file"); ok(0 == test_binary_content(), "test of the binary content of the file");
...@@ -163,19 +167,20 @@ static int delete_file(myf my_flags) ...@@ -163,19 +167,20 @@ static int delete_file(myf my_flags)
my_delete(file_name, my_flags); my_delete(file_name, my_flags);
expect_checkpoint_lsn= LSN_IMPOSSIBLE; expect_checkpoint_lsn= LSN_IMPOSSIBLE;
expect_logno= FILENO_IMPOSSIBLE; expect_logno= FILENO_IMPOSSIBLE;
expect_max_trid= 0;
return 0; return 0;
} }
/* /*
Verifies that global values last_checkpoint_lsn and last_logno (belonging Verifies that global values last_checkpoint_lsn, last_logno,
to the module) match what we expect. max_trid_in_control_file (belonging to the module) match what we expect.
*/ */
static int verify_module_values_match_expected() static int verify_module_values_match_expected(void)
{ {
RET_ERR_UNLESS(last_logno == expect_logno); RET_ERR_UNLESS(last_logno == expect_logno);
RET_ERR_UNLESS(last_checkpoint_lsn == RET_ERR_UNLESS(last_checkpoint_lsn == expect_checkpoint_lsn);
expect_checkpoint_lsn); RET_ERR_UNLESS(max_trid_in_control_file == expect_max_trid);
return 0; return 0;
} }
...@@ -184,16 +189,16 @@ static int verify_module_values_match_expected() ...@@ -184,16 +189,16 @@ static int verify_module_values_match_expected()
Verifies that global values last_checkpoint_lsn and last_logno (belonging Verifies that global values last_checkpoint_lsn and last_logno (belonging
to the module) are impossible (this is used when the file has been closed). to the module) are impossible (this is used when the file has been closed).
*/ */
static int verify_module_values_are_impossible() static int verify_module_values_are_impossible(void)
{ {
RET_ERR_UNLESS(last_logno == FILENO_IMPOSSIBLE); RET_ERR_UNLESS(last_logno == FILENO_IMPOSSIBLE);
RET_ERR_UNLESS(last_checkpoint_lsn == RET_ERR_UNLESS(last_checkpoint_lsn == LSN_IMPOSSIBLE);
LSN_IMPOSSIBLE); RET_ERR_UNLESS(max_trid_in_control_file == 0);
return 0; return 0;
} }
static int close_file() static int close_file(void)
{ {
/* Simulate shutdown */ /* Simulate shutdown */
ma_control_file_end(); ma_control_file_end();
...@@ -202,94 +207,81 @@ static int close_file() ...@@ -202,94 +207,81 @@ static int close_file()
return 0; return 0;
} }
static int create_or_open_file() static int open_file(void)
{ {
RET_ERR_UNLESS(local_ma_control_file_create_or_open() == CONTROL_FILE_OK); RET_ERR_UNLESS(local_ma_control_file_open() == CONTROL_FILE_OK);
/* Check that the module reports expected information */ /* Check that the module reports expected information */
RET_ERR_UNLESS(verify_module_values_match_expected() == 0); RET_ERR_UNLESS(verify_module_values_match_expected() == 0);
return 0; return 0;
} }
static int write_file(const LSN checkpoint_lsn, static int write_file(LSN checkpoint_lsn, uint32 logno, TrID trid)
uint32 logno,
uint objs_to_write)
{ {
RET_ERR_UNLESS(ma_control_file_write_and_force(checkpoint_lsn, logno, RET_ERR_UNLESS(ma_control_file_write_and_force(checkpoint_lsn, logno, trid)
objs_to_write) == 0); == 0);
/* Check that the module reports expected information */ /* Check that the module reports expected information */
RET_ERR_UNLESS(verify_module_values_match_expected() == 0); RET_ERR_UNLESS(verify_module_values_match_expected() == 0);
return 0; return 0;
} }
static int test_one_log() static int test_one_log(void)
{ {
uint objs_to_write; RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK);
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LOGNO;
expect_logno= 123; expect_logno= 123;
RET_ERR_UNLESS(write_file(LSN_IMPOSSIBLE, RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno,
expect_logno, max_trid_in_control_file) == 0);
objs_to_write) == 0);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
return 0; return 0;
} }
static int test_five_logs() static int test_five_logs_and_max_trid(void)
{ {
uint objs_to_write;
uint i; uint i;
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LOGNO;
expect_logno= 100; expect_logno= 100;
expect_max_trid= ULL(14111978111);
for (i= 0; i<5; i++) for (i= 0; i<5; i++)
{ {
expect_logno*= 3; expect_logno*= 3;
RET_ERR_UNLESS(write_file(LSN_IMPOSSIBLE, expect_logno, RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno,
objs_to_write) == 0); expect_max_trid) == 0);
} }
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
return 0; return 0;
} }
static int test_3_checkpoints_and_2_logs() static int test_3_checkpoints_and_2_logs(void)
{ {
uint objs_to_write;
/* /*
Simulate one checkpoint, one log creation, two checkpoints, one Simulate one checkpoint, one log creation, two checkpoints, one
log creation. log creation.
*/ */
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LSN;
expect_checkpoint_lsn= MAKE_LSN(5, 10000); expect_checkpoint_lsn= MAKE_LSN(5, 10000);
RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
expect_logno, objs_to_write) == 0); max_trid_in_control_file) == 0);
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LOGNO;
expect_logno= 17; expect_logno= 17;
RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
expect_logno, objs_to_write) == 0); max_trid_in_control_file) == 0);
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LSN;
expect_checkpoint_lsn= MAKE_LSN(17, 20000); expect_checkpoint_lsn= MAKE_LSN(17, 20000);
RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
expect_logno, objs_to_write) == 0); max_trid_in_control_file) == 0);
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LSN;
expect_checkpoint_lsn= MAKE_LSN(17, 45000); expect_checkpoint_lsn= MAKE_LSN(17, 45000);
RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
expect_logno, objs_to_write) == 0); max_trid_in_control_file) == 0);
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LOGNO;
expect_logno= 19; expect_logno= 19;
RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
expect_logno, objs_to_write) == 0); max_trid_in_control_file) == 0);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
return 0; return 0;
} }
static int test_binary_content() static int test_binary_content(void)
{ {
uint i; uint i;
int fd; int fd;
...@@ -310,7 +302,7 @@ static int test_binary_content() ...@@ -310,7 +302,7 @@ static int test_binary_content()
MYF(MY_WME))) >= 0); MYF(MY_WME))) >= 0);
RET_ERR_UNLESS(my_read(fd, buffer, 45, MYF(MY_FNABP | MY_WME)) == 0); RET_ERR_UNLESS(my_read(fd, buffer, 45, MYF(MY_FNABP | MY_WME)) == 0);
RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0); RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
i= uint3korr(buffer + 34 ); i= uint3korr(buffer + 34 );
RET_ERR_UNLESS(i == LSN_FILE_NO(last_checkpoint_lsn)); RET_ERR_UNLESS(i == LSN_FILE_NO(last_checkpoint_lsn));
i= uint4korr(buffer + 37); i= uint4korr(buffer + 37);
...@@ -321,35 +313,35 @@ static int test_binary_content() ...@@ -321,35 +313,35 @@ static int test_binary_content()
return 0; return 0;
} }
static int test_start_stop() static int test_start_stop(void)
{ {
/* TEST5: Simulate start/nothing/stop/start/nothing/stop/start */ /* TEST5: Simulate start/nothing/stop/start/nothing/stop/start */
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
return 0; return 0;
} }
static int test_2_open_and_2_close() static int test_2_open_and_2_close(void)
{ {
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
return 0; return 0;
} }
static int test_bad_magic_string() static int test_bad_magic_string(void)
{ {
uchar buffer[4]; uchar buffer[4];
int fd; int fd;
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
/* Corrupt magic string */ /* Corrupt magic string */
...@@ -361,22 +353,22 @@ static int test_bad_magic_string() ...@@ -361,22 +353,22 @@ static int test_bad_magic_string()
MYF(MY_FNABP | MY_WME)) == 0); MYF(MY_FNABP | MY_WME)) == 0);
/* Check that control file module sees the problem */ /* Check that control file module sees the problem */
RET_ERR_UNLESS(local_ma_control_file_create_or_open() == RET_ERR_UNLESS(local_ma_control_file_open() ==
CONTROL_FILE_BAD_MAGIC_STRING); CONTROL_FILE_BAD_MAGIC_STRING);
/* Restore magic string */ /* Restore magic string */
RET_ERR_UNLESS(my_pwrite(fd, buffer, 4, 0, MYF(MY_FNABP | MY_WME)) == 0); RET_ERR_UNLESS(my_pwrite(fd, buffer, 4, 0, MYF(MY_FNABP | MY_WME)) == 0);
RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0); RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
return 0; return 0;
} }
static int test_bad_checksum() static int test_bad_checksum(void)
{ {
uchar buffer[4]; uchar buffer[4];
int fd; int fd;
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
/* Corrupt checksum */ /* Corrupt checksum */
...@@ -387,7 +379,7 @@ static int test_bad_checksum() ...@@ -387,7 +379,7 @@ static int test_bad_checksum()
buffer[0]+= 3; /* mangle checksum */ buffer[0]+= 3; /* mangle checksum */
RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 30, MYF(MY_FNABP | MY_WME)) == 0); RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 30, MYF(MY_FNABP | MY_WME)) == 0);
/* Check that control file module sees the problem */ /* Check that control file module sees the problem */
RET_ERR_UNLESS(local_ma_control_file_create_or_open() == RET_ERR_UNLESS(local_ma_control_file_open() ==
CONTROL_FILE_BAD_CHECKSUM); CONTROL_FILE_BAD_CHECKSUM);
/* Restore checksum */ /* Restore checksum */
buffer[0]-= 3; buffer[0]-= 3;
...@@ -398,22 +390,22 @@ static int test_bad_checksum() ...@@ -398,22 +390,22 @@ static int test_bad_checksum()
} }
static int test_bad_blocksize() static int test_bad_blocksize(void)
{ {
maria_block_size<<= 1; maria_block_size<<= 1;
/* Check that control file module sees the problem */ /* Check that control file module sees the problem */
RET_ERR_UNLESS(local_ma_control_file_create_or_open() == RET_ERR_UNLESS(local_ma_control_file_open() ==
CONTROL_FILE_WRONG_BLOCKSIZE); CONTROL_FILE_WRONG_BLOCKSIZE);
/* Restore blocksize */ /* Restore blocksize */
maria_block_size>>= 1; maria_block_size>>= 1;
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
return 0; return 0;
} }
static int test_future_size() static int test_future_size(void)
{ {
/* /*
Here we check ability to add fields only so we can use Here we check ability to add fields only so we can use
...@@ -455,18 +447,18 @@ static int test_future_size() ...@@ -455,18 +447,18 @@ static int test_future_size()
CF_CHANGEABLE_TOTAL_SIZE + 2, CF_CHANGEABLE_TOTAL_SIZE + 2,
0, MYF(MY_FNABP | MY_WME)) == 0); 0, MYF(MY_FNABP | MY_WME)) == 0);
RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0); RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
return(0); return(0);
} }
static int test_bad_hchecksum() static int test_bad_hchecksum(void)
{ {
uchar buffer[4]; uchar buffer[4];
int fd; int fd;
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
/* Corrupt checksum */ /* Corrupt checksum */
...@@ -477,7 +469,7 @@ static int test_bad_hchecksum() ...@@ -477,7 +469,7 @@ static int test_bad_hchecksum()
buffer[0]+= 3; /* mangle checksum */ buffer[0]+= 3; /* mangle checksum */
RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 26, MYF(MY_FNABP | MY_WME)) == 0); RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 26, MYF(MY_FNABP | MY_WME)) == 0);
/* Check that control file module sees the problem */ /* Check that control file module sees the problem */
RET_ERR_UNLESS(local_ma_control_file_create_or_open() == RET_ERR_UNLESS(local_ma_control_file_open() ==
CONTROL_FILE_BAD_HEAD_CHECKSUM); CONTROL_FILE_BAD_HEAD_CHECKSUM);
/* Restore checksum */ /* Restore checksum */
buffer[0]-= 3; buffer[0]-= 3;
...@@ -488,7 +480,7 @@ static int test_bad_hchecksum() ...@@ -488,7 +480,7 @@ static int test_bad_hchecksum()
} }
static int test_bad_size() static int test_bad_size(void)
{ {
uchar buffer[]= uchar buffer[]=
"123456789012345678901234567890123456789012345678901234567890123456"; "123456789012345678901234567890123456789012345678901234567890123456";
...@@ -501,20 +493,20 @@ static int test_bad_size() ...@@ -501,20 +493,20 @@ static int test_bad_size()
MYF(MY_WME))) >= 0); MYF(MY_WME))) >= 0);
RET_ERR_UNLESS(my_write(fd, buffer, 10, MYF(MY_FNABP | MY_WME)) == 0); RET_ERR_UNLESS(my_write(fd, buffer, 10, MYF(MY_FNABP | MY_WME)) == 0);
/* Check that control file module sees the problem */ /* Check that control file module sees the problem */
RET_ERR_UNLESS(local_ma_control_file_create_or_open() == RET_ERR_UNLESS(local_ma_control_file_open() ==
CONTROL_FILE_TOO_SMALL); CONTROL_FILE_TOO_SMALL);
for (i= 0; i < 8; i++) for (i= 0; i < 8; i++)
{ {
RET_ERR_UNLESS(my_write(fd, buffer, 66, MYF(MY_FNABP | MY_WME)) == 0); RET_ERR_UNLESS(my_write(fd, buffer, 66, MYF(MY_FNABP | MY_WME)) == 0);
} }
/* Check that control file module sees the problem */ /* Check that control file module sees the problem */
RET_ERR_UNLESS(local_ma_control_file_create_or_open() == RET_ERR_UNLESS(local_ma_control_file_open() ==
CONTROL_FILE_TOO_BIG); CONTROL_FILE_TOO_BIG);
RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0); RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
/* Leave a correct control file */ /* Leave a correct control file */
RET_ERR_UNLESS(delete_file(MYF(MY_WME)) == 0); RET_ERR_UNLESS(delete_file(MYF(MY_WME)) == 0);
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
return 0; return 0;
...@@ -535,7 +527,7 @@ static struct my_option my_long_options[] = ...@@ -535,7 +527,7 @@ static struct my_option my_long_options[] =
}; };
static void version() static void version(void)
{ {
printf("ma_control_file_test: unit test for the control file " printf("ma_control_file_test: unit test for the control file "
"module of the Maria storage engine. Ver 1.0 \n"); "module of the Maria storage engine. Ver 1.0 \n");
...@@ -575,7 +567,7 @@ static void get_options(int argc, char *argv[]) ...@@ -575,7 +567,7 @@ static void get_options(int argc, char *argv[])
} /* get options */ } /* get options */
static void usage() static void usage(void)
{ {
printf("Usage: %s [options]\n\n", my_progname); printf("Usage: %s [options]\n\n", my_progname);
my_print_help(my_long_options); my_print_help(my_long_options);
......
...@@ -196,7 +196,7 @@ int main(int argc __attribute__((unused)), char *argv[]) ...@@ -196,7 +196,7 @@ int main(int argc __attribute__((unused)), char *argv[])
} }
#endif #endif
if (ma_control_file_create_or_open(TRUE)) if (ma_control_file_open(TRUE))
{ {
fprintf(stderr, "Can't init control file (%d)\n", errno); fprintf(stderr, "Can't init control file (%d)\n", errno);
exit(1); exit(1);
......
...@@ -67,7 +67,7 @@ int main(int argc __attribute__((unused)), char *argv[]) ...@@ -67,7 +67,7 @@ int main(int argc __attribute__((unused)), char *argv[])
} }
#endif #endif
if (ma_control_file_create_or_open(TRUE)) if (ma_control_file_open(TRUE))
{ {
fprintf(stderr, "Can't init control file (%d)\n", errno); fprintf(stderr, "Can't init control file (%d)\n", errno);
exit(1); exit(1);
......
...@@ -64,7 +64,7 @@ int main(int argc __attribute__((unused)), char *argv[]) ...@@ -64,7 +64,7 @@ int main(int argc __attribute__((unused)), char *argv[])
} }
#endif #endif
if (ma_control_file_create_or_open(TRUE)) if (ma_control_file_open(TRUE))
{ {
fprintf(stderr, "Can't init control file (%d)\n", errno); fprintf(stderr, "Can't init control file (%d)\n", errno);
exit(1); exit(1);
......
...@@ -184,7 +184,7 @@ int main(int argc __attribute__((unused)), char *argv[]) ...@@ -184,7 +184,7 @@ int main(int argc __attribute__((unused)), char *argv[])
} }
#endif #endif
if (ma_control_file_create_or_open(TRUE)) if (ma_control_file_open(TRUE))
{ {
fprintf(stderr, "Can't init control file (%d)\n", errno); fprintf(stderr, "Can't init control file (%d)\n", errno);
exit(1); exit(1);
...@@ -348,7 +348,7 @@ int main(int argc __attribute__((unused)), char *argv[]) ...@@ -348,7 +348,7 @@ int main(int argc __attribute__((unused)), char *argv[])
end_pagecache(&pagecache, 1); end_pagecache(&pagecache, 1);
ma_control_file_end(); ma_control_file_end();
if (ma_control_file_create_or_open(TRUE)) if (ma_control_file_open(TRUE))
{ {
fprintf(stderr, "pass2: Can't init control file (%d)\n", errno); fprintf(stderr, "pass2: Can't init control file (%d)\n", errno);
exit(1); exit(1);
......
...@@ -283,7 +283,7 @@ int main(int argc __attribute__((unused)), ...@@ -283,7 +283,7 @@ int main(int argc __attribute__((unused)),
my_thread_global_init(); my_thread_global_init();
if (ma_control_file_create_or_open(TRUE)) if (ma_control_file_open(TRUE))
{ {
fprintf(stderr, "Can't init control file (%d)\n", errno); fprintf(stderr, "Can't init control file (%d)\n", errno);
exit(1); exit(1);
......
...@@ -70,7 +70,7 @@ int main(int argc __attribute__((unused)), char *argv[]) ...@@ -70,7 +70,7 @@ int main(int argc __attribute__((unused)), char *argv[])
} }
#endif #endif
if (ma_control_file_create_or_open(TRUE)) if (ma_control_file_open(TRUE))
{ {
fprintf(stderr, "Can't init control file (%d)\n", errno); fprintf(stderr, "Can't init control file (%d)\n", errno);
exit(1); exit(1);
......
...@@ -67,7 +67,7 @@ int main(int argc __attribute__((unused)), char *argv[]) ...@@ -67,7 +67,7 @@ int main(int argc __attribute__((unused)), char *argv[])
} }
#endif #endif
if (ma_control_file_create_or_open(TRUE)) if (ma_control_file_open(TRUE))
{ {
fprintf(stderr, "Can't init control file (%d)\n", errno); fprintf(stderr, "Can't init control file (%d)\n", errno);
exit(1); exit(1);
...@@ -140,7 +140,7 @@ int main(int argc __attribute__((unused)), char *argv[]) ...@@ -140,7 +140,7 @@ int main(int argc __attribute__((unused)), char *argv[])
} }
} }
if (ma_control_file_create_or_open(TRUE)) if (ma_control_file_open(TRUE))
{ {
fprintf(stderr, "Can't init control file (%d)\n", errno); fprintf(stderr, "Can't init control file (%d)\n", errno);
exit(1); exit(1);
......
...@@ -95,7 +95,7 @@ int main(int argc __attribute__((unused)), char *argv[]) ...@@ -95,7 +95,7 @@ int main(int argc __attribute__((unused)), char *argv[])
} }
#endif #endif
if (ma_control_file_create_or_open(TRUE)) if (ma_control_file_open(TRUE))
{ {
fprintf(stderr, "Can't init control file (%d)\n", errno); fprintf(stderr, "Can't init control file (%d)\n", errno);
exit(1); exit(1);
......
...@@ -67,7 +67,7 @@ int main(int argc __attribute__((unused)), char *argv[]) ...@@ -67,7 +67,7 @@ int main(int argc __attribute__((unused)), char *argv[])
} }
#endif #endif
if (ma_control_file_create_or_open(TRUE)) if (ma_control_file_open(TRUE))
{ {
fprintf(stderr, "Can't init control file (%d)\n", errno); fprintf(stderr, "Can't init control file (%d)\n", errno);
exit(1); exit(1);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment