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 // test that an update doesn't infringe on the values read by another txn
40 // started with TXN_SNAPSHOT
41 
42 #include "test.h"
43 
44 const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
45 
46 DB_ENV *env;
47 
48 const int to_update[] = { 0, 1, 1, 1, 0, 0, 1, 0, 1, 0 };
49 
_v(const unsigned int i)50 static inline unsigned int _v(const unsigned int i) { return 10 - i; }
_e(const unsigned int i)51 static inline unsigned int _e(const unsigned int i) { return i + 4; }
_u(const unsigned int v,const unsigned int e)52 static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
53 
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)54 static int update_fun(DB *UU(db),
55                       const DBT *key,
56                       const DBT *old_val, const DBT *extra,
57                       void (*set_val)(const DBT *new_val,
58                                       void *set_extra),
59                       void *set_extra) {
60     unsigned int *k, *ov, *e, v;
61     assert(key->size == sizeof(*k));
62     CAST_FROM_VOIDP(k, key->data);
63     assert(old_val->size == sizeof(*ov));
64     CAST_FROM_VOIDP(ov, old_val->data);
65     assert(extra->size == sizeof(*e));
66     CAST_FROM_VOIDP(e, extra->data);
67     v = _u(*ov, *e);
68 
69     {
70         DBT newval;
71         set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
72     }
73 
74     return 0;
75 }
76 
setup(void)77 static void setup (void) {
78     toku_os_recursive_delete(TOKU_TEST_FILENAME);
79     { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
80     { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
81     env->set_errfile(env, stderr);
82     env->set_update(env, update_fun);
83     { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
84 }
85 
cleanup(void)86 static void cleanup (void) {
87     { int chk_r = env->close(env, 0); CKERR(chk_r); }
88 }
89 
do_inserts(DB_TXN * txn,DB * db)90 static int do_inserts(DB_TXN *txn, DB *db) {
91     int r = 0;
92     DBT key, val;
93     unsigned int i, v;
94     DBT *keyp = dbt_init(&key, &i, sizeof(i));
95     DBT *valp = dbt_init(&val, &v, sizeof(v));
96     for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
97         v = _v(i);
98         r = db->put(db, txn, keyp, valp, 0); CKERR(r);
99     }
100     return r;
101 }
102 
do_updates(DB_TXN * txn,DB * db)103 static int do_updates(DB_TXN *txn, DB *db) {
104     int r = 0;
105     DBT key, extra;
106     unsigned int i, e;
107     DBT *keyp = dbt_init(&key, &i, sizeof(i));
108     DBT *extrap = dbt_init(&extra, &e, sizeof(e));
109     for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
110         if (to_update[i] == 1) {
111             e = _e(i);  // E I O
112             r = db->update(db, txn, keyp, extrap, 0); CKERR(r);
113         }
114     }
115     return r;
116 }
117 
chk_updated(const unsigned int k,const unsigned int v)118 static void chk_updated(const unsigned int k, const unsigned int v) {
119     if (to_update[k]) {
120         assert(v == _u(_v(k), _e(k)));
121     } else {
122         assert(v == _v(k));
123     }
124 }
125 
chk_original(const unsigned int k,const unsigned int v)126 static void chk_original(const unsigned int k, const unsigned int v) {
127     assert(v == _v(k));
128 }
129 
do_verify_results(DB_TXN * txn,DB * db,void (* check_val)(const unsigned int k,const unsigned int v))130 static int do_verify_results(DB_TXN *txn, DB *db, void (*check_val)(const unsigned int k, const unsigned int v)) {
131     int r = 0;
132     DBT key, val;
133     unsigned int i, *vp;
134     DBT *keyp = dbt_init(&key, &i, sizeof(i));
135     DBT *valp = dbt_init(&val, NULL, 0);
136     for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
137         r = db->get(db, txn, keyp, valp, 0); CKERR(r);
138         assert(val.size == sizeof(*vp));
139         CAST_FROM_VOIDP(vp, val.data);
140         check_val(i, *vp);
141     }
142     return r;
143 }
144 
test_main(int argc,char * const argv[])145 int test_main(int argc, char * const argv[]) {
146     parse_args(argc, argv);
147     setup();
148 
149     DB *db;
150 
151     IN_TXN_COMMIT(env, NULL, txn_1, 0, {
152             { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
153             { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
154 
155             { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
156 
157             IN_TXN_COMMIT(env, txn_1, txn_11, 0, {
158                     { int chk_r = do_verify_results(txn_11, db, chk_original); CKERR(chk_r); }
159                 });
160         });
161 
162     {
163         DB_TXN *txn_2, *txn_3;
164         { int chk_r = env->txn_begin(env, NULL, &txn_2, DB_TXN_SNAPSHOT); CKERR(chk_r); }
165         { int chk_r = do_verify_results(txn_2, db, chk_original); CKERR(chk_r); }
166         { int chk_r = env->txn_begin(env, NULL, &txn_3, 0); CKERR(chk_r); }
167         { int chk_r = do_updates(txn_3, db); CKERR(chk_r); }
168         { int chk_r = do_verify_results(txn_2, db, chk_original); CKERR(chk_r); }
169         { int chk_r = do_verify_results(txn_3, db, chk_updated); CKERR(chk_r); }
170         { int chk_r = txn_2->abort(txn_2); CKERR(chk_r); }
171         { int chk_r = txn_3->abort(txn_3); CKERR(chk_r); }
172     }
173 
174     IN_TXN_COMMIT(env, NULL, txn_4, 0, {
175             { int chk_r = do_verify_results(txn_4, db, chk_original); CKERR(chk_r); }
176         });
177 
178     { int chk_r = db->close(db, 0); CKERR(chk_r); }
179 
180     cleanup();
181 
182     return 0;
183 }
184