1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3 #ident "$Id$"
4 /*======
5 This file is part of PerconaFT.
6 
7 
8 Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9 
10     PerconaFT is free software: you can redistribute it and/or modify
11     it under the terms of the GNU General Public License, version 2,
12     as published by the Free Software Foundation.
13 
14     PerconaFT is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with PerconaFT.  If not, see <http://www.gnu.org/licenses/>.
21 
22 ----------------------------------------
23 
24     PerconaFT is free software: you can redistribute it and/or modify
25     it under the terms of the GNU Affero General Public License, version 3,
26     as published by the Free Software Foundation.
27 
28     PerconaFT is distributed in the hope that it will be useful,
29     but WITHOUT ANY WARRANTY; without even the implied warranty of
30     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
31     GNU Affero General Public License for more details.
32 
33     You should have received a copy of the GNU Affero General Public License
34     along with PerconaFT.  If not, see <http://www.gnu.org/licenses/>.
35 ======= */
36 
37 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
38 
39 #include "test.h"
40 
41 // verify recovery of an update log entry which changes values at keys
42 
43 static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
44 static const unsigned int NUM_KEYS = 100;
45 
should_update(const unsigned int k)46 static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
47 
_v(const unsigned int k)48 static inline unsigned int _v(const unsigned int k) { return 10 - k; }
_e(const unsigned int k)49 static inline unsigned int _e(const unsigned int k) { return k + 4; }
_u(const unsigned int v,const unsigned int e)50 static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
51 
update_fun(DB * UU (db),const DBT * key,const DBT * old_val,const DBT * extra,void (* set_val)(const DBT * new_val,void * set_extra),void * set_extra)52 static int update_fun(DB *UU(db),
53                       const DBT *key,
54                       const DBT *old_val, const DBT *extra,
55                       void (*set_val)(const DBT *new_val,
56                                       void *set_extra),
57                       void *set_extra)
58 {
59     unsigned int *k, *ov, *e, v;
60     assert(key->size == sizeof(*k));
61     CAST_FROM_VOIDP(k, key->data);
62     assert(old_val->size == sizeof(*ov));
63     CAST_FROM_VOIDP(ov, old_val->data);
64     assert(extra->size == sizeof(*e));
65     CAST_FROM_VOIDP(e, extra->data);
66     v = _u(*ov, *e);
67 
68     {
69         DBT newval;
70         set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
71     }
72 
73     return 0;
74 }
75 
do_inserts(DB_TXN * txn,DB * db)76 static int do_inserts(DB_TXN *txn, DB *db)
77 {
78     int r = 0;
79     DBT key, val;
80     unsigned int i, v;
81     DBT *keyp = dbt_init(&key, &i, sizeof(i));
82     DBT *valp = dbt_init(&val, &v, sizeof(v));
83     for (i = 0; i < NUM_KEYS; ++i) {
84         v = _v(i);
85         r = db->put(db, txn, keyp, valp, 0);
86         CKERR(r);
87     }
88     return r;
89 }
90 
do_updates(DB_TXN * txn,DB * db)91 static int do_updates(DB_TXN *txn, DB *db) {
92     int r = 0;
93     DBT key, extra;
94     unsigned int i, e;
95     DBT *keyp = dbt_init(&key, &i, sizeof(i));
96     DBT *extrap = dbt_init(&extra, &e, sizeof(e));
97     for (i = 0; i < NUM_KEYS; ++i) {
98         if (should_update(i)) {
99             e = _e(i);
100             r = db->update(db, txn, keyp, extrap, 0);
101             CKERR(r);
102         }
103     }
104     return r;
105 }
106 
run_test(void)107 static void run_test(void)
108 {
109     DB_ENV *env;
110     DB *db;
111 
112     toku_os_recursive_delete(TOKU_TEST_FILENAME);
113     { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
114     { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
115     env->set_errfile(env, stderr);
116     env->set_update(env, update_fun);
117     { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
118 
119     IN_TXN_COMMIT(env, NULL, txn_1, 0, {
120             { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
121             { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
122 
123             { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
124         });
125 
126     { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
127 
128     IN_TXN_ABORT(env, NULL, txn_2, 0, {
129             { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
130         });
131 
132     toku_hard_crash_on_purpose();
133 }
134 
verify_unchanged(DB_ENV * env,DB * db)135 static int verify_unchanged(DB_ENV *env, DB *db)
136 {
137     int r = 0;
138     DBT key, val;
139     unsigned int i, *vp;
140     DBT *keyp = dbt_init(&key, &i, sizeof(i));
141     DBT *valp = dbt_init(&val, NULL, 0);
142 
143     IN_TXN_COMMIT(env, NULL, txn_1, 0, {
144             for (i = 0; i < NUM_KEYS; ++i) {
145                 r = db->get(db, txn_1, keyp, valp, 0);
146                 CKERR(r);
147                 assert(val.size == sizeof(*vp));
148                 CAST_FROM_VOIDP(vp, val.data);
149                 assert(*vp == _v(i));
150             }
151         });
152 
153     return r;
154 }
155 
run_recover(void)156 static void run_recover(void)
157 {
158     DB_ENV *env;
159     DB *db;
160 
161     { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
162     env->set_errfile(env, stderr);
163     env->set_update(env, update_fun);
164     { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
165     { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
166     { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
167     { int chk_r = verify_unchanged(env, db); CKERR(chk_r); }
168     { int chk_r = db->close(db, 0); CKERR(chk_r); }
169     { int chk_r = env->close(env, 0); CKERR(chk_r); }
170 }
171 
usage(void)172 static int usage(void)
173 {
174     return 1;
175 }
176 
test_main(int argc,char * const argv[])177 int test_main(int argc, char * const argv[])
178 {
179     bool do_test = false;
180     bool do_recover = false;
181 
182     for (int i = 1; i < argc; i++) {
183         char * const arg = argv[i];
184         if (strcmp(arg, "-v") == 0) {
185             verbose++;
186             continue;
187         }
188         if (strcmp(arg, "-q") == 0) {
189             verbose--;
190             if (verbose < 0)
191                 verbose = 0;
192             continue;
193         }
194         if (strcmp(arg, "--test") == 0) {
195             do_test = true;
196             continue;
197         }
198         if (strcmp(arg, "--recover") == 0) {
199             do_recover = true;
200             continue;
201         }
202         if (strcmp(arg, "--help") == 0) {
203             return usage();
204         }
205     }
206 
207     if (do_test) {
208         run_test();
209     }
210     if (do_recover) {
211         run_recover();
212     }
213 
214     return 0;
215 }
216