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 the LE_CURSOR toku_le_cursor_is_key_greater function
40 // - LE_CURSOR at neg infinity
41 // - LE_CURSOR at pos infinity
42 // - LE_CURSOR somewhere else
43
44
45 #include "cachetable/checkpoint.h"
46 #include "le-cursor.h"
47 #include "test.h"
48
49 static TOKUTXN const null_txn = 0;
50
51 static int
get_next_callback(uint32_t keylen,const void * key,uint32_t vallen UU (),const void * val UU (),void * extra,bool lock_only)52 get_next_callback(uint32_t keylen, const void *key, uint32_t vallen UU(), const void *val UU(), void *extra, bool lock_only) {
53 DBT *CAST_FROM_VOIDP(key_dbt, extra);
54 if (!lock_only) {
55 toku_dbt_set(keylen, key, key_dbt, NULL);
56 }
57 return 0;
58 }
59
60 static int
le_cursor_get_next(LE_CURSOR cursor,DBT * val)61 le_cursor_get_next(LE_CURSOR cursor, DBT *val) {
62 int r = toku_le_cursor_next(cursor, get_next_callback, val);
63 return r;
64 }
65
66 static int
test_keycompare(DB * UU (desc),const DBT * a,const DBT * b)67 test_keycompare(DB* UU(desc), const DBT *a, const DBT *b) {
68 return toku_keycompare(a->data, a->size, b->data, b->size);
69 }
70
71 // create a tree and populate it with n rows
72 static void
create_populate_tree(const char * logdir,const char * fname,int n)73 create_populate_tree(const char *logdir, const char *fname, int n) {
74 if (verbose) fprintf(stderr, "%s %s %s %d\n", __FUNCTION__, logdir, fname, n);
75 int error;
76
77 TOKULOGGER logger = NULL;
78 error = toku_logger_create(&logger);
79 assert(error == 0);
80 error = toku_logger_open(logdir, logger);
81 assert(error == 0);
82 CACHETABLE ct = NULL;
83 toku_cachetable_create(&ct, 0, ZERO_LSN, logger);
84 toku_logger_set_cachetable(logger, ct);
85 error = toku_logger_open_rollback(logger, ct, true);
86 assert(error == 0);
87
88 TOKUTXN txn = NULL;
89 error = toku_txn_begin_txn(NULL, NULL, &txn, logger, TXN_SNAPSHOT_NONE, false);
90 assert(error == 0);
91
92 FT_HANDLE ft = NULL;
93 error = toku_open_ft_handle(fname, 1, &ft, 1<<12, 1<<9, TOKU_DEFAULT_COMPRESSION_METHOD, ct, txn, test_keycompare);
94 assert(error == 0);
95
96 error = toku_txn_commit_txn(txn, true, NULL, NULL);
97 assert(error == 0);
98 toku_txn_close_txn(txn);
99
100 txn = NULL;
101 error = toku_txn_begin_txn(NULL, NULL, &txn, logger, TXN_SNAPSHOT_NONE, false);
102 assert(error == 0);
103
104 // insert keys 0, 1, 2, .. (n-1)
105 for (int i = 0; i < n; i++) {
106 int k = toku_htonl(i);
107 int v = i;
108 DBT key;
109 toku_fill_dbt(&key, &k, sizeof k);
110 DBT val;
111 toku_fill_dbt(&val, &v, sizeof v);
112 toku_ft_insert(ft, &key, &val, txn);
113 }
114
115 error = toku_txn_commit_txn(txn, true, NULL, NULL);
116 assert(error == 0);
117 toku_txn_close_txn(txn);
118
119 error = toku_close_ft_handle_nolsn(ft, NULL);
120 assert(error == 0);
121
122 CHECKPOINTER cp = toku_cachetable_get_checkpointer(ct);
123 error = toku_checkpoint(cp, logger, NULL, NULL, NULL, NULL, CLIENT_CHECKPOINT);
124 assert(error == 0);
125 toku_logger_close_rollback(logger);
126 assert(error == 0);
127
128 error = toku_checkpoint(cp, logger, NULL, NULL, NULL, NULL, CLIENT_CHECKPOINT);
129 assert(error == 0);
130
131 toku_logger_shutdown(logger);
132
133 error = toku_logger_close(&logger);
134 assert(error == 0);
135
136 toku_cachetable_close(&ct);
137 }
138
139 // test toku_le_cursor_is_key_greater when the LE_CURSOR is positioned at +infinity
140 static void
test_pos_infinity(const char * fname,int n)141 test_pos_infinity(const char *fname, int n) {
142 if (verbose) fprintf(stderr, "%s %s %d\n", __FUNCTION__, fname, n);
143 int error;
144
145 CACHETABLE ct = NULL;
146 toku_cachetable_create(&ct, 0, ZERO_LSN, nullptr);
147
148 FT_HANDLE ft = NULL;
149 error = toku_open_ft_handle(fname, 1, &ft, 1<<12, 1<<9, TOKU_DEFAULT_COMPRESSION_METHOD, ct, null_txn, test_keycompare);
150 assert(error == 0);
151
152 // position the cursor at -infinity
153 LE_CURSOR cursor = NULL;
154 error = toku_le_cursor_create(&cursor, ft, NULL);
155 assert(error == 0);
156
157 for (int i = 0; i < 2*n; i++) {
158 int k = toku_htonl(i);
159 DBT key;
160 toku_fill_dbt(&key, &k, sizeof k);
161 int right = toku_le_cursor_is_key_greater_or_equal(cursor, &key);
162 assert(right == false);
163 }
164
165 toku_le_cursor_close(cursor);
166
167 error = toku_close_ft_handle_nolsn(ft, 0);
168 assert(error == 0);
169
170 toku_cachetable_close(&ct);
171 }
172
173 // test toku_le_cursor_is_key_greater when the LE_CURSOR is positioned at -infinity
174 static void
test_neg_infinity(const char * fname,int n)175 test_neg_infinity(const char *fname, int n) {
176 if (verbose) fprintf(stderr, "%s %s %d\n", __FUNCTION__, fname, n);
177 int error;
178
179 CACHETABLE ct = NULL;
180 toku_cachetable_create(&ct, 0, ZERO_LSN, nullptr);
181
182 FT_HANDLE ft = NULL;
183 error = toku_open_ft_handle(fname, 1, &ft, 1<<12, 1<<9, TOKU_DEFAULT_COMPRESSION_METHOD, ct, null_txn, test_keycompare);
184 assert(error == 0);
185
186 // position the LE_CURSOR at +infinity
187 LE_CURSOR cursor = NULL;
188 error = toku_le_cursor_create(&cursor, ft, NULL);
189 assert(error == 0);
190
191 DBT key;
192 toku_init_dbt(&key); key.flags = DB_DBT_REALLOC;
193 DBT val;
194 toku_init_dbt(&val); val.flags = DB_DBT_REALLOC;
195
196 int i;
197 for (i = n-1; ; i--) {
198 error = le_cursor_get_next(cursor, &key);
199 if (error != 0)
200 break;
201
202 assert(key.size == sizeof (int));
203 int ii = *(int *)key.data;
204 assert((int) toku_htonl(i) == ii);
205 }
206 assert(i == -1);
207
208 toku_destroy_dbt(&key);
209 toku_destroy_dbt(&val);
210
211 for (i = 0; i < 2*n; i++) {
212 int k = toku_htonl(i);
213 DBT key2;
214 toku_fill_dbt(&key2, &k, sizeof k);
215 int right = toku_le_cursor_is_key_greater_or_equal(cursor, &key2);
216 assert(right == true);
217 }
218
219 toku_le_cursor_close(cursor);
220
221 error = toku_close_ft_handle_nolsn(ft, 0);
222 assert(error == 0);
223
224 toku_cachetable_close(&ct);
225 }
226
227 // test toku_le_cursor_is_key_greater when the LE_CURSOR is positioned in between -infinity and +infinity
228 static void
test_between(const char * fname,int n)229 test_between(const char *fname, int n) {
230 if (verbose) fprintf(stderr, "%s %s %d\n", __FUNCTION__, fname, n);
231 int error;
232
233 CACHETABLE ct = NULL;
234 toku_cachetable_create(&ct, 0, ZERO_LSN, nullptr);
235
236 FT_HANDLE ft = NULL;
237 error = toku_open_ft_handle(fname, 1, &ft, 1<<12, 1<<9, TOKU_DEFAULT_COMPRESSION_METHOD, ct, null_txn, test_keycompare);
238 assert(error == 0);
239
240 // position the LE_CURSOR at +infinity
241 LE_CURSOR cursor = NULL;
242 error = toku_le_cursor_create(&cursor, ft, NULL);
243 assert(error == 0);
244
245 DBT key;
246 toku_init_dbt(&key); key.flags = DB_DBT_REALLOC;
247 DBT val;
248 toku_init_dbt(&val); val.flags = DB_DBT_REALLOC;
249
250 int i;
251 for (i = 0; ; i++) {
252 // move the LE_CURSOR forward
253 error = le_cursor_get_next(cursor, &key);
254 if (error != 0)
255 break;
256
257 assert(key.size == sizeof (int));
258 int ii = *(int *)key.data;
259 assert((int) toku_htonl(n-i-1) == ii);
260
261 // test 0 .. i-1
262 for (int j = 0; j <= i; j++) {
263 int k = toku_htonl(n-j-1);
264 DBT key2;
265 toku_fill_dbt(&key2, &k, sizeof k);
266 int right = toku_le_cursor_is_key_greater_or_equal(cursor, &key2);
267 assert(right == true);
268 }
269
270 // test i .. n
271 for (int j = i+1; j < n; j++) {
272 int k = toku_htonl(n-j-1);
273 DBT key2;
274 toku_fill_dbt(&key2, &k, sizeof k);
275 int right = toku_le_cursor_is_key_greater_or_equal(cursor, &key2);
276 assert(right == false);
277 }
278
279 }
280 assert(i == n);
281
282 toku_destroy_dbt(&key);
283 toku_destroy_dbt(&val);
284
285 toku_le_cursor_close(cursor);
286
287 error = toku_close_ft_handle_nolsn(ft, 0);
288 assert(error == 0);
289
290 toku_cachetable_close(&ct);
291 }
292
293 static void
init_logdir(const char * logdir)294 init_logdir(const char *logdir) {
295 int error;
296
297 toku_os_recursive_delete(logdir);
298 error = toku_os_mkdir(logdir, 0777);
299 assert(error == 0);
300 }
301
302 int
test_main(int argc,const char * argv[])303 test_main (int argc , const char *argv[]) {
304 default_parse_args(argc, argv);
305 toku_os_recursive_delete(TOKU_TEST_FILENAME);
306 int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU);
307 assert_zero(r);
308
309 char logdir[TOKU_PATH_MAX+1];
310 toku_path_join(logdir, 2, TOKU_TEST_FILENAME, "logdir");
311 init_logdir(logdir);
312 int error = chdir(logdir);
313 assert(error == 0);
314
315 const int n = 10;
316 create_populate_tree(".", "ftfile", n);
317 test_pos_infinity("ftfile", n);
318 test_neg_infinity("ftfile", n);
319 test_between("ftfile", n);
320
321 return 0;
322 }
323