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 next function with provisionally deleted rows
40 
41 
42 #include "cachetable/checkpoint.h"
43 #include "le-cursor.h"
44 #include "test.h"
45 
46 
47 static int
get_next_callback(uint32_t keylen,const void * key,uint32_t vallen UU (),const void * val UU (),void * extra,bool lock_only)48 get_next_callback(uint32_t keylen, const void *key, uint32_t vallen UU(), const void *val UU(), void *extra, bool lock_only) {
49     DBT *CAST_FROM_VOIDP(key_dbt, extra);
50     if (!lock_only) {
51         toku_dbt_set(keylen, key, key_dbt, NULL);
52     }
53     return 0;
54 }
55 
56 static int
le_cursor_get_next(LE_CURSOR cursor,DBT * key)57 le_cursor_get_next(LE_CURSOR cursor, DBT *key) {
58     int r = toku_le_cursor_next(cursor, get_next_callback, key);
59     return r;
60 }
61 
test_ft_cursor_keycompare(DB * desc,const DBT * a,const DBT * b)62 static int test_ft_cursor_keycompare(DB *desc __attribute__((unused)), const DBT *a, const DBT *b) {
63     return toku_keycompare(a->data, a->size, b->data, b->size);
64 }
65 
66 // create a tree and populate it with n rows
67 static void
create_populate_tree(const char * logdir,const char * fname,int n)68 create_populate_tree(const char *logdir, const char *fname, int n) {
69     if (verbose) fprintf(stderr, "%s %s %s %d\n", __FUNCTION__, logdir, fname, n);
70     int error;
71 
72     TOKULOGGER logger = NULL;
73     error = toku_logger_create(&logger);
74     assert(error == 0);
75     error = toku_logger_open(logdir, logger);
76     assert(error == 0);
77     CACHETABLE ct = NULL;
78     toku_cachetable_create(&ct, 0, ZERO_LSN, logger);
79     toku_logger_set_cachetable(logger, ct);
80     error = toku_logger_open_rollback(logger, ct, true);
81     assert(error == 0);
82 
83     TOKUTXN txn = NULL;
84     error = toku_txn_begin_txn(NULL, NULL, &txn, logger, TXN_SNAPSHOT_NONE, false);
85     assert(error == 0);
86 
87     FT_HANDLE ft = NULL;
88     error = toku_open_ft_handle(fname, 1, &ft, 1<<12, 1<<9, TOKU_DEFAULT_COMPRESSION_METHOD, ct, txn, test_ft_cursor_keycompare);
89     assert(error == 0);
90 
91     error = toku_txn_commit_txn(txn, true, NULL, NULL);
92     assert(error == 0);
93     toku_txn_close_txn(txn);
94 
95     txn = NULL;
96     error = toku_txn_begin_txn(NULL, NULL, &txn, logger, TXN_SNAPSHOT_NONE, false);
97     assert(error == 0);
98 
99     // insert keys 0, 1, 2, .. (n-1)
100     for (int i = 0; i < n; i++) {
101         int k = toku_htonl(i);
102         int v = i;
103         DBT key;
104         toku_fill_dbt(&key, &k, sizeof k);
105         DBT val;
106         toku_fill_dbt(&val, &v, sizeof v);
107         toku_ft_insert(ft, &key, &val, txn);
108         assert(error == 0);
109     }
110 
111     error = toku_txn_commit_txn(txn, true, NULL, NULL);
112     assert(error == 0);
113     toku_txn_close_txn(txn);
114 
115     error = toku_close_ft_handle_nolsn(ft, NULL);
116     assert(error == 0);
117 
118     CHECKPOINTER cp = toku_cachetable_get_checkpointer(ct);
119     error = toku_checkpoint(cp, logger, NULL, NULL, NULL, NULL, CLIENT_CHECKPOINT);
120     assert(error == 0);
121 
122     toku_logger_close_rollback(logger);
123 
124     error = toku_checkpoint(cp, logger, NULL, NULL, NULL, NULL, CLIENT_CHECKPOINT);
125     assert(error == 0);
126 
127     toku_logger_shutdown(logger);
128 
129     error = toku_logger_close(&logger);
130     assert(error == 0);
131 
132     toku_cachetable_close(&ct);
133 }
134 
135 // provionally delete all of the even keys
136 // the LE_CURSOR should see all of the leaf entries
137 static void
test_provdel(const char * logdir,const char * fname,int n)138 test_provdel(const char *logdir, const char *fname, int n) {
139     if (verbose) fprintf(stderr, "%s %s %s %d\n", __FUNCTION__, logdir, fname, n);
140     int error;
141 
142     TOKULOGGER logger = NULL;
143     error = toku_logger_create(&logger);
144     assert(error == 0);
145     error = toku_logger_open(logdir, logger);
146     assert(error == 0);
147     CACHETABLE ct = NULL;
148     toku_cachetable_create(&ct, 0, ZERO_LSN, logger);
149     toku_logger_set_cachetable(logger, ct);
150     error = toku_logger_open_rollback(logger, ct, false);
151     assert(error == 0);
152 
153     TOKUTXN txn = NULL;
154     error = toku_txn_begin_txn(NULL, NULL, &txn, logger, TXN_SNAPSHOT_NONE, false);
155     assert(error == 0);
156 
157     FT_HANDLE ft = NULL;
158     error = toku_open_ft_handle(fname, 1, &ft, 1<<12, 1<<9, TOKU_DEFAULT_COMPRESSION_METHOD, ct, txn, test_ft_cursor_keycompare);
159     assert(error == 0);
160 
161     error = toku_txn_commit_txn(txn, true, NULL, NULL);
162     assert(error == 0);
163     toku_txn_close_txn(txn);
164 
165     txn = NULL;
166     error = toku_txn_begin_txn(NULL, NULL, &txn, logger, TXN_SNAPSHOT_NONE, false);
167     assert(error == 0);
168 
169     // del keys 0, 2, 4, ...
170     for (int i = 0; i < n; i += 2) {
171         int k = toku_htonl(i);
172         DBT key;
173         toku_fill_dbt(&key, &k, sizeof k);
174         toku_ft_delete(ft, &key, txn);
175         assert(error == 0);
176     }
177 
178     TOKUTXN cursortxn = NULL;
179     error = toku_txn_begin_txn(NULL, NULL, &cursortxn, logger, TXN_SNAPSHOT_NONE, false);
180     assert(error == 0);
181 
182     LE_CURSOR cursor = NULL;
183     error = toku_le_cursor_create(&cursor, ft, cursortxn);
184     assert(error == 0);
185 
186     DBT key;
187     toku_init_dbt(&key); key.flags = DB_DBT_REALLOC;
188     DBT val;
189     toku_init_dbt(&val); val.flags = DB_DBT_REALLOC;
190 
191     int i;
192     for (i=0; ; i++) {
193         error = le_cursor_get_next(cursor, &key);
194         if (error != 0)
195             break;
196 
197         assert(key.size == sizeof (int));
198         int ii = *(int *)key.data;
199         assert((int) toku_htonl(n-i-1) == ii);
200     }
201     assert(i == n);
202 
203     toku_destroy_dbt(&key);
204     toku_destroy_dbt(&val);
205 
206     toku_le_cursor_close(cursor);
207 
208     error = toku_txn_commit_txn(cursortxn, true, NULL, NULL);
209     assert(error == 0);
210     toku_txn_close_txn(cursortxn);
211 
212     error = toku_txn_commit_txn(txn, true, NULL, NULL);
213     assert(error == 0);
214     toku_txn_close_txn(txn);
215 
216     error = toku_close_ft_handle_nolsn(ft, NULL);
217     assert(error == 0);
218     CHECKPOINTER cp = toku_cachetable_get_checkpointer(ct);
219     error = toku_checkpoint(cp, logger, NULL, NULL, NULL, NULL, CLIENT_CHECKPOINT);
220     assert(error == 0);
221 
222     toku_logger_close_rollback(logger);
223     error = toku_logger_close(&logger);
224     assert(error == 0);
225 
226     toku_cachetable_close(&ct);
227 }
228 
229 static void
init_logdir(const char * logdir)230 init_logdir(const char *logdir) {
231     int error;
232 
233     toku_os_recursive_delete(logdir);
234     error = toku_os_mkdir(logdir, 0777);
235     assert(error == 0);
236 }
237 
238 int
test_main(int argc,const char * argv[])239 test_main (int argc , const char *argv[]) {
240     default_parse_args(argc, argv);
241     toku_os_recursive_delete(TOKU_TEST_FILENAME);
242     int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU);
243     assert_zero(r);
244 
245     char logdir[TOKU_PATH_MAX+1];
246     toku_path_join(logdir, 2, TOKU_TEST_FILENAME, "logdir");
247     init_logdir(logdir);
248     int error = chdir(logdir);
249     assert(error == 0);
250 
251     const int n = 10;
252     create_populate_tree(".", "ftfile", n);
253     test_provdel(".", "ftfile", n);
254 
255     return 0;
256 }
257