diff options
Diffstat (limited to 'storage/maria/unittest')
28 files changed, 10350 insertions, 0 deletions
diff --git a/storage/maria/unittest/CMakeLists.txt b/storage/maria/unittest/CMakeLists.txt new file mode 100644 index 00000000..a2da1507 --- /dev/null +++ b/storage/maria/unittest/CMakeLists.txt @@ -0,0 +1,136 @@ +# Copyright (C) 2007 MySQL 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 St, Fifth Floor, Boston, MA 02110-1335 USA + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/zlib + ${CMAKE_SOURCE_DIR}/unittest/mytap) +LINK_LIBRARIES(aria myisam mytap mysys dbug strings ${ZLIB_LIBRARY}) + +MY_ADD_TESTS(ma_control_file trnman) + +ADD_EXECUTABLE(ma_test_loghandler-t + ma_test_loghandler-t.c ma_maria_log_cleanup.c ma_loghandler_examples.c) +MY_ADD_TEST(ma_test_loghandler) + +ADD_EXECUTABLE(ma_test_loghandler_multigroup-t + ma_test_loghandler_multigroup-t.c ma_maria_log_cleanup.c ma_loghandler_examples.c sequence_storage.c) +MY_ADD_TEST(ma_test_loghandler_multigroup) + +ADD_EXECUTABLE(ma_test_loghandler_multithread-t + ma_test_loghandler_multithread-t.c ma_maria_log_cleanup.c ma_loghandler_examples.c) +MY_ADD_TEST(ma_test_loghandler_multithread) + +ADD_EXECUTABLE(ma_test_loghandler_pagecache-t + ma_test_loghandler_pagecache-t.c ma_maria_log_cleanup.c ma_loghandler_examples.c) +MY_ADD_TEST(ma_test_loghandler_pagecache) + +ADD_EXECUTABLE(ma_test_loghandler_long-t + ma_test_loghandler-t.c ma_maria_log_cleanup.c ma_loghandler_examples.c) +SET_TARGET_PROPERTIES(ma_test_loghandler_long-t PROPERTIES COMPILE_FLAGS "-DLONG_LOG_TEST") +MY_ADD_TEST(ma_test_loghandler_long) + +ADD_EXECUTABLE(ma_test_loghandler_noflush-t + ma_test_loghandler_noflush-t.c ma_maria_log_cleanup.c ma_loghandler_examples.c) +MY_ADD_TEST(ma_test_loghandler_noflush) + +ADD_EXECUTABLE(ma_test_loghandler_first_lsn-t + ma_test_loghandler_first_lsn-t.c ma_maria_log_cleanup.c ma_loghandler_examples.c) +MY_ADD_TEST(ma_test_loghandler_first_lsn) + +ADD_EXECUTABLE(ma_test_loghandler_max_lsn-t + ma_test_loghandler_max_lsn-t.c ma_maria_log_cleanup.c ma_loghandler_examples.c) +MY_ADD_TEST(ma_test_loghandler_max_lsn) + +ADD_EXECUTABLE(ma_test_loghandler_purge-t + ma_test_loghandler_purge-t.c ma_maria_log_cleanup.c ma_loghandler_examples.c) +MY_ADD_TEST(ma_test_loghandler_purge) + +ADD_EXECUTABLE(ma_test_loghandler_readonly-t + ma_test_loghandler_multigroup-t.c ma_maria_log_cleanup.c ma_loghandler_examples.c sequence_storage.c) +MY_ADD_TEST(ma_test_loghandler_readonly) + +SET_TARGET_PROPERTIES(ma_test_loghandler_readonly-t PROPERTIES COMPILE_FLAGS "-DREADONLY_TEST") +ADD_EXECUTABLE(ma_test_loghandler_nologs-t + ma_test_loghandler_nologs-t.c ma_maria_log_cleanup.c ma_loghandler_examples.c) +MY_ADD_TEST(ma_test_loghandler_nologs) + +SET(ma_pagecache_single_src ma_pagecache_single.c test_file.c test_file.h) +SET(ma_pagecache_consist_src ma_pagecache_consist.c test_file.c test_file.h) +SET(ma_pagecache_common_cppflags "-DEXTRA_DEBUG -DPAGECACHE_DEBUG -DMAIN") + +ADD_EXECUTABLE(ma_pagecache_single_1k-t ${ma_pagecache_single_src}) +SET_TARGET_PROPERTIES(ma_pagecache_single_1k-t + PROPERTIES COMPILE_FLAGS "${ma_pagecache_common_cppflags} -DTEST_PAGE_SIZE=1024") +MY_ADD_TEST(ma_pagecache_single_1k) + +ADD_EXECUTABLE(ma_pagecache_single_8k-t ${ma_pagecache_single_src}) +SET_TARGET_PROPERTIES(ma_pagecache_single_8k-t + PROPERTIES COMPILE_FLAGS "${ma_pagecache_common_cppflags} -DTEST_PAGE_SIZE=8192 -DBIG") +MY_ADD_TEST(ma_pagecache_single_8k) + +ADD_EXECUTABLE(ma_pagecache_single_64k-t ${ma_pagecache_single_src}) +SET_TARGET_PROPERTIES(ma_pagecache_single_64k-t + PROPERTIES COMPILE_FLAGS "${ma_pagecache_common_cppflags} -DTEST_PAGE_SIZE=65536 -DBIG") +MY_ADD_TEST(ma_pagecache_single_64k) + +ADD_EXECUTABLE(ma_pagecache_consist_1k-t ${ma_pagecache_consist_src}) +SET_TARGET_PROPERTIES(ma_pagecache_consist_1k-t + PROPERTIES COMPILE_FLAGS "${ma_pagecache_common_cppflags} -DTEST_PAGE_SIZE=1024") +MY_ADD_TEST(ma_pagecache_consist_1k) + +ADD_EXECUTABLE(ma_pagecache_consist_64k-t ${ma_pagecache_consist_src}) +SET_TARGET_PROPERTIES(ma_pagecache_consist_64k-t + PROPERTIES COMPILE_FLAGS "${ma_pagecache_common_cppflags} -DTEST_PAGE_SIZE=65536") +MY_ADD_TEST(ma_pagecache_consist_64k) + +ADD_EXECUTABLE(ma_pagecache_consist_1kHC-t + ${ma_pagecache_consist_src}) +SET_TARGET_PROPERTIES(ma_pagecache_consist_1kHC-t + PROPERTIES COMPILE_FLAGS "${ma_pagecache_common_cppflags} -DTEST_PAGE_SIZE=1024 -DTEST_HIGH_CONCURENCY") +MY_ADD_TEST(ma_pagecache_consist_1kHC) + +ADD_EXECUTABLE(ma_pagecache_consist_64kHC-t + ${ma_pagecache_consist_src}) +SET_TARGET_PROPERTIES(ma_pagecache_consist_64kHC-t + PROPERTIES COMPILE_FLAGS "${ma_pagecache_common_cppflags} -DTEST_PAGE_SIZE=65536 -DTEST_HIGH_CONCURENCY") +MY_ADD_TEST(ma_pagecache_consist_64kHC) + +ADD_EXECUTABLE(ma_pagecache_consist_1kRD-t ${ma_pagecache_consist_src}) +SET_TARGET_PROPERTIES(ma_pagecache_consist_1kRD-t + PROPERTIES COMPILE_FLAGS "${ma_pagecache_common_cppflags} -DTEST_PAGE_SIZE=1024 -DTEST_READERS") +MY_ADD_TEST(ma_pagecache_consist_1kRD) + +ADD_EXECUTABLE(ma_pagecache_consist_64kRD-t ${ma_pagecache_consist_src}) +SET_TARGET_PROPERTIES(ma_pagecache_consist_64kRD-t + PROPERTIES COMPILE_FLAGS "${ma_pagecache_common_cppflags} -DTEST_PAGE_SIZE=65536 -DTEST_READERS") +MY_ADD_TEST(ma_pagecache_consist_64kRD) + +ADD_EXECUTABLE(ma_pagecache_consist_1kWR-t ${ma_pagecache_consist_src}) +SET_TARGET_PROPERTIES(ma_pagecache_consist_1kWR-t + PROPERTIES COMPILE_FLAGS "${ma_pagecache_common_cppflags} -DTEST_PAGE_SIZE=1024 -DTEST_WRITERS") +MY_ADD_TEST(ma_pagecache_consist_1kWR) + +ADD_EXECUTABLE(ma_pagecache_consist_64kWR-t ${ma_pagecache_consist_src}) +SET_TARGET_PROPERTIES(ma_pagecache_consist_64kWR-t + PROPERTIES COMPILE_FLAGS "${ma_pagecache_common_cppflags} -DTEST_PAGE_SIZE=65536 -DTEST_WRITERS") +MY_ADD_TEST(ma_pagecache_consist_64kWR) + +ADD_EXECUTABLE(ma_pagecache_rwconsist_1k-t ma_pagecache_rwconsist.c) +SET_TARGET_PROPERTIES(ma_pagecache_rwconsist_1k-t PROPERTIES COMPILE_FLAGS "-DTEST_PAGE_SIZE=1024") +MY_ADD_TEST(ma_pagecache_rwconsist_1k) + +ADD_EXECUTABLE(ma_pagecache_rwconsist2_1k-t ma_pagecache_rwconsist2.c) +SET_TARGET_PROPERTIES(ma_pagecache_rwconsist2_1k-t PROPERTIES COMPILE_FLAGS "-DTEST_PAGE_SIZE=1024") +MY_ADD_TEST(ma_pagecache_rwconsist2_1k) + diff --git a/storage/maria/unittest/lockman-t.c b/storage/maria/unittest/lockman-t.c new file mode 100644 index 00000000..222618ac --- /dev/null +++ b/storage/maria/unittest/lockman-t.c @@ -0,0 +1,303 @@ +/* Copyright (C) 2006 MySQL 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 */ + +/* + lockman for row and table locks +*/ + +/* #define EXTRA_VERBOSE */ + +#include <tap.h> + +#include <my_global.h> +#include <my_sys.h> +#include <lf.h> +#include "../lockman.h" + +#define Nlos 100 +LOCK_OWNER loarray[Nlos]; +pthread_mutex_t mutexes[Nlos]; +pthread_cond_t conds[Nlos]; +LOCKMAN lockman; + +#ifndef EXTRA_VERBOSE +#define print_lockhash(X) /* no-op */ +#define DIAG(X) /* no-op */ +#else +#define DIAG(X) diag X +#endif + +LOCK_OWNER *loid2lo(uint16 loid) +{ + return loarray+loid-1; +} + +#define unlock_all(O) diag("lo" #O "> release all locks"); \ + lockman_release_locks(&lockman, loid2lo(O));print_lockhash(&lockman) +#define test_lock(O, R, L, S, RES) \ + ok(lockman_getlock(&lockman, loid2lo(O), R, L) == RES, \ + "lo" #O "> " S "lock resource " #R " with " #L "-lock"); \ + print_lockhash(&lockman) +#define lock_ok_a(O, R, L) \ + test_lock(O, R, L, "", GOT_THE_LOCK) +#define lock_ok_i(O, R, L) \ + test_lock(O, R, L, "", GOT_THE_LOCK_NEED_TO_LOCK_A_SUBRESOURCE) +#define lock_ok_l(O, R, L) \ + test_lock(O, R, L, "", GOT_THE_LOCK_NEED_TO_INSTANT_LOCK_A_SUBRESOURCE) +#define lock_conflict(O, R, L) \ + test_lock(O, R, L, "cannot ", DIDNT_GET_THE_LOCK); + +void test_lockman_simple() +{ + /* simple */ + lock_ok_a(1, 1, S); + lock_ok_i(2, 2, IS); + lock_ok_i(1, 2, IX); + /* lock escalation */ + lock_ok_a(1, 1, X); + lock_ok_i(2, 2, IX); + /* failures */ + lock_conflict(2, 1, X); + unlock_all(2); + lock_ok_a(1, 2, S); + lock_ok_a(1, 2, IS); + lock_ok_a(1, 2, LS); + lock_ok_i(1, 3, IX); + lock_ok_a(2, 3, LS); + lock_ok_i(1, 3, IX); + lock_ok_l(2, 3, IS); + unlock_all(1); + unlock_all(2); + + lock_ok_i(1, 1, IX); + lock_conflict(2, 1, S); + lock_ok_a(1, 1, LS); + unlock_all(1); + unlock_all(2); + + lock_ok_i(1, 1, IX); + lock_ok_a(2, 1, LS); + lock_ok_a(1, 1, LS); + lock_ok_i(1, 1, IX); + lock_ok_i(3, 1, IS); + unlock_all(1); + unlock_all(2); + unlock_all(3); + + lock_ok_i(1, 4, IS); + lock_ok_i(2, 4, IS); + lock_ok_i(3, 4, IS); + lock_ok_a(3, 4, LS); + lock_ok_i(4, 4, IS); + lock_conflict(4, 4, IX); + lock_conflict(2, 4, IX); + lock_ok_a(1, 4, LS); + unlock_all(1); + unlock_all(2); + unlock_all(3); + unlock_all(4); + + lock_ok_i(1, 1, IX); + lock_ok_i(2, 1, IX); + lock_conflict(1, 1, S); + lock_conflict(2, 1, X); + unlock_all(1); + unlock_all(2); +} + +int rt_num_threads; +int litmus; +int thread_number= 0, timeouts= 0; +void run_test(const char *test, pthread_handler handler, int n, int m) +{ + pthread_t *threads; + ulonglong now= my_getsystime(); + int i; + + thread_number= timeouts= 0; + litmus= 0; + + threads= (pthread_t *)my_malloc(sizeof(void *)*n, MYF(0)); + if (!threads) + { + diag("Out of memory"); + abort(); + } + + diag("Running %s with %d threads, %d iterations... ", test, n, m); + rt_num_threads= n; + for (i= 0; i < n ; i++) + if (pthread_create(threads+i, 0, handler, &m)) + { + diag("Could not create thread"); + abort(); + } + for (i= 0 ; i < n ; i++) + pthread_join(threads[i], 0); + now= my_getsystime()-now; + ok(litmus == 0, "Finished %s in %g secs (%d)", test, ((double)now)/1e7, litmus); + my_free((void*)threads, MYF(0)); +} + +pthread_mutex_t rt_mutex; +int Nrows= 100; +int Ntables= 10; +int table_lock_ratio= 10; +enum lockman_lock_type lock_array[6]= {S, X, LS, LX, IS, IX}; +char *lock2str[6]= {"S", "X", "LS", "LX", "IS", "IX"}; +char *res2str[4]= { + "DIDN'T GET THE LOCK", + "GOT THE LOCK", + "GOT THE LOCK NEED TO LOCK A SUBRESOURCE", + "GOT THE LOCK NEED TO INSTANT LOCK A SUBRESOURCE"}; +pthread_handler_t test_lockman(void *arg) +{ + int m= (*(int *)arg); + uint x, loid, row, table, res, locklevel, timeout= 0; + LOCK_OWNER *lo; + + pthread_mutex_lock(&rt_mutex); + loid= ++thread_number; + pthread_mutex_unlock(&rt_mutex); + lo= loid2lo(loid); + + for (x= ((int)(intptr)(&m)); m > 0; m--) + { + x= (x*3628273133 + 1500450271) % 9576890767; /* three prime numbers */ + row= x % Nrows + Ntables; + table= row % Ntables; + locklevel= (x/Nrows) & 3; + if (table_lock_ratio && (x/Nrows/4) % table_lock_ratio == 0) + { /* table lock */ + res= lockman_getlock(&lockman, lo, table, lock_array[locklevel]); + DIAG(("loid %2d, table %d, lock %s, res %s", loid, table, + lock2str[locklevel], res2str[res])); + if (res == DIDNT_GET_THE_LOCK) + { + lockman_release_locks(&lockman, lo); + DIAG(("loid %2d, release all locks", loid)); + timeout++; + continue; + } + DBUG_ASSERT(res == GOT_THE_LOCK); + } + else + { /* row lock */ + locklevel&= 1; + res= lockman_getlock(&lockman, lo, table, lock_array[locklevel + 4]); + DIAG(("loid %2d, row %d, lock %s, res %s", loid, row, + lock2str[locklevel+4], res2str[res])); + switch (res) + { + case DIDNT_GET_THE_LOCK: + lockman_release_locks(&lockman, lo); + DIAG(("loid %2d, release all locks", loid)); + timeout++; + continue; + case GOT_THE_LOCK: + continue; + case GOT_THE_LOCK_NEED_TO_INSTANT_LOCK_A_SUBRESOURCE: + /* not implemented, so take a regular lock */ + case GOT_THE_LOCK_NEED_TO_LOCK_A_SUBRESOURCE: + res= lockman_getlock(&lockman, lo, row, lock_array[locklevel]); + DIAG(("loid %2d, ROW %d, lock %s, res %s", loid, row, + lock2str[locklevel], res2str[res])); + if (res == DIDNT_GET_THE_LOCK) + { + lockman_release_locks(&lockman, lo); + DIAG(("loid %2d, release all locks", loid)); + timeout++; + continue; + } + DBUG_ASSERT(res == GOT_THE_LOCK); + continue; + default: + DBUG_ASSERT(0); + } + } + } + + lockman_release_locks(&lockman, lo); + + pthread_mutex_lock(&rt_mutex); + rt_num_threads--; + timeouts+= timeout; + if (!rt_num_threads) + diag("number of timeouts: %d", timeouts); + pthread_mutex_unlock(&rt_mutex); + + return 0; +} + +int main() +{ + int i; + + my_init(); + pthread_mutex_init(&rt_mutex, 0); + + plan(35); + + lockman_init(&lockman, &loid2lo, 50); + + for (i= 0; i < Nlos; i++) + { + loarray[i].pins= lf_alloc_get_pins(&lockman.alloc); + loarray[i].all_locks= 0; + loarray[i].waiting_for= 0; + pthread_mutex_init(&mutexes[i], MY_MUTEX_INIT_FAST); + pthread_cond_init (&conds[i], 0); + loarray[i].mutex= &mutexes[i]; + loarray[i].cond= &conds[i]; + loarray[i].loid= i+1; + } + + test_lockman_simple(); + +#define CYCLES 10000 +#define THREADS Nlos /* don't change this line */ + + /* mixed load, stress-test with random locks */ + Nrows= 100; + Ntables= 10; + table_lock_ratio= 10; + run_test("\"random lock\" stress test", test_lockman, THREADS, CYCLES); + + /* "real-life" simulation - many rows, no table locks */ + Nrows= 1000000; + Ntables= 10; + table_lock_ratio= 0; + run_test("\"real-life\" simulation test", test_lockman, THREADS, CYCLES*10); + + for (i= 0; i < Nlos; i++) + { + lockman_release_locks(&lockman, &loarray[i]); + pthread_mutex_destroy(loarray[i].mutex); + pthread_cond_destroy(loarray[i].cond); + lf_pinbox_put_pins(loarray[i].pins); + } + + { + ulonglong now= my_getsystime(); + lockman_destroy(&lockman); + now= my_getsystime()-now; + diag("lockman_destroy: %g secs", ((double)now)/1e7); + } + + pthread_mutex_destroy(&rt_mutex); + my_end(0); + return exit_status(); +} + diff --git a/storage/maria/unittest/lockman1-t.c b/storage/maria/unittest/lockman1-t.c new file mode 100644 index 00000000..986ac73e --- /dev/null +++ b/storage/maria/unittest/lockman1-t.c @@ -0,0 +1,329 @@ +/* Copyright (C) 2006 MySQL 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 */ + +/* + lockman for row locks, tablockman for table locks +*/ + +/* #define EXTRA_VERBOSE */ + +#include <tap.h> + +#include <my_global.h> +#include <my_sys.h> +#include <lf.h> +#include "../lockman.h" +#include "../tablockman.h" + +#define Nlos 100 +#define Ntbls 10 +LOCK_OWNER loarray[Nlos]; +TABLE_LOCK_OWNER loarray1[Nlos]; +pthread_mutex_t mutexes[Nlos]; +pthread_cond_t conds[Nlos]; +LOCKED_TABLE ltarray[Ntbls]; +LOCKMAN lockman; +TABLOCKMAN tablockman; + +#ifndef EXTRA_VERBOSE +#define print_lo1(X) /* no-op */ +#define DIAG(X) /* no-op */ +#else +#define DIAG(X) diag X +#endif + +LOCK_OWNER *loid2lo(uint16 loid) +{ + return loarray+loid-1; +} +TABLE_LOCK_OWNER *loid2lo1(uint16 loid) +{ + return loarray1+loid-1; +} + +#define unlock_all(O) diag("lo" #O "> release all locks"); \ + tablockman_release_locks(&tablockman, loid2lo1(O)); +#define test_lock(O, R, L, S, RES) \ + ok(tablockman_getlock(&tablockman, loid2lo1(O), <array[R], L) == RES, \ + "lo" #O "> " S "lock resource " #R " with " #L "-lock"); \ + print_lo1(loid2lo1(O)); +#define lock_ok_a(O, R, L) \ + test_lock(O, R, L, "", GOT_THE_LOCK) +#define lock_ok_i(O, R, L) \ + test_lock(O, R, L, "", GOT_THE_LOCK_NEED_TO_LOCK_A_SUBRESOURCE) +#define lock_ok_l(O, R, L) \ + test_lock(O, R, L, "", GOT_THE_LOCK_NEED_TO_INSTANT_LOCK_A_SUBRESOURCE) +#define lock_conflict(O, R, L) \ + test_lock(O, R, L, "cannot ", LOCK_TIMEOUT); + +void test_tablockman_simple() +{ + /* simple */ + lock_ok_a(1, 1, S); + lock_ok_i(2, 2, IS); + lock_ok_i(1, 2, IX); + /* lock escalation */ + lock_ok_a(1, 1, X); + lock_ok_i(2, 2, IX); + /* failures */ + lock_conflict(2, 1, X); + unlock_all(2); + lock_ok_a(1, 2, S); + lock_ok_a(1, 2, IS); + lock_ok_a(1, 2, LS); + lock_ok_i(1, 3, IX); + lock_ok_a(2, 3, LS); + lock_ok_i(1, 3, IX); + lock_ok_l(2, 3, IS); + unlock_all(1); + unlock_all(2); + + lock_ok_i(1, 1, IX); + lock_conflict(2, 1, S); + lock_ok_a(1, 1, LS); + unlock_all(1); + unlock_all(2); + + lock_ok_i(1, 1, IX); + lock_ok_a(2, 1, LS); + lock_ok_a(1, 1, LS); + lock_ok_i(1, 1, IX); + lock_ok_i(3, 1, IS); + unlock_all(1); + unlock_all(2); + unlock_all(3); + + lock_ok_i(1, 4, IS); + lock_ok_i(2, 4, IS); + lock_ok_i(3, 4, IS); + lock_ok_a(3, 4, LS); + lock_ok_i(4, 4, IS); + lock_conflict(4, 4, IX); + lock_conflict(2, 4, IX); + lock_ok_a(1, 4, LS); + unlock_all(1); + unlock_all(2); + unlock_all(3); + unlock_all(4); + + lock_ok_i(1, 1, IX); + lock_ok_i(2, 1, IX); + lock_conflict(1, 1, S); + lock_conflict(2, 1, X); + unlock_all(1); + unlock_all(2); +} + +int rt_num_threads; +int litmus; +int thread_number= 0, timeouts= 0; +void run_test(const char *test, pthread_handler handler, int n, int m) +{ + pthread_t *threads; + ulonglong now= my_getsystime(); + int i; + + thread_number= timeouts= 0; + litmus= 0; + + threads= (pthread_t *)my_malloc(sizeof(void *)*n, MYF(0)); + if (!threads) + { + diag("Out of memory"); + abort(); + } + + diag("Running %s with %d threads, %d iterations... ", test, n, m); + rt_num_threads= n; + for (i= 0; i < n ; i++) + if (pthread_create(threads+i, 0, handler, &m)) + { + diag("Could not create thread"); + abort(); + } + for (i= 0 ; i < n ; i++) + pthread_join(threads[i], 0); + now= my_getsystime()-now; + ok(litmus == 0, "Finished %s in %g secs (%d)", test, ((double)now)/1e7, litmus); + my_free((void*)threads, MYF(0)); +} + +pthread_mutex_t rt_mutex; +int Nrows= 100; +int Ntables= 10; +int table_lock_ratio= 10; +enum lockman_lock_type lock_array[6]= {S, X, LS, LX, IS, IX}; +char *lock2str[6]= {"S", "X", "LS", "LX", "IS", "IX"}; +char *res2str[]= { + "DIDN'T GET THE LOCK", + "OUT OF MEMORY", + "DEADLOCK", + "LOCK TIMEOUT", + "GOT THE LOCK", + "GOT THE LOCK NEED TO LOCK A SUBRESOURCE", + "GOT THE LOCK NEED TO INSTANT LOCK A SUBRESOURCE"}; +pthread_handler_t test_lockman(void *arg) +{ + int m= (*(int *)arg); + uint x, loid, row, table, res, locklevel, timeout= 0; + LOCK_OWNER *lo; TABLE_LOCK_OWNER *lo1; DBUG_ASSERT(Ntables <= Ntbls); + + pthread_mutex_lock(&rt_mutex); + loid= ++thread_number; + pthread_mutex_unlock(&rt_mutex); + lo= loid2lo(loid); lo1= loid2lo1(loid); + + for (x= ((int)(intptr)(&m)); m > 0; m--) + { + x= (x*3628273133 + 1500450271) % 9576890767; /* three prime numbers */ + row= x % Nrows + Ntables; + table= row % Ntables; + locklevel= (x/Nrows) & 3; + if (table_lock_ratio && (x/Nrows/4) % table_lock_ratio == 0) + { /* table lock */ + res= tablockman_getlock(&tablockman, lo1, ltarray+table, lock_array[locklevel]); + DIAG(("loid %2d, table %d, lock %s, res %s", loid, table, + lock2str[locklevel], res2str[res])); + if (res < GOT_THE_LOCK) + { + lockman_release_locks(&lockman, lo); tablockman_release_locks(&tablockman, lo1); + DIAG(("loid %2d, release all locks", loid)); + timeout++; + continue; + } + DBUG_ASSERT(res == GOT_THE_LOCK); + } + else + { /* row lock */ + locklevel&= 1; + res= tablockman_getlock(&tablockman, lo1, ltarray+table, lock_array[locklevel + 4]); + DIAG(("loid %2d, row %d, lock %s, res %s", loid, row, + lock2str[locklevel+4], res2str[res])); + switch (res) + { + case GOT_THE_LOCK: + continue; + case GOT_THE_LOCK_NEED_TO_INSTANT_LOCK_A_SUBRESOURCE: + /* not implemented, so take a regular lock */ + case GOT_THE_LOCK_NEED_TO_LOCK_A_SUBRESOURCE: + res= lockman_getlock(&lockman, lo, row, lock_array[locklevel]); + DIAG(("loid %2d, ROW %d, lock %s, res %s", loid, row, + lock2str[locklevel], res2str[res])); + if (res == DIDNT_GET_THE_LOCK) + { + lockman_release_locks(&lockman, lo); + tablockman_release_locks(&tablockman, lo1); + DIAG(("loid %2d, release all locks", loid)); + timeout++; + continue; + } + DBUG_ASSERT(res == GOT_THE_LOCK); + continue; + default: + lockman_release_locks(&lockman, lo); tablockman_release_locks(&tablockman, lo1); + DIAG(("loid %2d, release all locks", loid)); + timeout++; + continue; + } + } + } + + lockman_release_locks(&lockman, lo); + tablockman_release_locks(&tablockman, lo1); + + pthread_mutex_lock(&rt_mutex); + rt_num_threads--; + timeouts+= timeout; + if (!rt_num_threads) + diag("number of timeouts: %d", timeouts); + pthread_mutex_unlock(&rt_mutex); + + return 0; +} + +int main() +{ + int i; + + my_init(); + pthread_mutex_init(&rt_mutex, 0); + + plan(35); + + lockman_init(&lockman, &loid2lo, 50); + tablockman_init(&tablockman, &loid2lo1, 50); + + for (i= 0; i < Nlos; i++) + { + pthread_mutex_init(&mutexes[i], MY_MUTEX_INIT_FAST); + pthread_cond_init (&conds[i], 0); + + loarray[i].pins= lf_alloc_get_pins(&lockman.alloc); + loarray[i].all_locks= 0; + loarray[i].waiting_for= 0; + loarray[i].mutex= &mutexes[i]; + loarray[i].cond= &conds[i]; + loarray[i].loid= i+1; + + loarray1[i].active_locks= 0; + loarray1[i].waiting_lock= 0; + loarray1[i].waiting_for= 0; + loarray1[i].mutex= &mutexes[i]; + loarray1[i].cond= &conds[i]; + loarray1[i].loid= i+1; + } + + for (i= 0; i < Ntbls; i++) + { + tablockman_init_locked_table(ltarray+i, Nlos); + } + + test_tablockman_simple(); + +#define CYCLES 10000 +#define THREADS Nlos /* don't change this line */ + + /* mixed load, stress-test with random locks */ + Nrows= 100; + Ntables= 10; + table_lock_ratio= 10; + run_test("\"random lock\" stress test", test_lockman, THREADS, CYCLES); + + /* "real-life" simulation - many rows, no table locks */ + Nrows= 1000000; + Ntables= 10; + table_lock_ratio= 0; + run_test("\"real-life\" simulation test", test_lockman, THREADS, CYCLES*10); + + for (i= 0; i < Nlos; i++) + { + lockman_release_locks(&lockman, &loarray[i]); + pthread_mutex_destroy(loarray[i].mutex); + pthread_cond_destroy(loarray[i].cond); + lf_pinbox_put_pins(loarray[i].pins); + } + + { + ulonglong now= my_getsystime(); + lockman_destroy(&lockman); + now= my_getsystime()-now; + diag("lockman_destroy: %g secs", ((double)now)/1e7); + } + + pthread_mutex_destroy(&rt_mutex); + my_end(0); + return exit_status(); +} + diff --git a/storage/maria/unittest/lockman2-t.c b/storage/maria/unittest/lockman2-t.c new file mode 100644 index 00000000..7f9ed3cc --- /dev/null +++ b/storage/maria/unittest/lockman2-t.c @@ -0,0 +1,356 @@ +/* Copyright (C) 2006 MySQL 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 */ + +/* + tablockman for row and table locks +*/ + +/* #define EXTRA_VERBOSE */ + +#include <tap.h> + +#include <my_global.h> +#include <my_sys.h> +#include <lf.h> +#include "../tablockman.h" + +#define Nlos 100 +#define Ntbls 110 +TABLE_LOCK_OWNER loarray1[Nlos]; +pthread_mutex_t mutexes[Nlos]; +pthread_cond_t conds[Nlos]; +LOCKED_TABLE ltarray[Ntbls]; +TABLOCKMAN tablockman; + +#ifndef EXTRA_VERBOSE +#define print_lo1(X) /* no-op */ +#define DIAG(X) /* no-op */ +#else +#define DIAG(X) diag X +#endif + +TABLE_LOCK_OWNER *loid2lo1(uint16 loid) +{ + return loarray1+loid-1; +} + +#define unlock_all(O) diag("lo" #O "> release all locks"); \ + tablockman_release_locks(&tablockman, loid2lo1(O)); +#define test_lock(O, R, L, S, RES) \ + ok(tablockman_getlock(&tablockman, loid2lo1(O), <array[R], L) == RES, \ + "lo" #O "> " S "lock resource " #R " with " #L "-lock"); \ + print_lo1(loid2lo1(O)); +#define lock_ok_a(O, R, L) \ + test_lock(O, R, L, "", GOT_THE_LOCK) +#define lock_ok_i(O, R, L) \ + test_lock(O, R, L, "", GOT_THE_LOCK_NEED_TO_LOCK_A_SUBRESOURCE) +#define lock_ok_l(O, R, L) \ + test_lock(O, R, L, "", GOT_THE_LOCK_NEED_TO_INSTANT_LOCK_A_SUBRESOURCE) +#define lock_conflict(O, R, L) \ + test_lock(O, R, L, "cannot ", LOCK_TIMEOUT); + +void test_tablockman_simple() +{ + /* simple */ + lock_ok_a(1, 1, S); + lock_ok_i(2, 2, IS); + lock_ok_i(1, 2, IX); + /* lock escalation */ + lock_ok_a(1, 1, X); + lock_ok_i(2, 2, IX); + /* failures */ + lock_conflict(2, 1, X); + unlock_all(2); + lock_ok_a(1, 2, S); + lock_ok_a(1, 2, IS); + lock_ok_a(1, 2, LS); + lock_ok_i(1, 3, IX); + lock_ok_a(2, 3, LS); + lock_ok_i(1, 3, IX); + lock_ok_l(2, 3, IS); + unlock_all(1); + unlock_all(2); + + lock_ok_i(1, 1, IX); + lock_conflict(2, 1, S); + lock_ok_a(1, 1, LS); + unlock_all(1); + unlock_all(2); + + lock_ok_i(1, 1, IX); + lock_ok_a(2, 1, LS); + lock_ok_a(1, 1, LS); + lock_ok_i(1, 1, IX); + lock_ok_i(3, 1, IS); + unlock_all(1); + unlock_all(2); + unlock_all(3); + + lock_ok_i(1, 4, IS); + lock_ok_i(2, 4, IS); + lock_ok_i(3, 4, IS); + lock_ok_a(3, 4, LS); + lock_ok_i(4, 4, IS); + lock_conflict(4, 4, IX); + lock_conflict(2, 4, IX); + lock_ok_a(1, 4, LS); + unlock_all(1); + unlock_all(2); + unlock_all(3); + unlock_all(4); + + lock_ok_i(1, 1, IX); + lock_ok_i(2, 1, IX); + lock_conflict(1, 1, S); + lock_conflict(2, 1, X); + unlock_all(1); + unlock_all(2); + + lock_ok_i(1, 1, IS); + lock_conflict(2, 1, X); + lock_conflict(3, 1, IS); + unlock_all(1); + unlock_all(2); + unlock_all(3); + + lock_ok_a(1, 1, S); + lock_conflict(2, 1, IX); + lock_conflict(3, 1, IS); + unlock_all(1); + unlock_all(2); + unlock_all(3); +} + +int rt_num_threads; +int litmus; +int thread_number= 0, timeouts= 0; +void run_test(const char *test, pthread_handler handler, int n, int m) +{ + pthread_t *threads; + ulonglong now= my_getsystime(); + int i; + + thread_number= timeouts= 0; + litmus= 0; + + threads= (pthread_t *)my_malloc(sizeof(void *)*n, MYF(0)); + if (!threads) + { + diag("Out of memory"); + abort(); + } + + diag("Running %s with %d threads, %d iterations... ", test, n, m); + rt_num_threads= n; + for (i= 0; i < n ; i++) + if (pthread_create(threads+i, 0, handler, &m)) + { + diag("Could not create thread"); + abort(); + } + for (i= 0 ; i < n ; i++) + pthread_join(threads[i], 0); + now= my_getsystime()-now; + ok(litmus == 0, "Finished %s in %g secs (%d)", test, ((double)now)/1e7, litmus); + my_free((void*)threads, MYF(0)); +} + +static void reinit_tlo(TABLOCKMAN *lm, TABLE_LOCK_OWNER *lo) +{ +#ifdef NOT_USED_YET + TABLE_LOCK_OWNER backup= *lo; +#endif + + tablockman_release_locks(lm, lo); +#ifdef NOT_USED_YET + pthread_mutex_destroy(lo->mutex); + pthread_cond_destroy(lo->cond); + bzero(lo, sizeof(*lo)); + + lo->mutex= backup.mutex; + lo->cond= backup.cond; + lo->loid= backup.loid; + pthread_mutex_init(lo->mutex, MY_MUTEX_INIT_FAST); + pthread_cond_init(lo->cond, 0); +#endif +} + +pthread_mutex_t rt_mutex; +int Nrows= 100; +int Ntables= 10; +int table_lock_ratio= 10; +enum lockman_lock_type lock_array[6]= {S, X, LS, LX, IS, IX}; +const char *lock2str[6]= {"S", "X", "LS", "LX", "IS", "IX"}; +const char *res2str[]= { + 0, + "OUT OF MEMORY", + "DEADLOCK", + "LOCK TIMEOUT", + "GOT THE LOCK", + "GOT THE LOCK NEED TO LOCK A SUBRESOURCE", + "GOT THE LOCK NEED TO INSTANT LOCK A SUBRESOURCE"}; + +pthread_handler_t test_lockman(void *arg) +{ + int m= (*(int *)arg); + uint x, loid, row, table, res, locklevel, timeout= 0; + TABLE_LOCK_OWNER *lo1; + DBUG_ASSERT(Ntables <= Ntbls); + DBUG_ASSERT(Nrows + Ntables <= Ntbls); + + pthread_mutex_lock(&rt_mutex); + loid= ++thread_number; + pthread_mutex_unlock(&rt_mutex); + lo1= loid2lo1(loid); + + for (x= ((int)(intptr)(&m)); m > 0; m--) + { + /* three prime numbers */ + x= (uint) ((x*LL(3628273133) + LL(1500450271)) % LL(9576890767)); + row= x % Nrows + Ntables; + table= row % Ntables; + locklevel= (x/Nrows) & 3; + if (table_lock_ratio && (x/Nrows/4) % table_lock_ratio == 0) + { + /* table lock */ + res= tablockman_getlock(&tablockman, lo1, ltarray+table, + lock_array[locklevel]); + DIAG(("loid %2d, table %d, lock %s, res %s", loid, table, + lock2str[locklevel], res2str[res])); + if (res < GOT_THE_LOCK) + { + reinit_tlo(&tablockman, lo1); + DIAG(("loid %2d, release all locks", loid)); + timeout++; + continue; + } + DBUG_ASSERT(res == GOT_THE_LOCK); + } + else + { /* row lock */ + locklevel&= 1; + res= tablockman_getlock(&tablockman, lo1, ltarray+table, lock_array[locklevel + 4]); + DIAG(("loid %2d, row %d, lock %s, res %s", loid, row, + lock2str[locklevel+4], res2str[res])); + switch (res) + { + case GOT_THE_LOCK: + continue; + case GOT_THE_LOCK_NEED_TO_INSTANT_LOCK_A_SUBRESOURCE: + /* not implemented, so take a regular lock */ + case GOT_THE_LOCK_NEED_TO_LOCK_A_SUBRESOURCE: + res= tablockman_getlock(&tablockman, lo1, ltarray+row, lock_array[locklevel]); + DIAG(("loid %2d, ROW %d, lock %s, res %s", loid, row, + lock2str[locklevel], res2str[res])); + if (res < GOT_THE_LOCK) + { + reinit_tlo(&tablockman, lo1); + DIAG(("loid %2d, release all locks", loid)); + timeout++; + continue; + } + DBUG_ASSERT(res == GOT_THE_LOCK); + continue; + default: + reinit_tlo(&tablockman, lo1); + DIAG(("loid %2d, release all locks", loid)); + timeout++; + continue; + } + } + } + + reinit_tlo(&tablockman, lo1); + + pthread_mutex_lock(&rt_mutex); + rt_num_threads--; + timeouts+= timeout; + if (!rt_num_threads) + diag("number of timeouts: %d", timeouts); + pthread_mutex_unlock(&rt_mutex); + + return 0; +} + +int main(int argc __attribute__((unused)), char **argv) +{ + int i; + MY_INIT(argv[0]); + + my_init(); + pthread_mutex_init(&rt_mutex, 0); + + plan(40); + + tablockman_init(&tablockman, &loid2lo1, 50); + + for (i= 0; i < Nlos; i++) + { + pthread_mutex_init(&mutexes[i], MY_MUTEX_INIT_FAST); + pthread_cond_init (&conds[i], 0); + + loarray1[i].active_locks= 0; + loarray1[i].waiting_lock= 0; + loarray1[i].waiting_for= 0; + loarray1[i].mutex= &mutexes[i]; + loarray1[i].cond= &conds[i]; + loarray1[i].loid= i+1; + } + + for (i= 0; i < Ntbls; i++) + { + tablockman_init_locked_table(ltarray+i, Nlos); + } + + test_tablockman_simple(); + +#define CYCLES 10000 +#define THREADS Nlos /* don't change this line */ + + /* mixed load, stress-test with random locks */ + Nrows= 100; + Ntables= 10; + table_lock_ratio= 10; + run_test("\"random lock\" stress test", test_lockman, THREADS, CYCLES); +#if 0 + /* "real-life" simulation - many rows, no table locks */ + Nrows= 1000000; + Ntables= 10; + table_lock_ratio= 0; + run_test("\"real-life\" simulation test", test_lockman, THREADS, CYCLES*10); +#endif + for (i= 0; i < Nlos; i++) + { + tablockman_release_locks(&tablockman, &loarray1[i]); + pthread_mutex_destroy(loarray1[i].mutex); + pthread_cond_destroy(loarray1[i].cond); + } + + { + ulonglong now= my_getsystime(); + for (i= 0; i < Ntbls; i++) + { + tablockman_destroy_locked_table(ltarray+i); + } + tablockman_destroy(&tablockman); + now= my_getsystime()-now; + diag("lockman_destroy: %g secs", ((double)now)/1e7); + } + + pthread_mutex_destroy(&rt_mutex); + my_end(0); + return exit_status(); +} + 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" diff --git a/storage/maria/unittest/ma_loghandler_examples.c b/storage/maria/unittest/ma_loghandler_examples.c new file mode 100644 index 00000000..422e6961 --- /dev/null +++ b/storage/maria/unittest/ma_loghandler_examples.c @@ -0,0 +1,68 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +#include "../maria_def.h" + +static LOG_DESC INIT_LOGREC_FIXED_RECORD_0LSN_EXAMPLE= +{LOGRECTYPE_FIXEDLENGTH, 6, 6, NULL, NULL, NULL, 0, + "fixed0example", LOGREC_NOT_LAST_IN_GROUP, NULL, NULL}; + +static LOG_DESC INIT_LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE= +{LOGRECTYPE_VARIABLE_LENGTH, 0, 9, NULL, NULL, NULL, 0, +"variable0example", LOGREC_NOT_LAST_IN_GROUP, NULL, NULL}; + +static LOG_DESC INIT_LOGREC_FIXED_RECORD_1LSN_EXAMPLE= +{LOGRECTYPE_PSEUDOFIXEDLENGTH, 7, 7, NULL, NULL, NULL, 1, +"fixed1example", LOGREC_NOT_LAST_IN_GROUP, NULL, NULL}; + +static LOG_DESC INIT_LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE= +{LOGRECTYPE_VARIABLE_LENGTH, 0, 12, NULL, NULL, NULL, 1, +"variable1example", LOGREC_NOT_LAST_IN_GROUP, NULL, NULL}; + +static LOG_DESC INIT_LOGREC_FIXED_RECORD_2LSN_EXAMPLE= +{LOGRECTYPE_PSEUDOFIXEDLENGTH, 23, 23, NULL, NULL, NULL, 2, +"fixed2example", LOGREC_NOT_LAST_IN_GROUP, NULL, NULL}; + +static LOG_DESC INIT_LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE= +{LOGRECTYPE_VARIABLE_LENGTH, 0, 19, NULL, NULL, NULL, 2, +"variable2example", LOGREC_NOT_LAST_IN_GROUP, NULL, NULL}; + + +void translog_example_table_init() +{ + int i; + log_record_type_descriptor[LOGREC_FIXED_RECORD_0LSN_EXAMPLE]= + INIT_LOGREC_FIXED_RECORD_0LSN_EXAMPLE; + log_record_type_descriptor[LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE]= + INIT_LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE; + log_record_type_descriptor[LOGREC_FIXED_RECORD_1LSN_EXAMPLE]= + INIT_LOGREC_FIXED_RECORD_1LSN_EXAMPLE; + log_record_type_descriptor[LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE]= + INIT_LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE; + log_record_type_descriptor[LOGREC_FIXED_RECORD_2LSN_EXAMPLE]= + INIT_LOGREC_FIXED_RECORD_2LSN_EXAMPLE; + log_record_type_descriptor[LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE]= + INIT_LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE; + for (i= LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE + 1; + i < LOGREC_NUMBER_OF_TYPES; + i++) + log_record_type_descriptor[i].rclass= LOGRECTYPE_NOT_ALLOWED; +#ifndef DBUG_OFF + check_translog_description_table(LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE); +#endif +} + + + diff --git a/storage/maria/unittest/ma_maria_log_cleanup.c b/storage/maria/unittest/ma_maria_log_cleanup.c new file mode 100644 index 00000000..a4d0609f --- /dev/null +++ b/storage/maria/unittest/ma_maria_log_cleanup.c @@ -0,0 +1,88 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +#include "../maria_def.h" +#ifdef _WIN32 +#include <direct.h> /* rmdir */ +#endif + +my_bool maria_log_remove(const char *testdir) +{ + MY_DIR *dirp; + size_t i; + MY_STAT stat_buff; + char file_name[FN_REFLEN]; + + /* Removes control file */ + if (fn_format(file_name, CONTROL_FILE_BASE_NAME, + maria_data_root, "", MYF(MY_WME)) == NullS) + return 1; + if (my_stat(file_name, &stat_buff, MYF(0)) && + my_delete(file_name, MYF(MY_WME)) != 0) + return 1; + + /* Finds and removes transaction log files */ + if (!(dirp = my_dir(maria_data_root, MYF(MY_DONT_SORT)))) + return 1; + + for (i= 0; i < dirp->number_of_files; i++) + { + char *file= dirp->dir_entry[i].name; + if (strncmp(file, "aria_log.", 9) == 0 && + file[9] >= '0' && file[9] <= '9' && + file[10] >= '0' && file[10] <= '9' && + file[11] >= '0' && file[11] <= '9' && + file[12] >= '0' && file[12] <= '9' && + file[13] >= '0' && file[13] <= '9' && + file[14] >= '0' && file[14] <= '9' && + file[15] >= '0' && file[15] <= '9' && + file[16] >= '0' && file[16] <= '9' && + file[17] == '\0') + { + if (fn_format(file_name, file, + maria_data_root, "", MYF(MY_WME)) == NullS || + my_delete(file_name, MYF(MY_WME)) != 0) + { + my_dirend(dirp); + return 1; + } + } + } + my_dirend(dirp); + if (testdir) + rmdir(testdir); + return 0; +} + +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; +} diff --git a/storage/maria/unittest/ma_pagecache_consist.c b/storage/maria/unittest/ma_pagecache_consist.c new file mode 100644 index 00000000..ff4a2bcb --- /dev/null +++ b/storage/maria/unittest/ma_pagecache_consist.c @@ -0,0 +1,504 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +/* + TODO: use pthread_join instead of wait_for_thread_count_to_be_zero, like in + my_atomic-t.c (see BUG#22320). +*/ + +#include <tap.h> +#include <my_sys.h> +#include <m_string.h> +#include "test_file.h" +#include <tap.h> + +#define PCACHE_SIZE (TEST_PAGE_SIZE*1024*8) + +#ifndef DBUG_OFF +static const char* default_dbug_option; +#endif + +static const char *base_file1_name= "page_cache_test_file_1"; +static char file1_name[FN_REFLEN]; +static PAGECACHE_FILE file1; +static pthread_cond_t COND_thread_count; +static pthread_mutex_t LOCK_thread_count; +static uint thread_count; +static PAGECACHE pagecache; + +#ifdef TEST_HIGH_CONCURENCY +static uint number_of_readers= 10; +static uint number_of_writers= 20; +static uint number_of_tests= 30000; +static uint record_length_limit= TEST_PAGE_SIZE/200; +static uint number_of_pages= 20; +static uint flush_divider= 1000; +#else /*TEST_HIGH_CONCURENCY*/ +#ifdef TEST_READERS +static uint number_of_readers= 10; +static uint number_of_writers= 1; +static uint number_of_tests= 30000; +static uint record_length_limit= TEST_PAGE_SIZE/200; +static uint number_of_pages= 20; +static uint flush_divider= 1000; +#undef SKIP_BIG_TESTS +#define SKIP_BIG_TESTS(X) /* no-op */ +#else /*TEST_READERS*/ +#ifdef TEST_WRITERS +static uint number_of_readers= 0; +static uint number_of_writers= 10; +static uint number_of_tests= 30000; +static uint record_length_limit= TEST_PAGE_SIZE/200; +static uint number_of_pages= 20; +static uint flush_divider= 1000; +#undef SKIP_BIG_TESTS +#define SKIP_BIG_TESTS(X) /* no-op */ +#else /*TEST_WRITERS*/ +static uint number_of_readers= 10; +static uint number_of_writers= 10; +static uint number_of_tests= 50000; +static uint record_length_limit= TEST_PAGE_SIZE/200; +static uint number_of_pages= 20000; +static uint flush_divider= 1000; +#endif /*TEST_WRITERS*/ +#endif /*TEST_READERS*/ +#endif /*TEST_HIGH_CONCURENCY*/ + + +/* + Get pseudo-random length of the field in (0;limit) + + SYNOPSYS + get_len() + limit limit for generated value + + RETURN + length where length >= 0 & length < limit +*/ + +static uint get_len(uint limit) +{ + return (uint)((ulonglong)rand()*(limit-1)/RAND_MAX); +} + + +/* + Check page's consistency: layout is + 4 bytes: number 'num' of records in this page, then num occurences of + { 4 bytes: record's length 'len'; then 4 bytes unchecked ('tag') then + 'len' bytes each equal to the record's sequential number in this page, + modulo 256 }, then zeroes. + */ +uint check_page(uchar *buff, ulong offset, int page_locked, int page_no, + int tag) +{ + uint end= sizeof(uint); + uint num= uint4korr(buff); + uint i; + DBUG_ENTER("check_page"); + + for (i= 0; i < num; i++) + { + uint len= uint4korr(buff + end); + uint j; + end+= 4 + 4; + if (len + end > TEST_PAGE_SIZE) + { + diag("incorrect field header #%u by offset %lu\n", i, offset + end); + goto err; + } + for(j= 0; j < len; j++) + { + if (buff[end + j] != (uchar)((i+1) % 256)) + { + diag("incorrect %lu byte\n", offset + end + j); + goto err; + } + } + end+= len; + } + for(i= end; i < TEST_PAGE_SIZE; i++) + { + if (buff[i] != 0) + { + int h; + DBUG_PRINT("err", + ("byte %lu (%lu + %u), page %u (%s, end: %u, recs: %u, tag: %d) should be 0\n", + offset + i, offset, i, page_no, + (page_locked ? "locked" : "unlocked"), + end, num, tag)); + diag("byte %lu (%lu + %u), page %u (%s, end: %u, recs: %u, tag: %d) should be 0\n", + offset + i, offset, i, page_no, + (page_locked ? "locked" : "unlocked"), + end, num, tag); + h= my_open("wrong_page", O_CREAT | O_TRUNC | O_RDWR, MYF(0)); + my_pwrite(h, (uchar*) buff, TEST_PAGE_SIZE, 0, MYF(0)); + my_close(h, MYF(0)); + goto err; + } + } + DBUG_RETURN(end); +err: + DBUG_PRINT("err", ("try to flush")); + if (page_locked) + { + pagecache_delete(&pagecache, &file1, page_no, + PAGECACHE_LOCK_LEFT_WRITELOCKED, 1); + } + else + { + flush_pagecache_blocks(&pagecache, &file1, FLUSH_RELEASE); + } + exit(1); +} + +void put_rec(uchar *buff, uint end, uint len, uint tag) +{ + uint i; + uint num; + num= uint4korr(buff); + if (!len) + len= 1; + if (end + 4*2 + len > TEST_PAGE_SIZE) + return; + int4store(buff + end, len); + end+= 4; + int4store(buff + end, tag); + end+= 4; + num++; + int4store(buff, num); + for (i= end; i < (len + end); i++) + { + buff[i]= (uchar) num % 256; + } +} + +/* + Recreate and reopen a file for test + + SYNOPSIS + reset_file() + file File to reset + file_name Path (and name) of file which should be reset +*/ + +void reset_file(PAGECACHE_FILE file, char *file_name) +{ + flush_pagecache_blocks(&pagecache, &file1, FLUSH_RELEASE); + if (my_close(file1.file, MYF(0)) != 0) + { + diag("Got error during %s closing from close() (errno: %d)\n", + file_name, errno); + exit(1); + } + my_delete(file_name, MYF(0)); + if ((file.file= my_open(file_name, + O_CREAT | O_TRUNC | O_RDWR, MYF(0))) == -1) + { + diag("Got error during %s creation from open() (errno: %d)\n", + file_name, errno); + exit(1); + } +} + + +void reader(int num) +{ + unsigned char *buffr= malloc(TEST_PAGE_SIZE); + uint i; + + for (i= 0; i < number_of_tests; i++) + { + uint page= get_len(number_of_pages); + pagecache_read(&pagecache, &file1, page, 3, buffr, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + 0); + check_page(buffr, page * TEST_PAGE_SIZE, 0, page, -num); + + } + free(buffr); +} + + +void writer(int num) +{ + unsigned char *buffr= malloc(TEST_PAGE_SIZE); + uint i; + + for (i= 0; i < number_of_tests; i++) + { + uint end; + uint page= get_len(number_of_pages); + pagecache_read(&pagecache, &file1, page, 3, buffr, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_WRITE, + 0); + end= check_page(buffr, page * TEST_PAGE_SIZE, 1, page, num); + put_rec(buffr, end, get_len(record_length_limit), num); + pagecache_write(&pagecache, &file1, page, 3, buffr, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_WRITE_UNLOCK, + PAGECACHE_UNPIN, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + + if (i % flush_divider == 0) + flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE); + } + free(buffr); +} + + +static void *test_thread_reader(void *arg) +{ + int param=*((int*) arg); + my_thread_init(); + { + DBUG_ENTER("test_reader"); + DBUG_PRINT("enter", ("param: %d", param)); + + reader(param); + + DBUG_PRINT("info", ("Thread %s ended", my_thread_name())); + pthread_mutex_lock(&LOCK_thread_count); + ok(1, "reader%d: done", param); + thread_count--; + pthread_cond_signal(&COND_thread_count); /* Tell main we are ready */ + pthread_mutex_unlock(&LOCK_thread_count); + free((uchar*) arg); + my_thread_end(); + } + return 0; +} + + +static void *test_thread_writer(void *arg) +{ + int param=*((int*) arg); + my_thread_init(); + { + DBUG_ENTER("test_writer"); + DBUG_PRINT("enter", ("param: %d", param)); + + writer(param); + + DBUG_PRINT("info", ("Thread %s ended", my_thread_name())); + pthread_mutex_lock(&LOCK_thread_count); + ok(1, "writer%d: done", param); + thread_count--; + pthread_cond_signal(&COND_thread_count); /* Tell main we are ready */ + pthread_mutex_unlock(&LOCK_thread_count); + free((uchar*) arg); + my_thread_end(); + } + return 0; +} + +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 __attribute__((unused)), + char **argv __attribute__((unused))) +{ + pthread_t tid; + pthread_attr_t thr_attr; + int *param, error; + size_t pagen; + MY_INIT(argv[0]); + +#ifndef DBUG_OFF +#if defined(_WIN32) + default_dbug_option= "d:t:i:O,\\test_pagecache_consist.trace"; +#else + default_dbug_option= "d:t:i:o,/tmp/test_pagecache_consist.trace"; +#endif + if (argc > 1) + { + DBUG_SET(default_dbug_option); + DBUG_SET_INITIAL(default_dbug_option); + } +#endif + + { + DBUG_ENTER("main"); + DBUG_PRINT("info", ("Main thread: %s\n", my_thread_name())); + plan(number_of_writers + number_of_readers); + + SKIP_BIG_TESTS(number_of_writers + number_of_readers) + { + + char *test_dirname= create_tmpdir(argv[0]); + fn_format(file1_name, base_file1_name, test_dirname, "", MYF(0)); + + if ((file1.file= my_open(file1_name, + O_CREAT | O_TRUNC | O_RDWR, MYF(0))) == -1) + { + diag( "Got error during file1 creation from open() (errno: %d)\n", + errno); + exit(1); + } + + pagecache_file_set_null_hooks(&file1); + DBUG_PRINT("info", ("file1: %d", file1.file)); + if (my_chmod(file1_name, 0777, MYF(MY_WME))) + exit(1); + my_pwrite(file1.file, (const uchar *)"test file", 9, 0, MYF(0)); + + if ((error= pthread_cond_init(&COND_thread_count, NULL))) + { + diag( "COND_thread_count: %d from pthread_cond_init (errno: %d)\n", + error, errno); + exit(1); + } + if ((error= pthread_mutex_init(&LOCK_thread_count, MY_MUTEX_INIT_FAST))) + { + diag( "LOCK_thread_count: %d from pthread_cond_init (errno: %d)\n", + error, errno); + exit(1); + } + + if ((error= pthread_attr_init(&thr_attr))) + { + diag("Got error: %d from pthread_attr_init (errno: %d)\n", + error,errno); + exit(1); + } + if ((error= pthread_attr_setdetachstate(&thr_attr, PTHREAD_CREATE_DETACHED))) + { + diag( + "Got error: %d from pthread_attr_setdetachstate (errno: %d)\n", + error,errno); + exit(1); + } + +#ifdef HAVE_THR_SETCONCURRENCY + thr_setconcurrency(2); +#endif + + if ((pagen= init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, + TEST_PAGE_SIZE, 0, 0)) == 0) + { + diag("Got error: init_pagecache() (errno: %d)\n", + errno); + exit(1); + } + DBUG_PRINT("info", ("Page cache %zd pages", pagen)); + { + unsigned char *buffr= malloc(TEST_PAGE_SIZE); + uint i; + memset(buffr, '\0', TEST_PAGE_SIZE); + for (i= 0; i < number_of_pages; i++) + { + pagecache_write(&pagecache, &file1, i, 3, buffr, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_PIN_LEFT_UNPINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + } + flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE); + free(buffr); + } + pthread_mutex_lock(&LOCK_thread_count); + while (number_of_readers != 0 || number_of_writers != 0) + { + if (number_of_readers != 0) + { + param=(int*) malloc(sizeof(int)); + *param= number_of_readers; + if ((error= pthread_create(&tid, &thr_attr, test_thread_reader, + (void*) param))) + { + diag("Got error: %d from pthread_create (errno: %d)\n", + error,errno); + exit(1); + } + thread_count++; + number_of_readers--; + } + if (number_of_writers != 0) + { + param=(int*) malloc(sizeof(int)); + *param= number_of_writers; + if ((error= pthread_create(&tid, &thr_attr, test_thread_writer, + (void*) param))) + { + diag("Got error: %d from pthread_create (errno: %d)\n", + error,errno); + exit(1); + } + thread_count++; + number_of_writers--; + } + } + DBUG_PRINT("info", ("Thread started")); + pthread_mutex_unlock(&LOCK_thread_count); + + pthread_attr_destroy(&thr_attr); + + /* wait finishing */ + pthread_mutex_lock(&LOCK_thread_count); + while (thread_count) + { + if ((error= pthread_cond_wait(&COND_thread_count,&LOCK_thread_count))) + diag("COND_thread_count: %d from pthread_cond_wait\n",error); + } + pthread_mutex_unlock(&LOCK_thread_count); + DBUG_PRINT("info", ("thread ended")); + + flush_pagecache_blocks(&pagecache, &file1, FLUSH_IGNORE_CHANGED); + end_pagecache(&pagecache, 1); + DBUG_PRINT("info", ("Page cache ended")); + + if (my_close(file1.file, MYF(0)) != 0) + { + diag( "Got error during file1 closing from close() (errno: %d)\n", + errno); + exit(1); + } + my_delete(file1_name, MYF(0)); + + DBUG_PRINT("info", ("file1 (%d) closed", file1.file)); + DBUG_PRINT("info", ("Program end")); + + rmdir(test_dirname); + } /* SKIP_BIG_TESTS */ + my_end(0); + + return exit_status(); + } +} + +#include "../ma_check_standalone.h" diff --git a/storage/maria/unittest/ma_pagecache_rwconsist.c b/storage/maria/unittest/ma_pagecache_rwconsist.c new file mode 100644 index 00000000..24c30245 --- /dev/null +++ b/storage/maria/unittest/ma_pagecache_rwconsist.c @@ -0,0 +1,368 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +/* + TODO: use pthread_join instead of wait_for_thread_count_to_be_zero, like in + my_atomic-t.c (see BUG#22320). +*/ + +#include <tap.h> +#include <my_sys.h> +#include <m_string.h> +#include "test_file.h" +#include <tap.h> + +#define PCACHE_SIZE (TEST_PAGE_SIZE*1024*8) + +#ifndef DBUG_OFF +static const char* default_dbug_option; +#endif + + +#define SLEEP my_sleep(5) + +static const char *base_file1_name= "page_cache_test_file_1"; +static char file1_name[FN_REFLEN]; +static PAGECACHE_FILE file1; +static pthread_cond_t COND_thread_count; +static pthread_mutex_t LOCK_thread_count; +static uint thread_count= 0; +static PAGECACHE pagecache; + +static uint number_of_readers= 5; +static uint number_of_writers= 5; +static uint number_of_read_tests= 2000; +static uint number_of_write_tests= 1000; +static uint read_sleep_limit= 3; +static uint report_divisor= 50; + +/** + @brief Checks page consistency + + @param buff pointer to the page content + @param task task ID +*/ +void check_page(uchar *buff, int task) +{ + uint i; + DBUG_ENTER("check_page"); + + for (i= 1; i < TEST_PAGE_SIZE; i++) + { + if (buff[0] != buff[i]) + goto err; + } + DBUG_VOID_RETURN; +err: + diag("Task %d char #%u '%u' != '%u'", task, i, (uint) buff[0], + (uint) buff[i]); + DBUG_PRINT("err", ("try to flush")); + exit(1); +} + + + +void reader(int num) +{ + unsigned char *buff; + uint i; + PAGECACHE_BLOCK_LINK *link; + + for (i= 0; i < number_of_read_tests; i++) + { + if (i % report_divisor == 0) + diag("Reader %d - %u", num, i); + buff= pagecache_read(&pagecache, &file1, 0, 3, NULL, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_READ, + &link); + check_page(buff, num); + pagecache_unlock_by_link(&pagecache, link, + PAGECACHE_LOCK_READ_UNLOCK, + PAGECACHE_UNPIN, 0, 0, 0, FALSE); + { + int lim= rand() % read_sleep_limit; + int j; + for (j= 0; j < lim; j++) + SLEEP; + } + } +} + + +void writer(int num) +{ + uint i; + uchar *buff; + PAGECACHE_BLOCK_LINK *link; + + for (i= 0; i < number_of_write_tests; i++) + { + uchar c= (uchar) rand() % 256; + + if (i % report_divisor == 0) + diag("Writer %d - %u", num, i); + buff= pagecache_read(&pagecache, &file1, 0, 3, NULL, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_WRITE, + &link); + + check_page(buff, num); + bfill(buff, TEST_PAGE_SIZE / 2, c); + SLEEP; + bfill(buff + TEST_PAGE_SIZE/2, TEST_PAGE_SIZE / 2, c); + check_page(buff, num); + pagecache_unlock_by_link(&pagecache, link, + PAGECACHE_LOCK_WRITE_UNLOCK, + PAGECACHE_UNPIN, 0, 0, 1, FALSE); + SLEEP; + } +} + + +static void *test_thread_reader(void *arg) +{ + int param=*((int*) arg); + my_thread_init(); + { + DBUG_ENTER("test_reader"); + + DBUG_PRINT("enter", ("param: %d", param)); + + reader(param); + + DBUG_PRINT("info", ("Thread %s ended", my_thread_name())); + pthread_mutex_lock(&LOCK_thread_count); + ok(1, "reader%d: done", param); + thread_count--; + pthread_cond_signal(&COND_thread_count); /* Tell main we are ready */ + pthread_mutex_unlock(&LOCK_thread_count); + free((uchar*) arg); + my_thread_end(); + } + return 0; +} + + +static void *test_thread_writer(void *arg) +{ + int param=*((int*) arg); + my_thread_init(); + { + DBUG_ENTER("test_writer"); + + writer(param); + + DBUG_PRINT("info", ("Thread %s ended", my_thread_name())); + pthread_mutex_lock(&LOCK_thread_count); + ok(1, "writer%d: done", param); + thread_count--; + pthread_cond_signal(&COND_thread_count); /* Tell main we are ready */ + pthread_mutex_unlock(&LOCK_thread_count); + free((uchar*) arg); + my_thread_end(); + } + return 0; +} + +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 __attribute__((unused)), + char **argv __attribute__((unused))) +{ + pthread_t tid; + pthread_attr_t thr_attr; + int *param, error; + size_t pagen; + + MY_INIT(argv[0]); + +#ifndef DBUG_OFF +#if defined(_WIN32) + default_dbug_option= "d:t:i:O,\\test_pagecache_consist.trace"; +#else + default_dbug_option= "d:t:i:O,/tmp/test_pagecache_consist.trace"; +#endif + if (argc > 1) + { + DBUG_SET(default_dbug_option); + DBUG_SET_INITIAL(default_dbug_option); + } +#endif + + { + DBUG_ENTER("main"); + DBUG_PRINT("info", ("Main thread: %s\n", my_thread_name())); + plan(number_of_writers + number_of_readers); + SKIP_BIG_TESTS(number_of_writers + number_of_readers) + { + + char *test_dirname= create_tmpdir(argv[0]); + fn_format(file1_name, base_file1_name, test_dirname, "", MYF(0)); + + if ((file1.file= my_open(file1_name, + O_CREAT | O_TRUNC | O_RDWR, MYF(0))) == -1) + { + diag( "Got error during file1 creation from open() (errno: %d)\n", + errno); + exit(1); + } + pagecache_file_set_null_hooks(&file1); + DBUG_PRINT("info", ("file1: %d", file1.file)); + if (my_chmod(file1_name, 0777, MYF(MY_WME))) + exit(1); + my_pwrite(file1.file, (const uchar*) "test file", 9, 0, MYF(0)); + + if ((error= pthread_cond_init(&COND_thread_count, NULL))) + { + diag( "COND_thread_count: %d from pthread_cond_init (errno: %d)\n", + error, errno); + exit(1); + } + if ((error= pthread_mutex_init(&LOCK_thread_count, MY_MUTEX_INIT_FAST))) + { + diag( "LOCK_thread_count: %d from pthread_cond_init (errno: %d)\n", + error, errno); + exit(1); + } + + if ((error= pthread_attr_init(&thr_attr))) + { + diag("Got error: %d from pthread_attr_init (errno: %d)\n", + error,errno); + exit(1); + } + if ((error= pthread_attr_setdetachstate(&thr_attr, PTHREAD_CREATE_DETACHED))) + { + diag( + "Got error: %d from pthread_attr_setdetachstate (errno: %d)\n", + error,errno); + exit(1); + } + +#ifdef HAVE_THR_SETCONCURRENCY + thr_setconcurrency(2); +#endif + + if ((pagen= init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, + TEST_PAGE_SIZE, 0, 0)) == 0) + { + diag("Got error: init_pagecache() (errno: %d)\n", + errno); + exit(1); + } + DBUG_PRINT("info", ("Page cache %zu pages", pagen)); + { + unsigned char *buffr= malloc(TEST_PAGE_SIZE); + memset(buffr, '\0', TEST_PAGE_SIZE); + pagecache_write(&pagecache, &file1, 0, 3, buffr, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_PIN_LEFT_UNPINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + } + pthread_mutex_lock(&LOCK_thread_count); + + while (number_of_readers != 0 || number_of_writers != 0) + { + if (number_of_readers != 0) + { + param=(int*) malloc(sizeof(int)); + *param= number_of_readers + number_of_writers; + if ((error= pthread_create(&tid, &thr_attr, test_thread_reader, + (void*) param))) + { + diag("Got error: %d from pthread_create (errno: %d)\n", + error,errno); + exit(1); + } + thread_count++; + number_of_readers--; + } + if (number_of_writers != 0) + { + param=(int*) malloc(sizeof(int)); + *param= number_of_writers + number_of_readers; + if ((error= pthread_create(&tid, &thr_attr, test_thread_writer, + (void*) param))) + { + diag("Got error: %d from pthread_create (errno: %d)\n", + error,errno); + exit(1); + } + thread_count++; + number_of_writers--; + } + } + DBUG_PRINT("info", ("Thread started")); + pthread_mutex_unlock(&LOCK_thread_count); + + pthread_attr_destroy(&thr_attr); + + /* wait finishing */ + pthread_mutex_lock(&LOCK_thread_count); + while (thread_count) + { + if ((error= pthread_cond_wait(&COND_thread_count, &LOCK_thread_count))) + diag("COND_thread_count: %d from pthread_cond_wait\n", error); + } + pthread_mutex_unlock(&LOCK_thread_count); + DBUG_PRINT("info", ("thread ended")); + + flush_pagecache_blocks(&pagecache, &file1, FLUSH_IGNORE_CHANGED); + end_pagecache(&pagecache, 1); + DBUG_PRINT("info", ("Page cache ended")); + + if (my_close(file1.file, MYF(0)) != 0) + { + diag( "Got error during file1 closing from close() (errno: %d)\n", + errno); + exit(1); + } + my_delete(file1_name, MYF(0)); + + DBUG_PRINT("info", ("file1 (%d) closed", file1.file)); + DBUG_PRINT("info", ("Program end")); + + rmdir(test_dirname); + } /* SKIP_BIG_TESTS */ + my_end(0); + + return exit_status(); + } +} + +#include "../ma_check_standalone.h" diff --git a/storage/maria/unittest/ma_pagecache_rwconsist2.c b/storage/maria/unittest/ma_pagecache_rwconsist2.c new file mode 100644 index 00000000..c92bec3c --- /dev/null +++ b/storage/maria/unittest/ma_pagecache_rwconsist2.c @@ -0,0 +1,363 @@ +/* Copyright (C) 2006-2008 MySQL AB, 2008 Sun Microsystems, Inc. + + 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 */ + + +/** + @file this unit tests consistence of long block writing under write lock + and simultaneous reading of this block with read request without read lock + requirement. +*/ + +/* + TODO: use pthread_join instead of wait_for_thread_count_to_be_zero, like in + my_atomic-t.c (see BUG#22320). +*/ + +#include <tap.h> +#include <my_sys.h> +#include <m_string.h> +#include "test_file.h" +#include <tap.h> + +#define PCACHE_SIZE (TEST_PAGE_SIZE*1024*8) + +#ifndef DBUG_OFF +static const char* default_dbug_option; +#endif + + +#define SLEEP my_sleep(5) + +static const char *base_file1_name= "page_cache_test_file_1"; +static char file1_name[FN_REFLEN]; +static PAGECACHE_FILE file1; +static pthread_cond_t COND_thread_count; +static pthread_mutex_t LOCK_thread_count; +static uint thread_count= 0; +static PAGECACHE pagecache; + +static uint number_of_readers= 5; +static uint number_of_writers= 5; +static uint number_of_read_tests= 20000; +static uint number_of_write_tests= 1000; +static uint report_divisor= 50; + +/** + @brief Checks page consistency + + @param buff pointer to the page content + @param task task ID +*/ +void check_page(uchar *buff, int task) +{ + uint i; + DBUG_ENTER("check_page"); + + for (i= 1; i < TEST_PAGE_SIZE; i++) + { + if (buff[0] != buff[i]) + goto err; + } + DBUG_VOID_RETURN; +err: + diag("Task %d char #%u '%u' != '%u'", task, i, (uint) buff[0], + (uint) buff[i]); + DBUG_PRINT("err", ("try to flush")); + exit(1); +} + + + +void reader(int num) +{ + unsigned char buff[TEST_PAGE_SIZE]; + uint i; + + for (i= 0; i < number_of_read_tests; i++) + { + if (i % report_divisor == 0) + diag("Reader %d - %u", num, i); + pagecache_read(&pagecache, &file1, 0, 3, buff, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + NULL); + check_page(buff, num); + } +} + + +void writer(int num) +{ + uint i; + uchar *buff; + PAGECACHE_BLOCK_LINK *link; + + for (i= 0; i < number_of_write_tests; i++) + { + uchar c= (uchar) rand() % 256; + + if (i % report_divisor == 0) + diag("Writer %d - %u", num, i); + buff= pagecache_read(&pagecache, &file1, 0, 3, NULL, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_WRITE, + &link); + + check_page(buff, num); + bfill(buff, TEST_PAGE_SIZE / 2, c); + SLEEP; + bfill(buff + TEST_PAGE_SIZE/2, TEST_PAGE_SIZE / 2, c); + check_page(buff, num); + pagecache_unlock_by_link(&pagecache, link, + PAGECACHE_LOCK_WRITE_UNLOCK, + PAGECACHE_UNPIN, 0, 0, 1, FALSE); + SLEEP; + } +} + + +static void *test_thread_reader(void *arg) +{ + int param=*((int*) arg); + my_thread_init(); + { + DBUG_ENTER("test_reader"); + + DBUG_PRINT("enter", ("param: %d", param)); + + reader(param); + + DBUG_PRINT("info", ("Thread %s ended", my_thread_name())); + pthread_mutex_lock(&LOCK_thread_count); + ok(1, "reader%d: done", param); + thread_count--; + pthread_cond_signal(&COND_thread_count); /* Tell main we are ready */ + pthread_mutex_unlock(&LOCK_thread_count); + free((uchar*) arg); + my_thread_end(); + } + return 0; +} + + +static void *test_thread_writer(void *arg) +{ + int param=*((int*) arg); + my_thread_init(); + { + DBUG_ENTER("test_writer"); + + writer(param); + + DBUG_PRINT("info", ("Thread %s ended", my_thread_name())); + pthread_mutex_lock(&LOCK_thread_count); + ok(1, "writer%d: done", param); + thread_count--; + pthread_cond_signal(&COND_thread_count); /* Tell main we are ready */ + pthread_mutex_unlock(&LOCK_thread_count); + free((uchar*) arg); + my_thread_end(); + } + return 0; +} + +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 __attribute__((unused)), + char **argv __attribute__((unused))) +{ + pthread_t tid; + pthread_attr_t thr_attr; + int *param, error; + size_t pagen; + + MY_INIT(argv[0]); + +#ifndef DBUG_OFF +#if defined(_WIN32) + default_dbug_option= "d:t:i:O,\\test_pagecache_consist.trace"; +#else + default_dbug_option= "d:t:i:O,/tmp/test_pagecache_consist.trace"; +#endif + if (argc > 1) + { + DBUG_SET(default_dbug_option); + DBUG_SET_INITIAL(default_dbug_option); + } +#endif + + { + DBUG_ENTER("main"); + DBUG_PRINT("info", ("Main thread: %s\n", my_thread_name())); + plan(number_of_writers + number_of_readers); + SKIP_BIG_TESTS(number_of_writers + number_of_readers) + { + + char *test_dirname= create_tmpdir(argv[0]); + fn_format(file1_name, base_file1_name, test_dirname, "", MYF(0)); + + if ((file1.file= my_open(file1_name, + O_CREAT | O_TRUNC | O_RDWR, MYF(0))) == -1) + { + diag( "Got error during file1 creation from open() (errno: %d)\n", + errno); + exit(1); + } + pagecache_file_set_null_hooks(&file1); + DBUG_PRINT("info", ("file1: %d", file1.file)); + if (my_chmod(file1_name, 0777, MYF(MY_WME))) + exit(1); + my_pwrite(file1.file, (const uchar*) "test file", 9, 0, MYF(0)); + + if ((error= pthread_cond_init(&COND_thread_count, NULL))) + { + diag( "COND_thread_count: %d from pthread_cond_init (errno: %d)\n", + error, errno); + exit(1); + } + if ((error= pthread_mutex_init(&LOCK_thread_count, MY_MUTEX_INIT_FAST))) + { + diag( "LOCK_thread_count: %d from pthread_cond_init (errno: %d)\n", + error, errno); + exit(1); + } + + if ((error= pthread_attr_init(&thr_attr))) + { + diag("Got error: %d from pthread_attr_init (errno: %d)\n", + error,errno); + exit(1); + } + if ((error= pthread_attr_setdetachstate(&thr_attr, PTHREAD_CREATE_DETACHED))) + { + diag( + "Got error: %d from pthread_attr_setdetachstate (errno: %d)\n", + error,errno); + exit(1); + } + +#ifdef HAVE_THR_SETCONCURRENCY + thr_setconcurrency(2); +#endif + + if ((pagen= init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, + TEST_PAGE_SIZE, 0, 0)) == 0) + { + diag("Got error: init_pagecache() (errno: %d)\n", + errno); + exit(1); + } + DBUG_PRINT("info", ("Page cache %zd pages", pagen)); + { + unsigned char *buffr= malloc(TEST_PAGE_SIZE); + memset(buffr, '\0', TEST_PAGE_SIZE); + pagecache_write(&pagecache, &file1, 0, 3, buffr, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_PIN_LEFT_UNPINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + } + pthread_mutex_lock(&LOCK_thread_count); + + while (number_of_readers != 0 || number_of_writers != 0) + { + if (number_of_readers != 0) + { + param=(int*) malloc(sizeof(int)); + *param= number_of_readers + number_of_writers; + if ((error= pthread_create(&tid, &thr_attr, test_thread_reader, + (void*) param))) + { + diag("Got error: %d from pthread_create (errno: %d)\n", + error,errno); + exit(1); + } + thread_count++; + number_of_readers--; + } + if (number_of_writers != 0) + { + param=(int*) malloc(sizeof(int)); + *param= number_of_writers + number_of_readers; + if ((error= pthread_create(&tid, &thr_attr, test_thread_writer, + (void*) param))) + { + diag("Got error: %d from pthread_create (errno: %d)\n", + error,errno); + exit(1); + } + thread_count++; + number_of_writers--; + } + } + DBUG_PRINT("info", ("Thread started")); + pthread_mutex_unlock(&LOCK_thread_count); + + pthread_attr_destroy(&thr_attr); + + /* wait finishing */ + pthread_mutex_lock(&LOCK_thread_count); + while (thread_count) + { + if ((error= pthread_cond_wait(&COND_thread_count, &LOCK_thread_count))) + diag("COND_thread_count: %d from pthread_cond_wait\n", error); + } + pthread_mutex_unlock(&LOCK_thread_count); + DBUG_PRINT("info", ("thread ended")); + + end_pagecache(&pagecache, 1); + DBUG_PRINT("info", ("Page cache ended")); + + if (my_close(file1.file, MYF(0)) != 0) + { + diag( "Got error during file1 closing from close() (errno: %d)\n", + errno); + exit(1); + } + my_delete(file1_name, MYF(0)); + + DBUG_PRINT("info", ("file1 (%d) closed", file1.file)); + DBUG_PRINT("info", ("Program end")); + + rmdir(test_dirname); + } /* SKIP_BIG_TESTS */ + my_end(0); + + return exit_status(); + } +} + +#include "../ma_check_standalone.h" diff --git a/storage/maria/unittest/ma_pagecache_single.c b/storage/maria/unittest/ma_pagecache_single.c new file mode 100644 index 00000000..de2ecaec --- /dev/null +++ b/storage/maria/unittest/ma_pagecache_single.c @@ -0,0 +1,853 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +/* + TODO: use pthread_join instead of wait_for_thread_count_to_be_zero, like in + my_atomic-t.c (see BUG#22320). + Use diag() instead of fprintf(stderr). +*/ +#include <tap.h> +#include <my_sys.h> +#include <m_string.h> +#include "test_file.h" +#include <tap.h> + +#define PCACHE_SIZE (TEST_PAGE_SIZE*1024*10) + +#ifndef DBUG_OFF +static const char* default_dbug_option; +#endif + +#ifndef BIG +#undef SKIP_BIG_TESTS +#define SKIP_BIG_TESTS(X) /* no-op */ +#endif + +static const char *base_file1_name= "page_cache_test_file_1"; +static const char *base_file2_name= "page_cache_test_file_2"; +static char file1_name[FN_REFLEN], file2_name[FN_REFLEN]; +static PAGECACHE_FILE file1; +static pthread_cond_t COND_thread_count; +static pthread_mutex_t LOCK_thread_count; +static uint thread_count; +static PAGECACHE pagecache; + +/* + File contance descriptors +*/ +static struct file_desc simple_read_write_test_file[]= +{ + { TEST_PAGE_SIZE, '\1'}, + {0, 0} +}; +static struct file_desc simple_read_change_write_read_test_file[]= +{ + { TEST_PAGE_SIZE/2, '\65'}, + { TEST_PAGE_SIZE/2, '\1'}, + {0, 0} +}; +static struct file_desc simple_pin_test_file1[]= +{ + { TEST_PAGE_SIZE*2, '\1'}, + {0, 0} +}; +static struct file_desc simple_pin_test_file2[]= +{ + { TEST_PAGE_SIZE/2, '\1'}, + { TEST_PAGE_SIZE/2, (unsigned char)129}, + { TEST_PAGE_SIZE, '\1'}, + {0, 0} +}; +static struct file_desc simple_pin_no_lock_test_file1[]= +{ + { TEST_PAGE_SIZE, '\4'}, + {0, 0} +}; +static struct file_desc simple_pin_no_lock_test_file2[]= +{ + { TEST_PAGE_SIZE, '\5'}, + {0, 0} +}; +static struct file_desc simple_pin_no_lock_test_file3[]= +{ + { TEST_PAGE_SIZE, '\6'}, + {0, 0} +}; +static struct file_desc simple_delete_forget_test_file[]= +{ + { TEST_PAGE_SIZE, '\1'}, + {0, 0} +}; +static struct file_desc simple_delete_flush_test_file[]= +{ + { TEST_PAGE_SIZE, '\2'}, + {0, 0} +}; + + +/* + Recreate and reopen a file for test + + SYNOPSIS + reset_file() + file File to reset + file_name Path (and name) of file which should be reset +*/ + +void reset_file(PAGECACHE_FILE *file, const char *file_name) +{ + flush_pagecache_blocks(&pagecache, file, FLUSH_RELEASE); + if (my_close(file->file, MYF(MY_WME))) + exit(1); + my_delete(file_name, MYF(MY_WME)); + if ((file->file= my_open(file_name, + O_CREAT | O_TRUNC | O_RDWR, MYF(0))) == -1) + { + diag("Got error during %s creation from open() (errno: %d)\n", + file_name, my_errno); + exit(1); + } +} + +/* + Write then read page, check file on disk +*/ + +int simple_read_write_test() +{ + unsigned char *buffw= malloc(TEST_PAGE_SIZE); + unsigned char *buffr= malloc(TEST_PAGE_SIZE); + int res; + DBUG_ENTER("simple_read_write_test"); + bfill(buffw, TEST_PAGE_SIZE, '\1'); + pagecache_write(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_PIN_LEFT_UNPINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + pagecache_read(&pagecache, &file1, 0, 3, buffr, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + 0); + ok((res= MY_TEST(memcmp(buffr, buffw, TEST_PAGE_SIZE) == 0)), + "Simple write-read page "); + if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) + { + diag("Got error during flushing pagecache\n"); + exit(1); + } + ok((res&= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, + simple_read_write_test_file))), + "Simple write-read page file"); + if (res) + reset_file(&file1, file1_name); + free(buffw); + free(buffr); + DBUG_RETURN(res); +} + + +/* + Prepare page, then read (and lock), change (write new value and unlock), + then check the page in the cache and on the disk +*/ +int simple_read_change_write_read_test() +{ + unsigned char *buffw= malloc(TEST_PAGE_SIZE); + unsigned char *buffr= malloc(TEST_PAGE_SIZE); + int res, res2; + DBUG_ENTER("simple_read_change_write_read_test"); + + /* prepare the file */ + bfill(buffw, TEST_PAGE_SIZE, '\1'); + pagecache_write(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_PIN_LEFT_UNPINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) + { + diag("Got error during flushing pagecache\n"); + exit(1); + } + /* test */ + pagecache_read(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_WRITE, + 0); + bfill(buffw, TEST_PAGE_SIZE/2, '\65'); + pagecache_write(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_WRITE_UNLOCK, + PAGECACHE_UNPIN, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + + pagecache_read(&pagecache, &file1, 0, 3, buffr, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + 0); + ok((res= MY_TEST(memcmp(buffr, buffw, TEST_PAGE_SIZE) == 0)), + "Simple read-change-write-read page "); + DBUG_ASSERT(pagecache.blocks_changed == 1); + if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) + { + diag("Got error during flushing pagecache\n"); + exit(1); + } + DBUG_ASSERT(pagecache.blocks_changed == 0); + ok((res2= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, + simple_read_change_write_read_test_file))), + "Simple read-change-write-read page file"); + if (res && res2) + reset_file(&file1, file1_name); + free(buffw); + free(buffr); + DBUG_RETURN(res && res2); +} + + +/* + Prepare page, read page 0 (and pin) then write page 1 and page 0. + Flush the file (should flush only page 1 and return 1 (page 0 is + still pinned). + Check file on the disk. + Unpin and flush. + Check file on the disk. +*/ +int simple_pin_test() +{ + unsigned char *buffw= malloc(TEST_PAGE_SIZE); + int res; + DBUG_ENTER("simple_pin_test"); + /* prepare the file */ + bfill(buffw, TEST_PAGE_SIZE, '\1'); + pagecache_write(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_PIN_LEFT_UNPINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + /* test */ + if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) + { + diag("Got error during flushing pagecache\n"); + exit(1); + } + pagecache_read(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_WRITE, + 0); + pagecache_write(&pagecache, &file1, 1, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_PIN_LEFT_UNPINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + bfill(buffw + TEST_PAGE_SIZE/2, TEST_PAGE_SIZE/2, ((unsigned char) 129)); + pagecache_write(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_WRITELOCKED, + PAGECACHE_PIN_LEFT_PINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + /* + We have to get error because one page of the file is pinned, + other page should be flushed + */ + if (!flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) + { + diag("Did not get error in flush_pagecache_blocks\n"); + res= 0; + goto err; + } + ok((res= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE * 2, + TEST_PAGE_SIZE * 2, simple_pin_test_file1))), + "Simple pin page file with pin"); + pagecache_unlock(&pagecache, + &file1, + 0, + PAGECACHE_LOCK_WRITE_UNLOCK, + PAGECACHE_UNPIN, + 0, 0, 0); + if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) + { + diag("Got error in flush_pagecache_blocks\n"); + res= 0; + goto err; + } + ok((res&= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE * 2, + TEST_PAGE_SIZE, simple_pin_test_file2))), + "Simple pin page result file"); + if (res) + reset_file(&file1, file1_name); +err: + free(buffw); + DBUG_RETURN(res); +} + +/* + Prepare page, read page 0 (and pin) then write page 1 and page 0. + Flush the file (should flush only page 1 and return 1 (page 0 is + still pinned). + Check file on the disk. + Unpin and flush. + Check file on the disk. +*/ +int simple_pin_test2() +{ + unsigned char *buffw= malloc(TEST_PAGE_SIZE); + int res; + DBUG_ENTER("simple_pin_test2"); + /* prepare the file */ + bfill(buffw, TEST_PAGE_SIZE, '\1'); + pagecache_write(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_PIN_LEFT_UNPINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + /* test */ + if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) + { + diag("Got error during flushing pagecache\n"); + exit(1); + } + pagecache_read(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_WRITE, + 0); + pagecache_write(&pagecache, &file1, 1, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_PIN_LEFT_UNPINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + bfill(buffw + TEST_PAGE_SIZE/2, TEST_PAGE_SIZE/2, ((unsigned char) 129)); + pagecache_write(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_WRITE_TO_READ, + PAGECACHE_PIN_LEFT_PINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + /* + We have to get error because one page of the file is pinned, + other page should be flushed + */ + if (!flush_pagecache_blocks(&pagecache, &file1, FLUSH_KEEP_LAZY)) + { + diag("Did not get error in flush_pagecache_blocks 2\n"); + res= 0; + goto err; + } + ok((res= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE * 2, + TEST_PAGE_SIZE * 2, simple_pin_test_file1))), + "Simple pin page file with pin 2"); + + /* Test that a normal flush goes through */ + if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) + { + diag("Got error in flush_pagecache_blocks 3\n"); + res= 0; + goto err; + } + pagecache_unlock(&pagecache, + &file1, + 0, + PAGECACHE_LOCK_READ_UNLOCK, + PAGECACHE_UNPIN, + 0, 0, 0); + if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) + { + diag("Got error in flush_pagecache_blocks 4\n"); + res= 0; + goto err; + } + ok((res&= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE * 2, + TEST_PAGE_SIZE, simple_pin_test_file2))), + "Simple pin page result file 2"); + if (res) + reset_file(&file1, file1_name); +err: + free(buffw); + DBUG_RETURN(res); +} + +/* + Checks pins without lock. +*/ +int simple_pin_no_lock_test() +{ + unsigned char *buffw= malloc(TEST_PAGE_SIZE); + PAGECACHE_BLOCK_LINK *link; + int res; + DBUG_ENTER("simple_pin_no_lock_test"); + /* prepare the file */ + bfill(buffw, TEST_PAGE_SIZE, '\4'); + pagecache_write(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_PIN_LEFT_UNPINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + /* test */ + if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) + { + diag("Got error during flushing pagecache 2\n"); + exit(1); + } + bfill(buffw, TEST_PAGE_SIZE, '\5'); + pagecache_write(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_PIN, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + /* + We have to get error because one page of the file is pinned, + other page should be flushed + */ + if (!flush_pagecache_blocks(&pagecache, &file1, FLUSH_KEEP_LAZY)) + { + diag("Did not get error in flush_pagecache_blocks 2\n"); + res= 0; + goto err; + } + ok((res= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, + simple_pin_no_lock_test_file1))), + "Simple pin (no lock) page file with pin 2"); + pagecache_unlock(&pagecache, + &file1, + 0, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_UNPIN, + 0, 0, 0); + if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) + { + diag("Got error in flush_pagecache_blocks 2\n"); + res= 0; + goto err; + } + ok((res&= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, + simple_pin_no_lock_test_file2))), + "Simple pin (no lock) page result file 2"); + + bfill(buffw, TEST_PAGE_SIZE, '\6'); + pagecache_write(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_WRITE, + PAGECACHE_PIN, + PAGECACHE_WRITE_DELAY, + &link, LSN_IMPOSSIBLE); + pagecache_unlock_by_link(&pagecache, link, + PAGECACHE_LOCK_WRITE_UNLOCK, + PAGECACHE_PIN_LEFT_PINNED, 0, 0, 1, FALSE); + if (!flush_pagecache_blocks(&pagecache, &file1, FLUSH_KEEP_LAZY)) + { + diag("Did not get error in flush_pagecache_blocks 3\n"); + res= 0; + goto err; + } + ok((res= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, + simple_pin_no_lock_test_file2))), + "Simple pin (no lock) page file with pin 3"); + pagecache_unpin_by_link(&pagecache, link, 0); + if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) + { + diag("Got error in flush_pagecache_blocks 3\n"); + res= 0; + goto err; + } + ok((res&= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, + simple_pin_no_lock_test_file3))), + "Simple pin (no lock) page result file 3"); + if (res) + reset_file(&file1, file1_name); +err: + free(buffw); + DBUG_RETURN(res); +} +/* + Prepare page, write new value, then delete page from cache without flush, + on the disk should be page with old content written during preparation +*/ + +int simple_delete_forget_test() +{ + unsigned char *buffw= malloc(TEST_PAGE_SIZE); + unsigned char *buffr= malloc(TEST_PAGE_SIZE); + int res; + DBUG_ENTER("simple_delete_forget_test"); + /* prepare the file */ + bfill(buffw, TEST_PAGE_SIZE, '\1'); + pagecache_write(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_PIN_LEFT_UNPINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE); + /* test */ + bfill(buffw, TEST_PAGE_SIZE, '\2'); + pagecache_write(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_PIN_LEFT_UNPINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + pagecache_delete(&pagecache, &file1, 0, + PAGECACHE_LOCK_WRITE, 0); + flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE); + ok((res= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, + simple_delete_forget_test_file))), + "Simple delete-forget page file"); + if (res) + reset_file(&file1, file1_name); + free(buffw); + free(buffr); + DBUG_RETURN(res); +} + +/* + Prepare page with locking, write new content to the page, + delete page with flush and on existing lock, + check that page on disk contain new value. +*/ + +int simple_delete_flush_test() +{ + unsigned char *buffw= malloc(TEST_PAGE_SIZE); + unsigned char *buffr= malloc(TEST_PAGE_SIZE); + PAGECACHE_BLOCK_LINK *link; + int res; + DBUG_ENTER("simple_delete_flush_test"); + /* prepare the file */ + bfill(buffw, TEST_PAGE_SIZE, '\1'); + pagecache_write(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_WRITE, + PAGECACHE_PIN, + PAGECACHE_WRITE_DELAY, + &link, LSN_IMPOSSIBLE); + flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE); + /* test */ + bfill(buffw, TEST_PAGE_SIZE, '\2'); + pagecache_write(&pagecache, &file1, 0, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_WRITELOCKED, + PAGECACHE_PIN_LEFT_PINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + if (pagecache_delete_by_link(&pagecache, link, + PAGECACHE_LOCK_LEFT_WRITELOCKED, 1)) + { + diag("simple_delete_flush_test: error during delete"); + exit(1); + } + flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE); + ok((res= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, + simple_delete_flush_test_file))), + "Simple delete flush (link) page file"); + if (res) + reset_file(&file1, file1_name); + free(buffw); + free(buffr); + DBUG_RETURN(res); +} + + +/* + write then read file bigger then cache +*/ + +int simple_big_test() +{ + unsigned char *buffw= (unsigned char *) my_malloc(PSI_NOT_INSTRUMENTED, TEST_PAGE_SIZE, MYF(MY_WME)); + unsigned char *buffr= (unsigned char *) my_malloc(PSI_NOT_INSTRUMENTED, TEST_PAGE_SIZE, MYF(MY_WME)); + struct file_desc *desc= ((struct file_desc *) + my_malloc(PSI_NOT_INSTRUMENTED, + (PCACHE_SIZE/(TEST_PAGE_SIZE/2) + 1) * sizeof(struct file_desc), MYF(MY_WME))); + int res, i; + DBUG_ENTER("simple_big_test"); + + /* prepare the file twice larger then cache */ + for (i= 0; i < PCACHE_SIZE/(TEST_PAGE_SIZE/2); i++) + { + bfill(buffw, TEST_PAGE_SIZE, (unsigned char) (i & 0xff)); + desc[i].length= TEST_PAGE_SIZE; + desc[i].content= (i & 0xff); + pagecache_write(&pagecache, &file1, i, 3, buffw, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_PIN_LEFT_UNPINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + } + desc[i].length= 0; + desc[i].content= '\0'; + ok(1, "Simple big file write"); + /* check written pages sequentally read */ + for (i= 0; i < PCACHE_SIZE/(TEST_PAGE_SIZE/2); i++) + { + int j; + pagecache_read(&pagecache, &file1, i, 3, buffr, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + 0); + for(j= 0; j < TEST_PAGE_SIZE; j++) + { + if (buffr[j] != (i & 0xff)) + { + diag("simple_big_test seq: page %u byte %u mismatch\n", i, j); + res= 0; + goto err; + } + } + } + ok(1, "Simple big file sequential read"); + /* chack random reads */ + for (i= 0; i < PCACHE_SIZE/(TEST_PAGE_SIZE); i++) + { + int j, page; + page= rand() % (PCACHE_SIZE/(TEST_PAGE_SIZE/2)); + pagecache_read(&pagecache, &file1, page, 3, buffr, + PAGECACHE_PLAIN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + 0); + for(j= 0; j < TEST_PAGE_SIZE; j++) + { + if (buffr[j] != (page & 0xff)) + { + diag("simple_big_test rnd: page %u byte %u mismatch\n", page, j); + res= 0; + goto err; + } + } + } + ok(1, "Simple big file random read"); + flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE); + + ok((res= MY_TEST(test_file(file1, file1_name, PCACHE_SIZE * 2, TEST_PAGE_SIZE, + desc))), + "Simple big file"); + if (res) + reset_file(&file1, file1_name); + +err: + my_free(buffw); + my_free(buffr); + my_free(desc); + DBUG_RETURN(res); +} + + +/* + Thread function +*/ + +static void *test_thread(void *arg) +{ + my_thread_init(); + { + DBUG_ENTER("test_thread"); + DBUG_PRINT("enter", ("param: %d", *(int*) arg)); + + if (!simple_read_write_test() || + !simple_read_change_write_read_test() || + !simple_pin_test() || + !simple_pin_test2() || + !simple_pin_no_lock_test() || + !simple_delete_forget_test() || + !simple_delete_flush_test()) + exit(1); + + SKIP_BIG_TESTS(4) + { + if (!simple_big_test()) + exit(1); + } + + DBUG_PRINT("info", ("Thread %s ended\n", my_thread_name())); + pthread_mutex_lock(&LOCK_thread_count); + thread_count--; + pthread_cond_signal(&COND_thread_count); /* Tell main we are ready */ + pthread_mutex_unlock(&LOCK_thread_count); + free((uchar*) arg); + my_thread_end(); + DBUG_RETURN(0); + } +} + + +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 __attribute__((unused)), + char **argv __attribute__((unused))) +{ + pthread_t tid; + pthread_attr_t thr_attr; + int *param, error; + size_t pagen; + File tmp_file; + MY_INIT(argv[0]); + +#ifndef DBUG_OFF +#if defined(_WIN32) + default_dbug_option= "d:t:i:O,\\test_pagecache_single.trace"; +#else + default_dbug_option= "d:t:i:o,/tmp/test_pagecache_single.trace"; +#endif + if (argc > 1) + { + DBUG_SET(default_dbug_option); + DBUG_SET_INITIAL(default_dbug_option); + } +#endif + { + DBUG_ENTER("main"); + DBUG_PRINT("info", ("Main thread: %s\n", my_thread_name())); + + plan(18); + SKIP_BIG_TESTS(18) + { + char *test_dirname= create_tmpdir(argv[0]); + fn_format(file1_name, base_file1_name, test_dirname, "", MYF(0)); + fn_format(file2_name, base_file2_name, test_dirname, "", MYF(0)); + + if ((tmp_file= my_open(file2_name, O_CREAT | O_TRUNC | O_RDWR, + MYF(MY_WME))) < 0) + exit(1); + + if ((file1.file= my_open(file1_name, + O_CREAT | O_TRUNC | O_RDWR, MYF(0))) == -1) + { + fprintf(stderr, "Got error during file1 creation from open() (errno: %d)\n", + errno); + exit(1); + } + pagecache_file_set_null_hooks(&file1); + my_close(tmp_file, MYF(0)); + my_delete(file2_name, MYF(0)); + + DBUG_PRINT("info", ("file1: %d", file1.file)); + if (my_chmod(file1_name, 0777, MYF(MY_WME))) + exit(1); + my_pwrite(file1.file, (const uchar*)"test file", 9, 0, MYF(MY_WME)); + + if ((error= pthread_cond_init(&COND_thread_count, NULL))) + { + fprintf(stderr, "Got error: %d from pthread_cond_init (errno: %d)\n", + error, errno); + exit(1); + } + if ((error= pthread_mutex_init(&LOCK_thread_count, MY_MUTEX_INIT_FAST))) + { + fprintf(stderr, "Got error: %d from pthread_cond_init (errno: %d)\n", + error, errno); + exit(1); + } + + if ((error= pthread_attr_init(&thr_attr))) + { + fprintf(stderr,"Got error: %d from pthread_attr_init (errno: %d)\n", + error,errno); + exit(1); + } + if ((error= pthread_attr_setdetachstate(&thr_attr, PTHREAD_CREATE_DETACHED))) + { + fprintf(stderr, + "Got error: %d from pthread_attr_setdetachstate (errno: %d)\n", + error,errno); + exit(1); + } + +#ifdef HAVE_THR_SETCONCURRENCY + thr_setconcurrency(2); +#endif + + if ((pagen= init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, + TEST_PAGE_SIZE, 0, MYF(MY_WME))) == 0) + { + fprintf(stderr,"Got error: init_pagecache() (errno: %d)\n", + errno); + exit(1); + } + DBUG_PRINT("info", ("Page cache %zd pages", pagen)); + + pthread_mutex_lock(&LOCK_thread_count); + param=(int*) malloc(sizeof(int)); + *param= 1; + if ((error= pthread_create(&tid, &thr_attr, test_thread, (void*) param))) + { + fprintf(stderr,"Got error: %d from pthread_create (errno: %d)\n", + error,errno); + exit(1); + } + thread_count++; + DBUG_PRINT("info", ("Thread started")); + pthread_mutex_unlock(&LOCK_thread_count); + + pthread_attr_destroy(&thr_attr); + + pthread_mutex_lock(&LOCK_thread_count); + while (thread_count) + { + if ((error= pthread_cond_wait(&COND_thread_count,&LOCK_thread_count))) + fprintf(stderr,"Got error: %d from pthread_cond_wait\n",error); + } + pthread_mutex_unlock(&LOCK_thread_count); + DBUG_PRINT("info", ("thread ended")); + + end_pagecache(&pagecache, 1); + DBUG_PRINT("info", ("Page cache ended")); + + if (my_close(file1.file, MYF(MY_WME))) + exit(1); + + my_delete(file1_name, MYF(0)); + rmdir(test_dirname); + + } /* SKIP_BIG_TESTS */ + DBUG_PRINT("info", ("file1 (%d) closed", file1.file)); + DBUG_PRINT("info", ("Program end")); + + my_end(0); + } + return exit_status(); +} + +#include "../ma_check_standalone.h" diff --git a/storage/maria/unittest/ma_test_all-t b/storage/maria/unittest/ma_test_all-t new file mode 100755 index 00000000..8858649f --- /dev/null +++ b/storage/maria/unittest/ma_test_all-t @@ -0,0 +1,769 @@ +#!/usr/bin/env perl +# +# Run various unit tests. +# + +use Getopt::Long; +use File::Basename; + +$|= 1; +$^W = 1; # warnings, because env cannot parse 'perl -w' +$VER= "1.5"; + +$opt_version= 0; +$opt_help= 0; +$opt_verbose= 0; +$opt_abort_on_error= 0; +$opt_valgrind= "valgrind --alignment=8 --leak-check=yes"; +$opt_silent= "-s"; +$opt_number_of_tests= 0; +$opt_run_tests= undef(); + +my $maria_path; # path to "storage/maria" +my $maria_exe_path; # path to executables (ma_test1, aria_chk etc) +my $my_progname= $0; +$my_progname=~ s/.*[\/]//; +my $runtime_error= 0; # Return 1 if error(s) occur during run +my $NEW_TEST= 0; # Test group separator in an array of tests +my $test_begin= 0; +my $test_end= 0; +my $test_counter= 0; +my $using_internal_tmpdir= 0; +my $full_tmpdir; +my $tmpdir="tmp"; +my $exec_dir="TMP-ma_test_all"; # Run test in this directory +run_tests(); + +#### +#### Initialise variables, clean temporary files and run the tests +#### + +sub run_tests +{ + my $nr_tests= 0; + my $flag_exit= 0; + + if (!GetOptions("help" => \$opt_help, + "version" => \$opt_version, + "verbose" => \$opt_verbose, + "abort-on-error" => \$opt_abort_on_error, + "valgrind=s" => \$opt_valgrind, + "silent=s" => \$opt_silent, + "tmpdir=s" => \$full_tmpdir, + "number-of-tests" => \$opt_number_of_tests, + "run-tests=s" => \$opt_run_tests, + "start-from=s" => \$opt_run_tests)) + { + $flag_exit= 1; + } + if ($opt_version) + { + print "$my_progname version $VER\n"; + exit(0); + } + + if (! -d $exec_dir) + { + die if (!mkdir("$exec_dir")); + } + chdir($exec_dir); + + $maria_path= "../" . dirname($0) . "/.."; + + my $suffix= ( $^O =~ /win/i && $^O !~ /darwin/i ) ? ".exe" : ""; + $maria_exe_path= "$maria_path/release"; + # we use -f, sometimes -x is unexpectedly false in Cygwin + if ( ! -f "$maria_exe_path/ma_test1$suffix" ) + { + $maria_exe_path= "$maria_path/relwithdebinfo"; + if ( ! -f "$maria_exe_path/ma_test1$suffix" ) + { + $maria_exe_path= "$maria_path/debug"; + if ( ! -f "$maria_exe_path/ma_test1$suffix" ) + { + $maria_exe_path= $maria_path; + if ( ! -f "$maria_exe_path/ma_test1$suffix" ) + { + die("Cannot find ma_test1 executable in $maria_path\n"); + } + } + } + } + + usage() if ($opt_help || $flag_exit); + + if (defined($full_tmpdir)) + { + $tmpdir= $full_tmpdir; + } + else + { + $full_tmpdir= $tmpdir; + $using_internal_tmpdir= 1; + if (! -d "$full_tmpdir") + { + die if (!mkdir("$full_tmpdir")); + } + } + + # + # IMPORTANT: If you modify this file, please read this: + # + # Count total number of tests. Make sure that the functions return + # number of unit tests correctly, e.g. calls to ok(). The last argument + # for each function is a flag counter and will return the number of + # unit tests in each. Please see comments on function ok() at the end. + # + # If you modify any functions or add any new ones, please make sure the + # unit tests are appropriately detected here. A wrong count will + # make the unit test fail during 'make test'. $nr_tests must be right. + # + + $nr_tests+= run_check_tests(0, 0, 0, 0, 1) * 5; # + $nr_tests+= run_repair_tests(0, 0, 0, 0, 1) * 5; # called 4 times + $nr_tests+= run_pack_tests(0, 0, 0, 0, 1) * 5; # + $nr_tests+= run_tests_on_warnings_and_errors(0, 0, 0, 1); + $nr_tests+= run_ma_test_recovery(0, 1); + $nr_tests+= run_tests_on_clrs(0, 0, 1); + + if ($opt_number_of_tests) + { + print "Total number of tests is $nr_tests\n"; + exit(0); + } + + if (defined($opt_run_tests)) + { + if ($opt_run_tests =~ m/^(\d+)$/ || + $opt_run_tests =~ m/^(\d+)\.+$/) + { + $test_begin= $1; + } + elsif ($opt_run_tests =~ m/^(\d+)\.+(\d+)$/) + { + $test_begin= $1; + $test_end= $2; + } + else + { + print "Wrong syntax for option --run-tests=$opt_run_tests\n"; + print "Please use --run-tests=<begin>..<end>\nwhere 'begin' is the "; + print "first test to be run and 'end' is the last.\n"; + exit(1); + } + if ($test_end > $nr_tests) + { + print "Test range ($test_begin..$test_end) out of range. "; + print "There are only $nr_tests in the test suite.\n"; + exit(1); + } + $test_begin++ if (!$test_begin); # Handle zero, if user gave that + if ($test_end && $test_begin > $test_end) + { + print "Bad test range ($test_begin..$test_end)\n"; + exit(1); + } + # Now adjust number of tests + $nr_tests= ($test_end ? $test_end : $nr_tests) - $test_begin + 1; + } + + # + # clean-up + # + + unlink_all_possible_tmp_files(); + + # + # Run tests + # + + if (!$opt_verbose) + { + print "1..$nr_tests\n"; + } + else + { + print "Total tests: $nr_tests\n"; + } + + if ($opt_verbose) + { + print "Running tests with dynamic row format\n" + } + run_check_tests($suffix, $opt_silent, "", $opt_verbose, 0); + run_repair_tests($suffix, $opt_silent, "", $opt_verbose, 0); + run_pack_tests($suffix, $opt_silent, "", $opt_verbose, 0); + + if ($opt_verbose) + { + print "\nRunning tests with static row format\n"; + } + run_check_tests($suffix, $opt_silent, "-S", $opt_verbose, 0); + run_repair_tests($suffix, $opt_silent, "-S", $opt_verbose, 0); + run_pack_tests($suffix, $opt_silent, "-S", $opt_verbose, 0); + + if ($opt_verbose) + { + print "\nRunning tests with block row format\n"; + } + run_check_tests($suffix, $opt_silent, "-M", $opt_verbose, 0); + run_repair_tests($suffix, $opt_silent, "-M", $opt_verbose, 0); + run_pack_tests($suffix, $opt_silent, "-M", $opt_verbose, 0); + + if ($opt_verbose) + { + print "\nRunning tests with block row format and transactions\n"; + } + run_check_tests($suffix, $opt_silent, "-M -T", $opt_verbose, 0); + run_repair_tests($suffix, $opt_silent, "-M -T", $opt_verbose, 0); + run_pack_tests($suffix, $opt_silent, "-M -T", $opt_verbose, 0); + + if ($opt_verbose) + { + print "\nRunning tests with block row format, transactions and versioning\n"; + } + run_check_tests($suffix, $opt_silent, "-M -T -C", $opt_verbose, 0); + run_repair_tests($suffix, $opt_silent, "-M -T -C", $opt_verbose, 0); + run_pack_tests($suffix, $opt_silent, "-M -T -C", $opt_verbose, 0); + + + if ($opt_verbose) + { + print "\nRunning tests with warnings and recovery\n"; + } + run_tests_on_warnings_and_errors($suffix, $opt_silent, $opt_verbose, 0); + run_ma_test_recovery($opt_verbose, 0); + run_tests_on_clrs($suffix, $opt_verbose, 0); + + unlink_all_possible_tmp_files(); + if ($using_internal_tmpdir) + { + rmdir($tmpdir); + } + rmdir($exec_dir); + chdir(".."); + rmdir($exec_dir); + exit($runtime_error); +} + +#### +#### regular tests +#### + +sub run_check_tests +{ + my ($suffix, $silent, $row_type, $verbose, $count)= @_; + my ($i, $nr_tests); + my @ma_test1_opt= ( ["","-se"], + ["-N","-se"], + ["-P --checksum","-se"], + ["-P -N","-se"], + ["-B -N -R2","-sm"], + ["-a -k 480 --unique","-sm"], + ["-a -N -R1 ","-sm"], + ["-p","-sm"], + ["-p -N --unique","-sm"], + ["-p -N --key_length=127 --checksum","-sm"], + ["-p -N --key_length=128","-sm"], + ["-p --key_length=480","-sm"], + ["-a -B","-sm"], + ["-a -B --key_length=64 --unique","-sm"], + ["-a -B -k 480 --checksum","-sm"], + ["-a -B -k 480 -N --unique --checksum","-sm"], + ["-a -m","-sm"], + ["-a -m -P --unique --checksum","-sm"], + ["-a -m -P --key_length=480 --key_cache","-sm"], + ["-m -p","-sm"], + ["-w --unique","-sm"], + ["-a -w --key_length=64 --checksum","-sm"], + ["-a -w -N --key_length=480","-sm"], + ["-a -w --key_length=480 --checksum","-sm"], + ["-a -b -N","-sm"], + ["-a -b --key_length=480","-sm"], + ["-p -B --key_length=480","-sm"], + ["--checksum --unique","-se"], + ["--unique","-se"], + ["--rows-no-data", "-s"], + ["--key_multiple -N -S","-sm"], + ["--key_multiple -a -p --key_length=480","-sm"], + ["--key_multiple -a -B --key_length=480","-sm"], + ["--key_multiple -P -S","-sm"] ); + my @ma_test2_opt= ( ["-L -K -W -P","-sm"], + ["-L -K -W -P -A","-sm"], + ["-L -K -W -P -b32768", "-sm"], + ["-L -K -W -P -M -T -c -b32768 -t4 -m300", "-sm"], + ["-L -K -P -R3 -m50 -b1000000", "-sm"], + ["-L -B","-sm"], + ["-D -B -c","-sm"], + ["-m10000 -e4096 -K","-sm"], + ["-m10000 -e8192 -K","-sm"], + ["-m10000 -e16384 -E16384 -K -L","-sm"], + ["-L -K -W -P -b32768", "-se"], + ["-c -b65000","-se"] ); + my @ma_rt_test_opt= ( ); # (["--checksum", "-se"] ); + + + if ($count) + { + $nr_tests= 2; # Number of tests outside loops + for ($i= 0; defined($ma_test1_opt[$i]); $i++) { $nr_tests+=2; } + for ($i= 0; defined($ma_test2_opt[$i]); $i++) { $nr_tests+=2; } + for ($i= 0; defined($ma_rt_test_opt[$i]); $i++) { $nr_tests+=2; } + return $nr_tests; + } + + for ($i= 0; defined($ma_test1_opt[$i]); $i++) + { + unlink_log_files(); + ok("$maria_exe_path/ma_test1$suffix $silent -h$tmpdir $ma_test1_opt[$i][0] $row_type", + $verbose, $i + 1); + ok("$maria_exe_path/aria_chk$suffix -h$tmpdir $ma_test1_opt[$i][1] $tmpdir/test1", + $verbose, $i + 1); + } + # + # These tests are outside the loops. Make sure to include them in + # nr_tests manually + # + ok("$maria_exe_path/aria_pack$suffix --force -s $tmpdir/test1", $verbose, 0); + ok("$maria_exe_path/aria_chk$suffix -ess $tmpdir/test1", $verbose, 0); + + for ($i= 0; defined($ma_test2_opt[$i]); $i++) + { + unlink_log_files(); + ok("$maria_exe_path/ma_test2$suffix $silent -h$tmpdir $ma_test2_opt[$i][0] $row_type", + $verbose, $i + 1); + ok("$maria_exe_path/aria_chk$suffix -h$tmpdir $ma_test2_opt[$i][1] $tmpdir/test2", + $verbose, $i + 1); + } + + for ($i= 0; defined($ma_rt_test_opt[$i]); $i++) + { + unlink_log_files(); + ok("$maria_exe_path/ma_rt_test$suffix $silent -h$tmpdir $ma_rt_test_opt[$i][0] $row_type", + $verbose, $i + 1); + ok("$maria_exe_path/aria_chk$suffix -h$tmpdir $ma_rt_test_opt[$i][1] $tmpdir/rt_test", + $verbose, $i + 1); + } + + unlink_log_files(); + + return 0; +} + +#### +#### repair tests +#### + +sub run_repair_tests() +{ + my ($suffix, $silent, $row_type, $verbose, $count)= @_; + my ($i); + + my @t= ($NEW_TEST, + "$maria_exe_path/ma_test1$suffix $silent --checksum $row_type", + "$maria_exe_path/aria_chk$suffix -se test1", + "$maria_exe_path/aria_chk$suffix --silent -re --transaction-log test1", + "$maria_exe_path/aria_chk$suffix -rs test1", + "$maria_exe_path/aria_chk$suffix -se test1", + "$maria_exe_path/aria_chk$suffix -rqs test1", + "$maria_exe_path/aria_chk$suffix -se test1", + "$maria_exe_path/aria_chk$suffix -rs --correct-checksum test1", + "$maria_exe_path/aria_chk$suffix -se test1", + "$maria_exe_path/aria_chk$suffix -rqs --correct-checksum test1", + "$maria_exe_path/aria_chk$suffix -se test1", + "$maria_exe_path/aria_chk$suffix -ros --correct-checksum test1", + "$maria_exe_path/aria_chk$suffix -se test1", + "$maria_exe_path/aria_chk$suffix -rqos --correct-checksum test1", + "$maria_exe_path/aria_chk$suffix -se test1", + "$maria_exe_path/aria_chk$suffix -sz test1", + "$maria_exe_path/aria_chk$suffix -se test1", + "$maria_exe_path/ma_test2$suffix $silent -c -d1 $row_type", + "$maria_exe_path/aria_chk$suffix -s --parallel-recover test2", + "$maria_exe_path/aria_chk$suffix -se test2", + "$maria_exe_path/aria_chk$suffix -s --parallel-recover --quick test2", + "$maria_exe_path/aria_chk$suffix -se test2", + "$maria_exe_path/ma_test2$suffix $silent -c $row_type", + "$maria_exe_path/aria_chk$suffix -se test2", + "$maria_exe_path/aria_chk$suffix -sr test2", + "$maria_exe_path/aria_chk$suffix -se test2", + "$maria_exe_path/ma_test2$suffix $silent -c -t4 -b32768 $row_type", + "$maria_exe_path/aria_chk$suffix -s --zerofill test1", + "$maria_exe_path/aria_chk$suffix -se test1" + ); + + return &count_tests(\@t) if ($count); + &run_test_bunch(\@t, $verbose, 0); + return 0; +} + +#### +#### pack tests +#### + +sub run_pack_tests() +{ + my ($suffix, $silent, $row_type, $verbose, $count)= @_; + my ($i); + + my @t= ($NEW_TEST, + "$maria_exe_path/ma_test1$suffix $silent --checksum $row_type", + "$maria_exe_path/aria_pack$suffix --force -s test1", + "$maria_exe_path/aria_chk$suffix -ess test1", + "$maria_exe_path/aria_chk$suffix -rqs test1", + "$maria_exe_path/aria_chk$suffix -es test1", + "$maria_exe_path/aria_chk$suffix -rs test1", + "$maria_exe_path/aria_chk$suffix -es test1", + "$maria_exe_path/aria_chk$suffix -rus test1", + "$maria_exe_path/aria_chk$suffix -es test1", + $NEW_TEST, + "$maria_exe_path/ma_test1$suffix $silent --checksum $row_type", + "$maria_exe_path/aria_pack$suffix --force -s test1", + "$maria_exe_path/aria_chk$suffix -rus --safe-recover test1", + "$maria_exe_path/aria_chk$suffix -es test1", + $NEW_TEST, + "$maria_exe_path/ma_test1$suffix $silent --checksum -S $row_type", + "$maria_exe_path/aria_chk$suffix -se test1", + "$maria_exe_path/aria_chk$suffix -ros test1", + "$maria_exe_path/aria_chk$suffix -rqs test1", + "$maria_exe_path/aria_chk$suffix -se test1", + $NEW_TEST, + "$maria_exe_path/aria_pack$suffix --force -s test1", + "$maria_exe_path/aria_chk$suffix -rqs test1", + "$maria_exe_path/aria_chk$suffix -es test1", + "$maria_exe_path/aria_chk$suffix -rus test1", + "$maria_exe_path/aria_chk$suffix -es test1", + $NEW_TEST, + "$maria_exe_path/ma_test2$suffix $silent -c -d1 $row_type", + "$maria_exe_path/aria_chk$suffix -s --parallel-recover test2", + "$maria_exe_path/aria_chk$suffix -se test2", + "$maria_exe_path/aria_chk$suffix -s --unpack --parallel-recover test2", + "$maria_exe_path/aria_chk$suffix -se test2", + "$maria_exe_path/aria_pack$suffix --force -s test1", + "$maria_exe_path/aria_chk$suffix -s --unpack --parallel-recover test2", + "$maria_exe_path/aria_chk$suffix -se test2", + $NEW_TEST, + "$maria_exe_path/ma_test1$suffix $silent -c $row_type", + "cp test1.MAD test2.MAD", + "cp test1.MAI test2.MAI", + "$maria_exe_path/aria_pack$suffix --force -s --join=test3 test1 test2", + ); + + return (&count_tests(\@t) + 3) if ($count); + &run_test_bunch(\@t, $verbose, 0); + + ok("$maria_exe_path/aria_chk -s test3", $verbose, 0, 1); + @t= ("$maria_exe_path/aria_chk -s --safe-recover test3", + "$maria_exe_path/aria_chk -s test3"); + &run_test_bunch(\@t, $verbose, 0); + + return 0; +} + +#### +#### Tests that gives warnings or errors +#### + +sub run_tests_on_warnings_and_errors +{ + my ($suffix, $silent, $verbose, $count)= @_; + my ($com); + + return 9 if ($count); # Number of tests in this function, e.g. calls to ok() + + ok("$maria_exe_path/ma_test2$suffix -h$tmpdir $silent -L -K -W -P -S -R1 -m500", + $verbose, 0); + ok("$maria_exe_path/aria_chk$suffix -h$tmpdir -sm $tmpdir/test2", $verbose, 0); + # ma_test2$suffix $silent -L -K -R1 -m2000 ; Should give error 135\n + # In the following a failure is a success and success is a failure + $com= "$maria_exe_path/ma_test2$suffix -h$tmpdir $silent -L -K -R1 -m2000 "; + $com.= ">ma_test2_message.txt 2>&1"; + ok($com, $verbose, 0, 1); + ok("cat ma_test2_message.txt", $verbose, 0); + ok("grep \"Error: 135\" ma_test2_message.txt > /dev/null", $verbose, 0); + # maria_exe_path/aria_chk$suffix -h$tmpdir -sm $tmpdir/test2 will warn that + # Datafile is almost full + ok("$maria_exe_path/aria_chk$suffix -h$tmpdir -sm $tmpdir/test2 >ma_test2_message.txt 2>&1", + $verbose, 0, 1); + ok("cat ma_test2_message.txt", $verbose, 0); + ok("grep \"warning: Datafile is almost full\" ma_test2_message.txt>/dev/null", + $verbose, 0); + unlink <ma_test2_message.txt>; + ok("$maria_exe_path/aria_chk$suffix -h$tmpdir -ssm $tmpdir/test2", $verbose, 0); + + return 0; +} + +#### +#### Test that removing tables and applying the log leads to identical tables +#### + +sub run_ma_test_recovery +{ + my ($verbose, $count)= @_; + + return 1 if ($count); # Number of tests in this function + ok("$maria_path/unittest/ma_test_recovery.pl", $verbose, 0); + return 0; +} + +#### +#### Tests on CLR's +#### + +sub run_tests_on_clrs +{ + my ($suffix, $verbose, $count)= @_; + my ($i); + + my @t= ($NEW_TEST, + "$maria_exe_path/ma_test2$suffix -h$tmpdir -s -L -K -W -P -M -T -c -b -t2 -A1", + "cp $tmpdir/aria_log_control $tmpdir/aria_log_control.backup", + "$maria_exe_path/aria_read_log$suffix -a -s -h$tmpdir", + "$maria_exe_path/aria_chk$suffix -h$tmpdir -s -e $tmpdir/test2", + "mv $tmpdir/aria_log_control.backup $tmpdir/aria_log_control", + "rm $tmpdir/test2.MA?", + "$maria_exe_path/aria_read_log$suffix -a -s -h$tmpdir", + "$maria_exe_path/aria_chk$suffix -h$tmpdir -s -e $tmpdir/test2", + "rm $tmpdir/test2.MA?", + $NEW_TEST, + "$maria_exe_path/ma_test2$suffix -h$tmpdir -s -L -K -W -P -M -T -c -b -t2 -A1", + "$maria_exe_path/aria_read_log$suffix -a -s -h$tmpdir ", + "$maria_exe_path/aria_chk$suffix -h$tmpdir -s -e $tmpdir/test2", + "rm $tmpdir/test2.MA?", + "$maria_exe_path/aria_read_log$suffix -a -s -h$tmpdir", + "$maria_exe_path/aria_chk$suffix -h$tmpdir -e -s $tmpdir/test2", + "rm $tmpdir/test2.MA?", + $NEW_TEST, + "$maria_exe_path/ma_test2$suffix -h$tmpdir -s -L -K -W -P -M -T -c -b32768 -t4 -A1", + "$maria_exe_path/aria_read_log$suffix -a -s -h$tmpdir", + "$maria_exe_path/aria_chk$suffix -h$tmpdir -es $tmpdir/test2", + "$maria_exe_path/aria_read_log$suffix -a -s -h$tmpdir ", + "$maria_exe_path/aria_chk$suffix -h$tmpdir -es $tmpdir/test2", + "rm $tmpdir/test2.MA?", + "$maria_exe_path/aria_read_log$suffix -a -s -h$tmpdir", + "$maria_exe_path/aria_chk$suffix -h$tmpdir -es $tmpdir/test2", + "rm $tmpdir/test2.MA?" + ); + + return &count_tests(\@t) if ($count); + &run_test_bunch(\@t, $verbose, 1); + return 0; +} + +# +# Print "ok" on success and "not ok" on error +# +# Note: Every time this function is called it will be counted +# as a unit test. +# +# Args: $com: The actual command run. Will be printed on a failure +# $verbose: Be more verbose. +# $iteration: Number of iterations in a loop when the error +# occurred. If not in loop, this should be blank +# (e.g. send zero). +# $expected_error: Optional; put here expected error code. Test +# will pass with this result only. +# +# Return value: Will return 1 on success and 0 on an error +# + +sub ok +{ + my ($com, $verbose, $iteration, $expected_error)= @_; + my ($msg, $output, $err, $errcode, $len); + + $test_counter++; + if ($test_begin > $test_counter) + { + return 0; + } + if ($test_end && $test_end < $test_counter) + { + exit(0); + } + + $msg= ""; + $expected_error= 0 if (!defined($expected_error)); + + if ($verbose) + { + # Print command with out the long unittest/../ prefix + my $tmp; + $tmp= $com; + $tmp =~ s|^unittest/../||; + print "$tmp "; + $len= length($tmp); + } + $output= `$com 2>&1`; + if ($verbose) + { + print " " x (62 - $len); + } + $err= $?; + $errcode= ($? >> 8); + if ((!$err && !$expected_error) || + ($errcode == $expected_error && $expected_error)) + { + print "[ " if ($verbose); + print "ok"; + if ($verbose) + { + print " ]"; + print " " x (5 - length("$test_counter")); + print "$test_counter"; + } + else + { + print " $test_counter - $com" + } + print "\n"; + return 1; + } + print "[ " if ($verbose); + print "not ok"; + print " ]" if ($verbose); + print " $test_counter - $com" unless $verbose; + print "\n"; + if ($verbose && defined($output) && length($output)) + { + print "$output\n"; + } + if (!$verbose) + { + $msg= "\n"; # Get a nicer output in perl unit test mode + } + $msg.= "Failed test '$com' "; + if ($iteration) + { + $msg.= "(loop iteration $iteration.) "; + } + $msg.= "at line "; + $msg.= (caller)[2]; + $msg.= "\n(errcode: $errcode, test: $test_counter)\n"; + if ($expected_error) + { + $msg.= "Was expecting errcode: $expected_error\n"; + } + warn $msg; + $runtime_error= 1; + if ($opt_abort_on_error) + { + exit 1; + } + # Unlink all files so that we can continue on error + unlink_all_possible_tmp_files(); + return 0; +} + +# +# Print "skip" and the reason +# +# Note: Every time this function is called it will be counted +# as a unit test. +# +# Args: $com: The actual command run. Will be printed on a failure +# $reason: The reason to skip a test +# $verbose: Be more verbose. +# + +sub skip +{ + my ($com, $reason, $verbose)= @_; + + $test_counter++; + return 0 if $test_begin > $test_counter; + exit 0 if $test_end && $test_end < $test_counter; + printf '%-64s[ skipped ]%5d', $com, $test_counter if $verbose; + print "ok $test_counter # skip $reason" unless $verbose; + print "\n"; + return 1; +} + +#### +#### Count tests +#### Arguments: $t: an array of the tests +#### + +sub count_tests +{ + my ($t)= @_; + my ($i, $nr_tests); + + $nr_tests= 0; + for ($i= 0; defined(@$t[$i]); $i++) { $nr_tests++ if (@$t[$i]); } + return $nr_tests; +} + +sub unlink_log_files +{ + unlink "$full_tmpdir/aria_log_control", "$full_tmpdir/aria_log.00000001", "$full_tmpdir/aria_log.00000002"; +} + +sub unlink_all_possible_tmp_files() +{ + unlink_log_files(); + + # Unlink tmp files that may have been created when testing the test programs + unlink <$full_tmpdir/*.TMD $full_tmpdir/aria_read_log_test1.txt $full_tmpdir/test1*.MA? $full_tmpdir/ma_test_recovery.output aria_log_control aria_log.00000001 aria_log.00000002 aria_logtest1.MA? test1.MA? test2.MA? test3.MA? *.TMD>; +} + +#### +#### Run a bunch of tests +#### Arguments: $t: an array of the tests +#### $verbose: to be passed to ok() +#### $clear: clear log files if set +#### + +sub run_test_bunch +{ + my ($t, $verbose, $clear)= @_; + my ($i); + + for ($i= 0; defined(@$t[$i]); $i++) + { + if ($clear && @$t[$i] eq $NEW_TEST) + { + unlink_log_files(); + } + if (@$t[$i] ne $NEW_TEST) + { + ok(@$t[$i], $verbose, $i + 1); + } + } +} + +#### +#### usage +#### + +sub usage +{ + print <<EOF; +$my_progname version $VER + +Description: + +Run various Aria related tests. Typically used via make test as a unittest. + +Options +--help Show this help and exit. +--abort-on-error Abort at once in case of error. +--number-of-tests Print the total number of tests and exit. +--run-tests=... Test range that should be run. You can give just + one number, to start tests from this test, or a range. + For example 45..89. To run a specific test alone, + for example test 215, use --run-tests=215..215 + Use this option with caution, because some of the tests + might depend on previous ones. +--start-from=... Alias for --run-tests +--silent=... Silent option passed to ma_test* tests ('$opt_silent') +--tmpdir=... Store tests data in this directory (works for most tests) +--valgrind=... Options for valgrind. + ('$opt_valgrind') +--verbose Be more verbose. Will print each unittest on a line + and result after. This mode cannot be used with unit.pl + when running in normal unit test mode. +--version Show version number and exit. +EOF + exit(0); +} diff --git a/storage/maria/unittest/ma_test_loghandler-t.c b/storage/maria/unittest/ma_test_loghandler-t.c new file mode 100644 index 00000000..ccda66af --- /dev/null +++ b/storage/maria/unittest/ma_test_loghandler-t.c @@ -0,0 +1,668 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +#include "../maria_def.h" +#include <stdio.h> +#include <errno.h> +#include <tap.h> +#include "../trnman.h" + +extern my_bool maria_log_remove(const char *testdir); +extern char *create_tmpdir(const char *progname); +extern void example_loghandler_init(); + +#ifndef DBUG_OFF +static const char *default_dbug_option; +#endif +static TRN *trn= &dummy_transaction_object; + +#define PCACHE_SIZE (1024*1024*10) + +#define LONG_BUFFER_SIZE (100 * 1024) + +#ifdef LONG_LOG_TEST +#define LOG_FLAGS 0 +#define LOG_FILE_SIZE (1024L*1024L*8) +#define ITERATIONS (1600*4) + +#else +#undef SKIP_BIG_TESTS +#define SKIP_BIG_TESTS(X) /* no-op */ +#define LOG_FLAGS (TRANSLOG_SECTOR_PROTECTION | TRANSLOG_PAGE_CRC) +#define LOG_FILE_SIZE (1024L*1024L*8L) +#define ITERATIONS 1600 +#endif + +/* +#define LOG_FLAGS 0 +#define LOG_FILE_SIZE 1024L*1024L*1024L +#define ITERATIONS 181000 +*/ + +/* +#define LOG_FLAGS 0 +#define LOG_FILE_SIZE 1024L*1024L*3L +#define ITERATIONS 1600 +*/ + +/* +#define LOG_FLAGS 0 +#define LOG_FILE_SIZE 1024L*1024L*100L +#define ITERATIONS 65000 +*/ + +/* + Generate random value in the range (0,LONG_BUFFER_SIZE) +*/ +static uint32 rand_buffer_size() +{ + return (uint32)((ulonglong)rand()*(LONG_BUFFER_SIZE + 1)/RAND_MAX); +} + +/* + Check that the buffer filled correctly + + SYNOPSIS + check_content() + ptr Pointer to the buffer + length length of the buffer + + RETURN + 0 - OK + 1 - Error +*/ + + +static my_bool check_content(uchar *ptr, ulong length) +{ + ulong i; + uchar buff[2]; + for (i= 0; i < length; i++) + { + if (i % 2 == 0) + int2store(buff, i >> 1); + if (ptr[i] != buff[i % 2]) + { + fprintf(stderr, "Byte # %lu is %x instead of %x", + i, (uint) ptr[i], (uint) buff[i % 2]); + return 1; + } + } + return 0; +} + + +/* + Report OK for read operation + + SYNOPSIS + read_ok() + rec the record header +*/ + +void read_ok(TRANSLOG_HEADER_BUFFER *rec) +{ + ok(1, "read record type: %u LSN: " LSN_FMT, + rec->type, LSN_IN_PARTS(rec->lsn)); +} + +/* + Read whole record content, and check content (put with offset) + + SYNOPSIS + read_and_check_content() + rec The record header buffer + buffer The buffer to read the record in + skip Skip this number of bytes ot the record content + + RETURN + 0 - OK + 1 - Error +*/ + +static my_bool read_and_check_content(TRANSLOG_HEADER_BUFFER *rec, + uchar *buffer, uint skip) +{ + DBUG_ASSERT(rec->record_length < LONG_BUFFER_SIZE * 2 + 7 * 2 + 2); + if (translog_read_record(rec->lsn, 0, rec->record_length, buffer, NULL) != + rec->record_length) + return 1; + return check_content(buffer + skip, rec->record_length - skip); +} + + +int main(int argc __attribute__((unused)), char *argv[]) +{ + uint32 i; + uint32 rec_len; + uchar long_tr_id[6]; + uchar lsn_buff[23]= + { + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 + }; + uchar long_buffer[LONG_BUFFER_SIZE * 2 + LSN_STORE_SIZE * 2 + 2]; + PAGECACHE pagecache; + LSN lsn, lsn_base, first_lsn; + TRANSLOG_HEADER_BUFFER rec; + LEX_CUSTRING parts[TRANSLOG_INTERNAL_PARTS + 3]; + struct st_translog_scanner_data scanner; + int rc; + MY_INIT(argv[0]); + + if (my_set_max_open_files(100) < 100) + { + fprintf(stderr, "can't allocate 100 file descriptors\n"); + exit(1); + } + bzero(&pagecache, sizeof(pagecache)); + + maria_data_root= create_tmpdir(argv[0]); + if (maria_log_remove(0)) + exit(1); + + /* We don't need to do physical syncs in this test */ + my_disable_sync= 1; + + for (i= 0; i < (LONG_BUFFER_SIZE + LSN_STORE_SIZE * 2 + 2); i+= 2) + { + int2store(long_buffer + i, (i >> 1)); + /* long_buffer[i]= (i & 0xFF); */ + } + + bzero(long_tr_id, 6); +#ifndef DBUG_OFF +#if defined(_WIN32) + default_dbug_option= "d:t:i:O,\\ma_test_loghandler.trace"; +#else + default_dbug_option= "d:t:i:o,/tmp/ma_test_loghandler.trace"; +#endif + if (argc > 1) + { + DBUG_SET(default_dbug_option); + DBUG_SET_INITIAL(default_dbug_option); + } +#endif + + if (ma_control_file_open(TRUE, TRUE, TRUE)) + { + fprintf(stderr, "Can't init control file (%d)\n", errno); + exit(1); + } + if (init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, + TRANSLOG_PAGE_SIZE, 0, 0) == 0) + { + fprintf(stderr, "Got error: init_pagecache() (errno: %d)\n", errno); + exit(1); + } + if (translog_init_with_table(maria_data_root, LOG_FILE_SIZE, 50112, 0, &pagecache, + LOG_FLAGS, 0, &translog_example_table_init, + 0)) + { + fprintf(stderr, "Can't init loghandler (%d)\n", errno); + exit(1); + } + /* Suppressing of automatic record writing */ + trn->first_undo_lsn|= TRANSACTION_LOGGED_LONG_ID; + + plan(((ITERATIONS - 1) * 4 + 1)*2 + ITERATIONS - 1 + 1); + + SKIP_BIG_TESTS(((ITERATIONS - 1) * 4 + 1)*2 + ITERATIONS - 1 + 1) + { + + srand(122334817L); + + long_tr_id[5]= 0xff; + + int4store(long_tr_id, 0); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_tr_id; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 6; + trn->short_id= 0; + trn->first_undo_lsn= TRANSACTION_LOGGED_LONG_ID; + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + trn, NULL, 6, TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write record #%lu\n", (ulong) 0); + translog_destroy(); + ok(0, "write LOGREC_FIXED_RECORD_0LSN_EXAMPLE"); + exit(1); + } + ok(1, "write LOGREC_FIXED_RECORD_0LSN_EXAMPLE"); + lsn_base= first_lsn= lsn; + + for (i= 1; i < ITERATIONS; i++) + { + trn->short_id= i % 0xFFFF; + if (i % 2) + { + lsn_store(lsn_buff, lsn_base); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= lsn_buff; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= LSN_STORE_SIZE; + /* check auto-count feature */ + parts[TRANSLOG_INTERNAL_PARTS + 1].str= NULL; + parts[TRANSLOG_INTERNAL_PARTS + 1].length= 0; + if (translog_write_record(&lsn, LOGREC_FIXED_RECORD_1LSN_EXAMPLE, trn, + NULL, LSN_STORE_SIZE, 0, parts, NULL, NULL)) + { + fprintf(stderr, "1 Can't write reference defore record #%lu\n", + (ulong) i); + translog_destroy(); + ok(0, "write LOGREC_FIXED_RECORD_1LSN_EXAMPLE"); + exit(1); + } + ok(1, "write LOGREC_FIXED_RECORD_1LSN_EXAMPLE"); + lsn_store(lsn_buff, lsn_base); + if ((rec_len= rand_buffer_size()) < 12) + rec_len= 12; + parts[TRANSLOG_INTERNAL_PARTS + 0].str= lsn_buff; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= LSN_STORE_SIZE; + parts[TRANSLOG_INTERNAL_PARTS + 1].str= long_buffer; + parts[TRANSLOG_INTERNAL_PARTS + 1].length= rec_len; + /* check record length auto-counting */ + if (translog_write_record(&lsn, + LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE, + trn, NULL, 0, TRANSLOG_INTERNAL_PARTS + 2, + parts, NULL, NULL)) + { + fprintf(stderr, "1 Can't write var reference defore record #%lu\n", + (ulong) i); + translog_destroy(); + ok(0, "write LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE"); + exit(1); + } + ok(1, "write LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE"); + } + else + { + lsn_store(lsn_buff, lsn_base); + lsn_store(lsn_buff + LSN_STORE_SIZE, first_lsn); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= lsn_buff; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 23; + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_2LSN_EXAMPLE, + trn, NULL, 23, TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "0 Can't write reference defore record #%lu\n", + (ulong) i); + translog_destroy(); + ok(0, "write LOGREC_FIXED_RECORD_2LSN_EXAMPLE"); + exit(1); + } + ok(1, "write LOGREC_FIXED_RECORD_2LSN_EXAMPLE"); + lsn_store(lsn_buff, lsn_base); + lsn_store(lsn_buff + LSN_STORE_SIZE, first_lsn); + if ((rec_len= rand_buffer_size()) < 19) + rec_len= 19; + parts[TRANSLOG_INTERNAL_PARTS + 0].str= lsn_buff; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 14; + parts[TRANSLOG_INTERNAL_PARTS + 1].str= long_buffer; + parts[TRANSLOG_INTERNAL_PARTS + 1].length= rec_len; + if (translog_write_record(&lsn, + LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE, + trn, NULL, 14 + rec_len, + TRANSLOG_INTERNAL_PARTS + 2, parts, NULL, + NULL)) + { + fprintf(stderr, "0 Can't write var reference defore record #%lu\n", + (ulong) i); + translog_destroy(); + ok(0, "write LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE"); + exit(1); + } + ok(1, "write LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE"); + } + int4store(long_tr_id, i); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_tr_id; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 6; + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + trn, NULL, 6, + TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write record #%lu\n", (ulong) i); + translog_destroy(); + ok(0, "write LOGREC_FIXED_RECORD_0LSN_EXAMPLE"); + exit(1); + } + ok(1, "write LOGREC_FIXED_RECORD_0LSN_EXAMPLE"); + + lsn_base= lsn; + + if ((rec_len= rand_buffer_size()) < 9) + rec_len= 9; + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_buffer; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= rec_len; + if (translog_write_record(&lsn, + LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE, + trn, NULL, rec_len, + TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write variable record #%lu\n", (ulong) i); + translog_destroy(); + ok(0, "write LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE"); + exit(1); + } + ok(1, "write LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE"); + if (translog_flush(lsn)) + { + fprintf(stderr, "Can't flush #%lu\n", (ulong) i); + translog_destroy(); + ok(0, "flush"); + exit(1); + } + ok(1, "flush"); + } + + if (translog_flush(translog_get_horizon())) + { + fprintf(stderr, "Can't flush up to horizon\n"); + translog_destroy(); + ok(0, "flush"); + exit(1); + } + ok(1, "flush"); + + srand(122334817L); + + rc= 1; + + { + int len= translog_read_record_header(first_lsn, &rec); + if (len == RECHEADER_READ_ERROR) + { + fprintf(stderr, "translog_read_record_header failed (%d)\n", errno); + goto err; + } + if (rec.type !=LOGREC_FIXED_RECORD_0LSN_EXAMPLE || rec.short_trid != 0 || + rec.record_length != 6 || uint4korr(rec.header) != 0 || + ((uchar)rec.header[4]) != 0 || ((uchar)rec.header[5]) != 0xFF || + first_lsn != rec.lsn) + { + fprintf(stderr, "Incorrect LOGREC_FIXED_RECORD_0LSN_EXAMPLE " + "data read(0)\n" + "type %u, strid %u, len %u, i: %u, 4: %u 5: %u, " + "lsn" LSN_FMT "\n", + (uint) rec.type, (uint) rec.short_trid, (uint) rec.record_length, + (uint) uint4korr(rec.header), (uint) rec.header[4], + (uint) rec.header[5], + LSN_IN_PARTS(rec.lsn)); + goto err; + } + read_ok(&rec); + translog_free_record_header(&rec); + lsn= first_lsn; + if (translog_scanner_init(first_lsn, 1, &scanner, 0)) + { + fprintf(stderr, "scanner init failed\n"); + goto err; + } + for (i= 1;; i++) + { + len= translog_read_next_record_header(&scanner, &rec); + if (len == RECHEADER_READ_ERROR) + { + fprintf(stderr, "1-%d translog_read_next_record_header failed (%d)\n", + i, errno); + goto err; + } + if (len == RECHEADER_READ_EOF) + { + if (i != ITERATIONS) + { + fprintf(stderr, "EOL met at iteration %u instead of %u\n", + i, ITERATIONS); + goto err; + } + break; + } + if (i % 2) + { + LSN ref; + ref= lsn_korr(rec.header); + if (rec.type != LOGREC_FIXED_RECORD_1LSN_EXAMPLE || + rec.short_trid != (i % 0xFFFF) || + rec.record_length != 7 || ref != lsn) + { + fprintf(stderr, "Incorrect LOGREC_FIXED_RECORD_1LSN_EXAMPLE " + "data read(%d) " + "type: %u strid: %u len: %u" + "ref: " LSN_FMT " " LSN_FMT " " + "lsn" LSN_FMT "\n", + i, (uint) rec.type, (uint) rec.short_trid, + (uint) rec.record_length, + LSN_IN_PARTS(ref), LSN_IN_PARTS(lsn), + LSN_IN_PARTS(rec.lsn)); + goto err; + } + } + else + { + LSN ref1, ref2; + ref1= lsn_korr(rec.header); + ref2= lsn_korr(rec.header + LSN_STORE_SIZE); + if (rec.type != LOGREC_FIXED_RECORD_2LSN_EXAMPLE || + rec.short_trid != (i % 0xFFFF) || + rec.record_length != 23 || + ref1 != lsn || + ref2 != first_lsn || + ((uchar)rec.header[22]) != 0x55 || + ((uchar)rec.header[21]) != 0xAA || + ((uchar)rec.header[20]) != 0x55 || + ((uchar)rec.header[19]) != 0xAA || + ((uchar)rec.header[18]) != 0x55 || + ((uchar)rec.header[17]) != 0xAA || + ((uchar)rec.header[16]) != 0x55 || + ((uchar)rec.header[15]) != 0xAA || + ((uchar)rec.header[14]) != 0x55) + { + fprintf(stderr, "Incorrect LOGREC_FIXED_RECORD_2LSN_EXAMPLE " + "data read(%d) " + "type %u, strid %u, len %u, ref1" LSN_FMT ", " + "ref2" LSN_FMT " %x%x%x%x%x%x%x%x%x " + "lsn" LSN_FMT "\n", + i, (uint) rec.type, (uint) rec.short_trid, + (uint) rec.record_length, + LSN_IN_PARTS(ref1), LSN_IN_PARTS(ref2), + (uint) rec.header[14], (uint) rec.header[15], + (uint) rec.header[16], (uint) rec.header[17], + (uint) rec.header[18], (uint) rec.header[19], + (uint) rec.header[20], (uint) rec.header[21], + (uint) rec.header[22], + LSN_IN_PARTS(rec.lsn)); + goto err; + } + } + read_ok(&rec); + translog_free_record_header(&rec); + + len= translog_read_next_record_header(&scanner, &rec); + if (len == RECHEADER_READ_ERROR) + { + fprintf(stderr, "1-%d translog_read_next_record_header (var) " + "failed (%d)\n", i, errno); + goto err; + } + if (len == RECHEADER_READ_EOF) + { + fprintf(stderr, "EOL met at the middle of iteration (first var) %u " + "instead of beginning of %u\n", i, ITERATIONS); + goto err; + } + if (i % 2) + { + LSN ref; + ref= lsn_korr(rec.header); + if ((rec_len= rand_buffer_size()) < 12) + rec_len= 12; + if (rec.type != LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE || + rec.short_trid != (i % 0xFFFF) || + rec.record_length != rec_len + LSN_STORE_SIZE || + len != 12 || ref != lsn || + check_content(rec.header + LSN_STORE_SIZE, len - LSN_STORE_SIZE)) + { + fprintf(stderr, "Incorrect LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE " + "data read(%d)" + "type %u (%d), strid %u (%d), len %lu, %lu + 7 (%d), " + "hdr len: %u (%d), " + "ref" LSN_FMT ", lsn" LSN_FMT " (%d), content: %d\n", + i, (uint) rec.type, + rec.type != LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE, + (uint) rec.short_trid, + rec.short_trid != (i % 0xFFFF), + (ulong) rec.record_length, (ulong) rec_len, + rec.record_length != rec_len + LSN_STORE_SIZE, + (uint) len, + len != 12, + LSN_IN_PARTS(ref), LSN_IN_PARTS(rec.lsn), + (len != 12 || ref != lsn), + check_content(rec.header + LSN_STORE_SIZE, + len - LSN_STORE_SIZE)); + goto err; + } + if (read_and_check_content(&rec, long_buffer, LSN_STORE_SIZE)) + { + fprintf(stderr, + "Incorrect LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE " + "in whole rec read lsn" LSN_FMT "\n", + LSN_IN_PARTS(rec.lsn)); + goto err; + } + } + else + { + LSN ref1, ref2; + ref1= lsn_korr(rec.header); + ref2= lsn_korr(rec.header + LSN_STORE_SIZE); + if ((rec_len= rand_buffer_size()) < 19) + rec_len= 19; + if (rec.type != LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE || + rec.short_trid != (i % 0xFFFF) || + rec.record_length != rec_len + LSN_STORE_SIZE * 2 || + len != 19 || + ref1 != lsn || + ref2 != first_lsn || + check_content(rec.header + LSN_STORE_SIZE * 2, + len - LSN_STORE_SIZE * 2)) + { + fprintf(stderr, "Incorrect LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE " + "data read(%d) " + "type %u, strid %u, len %lu != %lu + 14, hdr len: %d, " + "ref1" LSN_FMT ", ref2" LSN_FMT ", " + "lsn" LSN_FMT "\n", + i, (uint) rec.type, (uint) rec.short_trid, + (ulong) rec.record_length, (ulong) rec_len, + len, LSN_IN_PARTS(ref1), LSN_IN_PARTS(ref2), + LSN_IN_PARTS(rec.lsn)); + goto err; + } + if (read_and_check_content(&rec, long_buffer, LSN_STORE_SIZE * 2)) + { + fprintf(stderr, + "Incorrect LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE " + "in whole rec read lsn" LSN_FMT "\n", + LSN_IN_PARTS(rec.lsn)); + goto err; + } + } + read_ok(&rec); + translog_free_record_header(&rec); + + len= translog_read_next_record_header(&scanner, &rec); + if (len == RECHEADER_READ_ERROR) + { + fprintf(stderr, "1-%d translog_read_next_record_header failed (%d)\n", + i, errno); + goto err; + } + if (len == RECHEADER_READ_EOF) + { + fprintf(stderr, "EOL met at the middle of iteration %u " + "instead of beginning of %u\n", i, ITERATIONS); + goto err; + } + if (rec.type != LOGREC_FIXED_RECORD_0LSN_EXAMPLE || + rec.short_trid != (i % 0xFFFF) || + rec.record_length != 6 || uint4korr(rec.header) != i || + ((uchar)rec.header[4]) != 0 || ((uchar)rec.header[5]) != 0xFF) + { + fprintf(stderr, "Incorrect LOGREC_FIXED_RECORD_0LSN_EXAMPLE " + "data read(%d)\n" + "type %u, strid %u, len %u, i: %u, 4: %u 5: %u " + "lsn" LSN_FMT "\n", + i, (uint) rec.type, (uint) rec.short_trid, + (uint) rec.record_length, + (uint) uint4korr(rec.header), (uint) rec.header[4], + (uint) rec.header[5], + LSN_IN_PARTS(rec.lsn)); + goto err; + } + lsn= rec.lsn; + read_ok(&rec); + translog_free_record_header(&rec); + + len= translog_read_next_record_header(&scanner, &rec); + if ((rec_len= rand_buffer_size()) < 9) + rec_len= 9; + if (rec.type != LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE || + rec.short_trid != (i % 0xFFFF) || + rec.record_length != rec_len || + len != 9 || check_content(rec.header, (uint)len)) + { + fprintf(stderr, "Incorrect LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE " + "data read(%d) " + "type %u, strid %u, len %lu != %lu, hdr len: %d, " + "lsn" LSN_FMT "\n", + i, (uint) rec.type, (uint) rec.short_trid, + (ulong) rec.record_length, (ulong) rec_len, + len, LSN_IN_PARTS(rec.lsn)); + goto err; + } + if (read_and_check_content(&rec, long_buffer, 0)) + { + fprintf(stderr, + "Incorrect LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE " + "in whole rec read lsn" LSN_FMT "\n", + LSN_IN_PARTS(rec.lsn)); + goto err; + } + read_ok(&rec); + translog_free_record_header(&rec); + } + } + + rc= 0; +err: + if (rc) + ok(0, "read record"); + } /* SKIP_BIG_TESTS */ + translog_destroy(); + end_pagecache(&pagecache, 1); + ma_control_file_end(); + if (maria_log_remove(maria_data_root)) + exit(1); + + my_uuid_end(); + my_free_open_file_info(); + my_end(0); + return(MY_TEST(exit_status())); +} + +#include "../ma_check_standalone.h" diff --git a/storage/maria/unittest/ma_test_loghandler_first_lsn-t.c b/storage/maria/unittest/ma_test_loghandler_first_lsn-t.c new file mode 100644 index 00000000..21f6b7d7 --- /dev/null +++ b/storage/maria/unittest/ma_test_loghandler_first_lsn-t.c @@ -0,0 +1,165 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +#include "../maria_def.h" +#include <stdio.h> +#include <errno.h> +#include <tap.h> +#include "../trnman.h" + +extern my_bool maria_log_remove(const char *testdir); +extern char *create_tmpdir(const char *progname); +extern void translog_example_table_init(); + +#ifndef DBUG_OFF +static const char *default_dbug_option; +#endif + +#define PCACHE_SIZE (1024*1024*10) +#define PCACHE_PAGE TRANSLOG_PAGE_SIZE +#define LOG_FILE_SIZE (1024L*1024L*1024L + 1024L*1024L*512) +#define LOG_FLAGS 0 + + +int main(int argc __attribute__((unused)), char *argv[]) +{ + uchar long_tr_id[6]; + PAGECACHE pagecache; + LSN lsn, first_lsn, theor_lsn; + LEX_CUSTRING parts[TRANSLOG_INTERNAL_PARTS + 1]; + MY_INIT(argv[0]); + + plan(2); + + bzero(&pagecache, sizeof(pagecache)); + /* + Don't give an error if we can't create dir, as it may already exist from a previously aborted + run + */ + maria_data_root= create_tmpdir(argv[0]); + if (maria_log_remove(0)) + exit(1); + + bzero(long_tr_id, 6); +#ifndef DBUG_OFF +#if defined(_WIN32) + default_dbug_option= "d:t:i:O,\\ma_test_loghandler.trace"; +#else + default_dbug_option= "d:t:i:o,/tmp/ma_test_loghandler.trace"; +#endif + if (argc > 1) + { + DBUG_SET(default_dbug_option); + DBUG_SET_INITIAL(default_dbug_option); + } +#endif + + if (ma_control_file_open(TRUE, TRUE,TRUE)) + { + fprintf(stderr, "Can't init control file (%d)\n", errno); + exit(1); + } + if (init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, + PCACHE_PAGE, 0, 0) == 0) + { + fprintf(stderr, "Got error: init_pagecache() (errno: %d)\n", errno); + exit(1); + } + if (translog_init_with_table(maria_data_root, LOG_FILE_SIZE, 50112, 0, &pagecache, + LOG_FLAGS, 0, &translog_example_table_init, 0)) + { + fprintf(stderr, "Can't init loghandler (%d)\n", errno); + exit(1); + } + /* Suppressing of automatic record writing */ + dummy_transaction_object.first_undo_lsn|= TRANSACTION_LOGGED_LONG_ID; + + theor_lsn= translog_first_theoretical_lsn(); + if (theor_lsn == 1) + { + fprintf(stderr, "Error reading the first log file."); + translog_destroy(); + exit(1); + } + if (theor_lsn == LSN_IMPOSSIBLE) + { + fprintf(stderr, "There is no first log file."); + translog_destroy(); + exit(1); + } + first_lsn= translog_first_lsn_in_log(); + if (first_lsn != LSN_IMPOSSIBLE) + { + fprintf(stderr, "Incorrect first lsn response " LSN_FMT ".", + LSN_IN_PARTS(first_lsn)); + translog_destroy(); + exit(1); + } + ok(1, "Empty log response"); + + + int4store(long_tr_id, 0); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_tr_id; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 6; + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + &dummy_transaction_object, NULL, 6, + TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write record #%lu\n", (ulong) 0); + translog_destroy(); + exit(1); + } + + theor_lsn= translog_first_theoretical_lsn(); + if (theor_lsn == 1) + { + fprintf(stderr, "Error reading the first log file\n"); + translog_destroy(); + exit(1); + } + if (theor_lsn == LSN_IMPOSSIBLE) + { + fprintf(stderr, "There is no first log file\n"); + translog_destroy(); + exit(1); + } + first_lsn= translog_first_lsn_in_log(); + if (first_lsn != theor_lsn) + { + fprintf(stderr, "Incorrect first lsn: " LSN_FMT " " + " theoretical first: " LSN_FMT "\n", + LSN_IN_PARTS(first_lsn), LSN_IN_PARTS(theor_lsn)); + translog_destroy(); + exit(1); + } + + ok(1, "Full log response"); + + translog_destroy(); + end_pagecache(&pagecache, 1); + ma_control_file_end(); + if (maria_log_remove(maria_data_root)) + exit(1); + + my_uuid_end(); + my_free_open_file_info(); + my_end(0); + + exit(0); +} + +#include "../ma_check_standalone.h" diff --git a/storage/maria/unittest/ma_test_loghandler_max_lsn-t.c b/storage/maria/unittest/ma_test_loghandler_max_lsn-t.c new file mode 100644 index 00000000..391d7851 --- /dev/null +++ b/storage/maria/unittest/ma_test_loghandler_max_lsn-t.c @@ -0,0 +1,163 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +#include "../maria_def.h" +#include <stdio.h> +#include <errno.h> +#include <tap.h> +#include "../trnman.h" + +extern my_bool maria_log_remove(const char *testdir); +extern char *create_tmpdir(const char *progname); +extern void translog_example_table_init(); + +#ifndef DBUG_OFF +static const char *default_dbug_option; +#endif + +#define PCACHE_SIZE (1024*1024*10) +#define PCACHE_PAGE TRANSLOG_PAGE_SIZE +#define LOG_FILE_SIZE (8*1024L*1024L) +#define LOG_FLAGS 0 + + +int main(int argc __attribute__((unused)), char *argv[]) +{ + ulong i; + uchar long_tr_id[6]; + PAGECACHE pagecache; + LSN lsn, max_lsn, last_lsn= LSN_IMPOSSIBLE; + LEX_CUSTRING parts[TRANSLOG_INTERNAL_PARTS + 1]; + MY_INIT(argv[0]); + + plan(2); + + bzero(&pagecache, sizeof(pagecache)); + + maria_data_root= create_tmpdir(argv[0]); + if (maria_log_remove(0)) + exit(1); + + bzero(long_tr_id, 6); +#ifndef DBUG_OFF +#if defined(_WIN32) + default_dbug_option= "d:t:i:O,\\ma_test_loghandler.trace"; +#else + default_dbug_option= "d:t:i:o,/tmp/ma_test_loghandler.trace"; +#endif + if (argc > 1) + { + DBUG_SET(default_dbug_option); + DBUG_SET_INITIAL(default_dbug_option); + } +#endif + + if (ma_control_file_open(TRUE, TRUE, TRUE)) + { + fprintf(stderr, "Can't init control file (%d)\n", errno); + exit(1); + } + if (init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, + PCACHE_PAGE, 0, 0) == 0) + { + fprintf(stderr, "Got error: init_pagecache() (errno: %d)\n", errno); + exit(1); + } + if (translog_init_with_table(maria_data_root, LOG_FILE_SIZE, 50112, 0, &pagecache, + LOG_FLAGS, 0, &translog_example_table_init, + 0)) + { + fprintf(stderr, "Can't init loghandler (%d)\n", errno); + exit(1); + } + /* Suppressing of automatic record writing */ + dummy_transaction_object.first_undo_lsn|= TRANSACTION_LOGGED_LONG_ID; + + max_lsn= translog_get_file_max_lsn_stored(1); + if (max_lsn == 1) + { + fprintf(stderr, "Error reading the first log file."); + translog_destroy(); + exit(1); + } + if (max_lsn != LSN_IMPOSSIBLE) + { + fprintf(stderr, "Incorrect first lsn response " LSN_FMT ".", + LSN_IN_PARTS(max_lsn)); + translog_destroy(); + exit(1); + } + ok(1, "Empty log response"); + + + /* write more then 1 file */ + int4store(long_tr_id, 0); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_tr_id; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 6; + for(i= 0; i < LOG_FILE_SIZE/6; i++) + { + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + &dummy_transaction_object, NULL, 6, + TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write record #%lu\n", (ulong) 0); + translog_destroy(); + exit(1); + } + if (LSN_FILE_NO(lsn) == 1) + last_lsn= lsn; + } + + + max_lsn= translog_get_file_max_lsn_stored(1); + if (max_lsn == 1) + { + fprintf(stderr, "Error reading the first log file\n"); + translog_destroy(); + exit(1); + } + if (max_lsn == LSN_IMPOSSIBLE) + { + fprintf(stderr, "Isn't first file still finished?!!\n"); + translog_destroy(); + exit(1); + } + if (max_lsn != last_lsn) + { + fprintf(stderr, "Incorrect max lsn: " LSN_FMT " " + " last lsn on first file: " LSN_FMT "\n", + LSN_IN_PARTS(max_lsn), LSN_IN_PARTS(last_lsn)); + translog_destroy(); + exit(1); + } + + ok(1, "First file max LSN"); + + translog_destroy(); + end_pagecache(&pagecache, 1); + ma_control_file_end(); + if (maria_log_remove(maria_data_root)) + exit(1); + + my_uuid_end(); + my_free_open_file_info(); + my_end(0); + + exit(0); +} + +#include "../ma_check_standalone.h" diff --git a/storage/maria/unittest/ma_test_loghandler_multigroup-t.c b/storage/maria/unittest/ma_test_loghandler_multigroup-t.c new file mode 100644 index 00000000..e8e114dd --- /dev/null +++ b/storage/maria/unittest/ma_test_loghandler_multigroup-t.c @@ -0,0 +1,771 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +#include "../maria_def.h" +#include <stdio.h> +#include <errno.h> +#include <tap.h> +#include "../trnman.h" +#include "sequence_storage.h" +#include <my_getopt.h> + +extern my_bool maria_log_remove(const char *testdir); +extern char *create_tmpdir(const char *progname); +extern void translog_example_table_init(); + +#ifndef DBUG_OFF +static const char *default_dbug_option; +#endif +static TRN *trn= &dummy_transaction_object; + + +#ifndef READONLY_TEST + +#define PCACHE_SIZE (1024*1024*10) +#define LONG_BUFFER_SIZE ((1024L*1024L*1024L) + (1024L*1024L*512)) +#define MIN_REC_LENGTH (1024L*1024L + 1024L*512L + 1) +#define LOG_FILE_SIZE (1024L*1024L*1024L + 1024L*1024L*512) +#define ITERATIONS 2 +#define READONLY 0 +#define BIG 1 + +#else + +#define PCACHE_SIZE (1024*1024*10) +#define LONG_BUFFER_SIZE (1024L*1024L) +#define MIN_REC_LENGTH (1024L) +#define LOG_FILE_SIZE (1024L*1024L*1024L + 1024L*1024L*512) +#define ITERATIONS 2 +#define READONLY 1 +#undef BIG + +#endif /*READONLY_TEST*/ + + +/* +#define LOG_FILE_SIZE 1024L*1024L*3L +#define ITERATIONS 1600 +*/ +/* +#define LOG_FILE_SIZE 1024L*1024L*100L +#define ITERATIONS 65000 +*/ + + +/* + Check that the buffer filled correctly + + SYNOPSIS + check_content() + ptr Pointer to the buffer + length length of the buffer + + RETURN + 0 - OK + 1 - Error +*/ + +static my_bool check_content(uchar *ptr, ulong length) +{ + ulong i; + uchar buff[4]; + DBUG_ENTER("check_content"); + for (i= 0; i < length; i++) + { + if (i % 4 == 0) + int4store(buff, (i >> 2)); + if (ptr[i] != buff[i % 4]) + { + fprintf(stderr, "Byte # %lu is %x instead of %x", + i, (uint) ptr[i], (uint) buff[i % 4]); + DBUG_DUMP("mem", ptr +(ulong) (i > 16 ? i - 16 : 0), + (i > 16 ? 16 : i) + (i + 16 < length ? 16 : length - i)); + DBUG_RETURN(1); + } + } + DBUG_RETURN(0); +} + + +/* + Read whole record content, and check content (put with offset) + + SYNOPSIS + read_and_check_content() + rec The record header buffer + buffer The buffer to read the record in + skip Skip this number of bytes ot the record content + + RETURN + 0 - OK + 1 - Error +*/ + +static my_bool read_and_check_content(TRANSLOG_HEADER_BUFFER *rec, + uchar *buffer, uint skip) +{ + int res= 0; + translog_size_t len; + DBUG_ENTER("read_and_check_content"); + DBUG_ASSERT(rec->record_length < LONG_BUFFER_SIZE + LSN_STORE_SIZE * 2 + 2); + if ((len= translog_read_record(rec->lsn, 0, rec->record_length, + buffer, NULL)) != rec->record_length) + { + fprintf(stderr, "Requested %lu byte, read %lu\n", + (ulong) rec->record_length, (ulong) len); + res= 1; + } + res|= check_content(buffer + skip, rec->record_length - skip); + DBUG_RETURN(res); +} + +static const char *load_default_groups[]= {"ma_unit_loghandler", 0}; +#ifndef DBUG_OFF +static const char *default_dbug_option= + IF_WIN("d:t:i:O,\\ma_test_loghandler.trace", + "d:t:i:o,/tmp/ma_test_loghandler.trace"); +#endif +static const char *opt_wfile= NULL; +static const char *opt_rfile= NULL; +static struct my_option my_long_options[] = +{ +#ifndef DBUG_OFF + {"debug", '#', "Output debug log. Often the argument is 'd:t:o,filename'.", + 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#endif + {"write-seq", 'w', "Path to file in which \"random\" sequence used in the test will be written", + (uchar**) &opt_wfile, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"read-seq", 'r', "Path to file from which \"random\" sequence used in the test will be read", + (uchar**) &opt_rfile, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"help", '?', "Display this help 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 SEQ_STORAGE seq; + +static uint32 get_len() +{ + uint32 res; + DBUG_ENTER("get_len"); + if (opt_rfile) + res= seq_storage_next(&seq); + else + { + res= (uint32) + ((ulonglong) rand() * + (LONG_BUFFER_SIZE - MIN_REC_LENGTH - 1) / RAND_MAX) + MIN_REC_LENGTH; + if (opt_wfile && + seq_storage_write(opt_wfile, res)) + exit(1); + } + DBUG_PRINT("info", ("length value : %lu", (ulong) res)); + DBUG_RETURN(res); +} + +static void usage(void) +{ + puts("Copyright (C) 2008 MySQL AB"); + puts("This software comes with ABSOLUTELY NO WARRANTY. This is free software,"); + puts("and you are welcome to modify and redistribute it under the GPL license\n"); + + puts("Unit test of maria engine"); + printf("\nUsage: %s [OPTIONS]\n", my_progname_short); + my_print_help(my_long_options); + print_defaults("my", load_default_groups); + my_print_variables(my_long_options); +} + + +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 '?': + usage(); + exit(0); +#ifndef DBUG_OFF + case '#': + DBUG_SET_INITIAL(argument ? argument : default_dbug_option); + break; +#endif + } + return 0; +} + + +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); + + if (opt_rfile && opt_wfile) + { + usage(); + exit(1); + } +} + + +int main(int argc __attribute__((unused)), char *argv[]) +{ + uint32 i; + uint32 rec_len; + uchar long_tr_id[6]; + uchar lsn_buff[23]= + { + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 + }; + uchar *long_buffer; + char **default_argv; + PAGECACHE pagecache; + LSN lsn, lsn_base, first_lsn; + TRANSLOG_HEADER_BUFFER rec; + LEX_CUSTRING parts[TRANSLOG_INTERNAL_PARTS + 2]; + struct st_translog_scanner_data scanner; + const char *progname=argv[0]; + int rc; + MY_INIT(argv[0]); + + plan(0); // read configuration (MYTAP_CONFIG) +#ifdef BIG + if (skip_big_tests) + { + plan(1); + ok(1, "skipped as big test"); + my_end(0); + return 0; + } +#endif + + long_buffer= malloc(LONG_BUFFER_SIZE + LSN_STORE_SIZE * 2 + 2); + load_defaults_or_exit("my", load_default_groups, &argc, &argv); + default_argv= argv; + get_options(&argc, &argv); + + bzero(&pagecache, sizeof(pagecache)); + maria_data_root= create_tmpdir(progname); + if (maria_log_remove(0)) + exit(1); + + /* We don't need to do physical syncs in this test */ + my_disable_sync= 1; + + { + uchar buff[4]; + for (i= 0; i < (LONG_BUFFER_SIZE + LSN_STORE_SIZE * 2 + 2); i++) + { + if (i % 4 == 0) + int4store(buff, (i >> 2)); + long_buffer[i]= buff[i % 4]; + } + } + + bzero(long_tr_id, 6); + + if (ma_control_file_open(TRUE, TRUE, TRUE)) + { + fprintf(stderr, "Can't init control file (%d)\n", errno); + exit(1); + } + if (init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, + TRANSLOG_PAGE_SIZE, 0, 0) == 0) + { + fprintf(stderr, "Got error: init_pagecache() (errno: %d)\n", errno); + exit(1); + } + if (translog_init_with_table(maria_data_root, LOG_FILE_SIZE, 50112, 0, &pagecache, + 0, 0, &translog_example_table_init, 0)) + { + fprintf(stderr, "Can't init loghandler (%d)\n", errno); + exit(1); + } + /* Suppressing of automatic record writing */ + trn->first_undo_lsn|= TRANSACTION_LOGGED_LONG_ID; + + plan(((ITERATIONS - 1) * 4 + 1) * 2); + + if (opt_rfile && + seq_storage_reader_init(&seq, opt_rfile)) + exit(1); + srand(122334817L); + + long_tr_id[5]= 0xff; + + int4store(long_tr_id, 0); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_tr_id; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 6; + trn->short_id= 0; + trn->first_undo_lsn= TRANSACTION_LOGGED_LONG_ID; + if (translog_write_record(&lsn, LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + trn, NULL, 6, TRANSLOG_INTERNAL_PARTS + 1, parts, + NULL, NULL)) + { + fprintf(stderr, "Can't write record #%u\n", 0); + translog_destroy(); + ok(0, "write LOGREC_FIXED_RECORD_0LSN_EXAMPLE"); + exit(1); + } + ok(1, "write LOGREC_FIXED_RECORD_0LSN_EXAMPLE"); + lsn_base= first_lsn= lsn; + + for (i= 1; i < ITERATIONS; i++) + { + if (i % 2) + { + lsn_store(lsn_buff, lsn_base); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= lsn_buff; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= LSN_STORE_SIZE; + trn->short_id= i % 0xFFFF; + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_1LSN_EXAMPLE, trn, NULL, + LSN_STORE_SIZE, TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "1 Can't write reference before record #%u\n", i); + translog_destroy(); + ok(0, "write LOGREC_FIXED_RECORD_1LSN_EXAMPLE"); + exit(1); + } + ok(1, "write LOGREC_FIXED_RECORD_1LSN_EXAMPLE"); + lsn_store(lsn_buff, lsn_base); + rec_len= get_len(); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= lsn_buff; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= LSN_STORE_SIZE; + parts[TRANSLOG_INTERNAL_PARTS + 1].str= long_buffer; + parts[TRANSLOG_INTERNAL_PARTS + 1].length= rec_len; + trn->short_id= i % 0xFFFF; + if (translog_write_record(&lsn, + LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE, + trn, NULL, LSN_STORE_SIZE + rec_len, + TRANSLOG_INTERNAL_PARTS + 2, + parts, NULL, NULL)) + { + fprintf(stderr, "1 Can't write var reference before record #%u\n", i); + translog_destroy(); + ok(0, "write LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE"); + exit(1); + } + ok(1, "write LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE"); + } + else + { + lsn_store(lsn_buff, lsn_base); + lsn_store(lsn_buff + LSN_STORE_SIZE, first_lsn); + parts[TRANSLOG_INTERNAL_PARTS + 1].str= lsn_buff; + parts[TRANSLOG_INTERNAL_PARTS + 1].length= 23; + trn->short_id= i % 0xFFFF; + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_2LSN_EXAMPLE, + trn, NULL, 23, TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "0 Can't write reference before record #%u\n", i); + translog_destroy(); + ok(0, "write LOGREC_FIXED_RECORD_2LSN_EXAMPLE"); + exit(1); + } + ok(1, "write LOGREC_FIXED_RECORD_2LSN_EXAMPLE"); + lsn_store(lsn_buff, lsn_base); + lsn_store(lsn_buff + LSN_STORE_SIZE, first_lsn); + rec_len= get_len(); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= lsn_buff; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= LSN_STORE_SIZE * 2; + parts[TRANSLOG_INTERNAL_PARTS + 1].str= long_buffer; + parts[TRANSLOG_INTERNAL_PARTS + 1].length= rec_len; + trn->short_id= i % 0xFFFF; + if (translog_write_record(&lsn, + LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE, + trn, NULL, LSN_STORE_SIZE * 2 + rec_len, + TRANSLOG_INTERNAL_PARTS + 2, + parts, NULL, NULL)) + { + fprintf(stderr, "0 Can't write var reference before record #%u\n", i); + translog_destroy(); + ok(0, "write LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE"); + exit(1); + } + ok(1, "write LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE"); + } + int4store(long_tr_id, i); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_tr_id; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 6; + trn->short_id= i % 0xFFFF; + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + trn, NULL, 6, + TRANSLOG_INTERNAL_PARTS + 1, parts, NULL, NULL)) + { + fprintf(stderr, "Can't write record #%u\n", i); + translog_destroy(); + ok(0, "write LOGREC_FIXED_RECORD_0LSN_EXAMPLE"); + exit(1); + } + ok(1, "write LOGREC_FIXED_RECORD_0LSN_EXAMPLE"); + + lsn_base= lsn; + + rec_len= get_len(); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_buffer; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= rec_len; + trn->short_id= i % 0xFFFF; + if (translog_write_record(&lsn, + LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE, + trn, NULL, rec_len, + TRANSLOG_INTERNAL_PARTS + 1, parts, NULL, NULL)) + { + fprintf(stderr, "Can't write variable record #%u\n", i); + translog_destroy(); + ok(0, "write LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE"); + exit(1); + } + ok(1, "write LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE"); + } + + translog_destroy(); + end_pagecache(&pagecache, 1); + ma_control_file_end(); + + if (ma_control_file_open(TRUE,TRUE,TRUE)) + { + fprintf(stderr, "pass2: Can't init control file (%d)\n", errno); + exit(1); + } + if (init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, + TRANSLOG_PAGE_SIZE, 0, 0) == 0) + { + fprintf(stderr, "pass2: Got error: init_pagecache() (errno: %d)\n", errno); + exit(1); + } + if (translog_init_with_table(maria_data_root, LOG_FILE_SIZE, 50112, 0, &pagecache, + 0, READONLY, &translog_example_table_init, 0)) + { + fprintf(stderr, "pass2: Can't init loghandler (%d)\n", errno); + exit(1); + } + + + /* If we were writing sequence we need it only once */ + opt_wfile= NULL; + if (opt_rfile) + seq_storage_rewind(&seq); + srand(122334817L); + + rc= 1; + + { + int len= translog_read_record_header(first_lsn, &rec); + if (len == RECHEADER_READ_ERROR) + { + fprintf(stderr, "translog_read_record_header failed (%d)\n", errno); + translog_free_record_header(&rec); + goto err; + } + if (rec.type !=LOGREC_FIXED_RECORD_0LSN_EXAMPLE || rec.short_trid != 0 || + rec.record_length != 6 || uint4korr(rec.header) != 0 || + ((uchar)rec.header[4]) != 0 || ((uchar)rec.header[5]) != 0xFF || + first_lsn != rec.lsn) + { + fprintf(stderr, "Incorrect LOGREC_FIXED_RECORD_0LSN_EXAMPLE " + "data read(0)\n" + "type %u, strid %u, len %u, i: %u, 4: %u 5: %u, " + LSN_FMT "\n", + (uint) rec.type, (uint) rec.short_trid, (uint) rec.record_length, + (uint)uint4korr(rec.header), (uint) rec.header[4], + (uint) rec.header[5], + LSN_IN_PARTS(rec.lsn)); + translog_free_record_header(&rec); + goto err; + } + ok(1, "read record"); + translog_free_record_header(&rec); + lsn= first_lsn; + if (translog_scanner_init(first_lsn, 1, &scanner, 0)) + { + fprintf(stderr, "scanner init failed\n"); + goto err; + } + for (i= 1;; i++) + { + len= translog_read_next_record_header(&scanner, &rec); + if (len == RECHEADER_READ_ERROR) + { + fprintf(stderr, "1-%d translog_read_next_record_header failed (%d)\n", + i, errno); + translog_free_record_header(&rec); + goto err; + } + if (len == RECHEADER_READ_EOF) + { + if (i != ITERATIONS) + { + fprintf(stderr, "EOL met at iteration %u instead of %u\n", + i, ITERATIONS); + translog_free_record_header(&rec); + goto err; + } + break; + } + + if (i % 2) + { + LSN ref; + ref= lsn_korr(rec.header); + if (rec.type != LOGREC_FIXED_RECORD_1LSN_EXAMPLE || + rec.short_trid != (i % 0xFFFF) || + rec.record_length != LSN_STORE_SIZE || ref != lsn) + { + fprintf(stderr, "Incorrect LOGREC_FIXED_RECORD_1LSN_EXAMPLE " + "data read(%d)" + "type %u, strid %u, len %u, ref" LSN_FMT ", lsn" LSN_FMT "\n", + i, (uint) rec.type, (uint) rec.short_trid, + (uint) rec.record_length, + LSN_IN_PARTS(ref), LSN_IN_PARTS(rec.lsn)); + translog_free_record_header(&rec); + goto err; + } + } + else + { + LSN ref1, ref2; + ref1= lsn_korr(rec.header); + ref2= lsn_korr(rec.header + LSN_STORE_SIZE); + if (rec.type != LOGREC_FIXED_RECORD_2LSN_EXAMPLE || + rec.short_trid != (i % 0xFFFF) || + rec.record_length != 23 || + ref1 != lsn || + ref2 != first_lsn || + ((uchar)rec.header[22]) != 0x55 || + ((uchar)rec.header[21]) != 0xAA || + ((uchar)rec.header[20]) != 0x55 || + ((uchar)rec.header[19]) != 0xAA || + ((uchar)rec.header[18]) != 0x55 || + ((uchar)rec.header[17]) != 0xAA || + ((uchar)rec.header[16]) != 0x55 || + ((uchar)rec.header[15]) != 0xAA || + ((uchar)rec.header[14]) != 0x55) + { + fprintf(stderr, "Incorrect LOGREC_FIXED_RECORD_2LSN_EXAMPLE " + "data read(%d) " + "type %u, strid %u, len %u, ref1" LSN_FMT ", " + "ref2" LSN_FMT " %x%x%x%x%x%x%x%x%x " + "lsn" LSN_FMT "\n", + i, (uint) rec.type, (uint) rec.short_trid, + (uint) rec.record_length, + LSN_IN_PARTS(ref1), LSN_IN_PARTS(ref2), + (uint) rec.header[14], (uint) rec.header[15], + (uint) rec.header[16], (uint) rec.header[17], + (uint) rec.header[18], (uint) rec.header[19], + (uint) rec.header[20], (uint) rec.header[21], + (uint) rec.header[22], + LSN_IN_PARTS(rec.lsn)); + translog_free_record_header(&rec); + DBUG_ASSERT(0); + goto err; + } + } + ok(1, "read record"); + translog_free_record_header(&rec); + + len= translog_read_next_record_header(&scanner, &rec); + if (len == RECHEADER_READ_ERROR) + { + fprintf(stderr, "1-%d translog_read_next_record_header (var) " + "failed (%d)\n", i, errno); + goto err; + } + if (len == RECHEADER_READ_EOF) + { + fprintf(stderr, "EOL met at the middle of iteration (first var) %u " + "instead of beginning of %u\n", i, ITERATIONS); + goto err; + } + if (i % 2) + { + LSN ref; + ref= lsn_korr(rec.header); + rec_len= get_len(); + if (rec.type !=LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE || + rec.short_trid != (i % 0xFFFF) || + rec.record_length != rec_len + LSN_STORE_SIZE || + len != 12 || ref != lsn || + check_content(rec.header + LSN_STORE_SIZE, len - LSN_STORE_SIZE)) + { + fprintf(stderr, "Incorrect LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE " + "data read(%d)" + "type %u (%d), strid %u (%d), len %lu, %lu + 7 (%d), " + "hdr len: %d (%d), " + "ref" LSN_FMT ", lsn" LSN_FMT " (%d), content: %d\n", + i, (uint) rec.type, + rec.type !=LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE, + (uint) rec.short_trid, + rec.short_trid != (i % 0xFFFF), + (ulong) rec.record_length, (ulong) rec_len, + rec.record_length != rec_len + LSN_STORE_SIZE, + len, + len != 12, + LSN_IN_PARTS(ref), LSN_IN_PARTS(rec.lsn), + (ref != lsn), + check_content(rec.header + LSN_STORE_SIZE, + len - LSN_STORE_SIZE)); + translog_free_record_header(&rec); + goto err; + } + if (read_and_check_content(&rec, long_buffer, LSN_STORE_SIZE)) + { + fprintf(stderr, + "Incorrect LOGREC_VARIABLE_RECORD_1LSN_EXAMPLE " + "in whole rec read lsn" LSN_FMT "\n", + LSN_IN_PARTS(rec.lsn)); + translog_free_record_header(&rec); + goto err; + } + } + else + { + LSN ref1, ref2; + ref1= lsn_korr(rec.header); + ref2= lsn_korr(rec.header + LSN_STORE_SIZE); + rec_len= get_len(); + if (rec.type != LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE || + rec.short_trid != (i % 0xFFFF) || + rec.record_length != rec_len + LSN_STORE_SIZE * 2 || + len != 19 || + ref1 != lsn || + ref2 != first_lsn || + check_content(rec.header + LSN_STORE_SIZE * 2, + len - LSN_STORE_SIZE * 2)) + { + fprintf(stderr, "Incorrect LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE " + " data read(%d) " + "type %u, strid %u, len %lu != %lu + 14, hdr len: %d, " + "ref1" LSN_FMT ", ref2" LSN_FMT ", " + "lsn" LSN_FMT "\n", + i, (uint) rec.type, (uint) rec.short_trid, + (ulong) rec.record_length, (ulong) rec_len, + len, + LSN_IN_PARTS(ref1), LSN_IN_PARTS(ref2), + LSN_IN_PARTS(rec.lsn)); + translog_free_record_header(&rec); + goto err; + } + if (read_and_check_content(&rec, long_buffer, LSN_STORE_SIZE * 2)) + { + fprintf(stderr, + "Incorrect LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE " + "in whole rec read lsn" LSN_FMT "\n", + LSN_IN_PARTS(rec.lsn)); + translog_free_record_header(&rec); + goto err; + } + } + ok(1, "read record"); + translog_free_record_header(&rec); + + len= translog_read_next_record_header(&scanner, &rec); + if (len == RECHEADER_READ_ERROR) + { + fprintf(stderr, "1-%d translog_read_next_record_header failed (%d)\n", + i, errno); + translog_free_record_header(&rec); + goto err; + } + if (len == RECHEADER_READ_EOF) + { + fprintf(stderr, "EOL met at the middle of iteration %u " + "instead of beginning of %u\n", i, ITERATIONS); + translog_free_record_header(&rec); + goto err; + } + if (rec.type != LOGREC_FIXED_RECORD_0LSN_EXAMPLE || + rec.short_trid != (i % 0xFFFF) || + rec.record_length != 6 || uint4korr(rec.header) != i || + ((uchar)rec.header[4]) != 0 || ((uchar)rec.header[5]) != 0xFF) + { + fprintf(stderr, "Incorrect LOGREC_FIXED_RECORD_0LSN_EXAMPLE " + "data read(%d)\n" + "type %u, strid %u, len %u, i: %u, 4: %u 5: %u " + "lsn" LSN_FMT "\n", + i, (uint) rec.type, (uint) rec.short_trid, + (uint) rec.record_length, + (uint)uint4korr(rec.header), (uint) rec.header[4], + (uint) rec.header[5], + LSN_IN_PARTS(rec.lsn)); + translog_free_record_header(&rec); + goto err; + } + ok(1, "read record"); + translog_free_record_header(&rec); + + lsn= rec.lsn; + + len= translog_read_next_record_header(&scanner, &rec); + rec_len= get_len(); + if (rec.type != LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE || + rec.short_trid != (i % 0xFFFF) || + rec.record_length != rec_len || + len != 9 || check_content(rec.header, len)) + { + fprintf(stderr, "Incorrect LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE " + "data read(%d) " + "type %u, strid %u, len %lu != %lu, hdr len: %d, " + "lsn" LSN_FMT "\n", + i, (uint) rec.type, (uint) rec.short_trid, + (ulong) rec.record_length, (ulong) rec_len, + len, LSN_IN_PARTS(rec.lsn)); + translog_free_record_header(&rec); + goto err; + } + if (read_and_check_content(&rec, long_buffer, 0)) + { + fprintf(stderr, + "Incorrect LOGREC_VARIABLE_RECORD_2LSN_EXAMPLE " + "in whole rec read lsn" LSN_FMT "\n", + LSN_IN_PARTS(rec.lsn)); + translog_free_record_header(&rec); + goto err; + } + ok(1, "read record"); + translog_free_record_header(&rec); + } + } + + rc= 0; +err: + if (rc) + ok(0, "read record"); + translog_destroy(); + end_pagecache(&pagecache, 1); + ma_control_file_end(); + free_defaults(default_argv); + seq_storage_destroy(&seq); + if (maria_log_remove(maria_data_root)) + exit(1); + + free(long_buffer); + + my_uuid_end(); + my_free_open_file_info(); + my_end(0); + + return (MY_TEST(exit_status())); +} + +#include "../ma_check_standalone.h" diff --git a/storage/maria/unittest/ma_test_loghandler_multithread-t.c b/storage/maria/unittest/ma_test_loghandler_multithread-t.c new file mode 100644 index 00000000..ec097ede --- /dev/null +++ b/storage/maria/unittest/ma_test_loghandler_multithread-t.c @@ -0,0 +1,557 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +#include "../maria_def.h" +#include <stdio.h> +#include <errno.h> +#include <tap.h> +#include "../trnman.h" + +extern my_bool maria_log_remove(const char *testdir); +extern char *create_tmpdir(const char *progname); + +#ifndef DBUG_OFF +static const char *default_dbug_option; +#endif + +#define PCACHE_SIZE (1024*1024*10) + +#define LOG_FILE_SIZE (1024L*1024L*1024L + 1024L*1024L*512) +/*#define LOG_FLAGS TRANSLOG_SECTOR_PROTECTION | TRANSLOG_PAGE_CRC */ +#define LOG_FLAGS 0 +/*#define LONG_BUFFER_SIZE (1024L*1024L*1024L + 1024L*1024L*512)*/ + +#ifdef MULTIFLUSH_TEST + +#define LONG_BUFFER_SZ (16384L) +#define MIN_REC_LENGTH 10 +#define SHOW_DIVIDER 20 +#define ITERATIONS 10000 +#define FLUSH_ITERATIONS 1000 +#define WRITERS 2 +#define FLUSHERS 10 + +#else + +#define LONG_BUFFER_SZ (512L*1024L*1024L) +#define MIN_REC_LENGTH 30 +#define SHOW_DIVIDER 10 +#define ITERATIONS 3 +#define FLUSH_ITERATIONS 0 +#define WRITERS 3 +#define FLUSHERS 0 + +#endif + +#define LONG_BUFFER_SIZE (LONG_BUFFER_SZ >> (skip_big_tests ? 4 : 0)) + +static uint number_of_writers= WRITERS; +static uint number_of_flushers= FLUSHERS; + +static pthread_cond_t COND_thread_count; +static pthread_mutex_t LOCK_thread_count; +static uint thread_count; + +static ulong lens[WRITERS][ITERATIONS]; +static LSN lsns1[WRITERS][ITERATIONS]; +static LSN lsns2[WRITERS][ITERATIONS]; +static uchar *long_buffer; + + +static LSN last_lsn; /* For test purposes the variable allow dirty read/write */ + +/* + Get pseudo-random length of the field in + limits [MIN_REC_LENGTH..LONG_BUFFER_SIZE] + + SYNOPSIS + get_len() + + RETURN + length - length >= 0 length <= LONG_BUFFER_SIZE +*/ + +static uint32 get_len() +{ + return MIN_REC_LENGTH + + (uint32)(((ulonglong)rand())* + (LONG_BUFFER_SIZE - MIN_REC_LENGTH - 1)/RAND_MAX); +} + + +/* + Check that the buffer filled correctly + + SYNOPSIS + check_content() + ptr Pointer to the buffer + length length of the buffer + + RETURN + 0 - OK + 1 - Error +*/ + +static my_bool check_content(uchar *ptr, ulong length) +{ + ulong i; + for (i= 0; i < length; i++) + { + if (((uchar)ptr[i]) != (i & 0xFF)) + { + fprintf(stderr, "Byte # %lu is %x instead of %x", + i, (uint) ptr[i], (uint) (i & 0xFF)); + return 1; + } + } + return 0; +} + + +/* + Read whole record content, and check content (put with offset) + + SYNOPSIS + read_and_check_content() + rec The record header buffer + buffer The buffer to read the record in + skip Skip this number of bytes ot the record content + + RETURN + 0 - OK + 1 - Error +*/ + + +static my_bool read_and_check_content(TRANSLOG_HEADER_BUFFER *rec, + uchar *buffer, uint skip) +{ + int res= 0; + translog_size_t len; + + if ((len= translog_read_record(rec->lsn, 0, rec->record_length, + buffer, NULL)) != rec->record_length) + { + fprintf(stderr, "Requested %lu byte, read %lu\n", + (ulong) rec->record_length, (ulong) len); + res= 1; + } + res|= check_content(buffer + skip, rec->record_length - skip); + return(res); +} + +void writer(int num) +{ + LSN lsn; + TRN trn; + uchar long_tr_id[6]; + uint i; + + trn.short_id= num; + trn.first_undo_lsn= TRANSACTION_LOGGED_LONG_ID; + for (i= 0; i < ITERATIONS; i++) + { + uint len= get_len(); + LEX_CUSTRING parts[TRANSLOG_INTERNAL_PARTS + 1]; + lens[num][i]= len; + + int2store(long_tr_id, num); + int4store(long_tr_id + 2, i); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_tr_id; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 6; + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + &trn, NULL, 6, TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write LOGREC_FIXED_RECORD_0LSN_EXAMPLE record #%lu " + "thread %i\n", (ulong) i, num); + translog_destroy(); + pthread_mutex_lock(&LOCK_thread_count); + ok(0, "write records"); + pthread_mutex_unlock(&LOCK_thread_count); + return; + } + lsns1[num][i]= lsn; + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_buffer; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= len; + if (translog_write_record(&lsn, + LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE, + &trn, NULL, + len, TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write variable record #%lu\n", (ulong) i); + translog_destroy(); + pthread_mutex_lock(&LOCK_thread_count); + ok(0, "write records"); + pthread_mutex_unlock(&LOCK_thread_count); + return; + } + lsns2[num][i]= lsn; + last_lsn= lsn; + pthread_mutex_lock(&LOCK_thread_count); + ok(1, "write records"); + pthread_mutex_unlock(&LOCK_thread_count); + } + return; +} + + +static void *test_thread_writer(void *arg) +{ + int param= *((int*) arg); + + my_thread_init(); + + writer(param); + + pthread_mutex_lock(&LOCK_thread_count); + thread_count--; + ok(1, "writer finished"); /* just to show progress */ + pthread_cond_signal(&COND_thread_count); /* Tell main we are + ready */ + pthread_mutex_unlock(&LOCK_thread_count); + free((uchar*) arg); + my_thread_end(); + return(0); +} + + +static void *test_thread_flusher(void *arg) +{ + int param= *((int*) arg); + int i; + + my_thread_init(); + + for(i= 0; i < FLUSH_ITERATIONS; i++) + { + translog_flush(last_lsn); + pthread_mutex_lock(&LOCK_thread_count); + ok(1, "-- flush %d", param); + pthread_mutex_unlock(&LOCK_thread_count); + } + + pthread_mutex_lock(&LOCK_thread_count); + thread_count--; + ok(1, "flusher finished"); /* just to show progress */ + pthread_cond_signal(&COND_thread_count); /* Tell main we are + ready */ + pthread_mutex_unlock(&LOCK_thread_count); + free((uchar*) arg); + my_thread_end(); + return(0); +} + + +int main(int argc __attribute__((unused)), + char **argv __attribute__ ((unused))) +{ + uint32 i; + PAGECACHE pagecache; + LSN first_lsn; + TRANSLOG_HEADER_BUFFER rec; + struct st_translog_scanner_data scanner; + pthread_t tid; + pthread_attr_t thr_attr; + int *param, error; + int rc; + MY_INIT(argv[0]); + + // plan read MYTAP_CONFIG so skip_big_tests will be set before using + plan(WRITERS + FLUSHERS + + ITERATIONS * WRITERS * 3 + FLUSH_ITERATIONS * FLUSHERS ); + /* We don't need to do physical syncs in this test */ + my_disable_sync= 1; + + bzero(&pagecache, sizeof(pagecache)); + maria_data_root= create_tmpdir(argv[0]); + if (maria_log_remove(0)) + exit(1); + + long_buffer= malloc(LONG_BUFFER_SIZE + 7 * 2 + 2); + if (long_buffer == 0) + { + fprintf(stderr, "End of memory\n"); + exit(1); + } + for (i= 0; i < (uint32)(LONG_BUFFER_SIZE + 7 * 2 + 2); i++) + long_buffer[i]= (i & 0xFF); + +#ifndef DBUG_OFF +#if defined(_WIN32) + default_dbug_option= "d:t:i:O,\\ma_test_loghandler.trace"; +#else + default_dbug_option= "d:t:i:o,/tmp/ma_test_loghandler.trace"; +#endif + if (argc > 1) + { + DBUG_SET(default_dbug_option); + DBUG_SET_INITIAL(default_dbug_option); + } +#endif + + + if ((error= pthread_cond_init(&COND_thread_count, NULL))) + { + fprintf(stderr, "COND_thread_count: %d from pthread_cond_init " + "(errno: %d)\n", error, errno); + exit(1); + } + if ((error= pthread_mutex_init(&LOCK_thread_count, MY_MUTEX_INIT_FAST))) + { + fprintf(stderr, "LOCK_thread_count: %d from pthread_cond_init " + "(errno: %d)\n", error, errno); + exit(1); + } + if ((error= pthread_attr_init(&thr_attr))) + { + fprintf(stderr, "Got error: %d from pthread_attr_init " + "(errno: %d)\n", error, errno); + exit(1); + } + if ((error= pthread_attr_setdetachstate(&thr_attr, PTHREAD_CREATE_DETACHED))) + { + fprintf(stderr, + "Got error: %d from pthread_attr_setdetachstate (errno: %d)\n", + error, errno); + exit(1); + } + +#ifdef HAVE_THR_SETCONCURRENCY + thr_setconcurrency(2); +#endif + + if (ma_control_file_open(TRUE, TRUE, TRUE)) + { + fprintf(stderr, "Can't init control file (%d)\n", errno); + exit(1); + } + if (init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, + TRANSLOG_PAGE_SIZE, 0, 0) == 0) + { + fprintf(stderr, "Got error: init_pagecache() (errno: %d)\n", errno); + exit(1); + } + if (translog_init_with_table(maria_data_root, LOG_FILE_SIZE, 50112, 0, &pagecache, + LOG_FLAGS, 0, &translog_example_table_init, + 0)) + { + fprintf(stderr, "Can't init loghandler (%d)\n", errno); + exit(1); + } + /* Suppressing of automatic record writing */ + dummy_transaction_object.first_undo_lsn|= TRANSACTION_LOGGED_LONG_ID; + + srand(122334817L); + { + LEX_CUSTRING parts[TRANSLOG_INTERNAL_PARTS + 1]; + uchar long_tr_id[6]= + { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 + }; + + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_tr_id; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 6; + dummy_transaction_object.first_undo_lsn= TRANSACTION_LOGGED_LONG_ID; + if (translog_write_record(&first_lsn, + LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + &dummy_transaction_object, NULL, 6, + TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write the first record\n"); + translog_destroy(); + exit(1); + } + } + + + pthread_mutex_lock(&LOCK_thread_count); + while (number_of_writers != 0 || number_of_flushers != 0) + { + if (number_of_writers) + { + param= (int*) malloc(sizeof(int)); + *param= number_of_writers - 1; + if ((error= pthread_create(&tid, &thr_attr, test_thread_writer, + (void*) param))) + { + fprintf(stderr, "Got error: %d from pthread_create (errno: %d)\n", + error, errno); + exit(1); + } + thread_count++; + number_of_writers--; + } + if (number_of_flushers) + { + param= (int*) malloc(sizeof(int)); + *param= number_of_flushers - 1; + if ((error= pthread_create(&tid, &thr_attr, test_thread_flusher, + (void*) param))) + { + fprintf(stderr, "Got error: %d from pthread_create (errno: %d)\n", + error, errno); + exit(1); + } + thread_count++; + number_of_flushers--; + } + } + pthread_mutex_unlock(&LOCK_thread_count); + + pthread_attr_destroy(&thr_attr); + + /* wait finishing */ + pthread_mutex_lock(&LOCK_thread_count); + while (thread_count) + { + if ((error= pthread_cond_wait(&COND_thread_count, &LOCK_thread_count))) + fprintf(stderr, "COND_thread_count: %d from pthread_cond_wait\n", error); + } + pthread_mutex_unlock(&LOCK_thread_count); + + /* Find last LSN and flush up to it (all our log) */ + { + LSN max= 0; + for (i= 0; i < WRITERS; i++) + { + if (cmp_translog_addr(lsns2[i][ITERATIONS - 1], max) > 0) + max= lsns2[i][ITERATIONS - 1]; + } + translog_flush(max); + } + + rc= 1; + + { + uint indeces[WRITERS]; + uint index, stage; + int len; + bzero(indeces, sizeof(uint) * WRITERS); + + bzero(indeces, sizeof(indeces)); + + if (translog_scanner_init(first_lsn, 1, &scanner, 0)) + { + fprintf(stderr, "scanner init failed\n"); + goto err; + } + for (i= 0;; i++) + { + len= translog_read_next_record_header(&scanner, &rec); + + if (len == RECHEADER_READ_ERROR) + { + fprintf(stderr, "1-%d translog_read_next_record_header failed (%d)\n", + i, errno); + translog_free_record_header(&rec); + goto err; + } + if (len == RECHEADER_READ_EOF) + { + if (i != WRITERS * ITERATIONS * 2) + { + fprintf(stderr, "EOL met at iteration %u instead of %u\n", + i, ITERATIONS * WRITERS * 2); + translog_free_record_header(&rec); + goto err; + } + break; + } + index= indeces[rec.short_trid] / 2; + stage= indeces[rec.short_trid] % 2; + if (stage == 0) + { + if (rec.type !=LOGREC_FIXED_RECORD_0LSN_EXAMPLE || + rec.record_length != 6 || + uint2korr(rec.header) != rec.short_trid || + index != uint4korr(rec.header + 2) || + cmp_translog_addr(lsns1[rec.short_trid][index], rec.lsn) != 0) + { + fprintf(stderr, "Incorrect LOGREC_FIXED_RECORD_0LSN_EXAMPLE " + "data read(%d)\n" + "type %u, strid %u %u, len %u, i: %u %u, " + "lsn" LSN_FMT " " LSN_FMT "\n", + i, (uint) rec.type, + (uint) rec.short_trid, (uint) uint2korr(rec.header), + (uint) rec.record_length, + (uint) index, (uint) uint4korr(rec.header + 2), + LSN_IN_PARTS(rec.lsn), + LSN_IN_PARTS(lsns1[rec.short_trid][index])); + translog_free_record_header(&rec); + goto err; + } + } + else + { + if (rec.type != LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE || + len != 9 || + rec.record_length != lens[rec.short_trid][index] || + cmp_translog_addr(lsns2[rec.short_trid][index], rec.lsn) != 0 || + check_content(rec.header, (uint)len)) + { + fprintf(stderr, + "Incorrect LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE " + "data read(%d) " + "thread: %d, iteration %d, stage %d\n" + "type %u (%d), len %d, length %lu %lu (%d) " + "lsn" LSN_FMT " " LSN_FMT "\n", + i, (uint) rec.short_trid, index, stage, + (uint) rec.type, (rec.type != + LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE), + len, + (ulong) rec.record_length, lens[rec.short_trid][index], + (rec.record_length != lens[rec.short_trid][index]), + LSN_IN_PARTS(rec.lsn), + LSN_IN_PARTS(lsns2[rec.short_trid][index])); + translog_free_record_header(&rec); + goto err; + } + if (read_and_check_content(&rec, long_buffer, 0)) + { + fprintf(stderr, + "Incorrect LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE " + "in whole rec read lsn" LSN_FMT "\n", + LSN_IN_PARTS(rec.lsn)); + translog_free_record_header(&rec); + goto err; + } + } + ok(1, "record read"); + translog_free_record_header(&rec); + indeces[rec.short_trid]++; + } + } + + rc= 0; +err: + if (rc) + ok(0, "record read"); + translog_destroy(); + end_pagecache(&pagecache, 1); + ma_control_file_end(); + if (maria_log_remove(maria_data_root)) + exit(1); + + my_uuid_end(); + my_free_open_file_info(); + my_end(0); + return(exit_status()); +} + +#include "../ma_check_standalone.h" diff --git a/storage/maria/unittest/ma_test_loghandler_noflush-t.c b/storage/maria/unittest/ma_test_loghandler_noflush-t.c new file mode 100644 index 00000000..46b3a8e7 --- /dev/null +++ b/storage/maria/unittest/ma_test_loghandler_noflush-t.c @@ -0,0 +1,147 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +#include "../maria_def.h" +#include <stdio.h> +#include <errno.h> +#include <tap.h> +#include "../trnman.h" + +extern my_bool maria_log_remove(const char *testdir); +extern char *create_tmpdir(const char *progname); +extern void translog_example_table_init(); + +#ifndef DBUG_OFF +static const char *default_dbug_option; +#endif + +#define PCACHE_SIZE (1024*1024*10) +#define PCACHE_PAGE TRANSLOG_PAGE_SIZE +#define LOG_FILE_SIZE (1024L*1024L*1024L + 1024L*1024L*512) +#define LOG_FLAGS 0 + +int main(int argc __attribute__((unused)), char *argv[]) +{ + int rc= 1; + uchar long_tr_id[6]; + PAGECACHE pagecache; + LSN first_lsn; + TRANSLOG_HEADER_BUFFER rec; + LEX_CUSTRING parts[TRANSLOG_INTERNAL_PARTS + 1]; + translog_size_t len; + + MY_INIT(argv[0]); + + plan(1); + + bzero(&pagecache, sizeof(pagecache)); + maria_data_root= create_tmpdir(argv[0]); + if (maria_log_remove(0)) + exit(1); + + bzero(long_tr_id, 6); +#ifndef DBUG_OFF +#if defined(_WIN32) + default_dbug_option= "d:t:i:O,\\ma_test_loghandler.trace"; +#else + default_dbug_option= "d:t:i:o,/tmp/ma_test_loghandler.trace"; +#endif + if (argc > 1) + { + DBUG_SET(default_dbug_option); + DBUG_SET_INITIAL(default_dbug_option); + } +#endif + + if (ma_control_file_open(TRUE, TRUE, TRUE)) + { + fprintf(stderr, "Can't init control file (%d)\n", errno); + exit(1); + } + if (init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, + PCACHE_PAGE, 0, 0) == 0) + { + fprintf(stderr, "Got error: init_pagecache() (errno: %d)\n", errno); + exit(1); + } + if (translog_init_with_table(maria_data_root, LOG_FILE_SIZE, 50112, 0, &pagecache, + LOG_FLAGS, 0, &translog_example_table_init, + 0)) + { + fprintf(stderr, "Can't init loghandler (%d)\n", errno); + exit(1); + } + /* Suppressing of automatic record writing */ + dummy_transaction_object.first_undo_lsn|= TRANSACTION_LOGGED_LONG_ID; + + int4store(long_tr_id, 0); + long_tr_id[5]= 0xff; + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_tr_id; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 6; + if (translog_write_record(&first_lsn, + LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + &dummy_transaction_object, NULL, 6, + TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write record #%lu\n", (ulong) 0); + translog_destroy(); + exit(1); + } + + len= translog_read_record_header(first_lsn, &rec); + if (len == 0) + { + fprintf(stderr, "translog_read_record_header failed (%d)\n", errno); + goto err; + } + if (rec.type !=LOGREC_FIXED_RECORD_0LSN_EXAMPLE || rec.short_trid != 0 || + rec.record_length != 6 || uint4korr(rec.header) != 0 || + ((uchar)rec.header[4]) != 0 || ((uchar)rec.header[5]) != 0xFF || + first_lsn != rec.lsn) + { + fprintf(stderr, "Incorrect LOGREC_FIXED_RECORD_0LSN_EXAMPLE " + "data read(0)\n" + "type: %u (%d) strid: %u (%d) len: %u (%d) i: %u (%d), " + "4: %u (%d) 5: %u (%d) " + "lsn" LSN_FMT " (%d)\n", + (uint) rec.type, (rec.type !=LOGREC_FIXED_RECORD_0LSN_EXAMPLE), + (uint) rec.short_trid, (rec.short_trid != 0), + (uint) rec.record_length, (rec.record_length != 6), + (uint) uint4korr(rec.header), (uint4korr(rec.header) != 0), + (uint) rec.header[4], (((uchar)rec.header[4]) != 0), + (uint) rec.header[5], (((uchar)rec.header[5]) != 0xFF), + LSN_IN_PARTS(rec.lsn), (first_lsn != rec.lsn)); + goto err; + } + + ok(1, "read OK"); + rc= 0; + +err: + translog_destroy(); + end_pagecache(&pagecache, 1); + ma_control_file_end(); + if (maria_log_remove(maria_data_root)) + exit(1); + + my_uuid_end(); + my_free_open_file_info(); + my_end(0); + + exit(rc); +} + +#include "../ma_check_standalone.h" diff --git a/storage/maria/unittest/ma_test_loghandler_nologs-t.c b/storage/maria/unittest/ma_test_loghandler_nologs-t.c new file mode 100644 index 00000000..b95d8bee --- /dev/null +++ b/storage/maria/unittest/ma_test_loghandler_nologs-t.c @@ -0,0 +1,203 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +#include "../maria_def.h" +#include <stdio.h> +#include <errno.h> +#include <tap.h> +#include "../trnman.h" + +extern my_bool maria_log_remove(const char *testdir); +extern char *create_tmpdir(const char *progname); + +#ifndef DBUG_OFF +static const char *default_dbug_option; +#endif + +#define PCACHE_SIZE (1024*1024*10) +#define PCACHE_PAGE TRANSLOG_PAGE_SIZE +#define LOG_FILE_SIZE (8*1024L*1024L) +#define LOG_FLAGS 0 +#define LONG_BUFFER_SIZE (LOG_FILE_SIZE + LOG_FILE_SIZE / 2) + + +int main(int argc __attribute__((unused)), char *argv[]) +{ + ulong i; + uchar long_tr_id[6]; + PAGECACHE pagecache; + LSN lsn; + LEX_CUSTRING parts[TRANSLOG_INTERNAL_PARTS + 1]; + uchar *long_buffer= malloc(LONG_BUFFER_SIZE); + + MY_INIT(argv[0]); + + plan(2); + + bzero(&pagecache, sizeof(pagecache)); + bzero(long_buffer, LONG_BUFFER_SIZE); + maria_data_root= create_tmpdir(argv[0]); + if (maria_log_remove(0)) + exit(1); + + bzero(long_tr_id, 6); +#ifndef DBUG_OFF +#if defined(_WIN32) + default_dbug_option= "d:t:i:O,\\ma_test_loghandler.trace"; +#else + default_dbug_option= "d:t:i:o,/tmp/ma_test_loghandler.trace"; +#endif + if (argc > 1) + { + DBUG_SET(default_dbug_option); + DBUG_SET_INITIAL(default_dbug_option); + } +#endif + + if (ma_control_file_open(TRUE, TRUE, TRUE)) + { + fprintf(stderr, "Can't init control file (%d)\n", errno); + exit(1); + } + if (init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, + PCACHE_PAGE, 0, 0) == 0) + { + fprintf(stderr, "Got error: init_pagecache() (errno: %d)\n", errno); + exit(1); + } + if (translog_init_with_table(maria_data_root, LOG_FILE_SIZE, 50112, 0, &pagecache, + LOG_FLAGS, 0, &translog_example_table_init, + 0)) + { + fprintf(stderr, "Can't init loghandler (%d)\n", errno); + exit(1); + } + /* Suppressing of automatic record writing */ + dummy_transaction_object.first_undo_lsn|= TRANSACTION_LOGGED_LONG_ID; + + /* write more then 1 file */ + int4store(long_tr_id, 0); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_tr_id; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 6; + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + &dummy_transaction_object, NULL, 6, + TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write record #0\n"); + translog_destroy(); + exit(1); + } + + for(i= 0; i < LOG_FILE_SIZE/6 && LSN_FILE_NO(lsn) == 1; i++) + { + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + &dummy_transaction_object, NULL, 6, + TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write record #0\n"); + translog_destroy(); + exit(1); + } + } + + translog_destroy(); + end_pagecache(&pagecache, 1); + ma_control_file_end(); + + { + char file_name[FN_REFLEN]; + for (i= 1; i <= 2; i++) + { + translog_filename_by_fileno(i, file_name); + if (my_access(file_name, W_OK)) + { + fprintf(stderr, "No file '%s'\n", file_name); + exit(1); + } + if (my_delete(file_name, MYF(MY_WME)) != 0) + { + fprintf(stderr, "Error %d during removing file'%s'\n", + errno, file_name); + exit(1); + } + } + } + + if (ma_control_file_open(TRUE, TRUE, TRUE)) + { + fprintf(stderr, "Can't init control file (%d)\n", errno); + exit(1); + } + if (init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, + PCACHE_PAGE, 0, 0) == 0) + { + fprintf(stderr, "Got error: init_pagecache() (errno: %d)\n", errno); + exit(1); + } + if (translog_init_with_table(maria_data_root, LOG_FILE_SIZE, 50112, 0, &pagecache, + LOG_FLAGS, 0, &translog_example_table_init, + 1)) + { + fprintf(stderr, "Can't init loghandler (%d)\n", errno); + exit(1); + } + /* Suppressing of automatic record writing */ + dummy_transaction_object.first_undo_lsn|= TRANSACTION_LOGGED_LONG_ID; + + ok(1, "Log init OK"); + + int4store(long_tr_id, 0); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_tr_id; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 6; + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + &dummy_transaction_object, NULL, 6, + TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write record #0\n"); + translog_destroy(); + exit(1); + } + + translog_destroy(); + end_pagecache(&pagecache, 1); + ma_control_file_end(); + + if (!translog_is_file(3)) + { + fprintf(stderr, "No file #3\n"); + exit(1); + } + + ok(1, "New log is OK"); + + if (maria_log_remove(maria_data_root)) + exit(1); + + free(long_buffer); + + my_uuid_end(); + my_free_open_file_info(); + my_end(0); + + exit(0); +} + +#include "../ma_check_standalone.h" diff --git a/storage/maria/unittest/ma_test_loghandler_pagecache-t.c b/storage/maria/unittest/ma_test_loghandler_pagecache-t.c new file mode 100644 index 00000000..892a773b --- /dev/null +++ b/storage/maria/unittest/ma_test_loghandler_pagecache-t.c @@ -0,0 +1,183 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +#include "../maria_def.h" +#include <stdio.h> +#include <errno.h> +#include <tap.h> +#include "../trnman.h" + +extern my_bool maria_log_remove(const char *testdir); +extern char *create_tmpdir(const char *progname); + +#ifndef DBUG_OFF +static const char *default_dbug_option; +#endif + +#define PCACHE_SIZE (1024*1024*10) +#define PCACHE_PAGE TRANSLOG_PAGE_SIZE +#define LOG_FILE_SIZE (1024L*1024L*1024L + 1024L*1024L*512) +#define LOG_FLAGS 0 + +static const char *base_first_translog_file= "aria_log.00000001"; +static const char *base_file1_name= "page_cache_test_file_1"; +static char file1_name[FN_REFLEN], first_translog_file[FN_REFLEN]; + +static PAGECACHE_FILE file1; + + +int main(int argc __attribute__((unused)), char *argv[]) +{ + uchar long_tr_id[6]; + PAGECACHE pagecache; + LSN lsn; + my_off_t file_size; + LEX_CUSTRING parts[TRANSLOG_INTERNAL_PARTS + 1]; + MY_INIT(argv[0]); + + plan(1); + + bzero(&pagecache, sizeof(pagecache)); + maria_data_root= create_tmpdir(argv[0]); + if (maria_log_remove(0)) + exit(1); + fn_format(first_translog_file, base_first_translog_file, maria_data_root, "", MYF(0)); + + bzero(long_tr_id, 6); +#ifndef DBUG_OFF +#if defined(_WIN32) + default_dbug_option= "d:t:i:O,\\ma_test_loghandler_pagecache.trace"; +#else + default_dbug_option= "d:t:i:o,/tmp/ma_test_loghandler_pagecache.trace"; +#endif + if (argc > 1) + { + DBUG_SET(default_dbug_option); + DBUG_SET_INITIAL(default_dbug_option); + } +#endif + + if (ma_control_file_open(TRUE, TRUE, TRUE)) + { + fprintf(stderr, "Can't init control file (%d)\n", errno); + exit(1); + } + if (init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, + PCACHE_PAGE, 0, 0) == 0) + { + fprintf(stderr, "Got error: init_pagecache() (errno: %d)\n", errno); + exit(1); + } + if (translog_init_with_table(maria_data_root, LOG_FILE_SIZE, 50112, 0, &pagecache, + LOG_FLAGS, 0, &translog_example_table_init, + 0)) + { + fprintf(stderr, "Can't init loghandler (%d)\n", errno); + exit(1); + } + /* Suppressing of automatic record writing */ + dummy_transaction_object.first_undo_lsn|= TRANSACTION_LOGGED_LONG_ID; + + if ((file1.file= my_open(first_translog_file, O_RDONLY, MYF(MY_WME))) < 0) + { + fprintf(stderr, "There is no %s (%d)\n", first_translog_file, errno); + exit(1); + } + file_size= my_seek(file1.file, 0, SEEK_END, MYF(MY_WME)); + if (file_size != TRANSLOG_PAGE_SIZE) + { + fprintf(stderr, + "incorrect initial size of %s: %ld instead of %ld\n", + first_translog_file, (long)file_size, (long)TRANSLOG_PAGE_SIZE); + exit(1); + } + my_close(file1.file, MYF(MY_WME)); + int4store(long_tr_id, 0); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_tr_id; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 6; + dummy_transaction_object.first_undo_lsn= TRANSACTION_LOGGED_LONG_ID; + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + &dummy_transaction_object, NULL, 6, + TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write record #%lu\n", (ulong) 0); + translog_destroy(); + exit(1); + } + + fn_format(file1_name, base_file1_name, maria_data_root, "", MYF(0)); + if ((file1.file= my_open(file1_name, + O_CREAT | O_TRUNC | O_RDWR, MYF(0))) == -1) + { + fprintf(stderr, "Got error during file1 creation from open() (errno: %d)\n", + errno); + exit(1); + } + pagecache_file_set_null_hooks(&file1); + file1.flush_log_callback= maria_flush_log_for_page; + + if (my_chmod(file1_name, 0777, MYF(MY_WME))) + exit(1); + + { + uchar page[PCACHE_PAGE]; + + bzero(page, PCACHE_PAGE); + lsn_store(page, lsn); + pagecache_write(&pagecache, &file1, 0, 3, page, + PAGECACHE_LSN_PAGE, + PAGECACHE_LOCK_LEFT_UNLOCKED, + PAGECACHE_PIN_LEFT_UNPINNED, + PAGECACHE_WRITE_DELAY, + 0, LSN_IMPOSSIBLE); + flush_pagecache_blocks(&pagecache, &file1, FLUSH_RELEASE); + } + my_close(file1.file, MYF(MY_WME)); + if ((file1.file= my_open(first_translog_file, O_RDONLY, MYF(MY_WME))) < 0) + { + fprintf(stderr, "can't open %s (%d)\n", first_translog_file, errno); + exit(1); + } + file_size= my_seek(file1.file, 0, SEEK_END, MYF(MY_WME)); + if (file_size != TRANSLOG_PAGE_SIZE * 2) + { + fprintf(stderr, + "incorrect initial size of %s: %ld instead of %ld\n", + first_translog_file, + (long)file_size, (long)(TRANSLOG_PAGE_SIZE * 2)); + ok(0, "log triggered"); + exit(1); + } + my_close(file1.file, MYF(MY_WME)); + ok(1, "log triggered"); + + translog_destroy(); + end_pagecache(&pagecache, 1); + ma_control_file_end(); + my_delete(file1_name, MYF(MY_WME)); + + if (maria_log_remove(maria_data_root)) + exit(1); + + my_uuid_end(); + my_free_open_file_info(); + my_end(0); + + exit(0); +} + +#include "../ma_check_standalone.h" diff --git a/storage/maria/unittest/ma_test_loghandler_purge-t.c b/storage/maria/unittest/ma_test_loghandler_purge-t.c new file mode 100644 index 00000000..07b50f19 --- /dev/null +++ b/storage/maria/unittest/ma_test_loghandler_purge-t.c @@ -0,0 +1,199 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +#include "../maria_def.h" +#include <stdio.h> +#include <errno.h> +#include <tap.h> +#include "../trnman.h" + +extern my_bool maria_log_remove(const char *testdir); +extern char *create_tmpdir(const char *progname); + +#ifndef DBUG_OFF +static const char *default_dbug_option; +#endif + +#define PCACHE_SIZE (1024*1024*10) +#define PCACHE_PAGE TRANSLOG_PAGE_SIZE +#define LOG_FILE_SIZE (8*1024L*1024L) +#define LOG_FLAGS 0 +#define LONG_BUFFER_SIZE (LOG_FILE_SIZE + LOG_FILE_SIZE / 2) + + +int main(int argc __attribute__((unused)), char *argv[]) +{ + ulong i; + size_t pagen; + uchar long_tr_id[6]; + PAGECACHE pagecache; + LSN lsn; + LEX_CUSTRING parts[TRANSLOG_INTERNAL_PARTS + 1]; + uchar *long_buffer= malloc(LONG_BUFFER_SIZE); + + MY_INIT(argv[0]); + + plan(4); + + bzero(&pagecache, sizeof(pagecache)); + bzero(long_buffer, LONG_BUFFER_SIZE); + maria_data_root= create_tmpdir(argv[0]); + if (maria_log_remove(0)) + exit(1); + + bzero(long_tr_id, 6); +#ifndef DBUG_OFF +#if defined(_WIN32) + default_dbug_option= "d:t:i:O,\\ma_test_loghandler.trace"; +#else + default_dbug_option= "d:t:i:o,/tmp/ma_test_loghandler.trace"; +#endif + if (argc > 1) + { + DBUG_SET(default_dbug_option); + DBUG_SET_INITIAL(default_dbug_option); + } +#endif + + if (ma_control_file_open(TRUE, TRUE, TRUE)) + { + fprintf(stderr, "Can't init control file (%d)\n", errno); + exit(1); + } + if ((pagen= init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, + PCACHE_PAGE, 0, 0)) == 0) + { + fprintf(stderr, "Got error: init_pagecache() (errno: %d)\n", errno); + exit(1); + } + if (translog_init_with_table(maria_data_root, LOG_FILE_SIZE, 50112, 0, &pagecache, + LOG_FLAGS, 0, &translog_example_table_init, + 0)) + { + fprintf(stderr, "Can't init loghandler (%d)\n", errno); + exit(1); + } + /* Suppressing of automatic record writing */ + dummy_transaction_object.first_undo_lsn|= TRANSACTION_LOGGED_LONG_ID; + + /* write more then 1 file */ + int4store(long_tr_id, 0); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_tr_id; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 6; + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + &dummy_transaction_object, NULL, 6, + TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write record #%lu\n", (ulong) 0); + translog_destroy(); + exit(1); + } + + translog_purge(lsn); + if (!translog_is_file(1)) + { + fprintf(stderr, "First file was removed after first record\n"); + translog_destroy(); + exit(1); + } + ok(1, "First is not removed"); + + for(i= 0; i < LOG_FILE_SIZE/6 && LSN_FILE_NO(lsn) == 1; i++) + { + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + &dummy_transaction_object, NULL, 6, + TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write record #%lu\n", (ulong) 0); + translog_destroy(); + exit(1); + } + } + + translog_purge(lsn); + if (translog_is_file(1)) + { + fprintf(stderr, "First file was not removed.\n"); + translog_destroy(); + exit(1); + } + + ok(1, "First file is removed"); + + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_buffer; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= LONG_BUFFER_SIZE; + if (translog_write_record(&lsn, + LOGREC_VARIABLE_RECORD_0LSN_EXAMPLE, + &dummy_transaction_object, NULL, LONG_BUFFER_SIZE, + TRANSLOG_INTERNAL_PARTS + 1, parts, NULL, NULL)) + { + fprintf(stderr, "Can't write variable record\n"); + translog_destroy(); + exit(1); + } + + translog_purge(lsn); + if (!translog_is_file(2) || !translog_is_file(3)) + { + fprintf(stderr, "Second file (%d) or third file (%d) is not present.\n", + translog_is_file(2), translog_is_file(3)); + translog_destroy(); + exit(1); + } + + ok(1, "Second and third files are not removed"); + + int4store(long_tr_id, 0); + parts[TRANSLOG_INTERNAL_PARTS + 0].str= long_tr_id; + parts[TRANSLOG_INTERNAL_PARTS + 0].length= 6; + if (translog_write_record(&lsn, + LOGREC_FIXED_RECORD_0LSN_EXAMPLE, + &dummy_transaction_object, NULL, 6, + TRANSLOG_INTERNAL_PARTS + 1, + parts, NULL, NULL)) + { + fprintf(stderr, "Can't write last record\n"); + translog_destroy(); + exit(1); + } + + translog_purge(lsn); + if (translog_is_file(2)) + { + fprintf(stderr, "Second file is not removed\n"); + translog_destroy(); + exit(1); + } + + ok(1, "Second file is removed"); + + translog_destroy(); + end_pagecache(&pagecache, 1); + ma_control_file_end(); + if (maria_log_remove(maria_data_root)) + exit(1); + + my_uuid_end(); + my_free_open_file_info(); + my_end(0); + + exit(0); +} + +#include "../ma_check_standalone.h" diff --git a/storage/maria/unittest/ma_test_recovery.expected b/storage/maria/unittest/ma_test_recovery.expected new file mode 100644 index 00000000..38e8e4d8 --- /dev/null +++ b/storage/maria/unittest/ma_test_recovery.expected @@ -0,0 +1,1578 @@ +Testing the REDO PHASE ALONE +TEST WITH ma_test1 -s -M -T -c +applying log +testing idempotency +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -d500 +applying log +testing idempotency +applying log +TEST WITH ma_test2 -s -M -T -c -b65000 +applying log +testing idempotency +applying log +TEST WITH ma_test2 -s -M -T -c -b65000 -d800 +applying log +testing idempotency +applying log +TEST WITH ma_test1 -s -M -T -c -C +applying log +testing idempotency +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -d500 -C +applying log +testing idempotency +applying log +Testing the REDO AND UNDO PHASE +TEST WITH ma_test1 -s -M -T -c -N --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N --testflag=2 --test-undo=1 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N --testflag=4 --test-undo=1 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N --versioning --testflag=4 --test-undo=1 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N --testflag=3 --test-undo=1 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t2 -A1 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t6 -A1 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test1 -s -M -T -c -N --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N --testflag=2 --test-undo=2 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N --testflag=4 --test-undo=2 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N --versioning --testflag=4 --test-undo=2 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N --testflag=3 --test-undo=2 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t2 -A2 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t6 -A2 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test1 -s -M -T -c -N --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N --testflag=2 --test-undo=3 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N --testflag=4 --test-undo=3 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N --versioning --testflag=4 --test-undo=3 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N --testflag=3 --test-undo=3 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t2 -A3 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t6 -A3 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test1 -s -M -T -c -N --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N --testflag=2 --test-undo=4 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N --testflag=4 --test-undo=4 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N --versioning --testflag=4 --test-undo=4 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N --testflag=3 --test-undo=4 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t2 -A4 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -t6 -A4 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=2 --test-undo=1 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=4 --test-undo=1 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 --versioning --testflag=4 --test-undo=1 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=3 --test-undo=1 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t2 -A1 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t6 -A1 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=2 --test-undo=2 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=4 --test-undo=2 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 --versioning --testflag=4 --test-undo=2 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=3 --test-undo=2 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t2 -A2 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t6 -A2 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=2 --test-undo=3 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=4 --test-undo=3 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 --versioning --testflag=4 --test-undo=3 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=3 --test-undo=3 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t2 -A3 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t6 -A3 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=2 --test-undo=4 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=4 --test-undo=4 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 --versioning --testflag=4 --test-undo=4 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N -b32768 --testflag=3 --test-undo=4 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t2 -A4 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -t6 -A4 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test1 -s -M -T -c -N -H1 --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N -H1 --testflag=2 --test-undo=1 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=4 --test-undo=1 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -H2 --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -H2 --versioning --testflag=4 --test-undo=1 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=3 --test-undo=1 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t2 -A1 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t6 -A1 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test1 -s -M -T -c -N -H1 --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N -H1 --testflag=2 --test-undo=2 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=4 --test-undo=2 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -H2 --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -H2 --versioning --testflag=4 --test-undo=2 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=3 --test-undo=2 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t2 -A2 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t6 -A2 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test1 -s -M -T -c -N -H1 --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N -H1 --testflag=2 --test-undo=3 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=4 --test-undo=3 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -H2 --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -H2 --versioning --testflag=4 --test-undo=3 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=3 --test-undo=3 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t2 -A3 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t6 -A3 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test1 -s -M -T -c -N -H1 --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N -H1 --testflag=2 --test-undo=4 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=4 --test-undo=4 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -H2 --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -H2 --versioning --testflag=4 --test-undo=4 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N -H2 --testflag=3 --test-undo=4 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t2 -A4 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -H1 -t6 -A4 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H1 --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H1 --testflag=2 --test-undo=1 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=4 --test-undo=1 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --versioning --testflag=4 --test-undo=1 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=3 --test-undo=1 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t2 -A1 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t6 -A1 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H1 --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H1 --testflag=2 --test-undo=2 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=4 --test-undo=2 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --versioning --testflag=4 --test-undo=2 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=3 --test-undo=2 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t2 -A2 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t6 -A2 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H1 --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H1 --testflag=2 --test-undo=3 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=4 --test-undo=3 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --versioning --testflag=4 --test-undo=3 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=3 --test-undo=3 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t2 -A3 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t6 -A3 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H1 --testflag=1 (commit at end) +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H1 --testflag=2 --test-undo=4 (additional aborted work) +Terminating after inserts +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=4 --test-undo=4 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --versioning --testflag=3 (commit at end) +Terminating after updates +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --versioning --testflag=4 --test-undo=4 (additional aborted work) +Terminating after deletes +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=2 (commit at end) +Terminating after inserts +TEST WITH ma_test1 -s -M -T -c -N -b32768 -H2 --testflag=3 --test-undo=4 (additional aborted work) +Terminating after updates +Dying on request without maria_commit()/maria_close() +applying log +testing idempotency +applying log +testing applying of CLRs to recreate table +applying log +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t2 -A4 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t1 (commit at end) +TEST WITH ma_test2 -s -L -K -W -P -M -T -c -b32768 -H1 -t6 -A4 (additional aborted work) +Dying on request without maria_commit()/maria_close() +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing idempotency +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= +testing applying of CLRs to recreate table +applying log +Differences in aria_chk -dvv, recovery not yet perfect ! +========DIFF START======= +7c7 +< Status: checked,analyzed,optimized keys,sorted index pages,zerofilled +--- +> Status: changed +========DIFF END======= diff --git a/storage/maria/unittest/ma_test_recovery.pl b/storage/maria/unittest/ma_test_recovery.pl new file mode 100755 index 00000000..fdc7ee68 --- /dev/null +++ b/storage/maria/unittest/ma_test_recovery.pl @@ -0,0 +1,496 @@ +#!/usr/bin/env perl + +use Getopt::Long; +use File::Copy; +use File::Compare; +use File::Basename; +use Digest::MD5; + +$|= 1; +$^W = 1; # warnings, because env cannot parse 'perl -w' +$VER= "1.2"; + +$opt_version= 0; +$opt_help= 0; +$opt_verbose= 0; +$opt_abort_on_error=0; + +my $silent= "-s"; +my $maria_path; # path to "storage/maria" +my $maria_exe_path; # path to executables (ma_test1, aria_chk etc) +my $tmp= "./tmp"; +my $my_progname= $0; +my $suffix; +my $zerofilled_tables= 0; + +$my_progname=~ s/.*[\/]//; +$maria_path= dirname($0) . "/.."; + +main(); + +#### +#### main function +#### + +sub main +{ + my ($res, $table); + + if (!GetOptions("abort-on-error", "help", "version", "verbose")) + { + $flag_exit= 1; + } + if ($opt_version) + { + print "$my_progname version $VER\n"; + exit(0); + } + usage() if ($opt_help || $flag_exit); + + $suffix= ( $^O =~ /win/i && $^O !~ /darwin/i ) ? ".exe" : ""; + $maria_exe_path= "$maria_path/release"; + # we use -f, sometimes -x is unexpectedly false in Cygwin + if ( ! -f "$maria_exe_path/ma_test1$suffix" ) + { + $maria_exe_path= "$maria_path/relwithdebinfo"; + if ( ! -f "$maria_exe_path/ma_test1$suffix" ) + { + $maria_exe_path= "$maria_path/debug"; + if ( ! -f "$maria_exe_path/ma_test1$suffix" ) + { + $maria_exe_path= $maria_path; + if ( ! -f "$maria_exe_path/ma_test1$suffix" ) + { + die("Cannot find ma_test1 executable\n"); + } + } + } + } + + # test data is always put in the current directory or a tmp subdirectory + # of it + + if (! -d "$tmp") + { + mkdir $tmp; + } + print "ARIA RECOVERY TESTS\n"; + + # To not flood the screen, we redirect all the commands below to a text file + # and just give a final error if their output is not as expected + + open (MY_LOG, ">$tmp/ma_test_recovery.output") or die "Can't open log file\n"; + print MY_LOG "Testing the REDO PHASE ALONE\n"; + + # runs a program inserting/deleting rows, then moves the resulting table + # elsewhere; applies the log and checks that the data file is + # identical to the saved original. + + my @t= ("ma_test1$suffix $silent -M -T -c", + "ma_test2$suffix $silent -L -K -W -P -M -T -c -d500", + "ma_test2$suffix $silent -M -T -c -b65000", + "ma_test2$suffix $silent -M -T -c -b65000 -d800", + "ma_test1$suffix $silent -M -T -c -C", + "ma_test2$suffix $silent -L -K -W -P -M -T -c -d500 -C", + #"ma_rt_test$suffix $silent -M -T -c -C", + # @todo: also add to @t2 + ); + + foreach my $prog (@t) + { + unlink <aria_log.* aria_log_control>; + my $prog_no_suffix= $prog; + $prog_no_suffix=~ s/$suffix// if ($suffix); + print MY_LOG "TEST WITH $prog_no_suffix\n"; + $res= my_exec("$maria_exe_path/$prog"); + print MY_LOG $res; + # derive table's name from program's name + if ($prog =~ m/^ma_(\S+)\s.*/) + { + $table= $1; + } + else + { + die("can't guess table name"); + } + $com= "$maria_exe_path/aria_chk$suffix -dvv $table "; + $com.= "| grep -v \"Creation time:\" | grep -v \"recover time:\" | grep -v \"file length\" | grep -v \"LSNs:\" | grep -v \"UUID:\""; + $com.= "> $tmp/aria_chk_message.good.txt 2>&1"; + my_exec($com); + my $checksum= my_exec("$maria_exe_path/aria_chk$suffix -dss $table"); + move("$table.MAD", "$tmp/$table-good.MAD") || + die "Can't move $table.MAD to $tmp/$table-good.MAD\n"; + move("$table.MAI", "$tmp/$table-good.MAI") || + die "Can't move $table.MAI to $tmp/$table-good.MAI\n"; + apply_log($table, "shouldnotchangelog"); + check_table_is_same($table, $checksum); + $res= physical_cmp($table, "$tmp/$table-good"); + print MY_LOG $res; + print MY_LOG "testing idempotency\n"; + apply_log($table, "shouldnotchangelog"); + check_table_is_same($table, $checksum); + $res= physical_cmp($table, "$tmp/$table-good"); + print MY_LOG $res; + } + + print MY_LOG "Testing the REDO AND UNDO PHASE\n"; + # The test programs look like: + # work; commit (time T1); work; exit-without-commit (time T2) + # We first run the test program and let it exit after T1's commit. + # Then we run it again and let it exit at T2. Then we compare + # and expect identity. + + my @take_checkpoints= ("no", "yes"); + my @blobs= ("", "-b32768"); + my @test_undo= (1, 2, 3, 4); + my @t2= ("ma_test1$suffix $silent -M -T -c -N blob -H1", + "--testflag=1", + "--testflag=2 --test-undo=", + "ma_test1$suffix $silent -M -T -c -N blob -H2", + "--testflag=3", + "--testflag=4 --test-undo=", + "ma_test1$suffix $silent -M -T -c -N blob -H2 --versioning", + "--testflag=3", + "--testflag=4 --test-undo=", + "ma_test1$suffix $silent -M -T -c -N blob -H2", + "--testflag=2", + "--testflag=3 --test-undo=", + "ma_test2$suffix $silent -L -K -W -P -M -T -c blob -H1", + "-t1", + "-t2 -A", + "ma_test2$suffix $silent -L -K -W -P -M -T -c blob -H1", + "-t1", + "-t6 -A"); + + foreach my $take_checkpoint (@take_checkpoints) + { + my ($i, $j, $k, $commit_run_args, $abort_run_args); + # we test table without blobs and then table with blobs + for ($i= 0; defined($blobs[$i]); $i++) + { + for ($j= 0; defined($test_undo[$j]); $j++) + { + # first iteration tests rollback of insert, second tests rollback of delete + # -N (create NULL fields) is needed because --test-undo adds it anyway + for ($k= 0; defined($t2[$k]); $k+= 3) + { + $prog= $t2[$k]; + $prog=~ s/blob/$blobs[$i]/; + if ("$take_checkpoint" eq "no") { + $prog=~ s/\s+\-H[0-9]+//; + } + $commit_run_args= $t2[$k + 1]; + $abort_run_args= $t2[$k + 2]; + unlink <aria_log.* aria_log_control>; + my $prog_no_suffix= $prog; + $prog_no_suffix=~ s/$suffix// if ($suffix); + print MY_LOG "TEST WITH $prog_no_suffix $commit_run_args (commit at end)\n"; + $res= my_exec("$maria_exe_path/$prog $commit_run_args"); + print MY_LOG $res; + # derive table's name from program's name + if ($prog =~ m/^ma_(\S+)\s.*/) + { + $table= $1; + } + else + { + die("can't guess table name"); + } + $com= "$maria_exe_path/aria_chk$suffix -dvv $table "; + $com.= "| grep -v \"Creation time:\" | grep -v \"recover time:\" | grep -v \"recover time:\" |grep -v \"file length\" | grep -v \"LSNs:\" | grep -v \"UUID:\" "; + $com.= "> $tmp/aria_chk_message.good.txt 2>&1"; + $res= my_exec($com); + print MY_LOG $res; + $checksum= my_exec("$maria_exe_path/aria_chk$suffix -dss $table"); + move("$table.MAD", "$tmp/$table-good.MAD") || + die "Can't move $table.MAD to $tmp/$table-good.MAD\n"; + move("$table.MAI", "$tmp/$table-good.MAI") || + die "Can't move $table.MAI to $tmp/$table-good.MAI\n"; + unlink <aria_log.* aria_log_control>; + print MY_LOG "TEST WITH $prog_no_suffix $abort_run_args$test_undo[$j] (additional aborted work)\n"; + $res= my_exec("$maria_exe_path/$prog $abort_run_args$test_undo[$j]"); + print MY_LOG $res; + copy("$table.MAD", "$tmp/$table-before_undo.MAD") || + die "Can't copy $table.MAD to $tmp/$table-before_undo.MAD\n"; + copy("$table.MAI", "$tmp/$table-before_undo.MAI") || + die "Can't copy $table.MAI to $tmp/$table-before_undo.MAI\n"; + + # The lines below seem unneeded, will be removed soon + # We have to copy and restore logs, as running aria_read_log will + # change the aria_control_file + # rm -f $tmp/aria_log.* $tmp/aria_log_control + # cp $maria_path/aria_log* $tmp + + if ($test_undo[$j] != 3) { + apply_log($table, "shouldchangelog"); # should undo aborted work + } else { + # probably nothing to undo went to log or data file + apply_log($table, "dontknow"); + } + copy("$table.MAD", "$tmp/$table-after_undo.MAD") || + die "Can't copy $table.MAD to $tmp/$table-after_undo.MAD\n"; + copy("$table.MAI", "$tmp/$table-after_undo.MAI") || + die "Can't copy $table.MAI to $tmp/$table-after_undo.MAI\n"; + + # It is impossible to do a "cmp" between .good and .after_undo, + # because the UNDO phase generated log + # records whose LSN tagged pages. Another reason is that rolling back + # INSERT only marks the rows free, does not empty them + # (optimization), so traces of the INSERT+rollback remain. + + check_table_is_same($table, $checksum); + print MY_LOG "testing idempotency\n"; + apply_log($table, "shouldnotchangelog"); + check_table_is_same($table, $checksum); + $res= physical_cmp($table, "$tmp/$table-after_undo"); + print MY_LOG $res; + print MY_LOG "testing applying of CLRs to recreate table\n"; + unlink <$table.MA?>; + # cp $tmp/aria_log* $maria_path #unneeded + apply_log($table, "shouldnotchangelog"); + check_table_is_same($table, $checksum); + $res= physical_cmp($table, "$tmp/$table-after_undo"); + print MY_LOG $res; + } + unlink <$table.* $tmp/$table* $tmp/aria_chk_*.txt $tmp/aria_read_log_$table.txt>; + } + } + } + + if ($? >> 8) { + print "Some test failed\n"; + exit(1); + } + + close(MY_LOG); + # also note that aria_chk -dvv shows differences for ma_test2 in UNDO phase, + # this is normal: removing records does not shrink the data/key file, + # does not put back the "analyzed,optimized keys"(etc) index state. + `diff -b $maria_path/unittest/ma_test_recovery.expected $tmp/ma_test_recovery.output`; + if ($? >> 8) { + print "UNEXPECTED OUTPUT OF TESTS, FAILED"; + print " (zerofilled $zerofilled_tables tables)\n"; + print "For more info, do diff -b $maria_path/unittest/ma_test_recovery.expected "; + print "$tmp/ma_test_recovery.output\n"; + exit(1); + } + print "ALL RECOVERY TESTS OK (zerofilled $zerofilled_tables tables)\n"; +} + +#### +#### check_table_is_same +#### + +sub check_table_is_same +{ + my ($table, $checksum)= @_; + my ($com, $checksum2, $res); + + # Computes checksum of new table and compares to checksum of old table + # Shows any difference in table's state (info from the index's header) + # Data/key file length is random in ma_test2 (as it uses srand() which + # may differ between machines). + + if ($opt_verbose) + { + print "checking if table $table has changed\n"; + } + + $com= "$maria_exe_path/aria_chk$suffix -dvv $table | grep -v \"Creation time:\" | grep -v \"recover time:\""; + $com.= "| grep -v \"file length\" | grep -v \"LSNs:\" | grep -v \"UUID:\" > $tmp/aria_chk_message.txt 2>&1"; + $res= my_exec2($com); + print MY_LOG $res; + $res= my_exec2("$maria_exe_path/aria_chk$suffix -ss -e --read-only $table"); + print MY_LOG $res; + $checksum2= my_exec2("$maria_exe_path/aria_chk$suffix -dss $table"); + if ("$checksum" ne "$checksum2") + { + print MY_LOG "checksum differs for $table before and after recovery\n"; + return 1; + } + + $com= "diff $tmp/aria_chk_message.good.txt $tmp/aria_chk_message.txt "; + $com.= "> $tmp/aria_chk_diff.txt || true"; + $res= my_exec2($com); + print MY_LOG $res; + + if (-s "$tmp/aria_chk_diff.txt") + { + print MY_LOG "Differences in aria_chk -dvv, recovery not yet perfect !\n"; + print MY_LOG "========DIFF START=======\n"; + open(MY_FILE, "<$tmp/aria_chk_diff.txt") || die "Can't open file aria_chk_diff.txt\n"; + while (<MY_FILE>) + { + print MY_LOG $_; + } + close(MY_FILE); + print MY_LOG "========DIFF END=======\n"; + } +} + +#### +#### apply_log +#### + +sub apply_log +{ + my ($table, $shouldchangelog)= @_; + my ($log_md5, $log_md5_2); + + # applies log, can verify if applying did write to log or not + + if ("$shouldchangelog" ne "shouldnotchangelog" && + "$shouldchangelog" ne "shouldchangelog" && + "$shouldchangelog" ne "dontknow" ) + { + print MY_LOG "bad argument '$shouldchangelog'\n"; + return 1; + } + foreach (<aria_log.*>) + { + $log_md5.= md5_conv($_); + } + print MY_LOG "applying log\n"; + my_exec("$maria_exe_path/aria_read_log$suffix -a > $tmp/aria_read_log_$table.txt"); + foreach (<aria_log.*>) + { + $log_md5_2.= md5_conv($_); + } + if ("$log_md5" ne "$log_md5_2" ) + { + if ("$shouldchangelog" eq "shouldnotchangelog") + { + print MY_LOG "aria_read_log should not have modified the log\n"; + return 1; + } + } + elsif ("$shouldchangelog" eq "shouldchangelog") + { + print MY_LOG "aria_read_log should have modified the log\n"; + return 1; + } +} + +#### +#### md5_conv +#### + +sub md5_conv +{ + my ($file)= @_; + + open(FILE, $file) or die "Can't open '$file': $!\n"; + binmode(FILE); + my $md5= Digest::MD5->new; + $md5->addfile(FILE); + close (FILE); + return $md5->hexdigest . "\n"; +} + +#### +#### physical_cmp: compares two tables (MAI and MAD) physically; +#### uses zerofill-keep-lsn to reduce irrelevant differences. +#### + +sub physical_cmp +{ + my ($table1, $table2)= @_; + my ($zerofilled, $ret_text)= (0, ""); + #return `cmp $table1.MAD $table2.MAD`.`cmp $table1.MAI $table2.MAI`; + foreach my $file_suffix ("MAD", "MAI") + { + my $file1= "$table1.$file_suffix"; + my $file2= "$table2.$file_suffix"; + my $res= File::Compare::compare($file1, $file2); + die() if ($res == -1); + if ($res == 1 # they differ + and !$zerofilled) + { + # let's try with --zerofill-keep-lsn + $zerofilled= 1; # but no need to do it twice + $zerofilled_tables= $zerofilled_tables + 1; + my $table_no= 1; + foreach my $table ($table1, $table2) + { + # save original tables to restore them later + copy("$table.MAD", "$tmp/before_zerofill$table_no.MAD") || die(); + copy("$table.MAI", "$tmp/before_zerofill$table_no.MAI") || die(); + $com= "$maria_exe_path/aria_chk$suffix -ss --zerofill-keep-lsn --skip-update-state $table"; + $res= `$com`; + print MY_LOG $res; + $table_no= $table_no + 1; + } + $res= File::Compare::compare($file1, $file2); + die() if ($res == -1); + } + $ret_text.= "$file1 and $file2 differ\n" if ($res != 0); + } + if ($zerofilled) + { + my $table_no= 1; + foreach my $table ($table1, $table2) + { + move("$tmp/before_zerofill$table_no.MAD", "$table.MAD") || die(); + move("$tmp/before_zerofill$table_no.MAI", "$table.MAI") || die(); + $table_no= $table_no + 1; + } + } + return $ret_text; +} + + +sub my_exec +{ + my($command)= @_; + my $res; + if ($opt_verbose) + { + print "$command\n"; + } + $res= `$command`; + if ($? != 0 && $opt_abort_on_error) + { + exit(1); + } + return $res; +} + +sub my_exec2 +{ + my($command)= @_; + my $res, $err; + $res= `$command`; + if ($? != 0 && $opt_abort_on_error) + { + $err= $?; + print "$command\n"; + print "failed with error: $err\n"; + exit(1); + } + return $res; +} + + +#### +#### usage +#### + +sub usage +{ + print <<EOF; +$my_progname version $VER + +Description: + +Run various Aria recovery tests and print the results + +Options +--help Show this help and exit. + +--abort-on-error Abort at once in case of error. +--verbose Show commands while there are executing. +--version Show version number and exit. + +EOF + exit(0); +} diff --git a/storage/maria/unittest/sequence_storage.c b/storage/maria/unittest/sequence_storage.c new file mode 100644 index 00000000..1e6b3fcb --- /dev/null +++ b/storage/maria/unittest/sequence_storage.c @@ -0,0 +1,111 @@ +/* Copyright (C) 2008 MySQL 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 */ + +#include "../maria_def.h" +#include "sequence_storage.h" + + +/** + @brief Initializes the sequence from the sequence file. + + @param seq Reference on the sequence storage. + @param file Path to the file where to write the sequence + + @retval 0 OK + @retval 1 Error +*/ + +my_bool seq_storage_reader_init(SEQ_STORAGE *seq, const char *file) +{ + FILE *fd; + seq->pos= 0; + if ((fd= my_fopen(file, O_RDONLY, MYF(MY_WME))) == NULL) + return 1; + if (my_init_dynamic_array(PSI_NOT_INSTRUMENTED, &seq->seq, sizeof(ulong), 10, + 10, MYF(0))) + return 1; + + for(;;) + { + ulong num; + char line[22]; + if (fgets(line, sizeof(line), fd) == NULL) + break; + num= atol(line); + if (insert_dynamic(&seq->seq, (uchar*) &num)) + return 1; + } + fclose(fd); + return 0; +} + + +/** + @brief Gets next number from the sequence storage + + @param seq Reference on the sequence storage. + + @return Next number from the sequence. +*/ + +ulong seq_storage_next(SEQ_STORAGE *seq) +{ + DBUG_ASSERT(seq->seq.elements > 0); + DBUG_ASSERT(seq->pos < seq->seq.elements); + return (*(dynamic_element(&seq->seq, seq->pos++, ulong *))); +} + + +/** + @brief Frees resources allocated for the storage + + @param seq Reference on the sequence storage. +*/ + +void seq_storage_destroy(SEQ_STORAGE *seq) +{ + delete_dynamic(&seq->seq); +} + + +/** + @brief Starts the sequence from beginning + + @param seq Reference on the sequence storage. +*/ + +void seq_storage_rewind(SEQ_STORAGE *seq) +{ + seq->pos= 0; +} + +/** + @brief Writes a number to the sequence file. + + @param file Path to the file where to write the sequence + @pagem num Number to be written + + @retval 0 OK + @retval 1 Error +*/ + +my_bool seq_storage_write(const char *file, ulong num) +{ + FILE *fd; + return ((fd= my_fopen(file, O_CREAT | O_APPEND | O_WRONLY, MYF(MY_WME))) == + NULL || + fprintf(fd, "%lu\n", num) < 0 || + fclose(fd) != 0); +} diff --git a/storage/maria/unittest/sequence_storage.h b/storage/maria/unittest/sequence_storage.h new file mode 100644 index 00000000..17490256 --- /dev/null +++ b/storage/maria/unittest/sequence_storage.h @@ -0,0 +1,28 @@ +/* Copyright (C) 2008 MySQL 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 */ + + +typedef struct st_seq_storage +{ + uint pos; + DYNAMIC_ARRAY seq; +} SEQ_STORAGE; + +extern my_bool seq_storage_reader_init(SEQ_STORAGE *seq, const char *file); +extern ulong seq_storage_next(SEQ_STORAGE *seq); +extern void seq_storage_destroy(SEQ_STORAGE *seq); +extern void seq_storage_rewind(SEQ_STORAGE *seq); +extern my_bool seq_storage_write(const char *file, ulong num); + diff --git a/storage/maria/unittest/test_file.c b/storage/maria/unittest/test_file.c new file mode 100644 index 00000000..853f5352 --- /dev/null +++ b/storage/maria/unittest/test_file.c @@ -0,0 +1,118 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +#include <tap.h> /* Includes my_global.h */ +#include <my_sys.h> +#include <my_dir.h> +#include "test_file.h" + + +/* + Check that file contance correspond to descriptor + + SYNOPSIS + test_file() + file File to test + file_name Path (and name) of file which is tested + size size of file + buff_size size of buffer which is enought to check the file + desc file descriptor to check with + + RETURN + 1 file if OK + 0 error +*/ + +int test_file(PAGECACHE_FILE file, char *file_name, + off_t size, size_t buff_size, struct file_desc *desc) +{ + unsigned char *buffr= my_malloc(PSI_NOT_INSTRUMENTED, buff_size, MYF(0)); + off_t pos= 0; + size_t byte; + int step= 0; + int res= 1; /* ok */ + +#ifdef _WIN32 + /* + On Windows, the info returned by stat(), specifically file length + is not necessarily current, because this is the behavior of + underlying FindFirstFile() function. + */ + WIN32_FILE_ATTRIBUTE_DATA file_attr; + LARGE_INTEGER li; + if(GetFileAttributesEx(file_name, GetFileExInfoStandard, &file_attr) == 0) + { + diag("Can't GetFileAttributesEx %s (errno: %lu)\n", file_name, + GetLastError()); + res= 0; + goto err; + } + li.HighPart= file_attr.nFileSizeHigh; + li.LowPart= file_attr.nFileSizeLow; + if(li.QuadPart != size) + { + diag("file %s size is %llu (should be %llu)\n", + file_name, (ulonglong)size, (ulonglong)li.QuadPart); + res= 0; /* failed */ + /* continue to get more information */ + } +#else + MY_STAT stat_buff, *stat; + if ((stat= my_stat(file_name, &stat_buff, MYF(0))) == NULL) + { + diag("Can't stat() %s (errno: %d)\n", file_name, errno); + res= 0; + goto err; + } + if (stat->st_size != size) + { + diag("file %s size is %lu (should be %lu)\n", + file_name, (ulong) stat->st_size, (ulong) size); + res= 0; /* failed */ + /* continue to get more information */ + } +#endif + + /* check content */ + my_seek(file.file, 0, SEEK_SET, MYF(MY_WME)); + while (desc[step].length != 0) + { + if (my_read(file.file, buffr, desc[step].length, MYF(0)) != + desc[step].length) + { + diag("Can't read %u bytes from %s (file: %d errno: %d)\n", + (uint)desc[step].length, file_name, file.file, errno); + res= 0; + goto err; + } + for (byte= 0; byte < desc[step].length; byte++) + { + if (buffr[byte] != desc[step].content) + { + diag("content of %s mismatch 0x%x in position %lu instead of 0x%x\n", + file_name, (uint) buffr[byte], (ulong) (pos + byte), + desc[step].content); + res= 0; + goto err; + } + } + pos+= desc[step].length; + step++; + } + +err: + my_free(buffr); + return res; +} diff --git a/storage/maria/unittest/test_file.h b/storage/maria/unittest/test_file.h new file mode 100644 index 00000000..22337b7a --- /dev/null +++ b/storage/maria/unittest/test_file.h @@ -0,0 +1,31 @@ +/* Copyright (C) 2006-2008 MySQL 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 */ + +#include <m_string.h> +#include "../ma_pagecache.h" +#ifdef _WIN32 +#include <direct.h> +#endif +/* + File content descriptor +*/ +struct file_desc +{ + unsigned int length; + unsigned char content; +}; + +int test_file(PAGECACHE_FILE file, char *file_name, + off_t size, size_t buff_size, struct file_desc *desc); diff --git a/storage/maria/unittest/trnman-t.c b/storage/maria/unittest/trnman-t.c new file mode 100644 index 00000000..43bca725 --- /dev/null +++ b/storage/maria/unittest/trnman-t.c @@ -0,0 +1,172 @@ +/* Copyright (C) 2006-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. + + 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 */ + +#include <tap.h> + +#include <my_global.h> +#include <my_sys.h> +#include <lf.h> +#include <m_string.h> +#include "../trnman.h" + +pthread_mutex_t rt_mutex; +pthread_attr_t attr; +size_t stacksize= 0; +#define STACK_SIZE (((int)stacksize-2048)*STACK_DIRECTION) + +int rt_num_threads; +int litmus; + +/* + create and end (commit or rollback) transactions randomly +*/ +#define MAX_ITER 100 +pthread_handler_t test_trnman(void *arg) +{ + uint x, y, i, n; + TRN *trn[MAX_ITER]; + int m= (*(int *)arg); + + if (my_thread_init()) + BAIL_OUT("my_thread_init failed!"); + + for (x= ((int)(intptr)(&m)); m > 0; ) + { + y= x= (x*3628273133LL + 1500450271LL) % 9576890767LL; /* three prime numbers */ + m-= n= x % MAX_ITER; + for (i= 0; i < n; i++) + { + trn[i]= trnman_new_trn(0); + if (!trn[i]) + { + diag("trnman_new_trn() failed"); + litmus++; + } + } + for (i= 0; i < n; i++) + { + y= (y*19 + 7) % 31; + trnman_end_trn(trn[i], y & 1); + } + } + pthread_mutex_lock(&rt_mutex); + rt_num_threads--; + pthread_mutex_unlock(&rt_mutex); + + my_thread_end(); + + return 0; +} +#undef MAX_ITER + +void run_test(const char *test, pthread_handler handler, int n, int m) +{ + pthread_t *threads; + ulonglong now= microsecond_interval_timer(); + int i; + + litmus= 0; + + threads= (pthread_t *)my_malloc(PSI_NOT_INSTRUMENTED, sizeof(void *)*n, MYF(0)); + if (!threads) + { + diag("Out of memory"); + abort(); + } + + diag("Testing %s with %d threads, %d iterations... ", test, n, m); + rt_num_threads= n; + for (i= 0; i < n ; i++) + if (pthread_create(threads+i, &attr, handler, &m)) + { + diag("Could not create thread"); + abort(); + } + for (i= 0 ; i < n ; i++) + pthread_join(threads[i], 0); + now= microsecond_interval_timer() - now; + ok(litmus == 0, "Tested %s in %g secs (%d)", test, ((double)now)/1e6, litmus); + my_free(threads); +} + +#define ok_read_from(T1, T2, RES) \ + i= trnman_can_read_from(trn[T1], trid[T2]); \ + ok(i == RES, "trn" #T1 " %s read from trn" #T2, i ? "can" : "cannot") +#define start_transaction(T) \ + trn[T]= trnman_new_trn(0); \ + trid[T]= trn[T]->trid +#define commit(T) trnman_commit_trn(trn[T]) +#define abort(T) trnman_abort_trn(trn[T]) + +#define Ntrns 4 +void test_trnman_read_from() +{ + TRN *trn[Ntrns]; + TrID trid[Ntrns]; + int i; + + start_transaction(0); /* start trn1 */ + start_transaction(1); /* start trn2 */ + ok_read_from(1, 0, 0); + commit(0); /* commit trn1 */ + start_transaction(2); /* start trn4 */ + abort(2); /* abort trn4 */ + start_transaction(3); /* start trn5 */ + ok_read_from(3, 0, 1); + ok_read_from(3, 1, 0); + ok_read_from(3, 2, 0); + ok_read_from(3, 3, 1); + commit(1); /* commit trn2 */ + ok_read_from(3, 1, 0); + commit(3); /* commit trn5 */ + +} + +int main(int argc __attribute__((unused)), char **argv) +{ + MY_INIT(argv[0]); + + plan(7); + + pthread_mutex_init(&rt_mutex, 0); + pthread_attr_init(&attr); +#ifdef HAVE_PTHREAD_ATTR_GETSTACKSIZE + pthread_attr_getstacksize(&attr, &stacksize); + if (stacksize == 0) +#endif + stacksize= PTHREAD_STACK_MIN; + +#define CYCLES 10000 +#define THREADS 10 + + trnman_init(0); + + test_trnman_read_from(); + run_test("trnman", test_trnman, THREADS, CYCLES); + + diag("mallocs: %d", trnman_allocated_transactions); + { + ulonglong now= microsecond_interval_timer(); + trnman_destroy(); + now= microsecond_interval_timer() - now; + diag("trnman_destroy: %g", ((double)now)/1e6); + } + + pthread_mutex_destroy(&rt_mutex); + my_end(0); + return exit_status(); +} + +#include "../ma_check_standalone.h" |