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 \
38     "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
39 
40 #include "test.h"
41 // to verify the DB_LOCKING_READ works to lock the read rows for snapshot
42 // isolaton.
43 // we create a db, then init a read transaction with repeatable-read isolation
44 // and
45 // locking read flag, then we start another transaction to grab the write lock.
46 // DB_LOCKING_READ is defined here to just make the before and after tests work
47 // (before
48 // test did not have DB_LOCKING_READ flag).
49 #if !defined(DB_LOCKING_READ)
50 #define DB_LOCKING_READ 0
51 #endif
prelock_range(DBC * cursor,int left,int right)52 static int prelock_range(DBC *cursor, int left, int right) {
53     DBT key_left;
54     dbt_init(&key_left, &left, sizeof left);
55     DBT key_right;
56     dbt_init(&key_right, &right, sizeof right);
57     int r = cursor->c_set_bounds(cursor, &key_left, &key_right, true, 0);
58     return r;
59 }
60 
test_read_write_range(DB_ENV * env,DB * db,uint32_t iso_flags,int expect_r)61 static void test_read_write_range(DB_ENV *env,
62                                   DB *db,
63                                   uint32_t iso_flags,
64                                   int expect_r) {
65     int r;
66 
67     DB_TXN *txn_a = NULL;
68     r = env->txn_begin(env, NULL, &txn_a, iso_flags);
69     assert_zero(r);
70     DB_TXN *txn_b = NULL;
71     r = env->txn_begin(env, NULL, &txn_b, iso_flags);
72     assert_zero(r);
73 
74     DBC *cursor_a = NULL;
75     r = db->cursor(db, txn_a, &cursor_a, DB_LOCKING_READ);
76     assert_zero(r);
77     DBC *cursor_b = NULL;
78     r = db->cursor(db, txn_b, &cursor_b, DB_RMW);
79     assert_zero(r);
80 
81     r = prelock_range(cursor_a, htonl(10), htonl(100));
82     assert_zero(r);
83     r = prelock_range(cursor_b, htonl(50), htonl(200));
84     assert(r == expect_r);
85 
86     r = cursor_a->c_close(cursor_a);
87     assert_zero(r);
88     r = cursor_b->c_close(cursor_b);
89     assert_zero(r);
90 
91     r = txn_a->commit(txn_a, 0);
92     assert_zero(r);
93     r = txn_b->commit(txn_b, 0);
94     assert_zero(r);
95 }
96 
test_read_write_point(DB_ENV * env,DB * db,uint32_t iso_flags,int expect_r)97 static void test_read_write_point(DB_ENV *env,
98                                   DB *db,
99                                   uint32_t iso_flags,
100                                   int expect_r) {
101     int r;
102 
103     DB_TXN *txn1 = NULL;
104     r = env->txn_begin(env, NULL, &txn1, iso_flags);
105     assert_zero(r);
106 
107     DB_TXN *txn2 = NULL;
108     r = env->txn_begin(env, NULL, &txn2, iso_flags);
109     assert_zero(r);
110 
111     DBC *c1 = NULL;
112     r = db->cursor(db, txn1, &c1, DB_LOCKING_READ);
113     assert_zero(r);
114 
115     DBC *c2 = NULL;
116     r = db->cursor(db, txn2, &c2, DB_RMW);
117     assert_zero(r);
118 
119     int k = htonl(42);
120     DBT key;
121     dbt_init(&key, &k, sizeof k);
122     DBT val;
123     memset(&val, 0, sizeof val);
124     r = c1->c_get(c1, &key, &val, DB_SET);
125     assert_zero(r);
126 
127     r = c2->c_get(c2, &key, &val, DB_SET);
128     assert(r == expect_r);
129 
130     r = c1->c_close(c1);
131     assert_zero(r);
132     r = c2->c_close(c2);
133     assert_zero(r);
134 
135     r = txn1->commit(txn1, 0);
136     assert_zero(r);
137     r = txn2->commit(txn2, 0);
138     assert_zero(r);
139 }
140 
test_main(int argc,char * const argv[])141 int test_main(int argc, char *const argv[]) {
142     int r;
143 
144     const char *env_dir = TOKU_TEST_FILENAME;
145     const char *db_filename = "lockingreadtest";
146 
147     parse_args(argc, argv);
148 
149     char rm_cmd[strlen(env_dir) + strlen("rm -rf ") + 1];
150     snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", env_dir);
151     r = system(rm_cmd);
152     assert_zero(r);
153 
154     r = toku_os_mkdir(env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
155     assert_zero(r);
156 
157     DB_ENV *env = NULL;
158     r = db_env_create(&env, 0);
159     assert_zero(r);
160     int env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN |
161                          DB_INIT_LOCK | DB_INIT_LOG;
162     r = env->open(
163         env, env_dir, env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
164     assert_zero(r);
165 
166     // create the db
167     DB *db = NULL;
168     r = db_create(&db, env, 0);
169     assert_zero(r);
170     DB_TXN *create_txn = NULL;
171     r = env->txn_begin(env, NULL, &create_txn, 0);
172     assert_zero(r);
173     r = db->open(db,
174                  create_txn,
175                  db_filename,
176                  NULL,
177                  DB_BTREE,
178                  DB_CREATE,
179                  S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
180     assert_zero(r);
181     r = create_txn->commit(create_txn, 0);
182     assert_zero(r);
183 
184     // add a record
185 
186     DB_TXN *write_txn = NULL;
187     r = env->txn_begin(env, NULL, &write_txn, 0);
188     assert_zero(r);
189 
190     int k = htonl(42);
191     int v = 42;
192     DBT key;
193     dbt_init(&key, &k, sizeof k);
194     DBT val;
195     dbt_init(&val, &v, sizeof v);
196     r = db->put(db, write_txn, &key, &val, DB_NOOVERWRITE);
197     assert_zero(r);
198     r = write_txn->commit(write_txn, 0);
199     assert_zero(r);
200 
201     test_read_write_range(env, db, DB_TXN_SNAPSHOT, DB_LOCK_NOTGRANTED);
202     test_read_write_point(env, db, DB_TXN_SNAPSHOT, DB_LOCK_NOTGRANTED);
203 
204     r = db->close(db, 0);
205     assert_zero(r);
206 
207     r = env->close(env, 0);
208     assert_zero(r);
209     return 0;
210 }
211