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