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