Commit 26fb3606 authored by unknown's avatar unknown

WL#3234 Maria control file manager.

Fitting ma_control_file_test into the mytap unittest framework:
new directories:
- unittest/storage/ for unit tests of any storage engine
- unittest/storage/maria for ... Maria, containing ma_control_file-t.
Later, older tests like ma_test*, ma_test_all (but which is Unix
dependent in its current form) could move here too.
The plugins macro enable building of unittest/storage/X for any
enabled engine X which has such a directory.
If Falcon wants to have unit tests there too, I may have to merge
this patch into 5.x one day.


config/ac-macros/plugins.m4:
  If a storage engine has a directory in unittest/storage, build this
  directory.
configure.in:
  build storage engines' unit tests.
storage/maria/Makefile.am:
  ma_control_file_test moves to unittest/storage/maria
storage/maria/ma_control_file.c:
  more error codes when opening the control file fails.
  ma_control_file_end() may now return an error if my_close() failed.
storage/maria/ma_control_file.h:
  more error codes when opening the control file fails.
unittest/Makefile.am:
  adding unit tests for storage engines.
  Note that unit.pl simply recurses into "storage", so if a unit test for
  storage engine X has been built previously, and now you re-configure
  (without making clean) to disable this engine, then the unit test of
  X will not be rebuilt but will still be present in storage/X, so will
  be run.
unittest/storage/maria/ma_control_file-t.c:
  Making the test fit the mytap framework (return all the way up
  the stack instead of assert(); use the mytap functions plan(), ok() etc).
  Adding test of file too short/long.
unittest/storage/maria/Makefile.am:
  a_control_file-t is added to the Maria unit tests.
  Later, older tests (ma_test1 etc) could also move here.
unittest/storage/Makefile.am:
  New BitKeeper file ``unittest/storage/Makefile.am''
parent e321f8eb
......@@ -280,6 +280,7 @@ AC_DEFUN([MYSQL_CONFIGURE_PLUGINS],[
_MYSQL_EMIT_PLUGIN_ACTIONS(m4_bpatsubst(__mysql_plugin_list__, :, [,]))
AC_SUBST([mysql_se_dirs])
AC_SUBST([mysql_pg_dirs])
AC_SUBST([mysql_se_unittest_dirs])
])
])
])
......@@ -315,6 +316,7 @@ AC_DEFUN([__MYSQL_EMIT_CHECK_PLUGIN],[
])
AC_MSG_CHECKING([whether to use ]$3)
mysql_use_plugin_dir=""
mysql_use_plugin_unittest_dir=""
m4_ifdef([$10],[
if test "X[$mysql_plugin_]$2" = Xyes -a \
"X[$with_plugin_]$2" != Xno -o \
......@@ -407,10 +409,24 @@ dnl Although this is "pretty", it breaks libmysqld build
m4_syscmd(test -f "$6/configure")
ifelse(m4_sysval, 0,
[AC_CONFIG_SUBDIRS($6)],
[AC_CONFIG_FILES($6/Makefile)]
[
AC_CONFIG_FILES($6/Makefile)
m4_syscmd(test -d "unittest/$6")
ifelse(m4_sysval, 0,
[
mysql_use_plugin_unittest_dir="$6"
AC_CONFIG_FILES(unittest/$6/Makefile)
], [])
]
)
ifelse(m4_substr($6, 0, 8), [storage/],
[mysql_se_dirs="$mysql_se_dirs ]m4_substr($6, 8)",
[
[mysql_se_name="]m4_substr($6, 8)"
mysql_se_dirs="$mysql_se_dirs $mysql_se_name"
if test -n "$mysql_use_plugin_unittest_dir" ; then
mysql_se_unittest_dirs="$mysql_se_unitest_dirs $mysql_se_name"
fi
],
m4_substr($6, 0, 7), [plugin/],
[mysql_pg_dirs="$mysql_pg_dirs ]m4_substr($6, 7)",
[AC_FATAL([don't know how to handle plugin dir ]$6)])
......
......@@ -2482,6 +2482,7 @@ AC_SUBST(MAKE_BINARY_DISTRIBUTION_OPTIONS)
AC_CONFIG_FILES(Makefile extra/Makefile mysys/Makefile dnl
unittest/Makefile unittest/mytap/Makefile unittest/mytap/t/Makefile dnl
unittest/mysys/Makefile unittest/examples/Makefile dnl
unittest/storage/Makefile dnl
strings/Makefile regex/Makefile storage/Makefile dnl
man/Makefile BUILD/Makefile vio/Makefile dnl
libmysql/Makefile client/Makefile dnl
......
......@@ -47,7 +47,7 @@ maria_pack_LDADD= @CLIENT_EXTRA_LDFLAGS@ libmaria.a \
$(top_builddir)/mysys/libmysys.a \
$(top_builddir)/dbug/libdbug.a \
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
noinst_PROGRAMS = ma_test1 ma_test2 ma_test3 ma_rt_test ma_sp_test ma_control_file_test
noinst_PROGRAMS = ma_test1 ma_test2 ma_test3 ma_rt_test ma_sp_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_control_file.h ha_maria.h
......@@ -89,12 +89,6 @@ ma_sp_test_LDADD= @CLIENT_EXTRA_LDFLAGS@ libmaria.a \
$(top_builddir)/mysys/libmysys.a \
$(top_builddir)/dbug/libdbug.a \
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
ma_control_file_test_DEPENDENCIES= $(LIBRARIES)
ma_control_file_test_LDADD= @CLIENT_EXTRA_LDFLAGS@ libmaria.a \
$(top_builddir)/storage/myisam/libmyisam.a \
$(top_builddir)/mysys/libmysys.a \
$(top_builddir)/dbug/libdbug.a \
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
libmaria_a_SOURCES = ma_init.c ma_open.c ma_extra.c ma_info.c ma_rkey.c \
ma_rnext.c ma_rnext_same.c \
ma_search.c ma_page.c ma_key.c ma_locking.c \
......@@ -113,7 +107,7 @@ libmaria_a_SOURCES = ma_init.c ma_open.c ma_extra.c ma_info.c ma_rkey.c \
ha_maria.cc \
ma_rt_index.c ma_rt_key.c ma_rt_mbr.c ma_rt_split.c \
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
CLEANFILES = test?.MA? FT?.MA? isam.log ma_test_all ma_rt_test.MA? sp_test.MA?
SUFFIXES = .sh
......
......@@ -41,7 +41,7 @@ uint32 last_logno;
Control file is less then 512 bytes (a disk sector),
to be as atomic as possible
*/
static int control_file_fd;
static int control_file_fd= -1;
static void lsn8store(char *buffer, const LSN *lsn)
{
......@@ -87,15 +87,16 @@ static char simple_checksum(char *buffer, uint size)
RETURN
0 - OK
1 - Error
1 - Error (in which case the file is left closed)
*/
int ma_control_file_create_or_open()
CONTROL_FILE_ERROR ma_control_file_create_or_open()
{
char buffer[CONTROL_FILE_SIZE];
char name[FN_REFLEN];
MY_STAT stat_buff;
my_bool create_file;
int open_flags= O_BINARY | /*O_DIRECT |*/ O_RDWR;
int error= CONTROL_FILE_UNKNOWN_ERROR;
DBUG_ENTER("ma_control_file_create_or_open");
/*
......@@ -106,16 +107,19 @@ int ma_control_file_create_or_open()
DBUG_ASSERT(CONTROL_FILE_LSN_SIZE == (4+4));
DBUG_ASSERT(CONTROL_FILE_FILENO_SIZE == 4);
/* name is concatenation of Maria's home dir and "control" */
if (fn_format(name, "control", maria_data_root, "", MYF(MY_WME)) == NullS)
DBUG_RETURN(1);
if (control_file_fd >= 0) /* already open */
DBUG_RETURN(0);
if (fn_format(name, CONTROL_FILE_BASE_NAME,
maria_data_root, "", MYF(MY_WME)) == NullS)
DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
create_file= test(my_access(name,F_OK));
if (create_file)
{
if ((control_file_fd= my_create(name, 0, open_flags, MYF(0))) < 0)
DBUG_RETURN(1);
DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
/*
TODO: from "man fsync" on Linux:
"fsync does not necessarily ensure that the entry in the directory
......@@ -127,10 +131,10 @@ int ma_control_file_create_or_open()
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).
TODO: For now we trust (this is alpha version), but for beta if would
be great to verify.
We could have a tool which can rebuild the control file, by reading the
directory of logs, finding the newest log, reading it to find last
checkpoint... Slow but can save your db.
......@@ -138,7 +142,7 @@ int ma_control_file_create_or_open()
LSN imposs_lsn= CONTROL_FILE_IMPOSSIBLE_LSN;
uint32 imposs_logno= CONTROL_FILE_IMPOSSIBLE_FILENO;
/* init the file with these "undefined" values */
DBUG_RETURN(ma_control_file_write_and_force(&imposs_lsn, imposs_logno,
CONTROL_FILE_UPDATE_ALL));
......@@ -147,12 +151,12 @@ int ma_control_file_create_or_open()
/* Otherwise, file exists */
if ((control_file_fd= my_open(name, open_flags, MYF(MY_WME))) < 0)
DBUG_RETURN(1);
goto err;
if (my_stat(name, &stat_buff, MYF(MY_WME)) == NULL)
DBUG_RETURN(1);
goto err;
if ((uint)stat_buff.st_size != CONTROL_FILE_SIZE)
if ((uint)stat_buff.st_size < CONTROL_FILE_SIZE)
{
/*
Given that normally we write only a sector and it's atomic, the only
......@@ -165,31 +169,43 @@ int ma_control_file_create_or_open()
disk/filesystem has a problem.
So let's be rigid.
*/
my_message(0, "wrong file size", MYF(0)); /* TODO: improve errors */
my_error(HA_ERR_CRASHED, MYF(0), name);
DBUG_RETURN(1);
my_message(0, "too small file", MYF(0)); /* TODO: improve errors */
error= CONTROL_FILE_TOO_SMALL;
goto err;
}
if ((uint)stat_buff.st_size > CONTROL_FILE_SIZE)
{
my_message(0, "too big file", MYF(0)); /* TODO: improve errors */
error= CONTROL_FILE_TOO_BIG;
goto err;
}
if (my_read(control_file_fd, buffer, CONTROL_FILE_SIZE,
MYF(MY_FNABP | MY_WME)))
DBUG_RETURN(1);
goto err;
if (memcmp(buffer + CONTROL_FILE_MAGIC_STRING_OFFSET,
CONTROL_FILE_MAGIC_STRING, CONTROL_FILE_MAGIC_STRING_SIZE))
{
my_message(0, "bad magic string", MYF(0));
DBUG_RETURN(1);
error= CONTROL_FILE_BAD_MAGIC_STRING;
goto err;
}
if (simple_checksum(buffer + CONTROL_FILE_LSN_OFFSET,
CONTROL_FILE_SIZE - CONTROL_FILE_LSN_OFFSET) !=
buffer[CONTROL_FILE_CHECKSUM_OFFSET])
{
my_message(0, "checksum mismatch", MYF(0));
DBUG_RETURN(1);
error= CONTROL_FILE_BAD_CHECKSUM;
goto err;
}
last_checkpoint_lsn= lsn8korr(buffer + CONTROL_FILE_LSN_OFFSET);
last_logno= uint4korr(buffer + CONTROL_FILE_FILENO_OFFSET);
DBUG_RETURN(0);
err:
ma_control_file_end();
DBUG_RETURN(error);
}
......@@ -227,6 +243,8 @@ int ma_control_file_write_and_force(const LSN *checkpoint_lsn, uint32 logno,
my_bool update_checkpoint_lsn= FALSE, update_logno= FALSE;
DBUG_ENTER("ma_control_file_write_and_force");
DBUG_ASSERT(control_file_fd >= 0); /* must be open */
memcpy(buffer + CONTROL_FILE_MAGIC_STRING_OFFSET,
CONTROL_FILE_MAGIC_STRING, CONTROL_FILE_MAGIC_STRING_SIZE);
......@@ -259,7 +277,7 @@ int ma_control_file_write_and_force(const LSN *checkpoint_lsn, uint32 logno,
0, MYF(MY_FNABP | MY_WME)) ||
my_sync(control_file_fd, MYF(MY_WME)))
DBUG_RETURN(1);
/* TODO: you need some protection to be able to write last_* global vars */
if (update_checkpoint_lsn)
last_checkpoint_lsn= *checkpoint_lsn;
......@@ -277,15 +295,26 @@ int ma_control_file_write_and_force(const LSN *checkpoint_lsn, uint32 logno,
ma_control_file_end()
*/
void ma_control_file_end()
int ma_control_file_end()
{
int close_error;
DBUG_ENTER("ma_control_file_end");
my_close(control_file_fd, MYF(MY_WME));
if (control_file_fd < 0) /* already closed */
DBUG_RETURN(0);
close_error= my_close(control_file_fd, MYF(MY_WME));
/*
As my_close() frees structures even if close() fails, we do the same,
i.e. we mark the file as closed in all cases.
*/
control_file_fd= -1;
/*
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;
DBUG_RETURN(close_error);
}
......@@ -24,6 +24,7 @@ typedef struct st_lsn {
#define maria_data_root "."
#endif
#define CONTROL_FILE_BASE_NAME "maria_control"
/*
indicate absence of the log file number; first log is always number 1, 0 is
impossible.
......@@ -55,7 +56,15 @@ extern uint32 last_logno;
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();
typedef enum enum_control_file_error {
CONTROL_FILE_OK= 0,
CONTROL_FILE_TOO_SMALL,
CONTROL_FILE_TOO_BIG,
CONTROL_FILE_BAD_MAGIC_STRING,
CONTROL_FILE_BAD_CHECKSUM,
CONTROL_FILE_UNKNOWN_ERROR /* any other error */
} CONTROL_FILE_ERROR;
CONTROL_FILE_ERROR ma_control_file_create_or_open();
/*
Write information durably to the control file.
......@@ -66,10 +75,10 @@ int ma_control_file_create_or_open();
#define CONTROL_FILE_UPDATE_ONLY_LSN 1
#define CONTROL_FILE_UPDATE_ONLY_LOGNO 2
int ma_control_file_write_and_force(const LSN *checkpoint_lsn, uint32 logno,
uint objs_to_write);
uint objs_to_write);
/* Free resources taken by control file subsystem */
void ma_control_file_end();
int ma_control_file_end();
#endif
This diff is collapsed.
SUBDIRS = mytap . mysys examples
SUBDIRS = mytap . mysys storage examples
noinst_SCRIPTS = unit
EXTRA_DIST = unit.pl
CLEANFILES = unit
unittests = mytap mysys
unittests = mytap mysys storage
test: unit
./unit run $(unittests)
......
# Copyright (C) 2000 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
# Process this file with automake to create Makefile.in
AUTOMAKE_OPTIONS = foreign
# These are built from source in the Docs directory
EXTRA_DIST =
# Cannot use @mysql_se_dirs@ as not all engines have unit tests here
SUBDIRS = @mysql_se_unittest_dirs@
# Don't update the files from bitkeeper
%::SCCS/s.%
# Copyright (C) 2000 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
AM_CPPFLAGS = @ZLIB_INCLUDES@ -I$(top_builddir)/include
AM_CPPFLAGS += -I$(top_srcdir)/include -I$(top_srcdir)/unittest/mytap
# Only reason to link with libmyisam.a here is that it's where some fulltext
# pieces are (but soon we'll remove fulltext dependencies from Maria).
LDADD= $(top_builddir)/unittest/mytap/libmytap.a \
$(top_builddir)/storage/maria/libmaria.a \
$(top_builddir)/storage/myisam/libmyisam.a \
$(top_builddir)/mysys/libmysys.a \
$(top_builddir)/dbug/libdbug.a \
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
noinst_PROGRAMS = ma_control_file-t
CLEANFILES = maria_control
This diff is collapsed.
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