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