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 // verify that the comparison function get a valid db object pointer
40
41 #include <sys/stat.h>
42 #include "test.h"
43
44
45 const char *descriptor_contents[] = {
46 "Spoon full of sugar",
47 "Bucket full of pants"
48 };
49
50 const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
51 const char *namea="a.db";
52
53 int verified = 0;
54 uint32_t forced_version = 2;
55
my_compare(DB * UU (db),const DBT * a,const DBT * b)56 static int my_compare(DB *UU(db), const DBT *a, const DBT *b) {
57 assert(db);
58 assert(db->cmp_descriptor);
59 uint32_t which = forced_version-1;
60 size_t len = strlen(descriptor_contents[which])+1;
61
62 assert(db->cmp_descriptor->dbt.size == len);
63 assert(memcmp(db->cmp_descriptor->dbt.data, descriptor_contents[which], len) == 0);
64
65 assert(a->size == b->size);
66 verified = 1;
67 return memcmp(a->data, b->data, a->size);
68 }
69
70 static void
change_descriptor(DB * db,int which,DB_ENV * env)71 change_descriptor(DB* db, int which, DB_ENV* env) {
72 DBT descriptor;
73 size_t len = strlen(descriptor_contents[which])+1;
74 dbt_init(&descriptor, descriptor_contents[which], len);
75 IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
76 { int chk_r = db->change_descriptor(db, txn_desc, &descriptor, DB_UPDATE_CMP_DESCRIPTOR); CKERR(chk_r); }
77 });
78 }
79
80 static void
do_x1_shutdown(bool do_commit,bool do_abort)81 do_x1_shutdown (bool do_commit, bool do_abort) {
82 int r;
83 toku_os_recursive_delete(TOKU_TEST_FILENAME);
84 r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
85 char datadir[TOKU_PATH_MAX+1];
86 r = toku_os_mkdir(toku_path_join(datadir, 2, TOKU_TEST_FILENAME, "data"), S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
87 DB_ENV *env;
88 DB *dba;
89 r = db_env_create(&env, 0); CKERR(r);
90 r = env->set_data_dir(env, "data"); CKERR(r);
91 r = env->set_default_bt_compare(env, my_compare); CKERR(r);
92 r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
93
94 r = db_create(&dba, env, 0); CKERR(r);
95 r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
96 change_descriptor(dba, 0, env);
97 r = dba->close(dba, 0); CKERR(r);
98
99 r = db_create(&dba, env, 0); CKERR(r);
100 r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
101 change_descriptor(dba, 1, env);
102
103
104
105 DB_TXN *txn;
106 r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
107 {
108 DBT a,b;
109 dbt_init(&a, "a", 2);
110 dbt_init(&b, "b", 2);
111 r = dba->put(dba, txn, &a, &b, 0); CKERR(r);
112 r = dba->put(dba, txn, &b, &a, 0); CKERR(r);
113 }
114 //printf("opened\n");
115 if (do_commit) {
116 r = txn->commit(txn, 0); CKERR(r);
117 } else if (do_abort) {
118 r = txn->abort(txn); CKERR(r);
119
120 // force an fsync of the log
121 r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
122 r = txn->commit(txn, 0); CKERR(r);
123 }
124 //printf("shutdown\n");
125 assert(verified);
126 toku_hard_crash_on_purpose();
127 }
128
129 static void
do_x1_recover(bool did_commit)130 do_x1_recover (bool did_commit) {
131 DB_ENV *env;
132 DB *dba;
133 int r;
134 char datadir[TOKU_PATH_MAX+1];
135 toku_path_join(datadir, 2, TOKU_TEST_FILENAME, "data");
136 toku_os_recursive_delete(datadir);
137 r = toku_os_mkdir(datadir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
138 r = db_env_create(&env, 0); CKERR(r);
139 r = env->set_data_dir(env, "data"); CKERR(r);
140 r = env->set_default_bt_compare(env, my_compare); CKERR(r);
141 r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
142 r = db_create(&dba, env, 0); CKERR(r);
143 r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
144 DBT aa, ab;
145 dbt_init(&aa, NULL, 0);
146 dbt_init(&ab, NULL, 0);
147 DB_TXN *txn;
148 DBC *ca;
149 r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
150 r = dba->cursor(dba, txn, &ca, 0); CKERR(r);
151 int ra = ca->c_get(ca, &aa, &ab, DB_FIRST); CKERR(r);
152 if (did_commit) {
153 assert(ra==0);
154 // verify key-value pairs
155 assert(aa.size==2);
156 assert(ab.size==2);
157 const char a[2] = "a";
158 const char b[2] = "b";
159 assert(memcmp(aa.data, &a, 2)==0);
160 assert(memcmp(ab.data, &b, 2)==0);
161 assert(memcmp(ab.data, &b, 2)==0);
162 assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == 0);
163 assert(aa.size == 2 && ab.size == 2 && memcmp(aa.data, b, 2) == 0 && memcmp(ab.data, a, 2) == 0);
164 // make sure no other entries in DB
165 assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == DB_NOTFOUND);
166 } else {
167 // It wasn't committed (it also wasn't aborted), but a checkpoint happened.
168 assert(ra==DB_NOTFOUND);
169 }
170 r = ca->c_close(ca); CKERR(r);
171 r = txn->commit(txn, 0); CKERR(r);
172 r = dba->close(dba, 0); CKERR(r);
173 r = env->close(env, 0); CKERR(r);
174 assert(verified);
175 exit(0);
176 }
177
178 static void
do_x1_recover_only(void)179 do_x1_recover_only (void) {
180 DB_ENV *env;
181 int r;
182
183 r = db_env_create(&env, 0); CKERR(r);
184 r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
185 r = env->close(env, 0); CKERR(r);
186 exit(0);
187 }
188
189 static void
do_x1_no_recover(void)190 do_x1_no_recover (void) {
191 DB_ENV *env;
192 int r;
193
194 r = db_env_create(&env, 0); CKERR(r);
195 r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
196 assert(r == DB_RUNRECOVERY);
197 r = env->close(env, 0); CKERR(r);
198 exit(0);
199 }
200
201 const char *cmd;
202
203 #if 0
204
205 static void
206 do_test_internal (bool commit)
207 {
208 pid_t pid;
209 if (0 == (pid=fork())) {
210 int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--commit" : "--abort", NULL);
211 assert(r==-1);
212 printf("execl failed: %d (%s)\n", errno, strerror(errno));
213 assert(0);
214 }
215 {
216 int r;
217 int status;
218 r = waitpid(pid, &status, 0);
219 //printf("signaled=%d sig=%d\n", WIFSIGNALED(status), WTERMSIG(status));
220 assert(WIFSIGNALED(status) && WTERMSIG(status)==SIGABRT);
221 }
222 // Now find out what happend
223
224 if (0 == (pid = fork())) {
225 int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--recover-committed" : "--recover-aborted", NULL);
226 assert(r==-1);
227 printf("execl failed: %d (%s)\n", errno, strerror(errno));
228 assert(0);
229 }
230 {
231 int r;
232 int status;
233 r = waitpid(pid, &status, 0);
234 //printf("recovery exited=%d\n", WIFEXITED(status));
235 assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
236 }
237 }
238
239 static void
240 do_test (void) {
241 do_test_internal(true);
242 do_test_internal(false);
243 }
244
245 #endif
246
247
248 bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false, do_recover_aborted=false, do_recover_only=false, do_no_recover = false;
249
250 static void
x1_parse_args(int argc,char * const argv[])251 x1_parse_args (int argc, char * const argv[]) {
252 int resultcode;
253 cmd = argv[0];
254 argc--; argv++;
255 while (argc>0) {
256 if (strcmp(argv[0], "-v") == 0) {
257 verbose++;
258 } else if (strcmp(argv[0],"-q")==0) {
259 verbose--;
260 if (verbose<0) verbose=0;
261 } else if (strcmp(argv[0], "--commit")==0 || strcmp(argv[0], "--test") == 0) {
262 do_commit=true;
263 } else if (strcmp(argv[0], "--abort")==0) {
264 do_abort=true;
265 } else if (strcmp(argv[0], "--explicit-abort")==0) {
266 do_explicit_abort=true;
267 } else if (strcmp(argv[0], "--recover-committed")==0 || strcmp(argv[0], "--recover") == 0) {
268 do_recover_committed=true;
269 } else if (strcmp(argv[0], "--recover-aborted")==0) {
270 do_recover_aborted=true;
271 } else if (strcmp(argv[0], "--recover-only") == 0) {
272 do_recover_only=true;
273 } else if (strcmp(argv[0], "--no-recover") == 0) {
274 do_no_recover=true;
275 } else if (strcmp(argv[0], "-h")==0) {
276 resultcode=0;
277 do_usage:
278 fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
279 exit(resultcode);
280 } else {
281 fprintf(stderr, "Unknown arg: %s\n", argv[0]);
282 resultcode=1;
283 goto do_usage;
284 }
285 argc--;
286 argv++;
287 }
288 {
289 int n_specified=0;
290 if (do_commit) n_specified++;
291 if (do_abort) n_specified++;
292 if (do_explicit_abort) n_specified++;
293 if (do_recover_committed) n_specified++;
294 if (do_recover_aborted) n_specified++;
295 if (do_recover_only) n_specified++;
296 if (do_no_recover) n_specified++;
297 if (n_specified>1) {
298 printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
299 resultcode=1;
300 goto do_usage;
301 }
302 }
303 }
304
305 int
test_main(int argc,char * const argv[])306 test_main (int argc, char * const argv[])
307 {
308 x1_parse_args(argc, argv);
309 if (do_commit) {
310 do_x1_shutdown (true, false);
311 } else if (do_abort) {
312 do_x1_shutdown (false, false);
313 } else if (do_explicit_abort) {
314 do_x1_shutdown(false, true);
315 } else if (do_recover_committed) {
316 do_x1_recover(true);
317 } else if (do_recover_aborted) {
318 do_x1_recover(false);
319 } else if (do_recover_only) {
320 do_x1_recover_only();
321 } else if (do_no_recover) {
322 do_x1_no_recover();
323 }
324 #if 0
325 else {
326 do_test();
327 }
328 #endif
329 return 0;
330 }
331