summaryrefslogtreecommitdiffstats
path: root/storage/maria/unittest/ma_control_file-t.c
diff options
context:
space:
mode:
Diffstat (limited to 'storage/maria/unittest/ma_control_file-t.c')
-rw-r--r--storage/maria/unittest/ma_control_file-t.c623
1 files changed, 623 insertions, 0 deletions
diff --git a/storage/maria/unittest/ma_control_file-t.c b/storage/maria/unittest/ma_control_file-t.c
new file mode 100644
index 00000000..859d5514
--- /dev/null
+++ b/storage/maria/unittest/ma_control_file-t.c
@@ -0,0 +1,623 @@
+/* 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+/* Unit test of the control file module of the Aria engine WL#3234 */
+
+/*
+ Note that it is not possible to test the durability of the write (can't
+ pull the plug programmatically :)
+*/
+
+#include <my_global.h>
+#include <my_sys.h>
+#include <tap.h>
+#ifdef _WIN32
+#include <direct.h> /* rmdir */
+#endif
+#ifndef WITH_ARIA_STORAGE_ENGINE
+/*
+ If Aria is not compiled in, normally we don't come to building this test.
+*/
+#error "Aria engine is not compiled in, test cannot be built"
+#endif
+
+#include "maria.h"
+#include "../../../storage/maria/maria_def.h"
+#include <my_getopt.h>
+
+#define EXTRACT_DEFINITIONS
+#include "../ma_control_file.c"
+#undef EXTRACT_DEFINITIONS
+
+char file_name[FN_REFLEN];
+
+/* The values we'll set and expect the control file module to return */
+LSN expect_checkpoint_lsn;
+uint32 expect_logno;
+TrID expect_max_trid;
+uint8 expect_recovery_failures;
+
+static int delete_file(myf my_flags);
+/*
+ Those are test-specific wrappers around the module's API functions: after
+ calling the module's API functions they perform checks on the result.
+*/
+static int close_file(void); /* wraps ma_control_file_end */
+/* wraps ma_control_file_open_or_create */
+static int open_file(void);
+/* wraps ma_control_file_write_and_force */
+static int write_file(LSN checkpoint_lsn, uint32 logno, TrID trid,
+ uint8 rec_failures);
+
+/* Tests */
+static int test_one_log_and_recovery_failures(void);
+static int test_five_logs_and_max_trid(void);
+static int test_3_checkpoints_and_2_logs(void);
+static int test_binary_content(void);
+static int test_start_stop(void);
+static int test_2_open_and_2_close(void);
+static int test_bad_magic_string(void);
+static int test_bad_checksum(void);
+static int test_bad_hchecksum(void);
+static int test_future_size(void);
+static int test_bad_blocksize(void);
+static int test_bad_size(void);
+
+/* Utility */
+static int verify_module_values_match_expected(void);
+static int verify_module_values_are_impossible(void);
+static void usage(void);
+static void get_options(int argc, char *argv[]);
+
+/*
+ If "expr" is FALSE, this macro will make the function print a diagnostic
+ message and immediately return 1.
+ This is inspired from assert() but does not crash the binary (sometimes we
+ may want to see how other tests go even if one fails).
+ RET_ERR means "return error".
+*/
+
+#define RET_ERR_UNLESS(expr) \
+ {if (!(expr)) {diag("line %d: failure: '%s'", __LINE__, #expr); assert(0);return 1;}}
+
+
+/* Used to ignore error messages from ma_control_file_open() */
+
+static void my_ignore_message(uint error __attribute__((unused)),
+ const char *str __attribute__((unused)),
+ myf MyFlags __attribute__((unused)))
+{
+ DBUG_ENTER("my_message_no_curses");
+ DBUG_PRINT("enter",("message: %s",str));
+ DBUG_VOID_RETURN;
+}
+
+void (*default_error_handler_hook)(uint my_err, const char *str,
+ myf MyFlags) = 0;
+
+
+/* like ma_control_file_open(), but without error messages */
+
+static CONTROL_FILE_ERROR local_ma_control_file_open(void)
+{
+ CONTROL_FILE_ERROR error;
+ error_handler_hook= my_ignore_message;
+ error= ma_control_file_open(TRUE, TRUE, TRUE);
+ error_handler_hook= default_error_handler_hook;
+ return error;
+}
+
+static char *create_tmpdir(const char *progname)
+{
+ static char test_dirname[FN_REFLEN];
+ char tmp_name[FN_REFLEN];
+ size_t length;
+
+ /* Create a temporary directory of name TMP-'executable', but without the -t extension */
+ fn_format(tmp_name, progname, "", "", MY_REPLACE_DIR | MY_REPLACE_EXT);
+ length= strlen(tmp_name);
+ if (length > 2 && tmp_name[length-2] == '-' && tmp_name[length-1] == 't')
+ tmp_name[length-2]= 0;
+ strxmov(test_dirname, "TMP-", tmp_name, NullS);
+
+ /*
+ Don't give an error if we can't create dir, as it may already exist from a previously aborted
+ run
+ */
+ (void) my_mkdir(test_dirname, 0777, MYF(0));
+ return test_dirname;
+}
+
+
+int main(int argc,char *argv[])
+{
+ MY_INIT(argv[0]);
+ my_init();
+
+ default_error_handler_hook= error_handler_hook;
+
+ plan(12);
+
+ maria_data_root= create_tmpdir(argv[0]);
+
+ diag("Unit tests for control file");
+
+ get_options(argc,argv);
+
+ diag("Deleting control file at startup, if there is an old one");
+ RET_ERR_UNLESS(0 == delete_file(0)); /* if fails, can't continue */
+
+ diag("Tests of normal conditions");
+ ok(0 == test_one_log_and_recovery_failures(),
+ "test of creating one log and recording recovery failures");
+ ok(0 == test_five_logs_and_max_trid(),
+ "test of creating five logs and many transactions");
+ ok(0 == test_3_checkpoints_and_2_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_start_stop(), "test of multiple starts and stops");
+ diag("Tests of abnormal conditions");
+ ok(0 == test_2_open_and_2_close(),
+ "test of two open and two close (strange call sequence)");
+ ok(0 == test_bad_magic_string(), "test of bad magic string");
+ ok(0 == test_bad_checksum(), "test of bad checksum");
+ ok(0 == test_bad_hchecksum(), "test of bad hchecksum");
+ ok(0 == test_future_size(), "test of ability to handlr future versions");
+ ok(0 == test_bad_blocksize(), "test of bad blocksize");
+ ok(0 == test_bad_size(), "test of too small/big file");
+
+ delete_file(0);
+ rmdir(maria_data_root);
+
+ my_uuid_end();
+ my_end(0);
+ return exit_status();
+}
+
+
+static int delete_file(myf my_flags)
+{
+ RET_ERR_UNLESS(fn_format(file_name, CONTROL_FILE_BASE_NAME,
+ maria_data_root, "", MYF(MY_WME)) != NullS);
+ /*
+ Maybe file does not exist, ignore error.
+ The error will however be printed on stderr.
+ */
+ my_delete(file_name, my_flags);
+ expect_checkpoint_lsn= LSN_IMPOSSIBLE;
+ expect_logno= FILENO_IMPOSSIBLE;
+ expect_max_trid= expect_recovery_failures= 0;
+
+ return 0;
+}
+
+/*
+ Verifies that global values last_checkpoint_lsn, last_logno,
+ max_trid_in_control_file (belonging to the module) match what we expect.
+*/
+static int verify_module_values_match_expected(void)
+{
+ RET_ERR_UNLESS(last_logno == expect_logno);
+ RET_ERR_UNLESS(last_checkpoint_lsn == expect_checkpoint_lsn);
+ RET_ERR_UNLESS(max_trid_in_control_file == expect_max_trid);
+ RET_ERR_UNLESS(recovery_failures == expect_recovery_failures);
+ return 0;
+}
+
+
+/*
+ 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).
+*/
+static int verify_module_values_are_impossible(void)
+{
+ RET_ERR_UNLESS(last_logno == FILENO_IMPOSSIBLE);
+ RET_ERR_UNLESS(last_checkpoint_lsn == LSN_IMPOSSIBLE);
+ RET_ERR_UNLESS(max_trid_in_control_file == 0);
+ return 0;
+}
+
+
+static int close_file(void)
+{
+ /* Simulate shutdown */
+ ma_control_file_end();
+ /* Verify amnesia */
+ RET_ERR_UNLESS(verify_module_values_are_impossible() == 0);
+ return 0;
+}
+
+static int open_file(void)
+{
+ RET_ERR_UNLESS(local_ma_control_file_open() == CONTROL_FILE_OK);
+ /* Check that the module reports expected information */
+ RET_ERR_UNLESS(verify_module_values_match_expected() == 0);
+ return 0;
+}
+
+static int write_file(LSN checkpoint_lsn, uint32 logno, TrID trid,
+ uint8 rec_failures)
+{
+ RET_ERR_UNLESS(ma_control_file_write_and_force(checkpoint_lsn, logno, trid,
+ rec_failures)
+ == 0);
+ /* Check that the module reports expected information */
+ RET_ERR_UNLESS(verify_module_values_match_expected() == 0);
+ return 0;
+}
+
+static int test_one_log_and_recovery_failures(void)
+{
+ RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
+ expect_logno= 123;
+ RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno,
+ max_trid_in_control_file,
+ recovery_failures) == 0);
+ expect_recovery_failures= 158;
+ RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno,
+ max_trid_in_control_file,
+ expect_recovery_failures) == 0);
+ RET_ERR_UNLESS(close_file() == 0);
+ return 0;
+}
+
+static int test_five_logs_and_max_trid(void)
+{
+ uint i;
+
+ RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
+ expect_logno= 100;
+ expect_max_trid= 14111978111ULL;
+ for (i= 0; i<5; i++)
+ {
+ expect_logno*= 3;
+ RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno,
+ expect_max_trid,
+ recovery_failures) == 0);
+ }
+ RET_ERR_UNLESS(close_file() == 0);
+ return 0;
+}
+
+static int test_3_checkpoints_and_2_logs(void)
+{
+ /*
+ Simulate one checkpoint, one log creation, two checkpoints, one
+ log creation.
+ */
+ RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
+ expect_checkpoint_lsn= MAKE_LSN(5, 10000);
+ RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
+ max_trid_in_control_file,
+ recovery_failures) == 0);
+
+ expect_logno= 17;
+ RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
+ max_trid_in_control_file,
+ recovery_failures) == 0);
+
+ expect_checkpoint_lsn= MAKE_LSN(17, 20000);
+ RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
+ max_trid_in_control_file,
+ recovery_failures) == 0);
+
+ expect_checkpoint_lsn= MAKE_LSN(17, 45000);
+ RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
+ max_trid_in_control_file,
+ recovery_failures) == 0);
+
+ expect_logno= 19;
+ RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
+ max_trid_in_control_file,
+ recovery_failures) == 0);
+ RET_ERR_UNLESS(close_file() == 0);
+ return 0;
+}
+
+static int test_binary_content(void)
+{
+ uint i;
+ int fd;
+
+ /*
+ 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.
+ TODO: when we reach the format-freeze state, we may even just do a
+ comparison with a raw binary string, to not depend on any uint4korr
+ future change/breakage.
+ */
+
+ uchar buffer[45];
+ RET_ERR_UNLESS((fd= my_open(file_name,
+ O_BINARY | O_RDWR,
+ MYF(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(open_file() == CONTROL_FILE_OK);
+ i= uint3korr(buffer + 34 );
+ RET_ERR_UNLESS(i == LSN_FILE_NO(last_checkpoint_lsn));
+ i= uint4korr(buffer + 37);
+ RET_ERR_UNLESS(i == LSN_OFFSET(last_checkpoint_lsn));
+ i= uint4korr(buffer + 41);
+ RET_ERR_UNLESS(i == last_logno);
+ RET_ERR_UNLESS(close_file() == 0);
+ return 0;
+}
+
+static int test_start_stop(void)
+{
+ /* TEST5: Simulate start/nothing/stop/start/nothing/stop/start */
+
+ RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
+ RET_ERR_UNLESS(close_file() == 0);
+ RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
+ RET_ERR_UNLESS(close_file() == 0);
+ RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
+ RET_ERR_UNLESS(close_file() == 0);
+ return 0;
+}
+
+static int test_2_open_and_2_close(void)
+{
+ RET_ERR_UNLESS(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;
+}
+
+
+static int test_bad_magic_string(void)
+{
+ uchar buffer[4];
+ int fd;
+
+ RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
+ RET_ERR_UNLESS(close_file() == 0);
+
+ /* Corrupt magic string */
+ RET_ERR_UNLESS((fd= my_open(file_name,
+ O_BINARY | O_RDWR,
+ MYF(MY_WME))) >= 0);
+ RET_ERR_UNLESS(my_pread(fd, buffer, 4, 0, MYF(MY_FNABP | MY_WME)) == 0);
+ RET_ERR_UNLESS(my_pwrite(fd, (const uchar *)"papa", 4, 0,
+ MYF(MY_FNABP | MY_WME)) == 0);
+
+ /* Check that control file module sees the problem */
+ RET_ERR_UNLESS(local_ma_control_file_open() ==
+ CONTROL_FILE_BAD_MAGIC_STRING);
+ /* Restore magic string */
+ 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(open_file() == CONTROL_FILE_OK);
+ RET_ERR_UNLESS(close_file() == 0);
+ return 0;
+}
+
+static int test_bad_checksum(void)
+{
+ uchar buffer[4];
+ int fd;
+
+ RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
+ RET_ERR_UNLESS(close_file() == 0);
+
+ /* Corrupt checksum */
+ RET_ERR_UNLESS((fd= my_open(file_name,
+ O_BINARY | O_RDWR,
+ MYF(MY_WME))) >= 0);
+ RET_ERR_UNLESS(my_pread(fd, buffer, 1, 30, MYF(MY_FNABP | MY_WME)) == 0);
+ buffer[0]+= 3; /* mangle checksum */
+ RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 30, MYF(MY_FNABP | MY_WME)) == 0);
+ /* Check that control file module sees the problem */
+ RET_ERR_UNLESS(local_ma_control_file_open() ==
+ CONTROL_FILE_BAD_CHECKSUM);
+ /* Restore checksum */
+ buffer[0]-= 3;
+ RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 30, MYF(MY_FNABP | MY_WME)) == 0);
+ RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
+
+ return 0;
+}
+
+
+static int test_bad_blocksize(void)
+{
+ maria_block_size<<= 1;
+ /* Check that control file module sees the problem */
+ RET_ERR_UNLESS(local_ma_control_file_open() ==
+ CONTROL_FILE_WRONG_BLOCKSIZE);
+ /* Restore blocksize */
+ maria_block_size>>= 1;
+
+ RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
+ RET_ERR_UNLESS(close_file() == 0);
+ return 0;
+}
+
+
+static int test_future_size(void)
+{
+ /*
+ Here we check ability to add fields only so we can use
+ defined constants
+ */
+ uint32 sum;
+ int fd;
+ uchar buffer[CF_CREATE_TIME_TOTAL_SIZE + CF_CHANGEABLE_TOTAL_SIZE + 2];
+ RET_ERR_UNLESS((fd= my_open(file_name,
+ O_BINARY | O_RDWR,
+ MYF(MY_WME))) >= 0);
+ RET_ERR_UNLESS(my_read(fd, buffer,
+ CF_CREATE_TIME_TOTAL_SIZE + CF_CHANGEABLE_TOTAL_SIZE,
+ MYF(MY_FNABP | MY_WME)) == 0);
+ RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
+ /* "add" new field of 1 byte (value 1) to header and variable part */
+ memmove(buffer + CF_CREATE_TIME_TOTAL_SIZE + 1,
+ buffer + CF_CREATE_TIME_TOTAL_SIZE,
+ CF_CHANGEABLE_TOTAL_SIZE);
+ buffer[CF_CREATE_TIME_TOTAL_SIZE - CF_CHECKSUM_SIZE]= '\1';
+ buffer[CF_CREATE_TIME_TOTAL_SIZE + CF_CHANGEABLE_TOTAL_SIZE + 1]= '\1';
+ /* fix lengths */
+ int2store(buffer + CF_CREATE_TIME_SIZE_OFFSET, CF_CREATE_TIME_TOTAL_SIZE + 1);
+ int2store(buffer + CF_CHANGEABLE_SIZE_OFFSET, CF_CHANGEABLE_TOTAL_SIZE + 1);
+ /* recalculete checksums */
+ sum= (uint32) my_checksum(0, buffer, CF_CREATE_TIME_TOTAL_SIZE -
+ CF_CHECKSUM_SIZE + 1);
+ int4store(buffer + CF_CREATE_TIME_TOTAL_SIZE - CF_CHECKSUM_SIZE + 1, sum);
+ sum= (uint32) my_checksum(0, buffer + CF_CREATE_TIME_TOTAL_SIZE + 1 +
+ CF_CHECKSUM_SIZE,
+ CF_CHANGEABLE_TOTAL_SIZE - CF_CHECKSUM_SIZE + 1);
+ int4store(buffer + CF_CREATE_TIME_TOTAL_SIZE + 1, sum);
+ /* write new file and check it */
+ RET_ERR_UNLESS((fd= my_open(file_name,
+ O_BINARY | O_RDWR,
+ MYF(MY_WME))) >= 0);
+ RET_ERR_UNLESS(my_pwrite(fd, buffer,
+ CF_CREATE_TIME_TOTAL_SIZE +
+ CF_CHANGEABLE_TOTAL_SIZE + 2,
+ 0, MYF(MY_FNABP | MY_WME)) == 0);
+ RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
+ RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
+ RET_ERR_UNLESS(close_file() == 0);
+
+ return(0);
+}
+
+static int test_bad_hchecksum(void)
+{
+ uchar buffer[4];
+ int fd;
+
+ RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
+ RET_ERR_UNLESS(close_file() == 0);
+
+ /* Corrupt checksum */
+ RET_ERR_UNLESS((fd= my_open(file_name,
+ O_BINARY | O_RDWR,
+ MYF(MY_WME))) >= 0);
+ RET_ERR_UNLESS(my_pread(fd, buffer, 1, 26, MYF(MY_FNABP | MY_WME)) == 0);
+ buffer[0]+= 3; /* mangle checksum */
+ RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 26, MYF(MY_FNABP | MY_WME)) == 0);
+ /* Check that control file module sees the problem */
+ RET_ERR_UNLESS(local_ma_control_file_open() ==
+ CONTROL_FILE_BAD_HEAD_CHECKSUM);
+ /* Restore checksum */
+ buffer[0]-= 3;
+ RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 26, MYF(MY_FNABP | MY_WME)) == 0);
+ RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
+
+ return 0;
+}
+
+
+static int test_bad_size(void)
+{
+ uchar buffer[]=
+ "123456789012345678901234567890123456789012345678901234567890123456";
+ int fd, i;
+
+ /* A too short file */
+ RET_ERR_UNLESS(delete_file(MYF(MY_WME)) == 0);
+ RET_ERR_UNLESS((fd= my_open(file_name,
+ O_BINARY | O_RDWR | O_CREAT,
+ MYF(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 */
+ RET_ERR_UNLESS(local_ma_control_file_open() ==
+ CONTROL_FILE_TOO_SMALL);
+ for (i= 0; i < 8; i++)
+ {
+ RET_ERR_UNLESS(my_write(fd, buffer, 66, MYF(MY_FNABP | MY_WME)) == 0);
+ }
+ /* Check that control file module sees the problem */
+ RET_ERR_UNLESS(local_ma_control_file_open() ==
+ CONTROL_FILE_TOO_BIG);
+ RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
+
+ /* Leave a correct control file */
+ RET_ERR_UNLESS(delete_file(MYF(MY_WME)) == 0);
+ RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
+ RET_ERR_UNLESS(close_file() == 0);
+
+ return 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(void)
+{
+ printf("ma_control_file_test: unit test for the control file "
+ "module of the Aria storage engine. Ver 1.0 \n");
+}
+
+static my_bool
+get_one_option(const struct my_option *opt,
+ const char *argument __attribute__((unused)),
+ const char *filename __attribute__((unused)))
+{
+ switch(opt->id) {
+ 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(void)
+{
+ printf("Usage: %s [options]\n\n", my_progname);
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+
+#include "../ma_check_standalone.h"