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 <toku_portability.h>
40 #include <memory.h>
41 #include <toku_portability.h>
42 #include <db.h>
43
44 #include <errno.h>
45 #include <sys/stat.h>
46
47 #include "test.h"
48
49 // TOKU_TEST_FILENAME is defined in the Makefile
50
51 static DB *db;
52 static DB_TXN* txns[(int)256];
53 static DB_ENV* dbenv;
54 static DBC* cursors[(int)256];
55
56 static void
put(bool success,char txn,int _key,int _data)57 put(bool success, char txn, int _key, int _data) {
58 assert(txns[(int)txn]);
59
60 int r;
61 DBT key;
62 DBT data;
63
64 r = db->put(db, txns[(int)txn],
65 dbt_init(&key, &_key, sizeof(int)),
66 dbt_init(&data, &_data, sizeof(int)),
67 0);
68
69 if (success) CKERR(r);
70 else CKERR2s(r, DB_LOCK_DEADLOCK, DB_LOCK_NOTGRANTED);
71 }
72
73 static void
cget(bool success,bool find,char txn,int _key,int _data,int _key_expect,int _data_expect,uint32_t flags)74 cget(bool success, bool find, char txn, int _key, int _data,
75 int _key_expect, int _data_expect, uint32_t flags) {
76 assert(txns[(int)txn] && cursors[(int)txn]);
77
78 int r;
79 DBT key;
80 DBT data;
81
82 r = cursors[(int)txn]->c_get(cursors[(int)txn],
83 dbt_init(&key, &_key, sizeof(int)),
84 dbt_init(&data, &_data, sizeof(int)),
85 flags);
86 if (success) {
87 if (find) {
88 CKERR(r);
89 assert(*(int *)key.data == _key_expect);
90 assert(*(int *)data.data == _data_expect);
91 }
92 else CKERR2(r, DB_NOTFOUND);
93 }
94 else CKERR2s(r, DB_LOCK_DEADLOCK, DB_LOCK_NOTGRANTED);
95 }
96
97 static void
dbdel(bool success,bool find,char txn,int _key)98 dbdel (bool success, bool find, char txn, int _key) {
99 int r;
100 DBT key;
101
102 /* If DB_DELETE_ANY changes to 0, then find is meaningful and
103 has to be fixed in test_dbdel*/
104 r = db->del(db, txns[(int)txn], dbt_init(&key,&_key, sizeof(int)),
105 DB_DELETE_ANY);
106 if (success) {
107 if (find) CKERR(r);
108 else CKERR2( r, DB_NOTFOUND);
109 }
110 else CKERR2s(r, DB_LOCK_DEADLOCK, DB_LOCK_NOTGRANTED);
111 }
112
113 static void
init_txn(char name)114 init_txn (char name) {
115 int r;
116 assert(!txns[(int)name]);
117 r = dbenv->txn_begin(dbenv, NULL, &txns[(int)name], DB_TXN_NOWAIT);
118 CKERR(r);
119 assert(txns[(int)name]);
120 }
121
122 static void
init_dbc(char name)123 init_dbc (char name) {
124 int r;
125
126 assert(!cursors[(int)name] && txns[(int)name]);
127 r = db->cursor(db, txns[(int)name], &cursors[(int)name], 0);
128 CKERR(r);
129 assert(cursors[(int)name]);
130 }
131
132 static void
commit_txn(char name)133 commit_txn (char name) {
134 int r;
135 assert(txns[(int)name] && !cursors[(int)name]);
136
137 r = txns[(int)name]->commit(txns[(int)name], 0);
138 CKERR(r);
139 txns[(int)name] = NULL;
140 }
141
142 static void
abort_txn(char name)143 abort_txn (char name) {
144 int r;
145 assert(txns[(int)name] && !cursors[(int)name]);
146
147 r = txns[(int)name]->abort(txns[(int)name]);
148 CKERR(r);
149 txns[(int)name] = NULL;
150 }
151
152 static void
close_dbc(char name)153 close_dbc (char name) {
154 int r;
155
156 assert(cursors[(int)name]);
157 r = cursors[(int)name]->c_close(cursors[(int)name]);
158 CKERR(r);
159 cursors[(int)name] = NULL;
160 }
161
162 static void
early_commit(char name)163 early_commit (char name) {
164 assert(cursors[(int)name] && txns[(int)name]);
165 close_dbc(name);
166 commit_txn(name);
167 }
168
169 static void
early_abort(char name)170 early_abort (char name) {
171 assert(cursors[(int)name] && txns[(int)name]);
172 close_dbc(name);
173 abort_txn(name);
174 }
175
176 static void
setup_dbs(void)177 setup_dbs (void) {
178 int r;
179
180 toku_os_recursive_delete(TOKU_TEST_FILENAME);
181 toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
182 dbenv = NULL;
183 db = NULL;
184 /* Open/create primary */
185 r = db_env_create(&dbenv, 0);
186 CKERR(r);
187 r = dbenv->set_default_bt_compare(dbenv, int_dbt_cmp);
188 CKERR(r);
189 uint32_t env_txn_flags = DB_INIT_TXN | DB_INIT_LOCK;
190 uint32_t env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL;
191 r = dbenv->open(dbenv, TOKU_TEST_FILENAME, env_open_flags | env_txn_flags, 0600);
192 CKERR(r);
193
194 r = db_create(&db, dbenv, 0);
195 CKERR(r);
196
197 char a;
198 for (a = 'a'; a <= 'z'; a++) init_txn(a);
199 init_txn('\0');
200 r = db->open(db, txns[(int)'\0'], "foobar.db", NULL, DB_BTREE, DB_CREATE, 0600);
201 CKERR(r);
202 commit_txn('\0');
203 for (a = 'a'; a <= 'z'; a++) init_dbc(a);
204 }
205
206 static void
close_dbs(void)207 close_dbs(void) {
208 char a;
209 for (a = 'a'; a <= 'z'; a++) {
210 if (cursors[(int)a]) close_dbc(a);
211 if (txns[(int)a]) commit_txn(a);
212 }
213
214 int r;
215 r = db->close(db, 0);
216 CKERR(r);
217 db = NULL;
218 r = dbenv->close(dbenv, 0);
219 CKERR(r);
220 dbenv = NULL;
221 }
222
223
224 static __attribute__((__unused__))
225 void
test_abort(void)226 test_abort (void) {
227 /* ********************************************************************** */
228 setup_dbs();
229 put(true, 'a', 1, 1);
230 early_abort('a');
231 cget(true, false, 'b', 1, 1, 0, 0, DB_SET);
232 close_dbs();
233 /* ********************************************************************** */
234 setup_dbs();
235 cget(true, false, 'a', 1, 1, 0, 0, DB_SET);
236 cget(true, false, 'b', 1, 1, 0, 0, DB_SET);
237 put(false, 'a', 1, 1);
238 early_commit('b');
239 put(true, 'a', 1, 1);
240 cget(true, true, 'a', 1, 1, 1, 1, DB_SET);
241 cget(true, false, 'a', 2, 1, 1, 1, DB_SET);
242 cget(false, true, 'c', 1, 1, 0, 0, DB_SET);
243 early_abort('a');
244 cget(true, false, 'c', 1, 1, 0, 0, DB_SET);
245 close_dbs();
246 /* ********************************************************************** */
247 }
248
249 static void
test_both(uint32_t db_flags)250 test_both (uint32_t db_flags) {
251 /* ********************************************************************** */
252 setup_dbs();
253 cget(true, false, 'a', 1, 1, 0, 0, db_flags);
254 close_dbs();
255 /* ********************************************************************** */
256 setup_dbs();
257 cget(true, false, 'a', 1, 1, 0, 0, db_flags);
258 cget(true, false, 'a', 2, 1, 0, 0, db_flags);
259 close_dbs();
260 /* ********************************************************************** */
261 setup_dbs();
262 cget(true, false, 'a', 1, 1, 0, 0, db_flags);
263 cget(true, false, 'a', 1, 1, 0, 0, db_flags);
264 close_dbs();
265 /* ********************************************************************** */
266 setup_dbs();
267 cget(true, false, 'a', 1, 1, 0, 0, db_flags);
268 cget(true, false, 'b', 2, 1, 0, 0, db_flags);
269 close_dbs();
270 /* ********************************************************************** */
271 setup_dbs();
272 cget(true, false, 'a', 1, 1, 0, 0, db_flags);
273 #ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
274 cget(false, false, 'b', 1, 1, 0, 0, db_flags);
275 #else
276 cget(true, false, 'b', 1, 1, 0, 0, db_flags);
277 #endif
278 close_dbs();
279 /* ********************************************************************** */
280 setup_dbs();
281 cget(true, false, 'a', 1, 1, 0, 0, db_flags);
282 #ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
283 cget(false, false, 'b', 1, 1, 0, 0, db_flags);
284 put(true, 'a', 1, 1);
285 #else
286 cget(true, false, 'b', 1, 1, 0, 0, db_flags);
287 put(false, 'a', 1, 1);
288 #endif
289 early_commit('b');
290 put(true, 'a', 1, 1);
291 cget(true, true, 'a', 1, 1, 1, 1, db_flags);
292 cget(true, false, 'a', 2, 1, 0, 0, db_flags);
293 cget(false, true, 'c', 1, 1, 0, 0, db_flags);
294 early_commit('a');
295 cget(true, true, 'c', 1, 1, 1, 1, db_flags);
296 close_dbs();
297 }
298
299
300 static void
test_last(void)301 test_last (void) {
302 /* ********************************************************************** */
303 setup_dbs();
304 cget(true, false, 'a', 0, 0, 0, 0, DB_LAST);
305 put(false, 'b', 2, 1);
306 put(true, 'a', 2, 1);
307 cget(true, true, 'a', 0, 0, 2, 1, DB_LAST);
308 early_commit('a');
309 put(true, 'b', 2, 1);
310 close_dbs();
311 /* ****************************************** */
312 setup_dbs();
313 put(true, 'a', 1, 1);
314 cget(true, true, 'a', 0, 0, 1, 1, DB_LAST);
315 put(false, 'b', 2, 1);
316 put(true, 'b', -1, 1);
317 cget(true, true, 'a', 0, 0, 1, 1, DB_LAST);
318 close_dbs();
319 /* ****************************************** */
320 setup_dbs();
321 put(true, 'a', 1, 1);
322 put(true, 'a', 3, 1);
323 put(true, 'a', 6, 1);
324 cget(true, true, 'a', 0, 0, 6, 1, DB_LAST);
325 put(true, 'b', 2, 1);
326 put(true, 'b', 4, 1);
327 put(false, 'b', 7, 1);
328 put(true, 'b', -1, 1);
329 close_dbs();
330 /* ****************************************** */
331 setup_dbs();
332 put(true, 'a', 1, 1);
333 cget(true, true, 'a', 0, 0, 1, 1, DB_LAST);
334 put(false, 'b', 1, 0);
335 close_dbs();
336 }
337
338 static void
test_first(void)339 test_first (void) {
340 /* ********************************************************************** */
341 setup_dbs();
342 cget(true, false, 'a', 0, 0, 0, 0, DB_FIRST);
343 put(false, 'b', 2, 1);
344 put(true, 'a', 2, 1);
345 cget(true, true, 'a', 0, 0, 2, 1, DB_FIRST);
346 early_commit('a');
347 put(true, 'b', 2, 1);
348 close_dbs();
349 /* ****************************************** */
350 setup_dbs();
351 put(true, 'a', 1, 1);
352 cget(true, true, 'a', 0, 0, 1, 1, DB_FIRST);
353 put(true, 'b', 2, 1);
354 put(false, 'b', -1, 1);
355 cget(true, true, 'a', 0, 0, 1, 1, DB_FIRST);
356 close_dbs();
357 /* ****************************************** */
358 setup_dbs();
359 put(true, 'a', 1, 1);
360 put(true, 'a', 3, 1);
361 put(true, 'a', 6, 1);
362 cget(true, true, 'a', 0, 0, 1, 1, DB_FIRST);
363 put(true, 'b', 2, 1);
364 put(true, 'b', 4, 1);
365 put(true, 'b', 7, 1);
366 put(false, 'b', -1, 1);
367 close_dbs();
368 /* ****************************************** */
369 setup_dbs();
370 put(true, 'a', 1, 1);
371 cget(true, true, 'a', 0, 0, 1, 1, DB_FIRST);
372 put(false, 'b', 1, 2);
373 close_dbs();
374 }
375
376 static void
test_set_range(uint32_t flag,int i)377 test_set_range (uint32_t flag, int i) {
378 /* ********************************************************************** */
379 setup_dbs();
380 cget(true, false, 'a', i*1, i*1, 0, 0, flag);
381 close_dbs();
382 /* ********************************************************************** */
383 setup_dbs();
384 cget(true, false, 'a', i*1, i*1, 0, 0, flag);
385 cget(true, false, 'a', i*2, i*1, 0, 0, flag);
386 close_dbs();
387 /* ********************************************************************** */
388 setup_dbs();
389 cget(true, false, 'a', i*1, i*1, 0, 0, flag);
390 cget(true, false, 'a', i*1, i*1, 0, 0, flag);
391 close_dbs();
392 /* ********************************************************************** */
393 setup_dbs();
394 cget(true, false, 'a', i*1, i*1, 0, 0, flag);
395 #ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
396 cget(false, false, 'b', i*2, i*1, 0, 0, flag);
397 #else
398 cget(true, false, 'b', i*2, i*1, 0, 0, flag);
399 #endif
400 close_dbs();
401 /* ********************************************************************** */
402 setup_dbs();
403 cget(true, false, 'a', i*1, i*1, 0, 0, flag);
404 #ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
405 cget(false, false, 'b', i*1, i*1, 0, 0, flag);
406 #else
407 cget(true, false, 'b', i*1, i*1, 0, 0, flag);
408 #endif
409 close_dbs();
410 /* ********************************************************************** */
411 setup_dbs();
412 cget(true, false, 'a', i*1, i*1, 0, 0, flag);
413 #ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
414 cget(false, false, 'b', i*5, i*5, 0, 0, flag);
415 put(true, 'a', i*7, i*6);
416 put(true, 'a', i*5, i*5);
417 #else
418 cget(true, false, 'b', i*5, i*5, 0, 0, flag);
419 put(false, 'a', i*7, i*6);
420 put(false, 'a', i*5, i*5);
421 #endif
422 put(true, 'a', i*4, i*4);
423 put(true, 'b', -i*1, i*4);
424 put(false, 'b', i*2, i*4);
425 #ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
426 put(true, 'a', i*5, i*4);
427 #else
428 put(false, 'a', i*5, i*4);
429 #endif
430 early_commit('b');
431 put(true, 'a', i*7, i*6);
432 put(true, 'a', i*5, i*5);
433 put(true, 'a', i*4, i*4);
434 put(true, 'a', i*5, i*4);
435 cget(true, true, 'a', i*1, i*1, i*4, i*4, flag);
436 cget(true, true, 'a', i*2, i*1, i*4, i*4, flag);
437 cget(false, true, 'c', i*6, i*6, i*7, i*6, flag);
438 early_commit('a');
439 cget(true, true, 'c', i*6, i*6, i*7, i*6, flag);
440 close_dbs();
441 }
442
443 static void
test_next(uint32_t next_type)444 test_next (uint32_t next_type) {
445 /* ********************************************************************** */
446 setup_dbs();
447 put(true, 'a', 2, 1);
448 put(true, 'a', 5, 1);
449 cget(true, true, 'a', 0, 0, 2, 1, next_type);
450 put(false, 'b', 2, 1);
451 put(true, 'b', 4, 1);
452 put(false, 'b', -1, 1);
453 cget(false, true, 'a', 0, 0, 4, 1, next_type);
454 early_commit('b');
455 cget(true, true, 'a', 2, 1, 2, 1, DB_SET);
456 cget(true, true, 'a', 0, 0, 4, 1, next_type);
457 cget(true, true, 'a', 0, 0, 5, 1, next_type);
458 close_dbs();
459 /* ****************************************** */
460 setup_dbs();
461 put(true, 'a', 1, 1);
462 put(true, 'a', 3, 1);
463 put(true, 'a', 6, 1);
464 cget(true, true, 'a', 0, 0, 1, 1, next_type);
465 cget(true, true, 'a', 0, 0, 3, 1, next_type);
466 put(false, 'b', 2, 1);
467 put(true, 'b', 4, 1);
468 put(true, 'b', 7, 1);
469 put(false, 'b', -1, 1);
470 close_dbs();
471 }
472
473 static void
test_prev(uint32_t next_type)474 test_prev (uint32_t next_type) {
475 /* ********************************************************************** */
476 setup_dbs();
477 put(true, 'a', -2, -1);
478 put(true, 'a', -5, -1);
479 cget(true, true, 'a', 0, 0, -2, -1, next_type);
480 put(false, 'b', -2, -1);
481 put(true, 'b', -4, -1);
482 put(false, 'b', 1, -1);
483 cget(false, true, 'a', 0, 0, -4, -1, next_type);
484 early_commit('b');
485 cget(true, true, 'a', -2, -1, -2, -1, DB_SET);
486 cget(true, true, 'a', 0, 0, -4, -1, next_type);
487 cget(true, true, 'a', 0, 0, -5, -1, next_type);
488 close_dbs();
489 /* ****************************************** */
490 setup_dbs();
491 put(true, 'a', -1, -1);
492 put(true, 'a', -3, -1);
493 put(true, 'a', -6, -1);
494 cget(true, true, 'a', 0, 0, -1, -1, next_type);
495 cget(true, true, 'a', 0, 0, -3, -1, next_type);
496 put(false, 'b', -2, -1);
497 put(true, 'b', -4, -1);
498 put(true, 'b', -7, -1);
499 put(false, 'b', 1, -1);
500 close_dbs();
501 }
502
503 static void
test_dbdel(void)504 test_dbdel (void) {
505 /* If DB_DELETE_ANY changes to 0, then find is meaningful and
506 has to be fixed in test_dbdel*/
507 /* ********************************************************************** */
508 setup_dbs();
509 put(true, 'c', 1, 1);
510 early_commit('c');
511 dbdel(true, true, 'a', 1);
512 cget(false, true, 'b', 1, 1, 1, 1, DB_SET);
513 cget(false, true, 'b', 1, 4, 1, 4, DB_SET);
514 cget(false, true, 'b', 1, 0, 1, 4, DB_SET);
515 cget(true, false, 'b', 0, 0, 0, 0, DB_SET);
516 cget(true, false, 'b', 2, 10, 2, 10, DB_SET);
517 close_dbs();
518 /* ********************************************************************** */
519 setup_dbs();
520 dbdel(true, true, 'a', 1);
521 cget(false, true, 'b', 1, 1, 1, 1, DB_SET);
522 cget(false, true, 'b', 1, 4, 1, 4, DB_SET);
523 cget(false, true, 'b', 1, 0, 1, 4, DB_SET);
524 cget(true, false, 'b', 0, 0, 0, 0, DB_SET);
525 cget(true, false, 'b', 2, 10, 2, 10, DB_SET);
526 close_dbs();
527 /* ********************************************************************** */
528 setup_dbs();
529 put(true, 'c', 1, 1);
530 early_commit('c');
531 cget(true, true, 'b', 1, 1, 1, 1, DB_SET);
532 dbdel(false, true, 'a', 1);
533 dbdel(true, true, 'a', 2);
534 dbdel(true, true, 'a', 0);
535 close_dbs();
536 }
537
538 static void
test_current(void)539 test_current (void) {
540 /* ********************************************************************** */
541 setup_dbs();
542 put(true, 'a', 1, 1);
543 early_commit('a');
544 cget(true, true, 'b', 1, 1, 1, 1, DB_SET);
545 cget(true, true, 'b', 1, 1, 1, 1, DB_CURRENT);
546 close_dbs();
547 }
548
549 struct dbt_pair {
550 DBT key;
551 DBT val;
552 };
553
554 struct int_pair {
555 int key;
556 int val;
557 };
558
559 int got_r_h;
560
561 static __attribute__((__unused__))
562 void
ignore(void * ignore)563 ignore (void *ignore __attribute__((__unused__))) {
564 }
565 #define TOKU_IGNORE(x) ignore((void*)x)
566
567 static void
test(void)568 test (void) {
569 /* ********************************************************************** */
570 setup_dbs();
571 close_dbs();
572 /* ********************************************************************** */
573 setup_dbs();
574 early_abort('a');
575 close_dbs();
576 /* ********************************************************************** */
577 setup_dbs();
578 early_commit('a');
579 close_dbs();
580 /* ********************************************************************** */
581 setup_dbs();
582 put(true, 'a', 1, 1);
583 close_dbs();
584 /* ********************************************************************** */
585 test_both( DB_SET);
586 /* ********************************************************************** */
587 test_first();
588 /* ********************************************************************** */
589 test_last();
590 /* ********************************************************************** */
591 test_set_range( DB_SET_RANGE, 1);
592 #ifdef DB_SET_RANGE_REVERSE
593 test_set_range( DB_SET_RANGE_REVERSE, -1);
594 #endif
595 /* ********************************************************************** */
596 test_next(DB_NEXT);
597 /* ********************************************************************** */
598 test_prev(DB_PREV);
599 /* ********************************************************************** */
600 test_dbdel();
601 /* ********************************************************************** */
602 test_current();
603 /* ********************************************************************** */
604 }
605
606
607 int
test_main(int argc,char * const argv[])608 test_main(int argc, char *const argv[]) {
609 parse_args(argc, argv);
610 test();
611 return 0;
612 }
613