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 TokuDB
6 
7 
8 Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9 
10     TokuDBis 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     TokuDB 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 TokuDB.  If not, see <http://www.gnu.org/licenses/>.
21 
22 ======= */
23 
24 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
25 
26 #ifndef _TOKUDB_STATUS_H
27 #define _TOKUDB_STATUS_H
28 
29 // These are keys that will be used for retrieving metadata in status.tokudb
30 // To get the version, one looks up the value associated with key hatoku_version
31 // in status.tokudb
32 typedef ulonglong HA_METADATA_KEY;
33 #define hatoku_old_version 0
34 #define hatoku_capabilities 1
35 #define hatoku_max_ai 2 //maximum auto increment value found so far
uint_mask(uint length_bits)36 #define hatoku_ai_create_value 3
37 #define hatoku_key_name 4
38 #define hatoku_frm_data 5
39 #define hatoku_new_version 6
40 #define hatoku_cardinality 7
41 
42 // use a very small pagesize for the status dictionary
43 #define status_dict_pagesize 1024
44 
45 namespace tokudb {
46 namespace metadata {
47 
48 // get the value for a given key in the status dictionary.
49 // copy the value to the supplied buffer.
50 // returns 0 if successful.
51 int read(
52     DB* status_db,
53     DB_TXN* txn,
54     HA_METADATA_KEY k,
55     void* p,
56     size_t s,
57     size_t* sp) {
58 
59     DBT key = {};
60     key.data = &k;
61     key.size = sizeof(k);
62     DBT val = {};
63     val.data = p;
64     val.ulen = (uint32_t)s;
65     val.flags = DB_DBT_USERMEM;
66     int error = status_db->get(status_db, txn, &key, &val, 0);
67     if (error == 0) {
68         *sp = val.size;
69     }
70     return error;
71 }
72 
73 // get the value for a given key in the status dictionary.
74 // put the value in a realloced buffer.
75 // returns 0 if successful.
76 int read_realloc(
77     DB* status_db,
78     DB_TXN* txn,
79     HA_METADATA_KEY k,
80     void** pp,
81     size_t* sp) {
82 
83     DBT key = {};
84     key.data = &k;
85     key.size = sizeof(k);
86     DBT val = {};
87     val.data = *pp;
88     val.size = (uint32_t)*sp;
89     val.flags = DB_DBT_REALLOC;
90     int error = status_db->get(status_db, txn, &key, &val, 0);
91     if (error == 0) {
92         *pp = val.data;
93         *sp = val.size;
94     }
95     return error;
96 }
97 
98 // write a key value pair into the status dictionary,
99 // overwriting the previous value if any.
100 // auto create a txn if necessary.
101 // returns 0 if successful.
102 int write_low(
103     DB* status_db,
104     void* key_data,
105     uint key_size,
106     void* val_data,
107     uint val_size,
108     DB_TXN *txn) {
109 
110     DBT key = {};
111     key.data = key_data;
112     key.size = key_size;
113     DBT value = {};
114     value.data = val_data;
115     value.size = val_size;
116     int error = status_db->put(status_db, txn, &key, &value, 0);
117     return error;
118 }
119 
120 // write a key value pair into the status dictionary,
121 // overwriting the previous value if any.
122 // the key must be a HA_METADATA_KEY.
123 // returns 0 if successful.
124 int write(
125     DB* status_db,
126     HA_METADATA_KEY curr_key_data,
127     void* val,
128     size_t val_size,
129     DB_TXN* txn) {
130 
131     return
132         tokudb::metadata::write_low(
133             status_db,
134             &curr_key_data,
135             sizeof(curr_key_data),
136             val,
137             val_size,
138             txn);
139 }
140 
141 // remove a key from the status dictionary.
142 // auto create a txn if necessary.
143 // returns 0 if successful.
144 int remove_low(
145     DB* status_db,
146     void* key_data,
147     uint key_size,
148     DB_TXN* txn) {
149 
150     DBT key = {};
151     key.data = key_data;
152     key.size = key_size;
153     int error = status_db->del(status_db, txn, &key, DB_DELETE_ANY);
154     return error;
155 }
156 
157 // remove a key from the status dictionary.
158 // the key must be a HA_METADATA_KEY
159 // returns 0 if successful.
160 int remove(
161     DB* status_db,
162     HA_METADATA_KEY curr_key_data,
163     DB_TXN* txn) {
164     return
165         tokudb::metadata::remove_low(
166             status_db,
167             &curr_key_data,
168             sizeof(curr_key_data),
169             txn);
170 }
171 
172 int close(DB** status_db_ptr) {
173     int error = 0;
174     DB* status_db = *status_db_ptr;
175     if (status_db) {
176         error = status_db->close(status_db, 0);
177         if (error == 0)
178             *status_db_ptr = NULL;
179     }
180     return error;
181 }
182 
183 int create(
184     DB_ENV* env,
185     DB** status_db_ptr,
186     const char* name,
187     DB_TXN* txn) {
188 
189     int error;
190     DB *status_db = NULL;
191 
192     error = db_create(&status_db, env, 0);
193     if (error == 0) {
194         error = status_db->set_pagesize(status_db, status_dict_pagesize);
195     }
196     if (error == 0) {
197         error =
198             status_db->open(
199                 status_db,
200                 txn,
201                 name,
202                 NULL,
203                 DB_BTREE, DB_CREATE | DB_EXCL,
204                 S_IWUSR);
205     }
206     if (error == 0) {
207         *status_db_ptr = status_db;
208     } else {
209         int r = tokudb::metadata::close(&status_db);
210         assert_always(r == 0);
211     }
212     return error;
213 }
214 
215 int open(
216     DB_ENV* env,
217     DB** status_db_ptr,
218     const char* name,
219     DB_TXN* txn) {
220 
221     int error = 0;
222     DB* status_db = NULL;
223     error = db_create(&status_db, env, 0);
224     if (error == 0) {
225         error =
226             status_db->open(
227                 status_db,
228                 txn,
229                 name,
230                 NULL,
231                 DB_BTREE,
232                 DB_THREAD,
233                 S_IWUSR);
234     }
235     if (error == 0) {
236         uint32_t pagesize = 0;
237         error = status_db->get_pagesize(status_db, &pagesize);
238         if (error == 0 && pagesize > status_dict_pagesize) {
239             error =
240                 status_db->change_pagesize(status_db, status_dict_pagesize);
241         }
242     }
243     if (error == 0) {
244         *status_db_ptr = status_db;
245     } else {
246         int r = tokudb::metadata::close(&status_db);
247         assert_always(r == 0);
248     }
249     return error;
250 }
251 
252 int strip_frm_data(DB_ENV* env) {
253     int r;
254     DB_TXN* txn = NULL;
255 
256     fprintf(stderr, "TokuDB strip_frm_data : Beginning stripping process.\n");
257 
258     r = db_env->txn_begin(env, NULL, &txn, 0);
259     assert_always(r == 0);
260 
261     DBC* c = NULL;
262     r = env->get_cursor_for_directory(env, txn, &c);
263     assert_always(r == 0);
264 
265     DBT key = { };
266     key.flags = DB_DBT_REALLOC;
267 
268     DBT val = { };
269     val.flags = DB_DBT_REALLOC;
270     while (1) {
271         r = c->c_get(c, &key, &val, DB_NEXT);
272         if (r == DB_NOTFOUND)
273             break;
274         const char* dname = (const char*) key.data;
275         const char* iname = (const char*) val.data;
276         assert_always(r == 0);
277 
278         if (strstr(iname, "_status_")) {
279             fprintf(
280                 stderr,
281                 "TokuDB strip_frm_data : stripping from dname=%s iname=%s\n",
282                 dname,
283                 iname);
284 
285             DB* status_db;
286             r = tokudb::metadata::open(db_env, &status_db, dname, txn);
287             if (r != 0) {
288                 fprintf(
289                     stderr,
290                     "TokuDB strip_frm_data : unable to open status file %s, "
291                     "error = %d\n",
292                     dname,
293                     r);
294                 continue;
295             }
296 
297             // GOL : this is a godawful hack. The inventors of this did not
298             // think it would be a good idea to use some kind of magic
299             // identifier k/v pair so that you can in fact tell a proper status
300             // file from any other file that might have the string _status_ in
301             // it. Out in ha_tokudb::create, when the status file is initially
302             // created, it is immediately populated with:
303             //    uint hatoku_new_version=HA_TOKU_VERSION=4 and
304             //    uint hatoku_capabilities=HA_TOKU_CAP=0
305             // Since I can't count on the fact that these values are/were
306             // _always_ 4 and 0, I can count on the fact that they _must_ be
307             // there and the _must_ be sizeof(uint). That will at least give us
308             // a much better idea that these are in fact status files.
309             void* p = NULL;
310             size_t sz;
311             r =
312                 tokudb::metadata::read_realloc(
313                     status_db,
314                     txn,
315                     hatoku_new_version,
316                     &p,
317                     &sz);
318             if (r != 0) {
319                 fprintf(
320                     stderr,
321                     "TokuDB strip_frm_data : does not look like a real TokuDB "
322                     "status file, new_verion is missing, leaving alone %s \n",
323                     dname);
324 
325                 r = tokudb::metadata::close(&status_db);
326                 assert_always(r == 0);
327                 continue;
328             } else if (sz != sizeof(uint)) {
329                 fprintf(
330                     stderr,
331                     "TokuDB strip_frm_data : does not look like a real TokuDB "
332                     "status file, new_verion is the wrong size, "
333                     "leaving alone %s \n",
334                     dname);
335 
336                 tokudb::memory::free(p);
337                 r = tokudb::metadata::close(&status_db);
338                 assert_always(r == 0);
339                 continue;
340             }
341             tokudb::memory::free(p);
342             p = NULL;
343 
344             r =
345                 tokudb::metadata::read_realloc(
346                     status_db,
347                     txn,
348                     hatoku_capabilities,
349                     &p,
350                     &sz);
351             if (r != 0) {
352                 fprintf(
353                     stderr,
354                     "TokuDB strip_frm_data : does not look like a real TokuDB "
355                     "status file, capabilities is missing, leaving alone %s \n",
356                     dname);
357 
358                 r = tokudb::metadata::close(&status_db);
359                 assert_always(r == 0);
360                 continue;
361             } else if (sz != sizeof(uint)) {
362                 fprintf(
363                     stderr,
364                     "TokuDB strip_frm_data : does not look like a real TokuDB "
365                     "status file, capabilities is the wrong size, "
366                     "leaving alone %s \n",
367                     dname);
368 
369                 tokudb::memory::free(p);
370                 r = tokudb::metadata::close(&status_db);
371                 assert_always(r == 0);
372                 continue;
373             }
374             tokudb::memory::free(p);
375 
376             // OK, st this point, it is probably a status file, not 100% but
377             // it looks like it :(
378             r = tokudb::metadata::remove(status_db, hatoku_frm_data, txn);
379             if (r != 0) {
380                 fprintf(
381                     stderr,
382                     "TokuDB strip_frm_data : unable to find/strip frm data "
383                     "from status file %s, error = %d\n",
384                     dname,
385                     r);
386             }
387 
388             r = tokudb::metadata::close(&status_db);
389             assert_always(r == 0);
390         }
391     }
392     tokudb::memory::free(key.data);
393     tokudb::memory::free(val.data);
394 
395     fprintf(
396         stderr,
397         "TokuDB strip_frm_data : Stripping process complete, beginning "
398         "commit, this may take some time.\n");
399 
400     r = c->c_close(c);
401     assert_always(r == 0);
402 
403     r = txn->commit(txn, 0);
404     assert_always(r == 0);
405 
406     fprintf(
407         stderr,
408         "TokuDB strip_frm_data : Commit complete, resuming server init "
409         "process.");
410 
411     return 0;
412 }
413 
414 } // namespace metadata
415 } // namespace tokudb
416 #endif
417