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
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.
read(DB * status_db,DB_TXN * txn,HA_METADATA_KEY k,void * p,size_t s,size_t * sp)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.
read_realloc(DB * status_db,DB_TXN * txn,HA_METADATA_KEY k,void ** pp,size_t * sp)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.
write_low(DB * status_db,void * key_data,uint key_size,void * val_data,uint val_size,DB_TXN * txn)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.
write(DB * status_db,HA_METADATA_KEY curr_key_data,void * val,size_t val_size,DB_TXN * txn)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.
remove_low(DB * status_db,void * key_data,uint key_size,DB_TXN * txn)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.
remove(DB * status_db,HA_METADATA_KEY curr_key_data,DB_TXN * txn)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 
close(DB ** status_db_ptr)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 
create(DB_ENV * env,DB ** status_db_ptr,const char * name,DB_TXN * txn)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 extern "C" {
216   extern uint         force_recovery;
217 }
218 
219 
open(DB_ENV * env,DB ** status_db_ptr,const char * name,DB_TXN * txn)220 int open(
221     DB_ENV* env,
222     DB** status_db_ptr,
223     const char* name,
224     DB_TXN* txn) {
225 
226     int error = 0;
227     DB* status_db = NULL;
228     error = db_create(&status_db, env, 0);
229     if (error == 0) {
230         error =
231             status_db->open(
232                 status_db,
233                 txn,
234                 name,
235                 NULL,
236                 DB_BTREE,
237                 DB_THREAD,
238                 force_recovery ? 0 : S_IWUSR);
239     }
240     if (error == 0) {
241         uint32_t pagesize = 0;
242         error = status_db->get_pagesize(status_db, &pagesize);
243         if (error == 0 && pagesize > status_dict_pagesize) {
244             error =
245                 status_db->change_pagesize(status_db, status_dict_pagesize);
246         }
247     }
248     if (error == 0) {
249         *status_db_ptr = status_db;
250     } else {
251         int r = tokudb::metadata::close(&status_db);
252         assert_always(r == 0);
253     }
254     return error;
255 }
256 
strip_frm_data(DB_ENV * env)257 int strip_frm_data(DB_ENV* env) {
258     int r;
259     DB_TXN* txn = NULL;
260 
261     fprintf(stderr, "TokuDB strip_frm_data : Beginning stripping process.\n");
262 
263     r = db_env->txn_begin(env, NULL, &txn, 0);
264     assert_always(r == 0);
265 
266     DBC* c = NULL;
267     r = env->get_cursor_for_directory(env, txn, &c);
268     assert_always(r == 0);
269 
270     DBT key = { };
271     key.flags = DB_DBT_REALLOC;
272 
273     DBT val = { };
274     val.flags = DB_DBT_REALLOC;
275     while (1) {
276         r = c->c_get(c, &key, &val, DB_NEXT);
277         if (r == DB_NOTFOUND)
278             break;
279         const char* dname = (const char*) key.data;
280         const char* iname = (const char*) val.data;
281         assert_always(r == 0);
282 
283         if (strstr(iname, "_status_")) {
284             fprintf(
285                 stderr,
286                 "TokuDB strip_frm_data : stripping from dname=%s iname=%s\n",
287                 dname,
288                 iname);
289 
290             DB* status_db;
291             r = tokudb::metadata::open(db_env, &status_db, dname, txn);
292             if (r != 0) {
293                 fprintf(
294                     stderr,
295                     "TokuDB strip_frm_data : unable to open status file %s, "
296                     "error = %d\n",
297                     dname,
298                     r);
299                 continue;
300             }
301 
302             // GOL : this is a godawful hack. The inventors of this did not
303             // think it would be a good idea to use some kind of magic
304             // identifier k/v pair so that you can in fact tell a proper status
305             // file from any other file that might have the string _status_ in
306             // it. Out in ha_tokudb::create, when the status file is initially
307             // created, it is immediately populated with:
308             //    uint hatoku_new_version=HA_TOKU_VERSION=4 and
309             //    uint hatoku_capabilities=HA_TOKU_CAP=0
310             // Since I can't count on the fact that these values are/were
311             // _always_ 4 and 0, I can count on the fact that they _must_ be
312             // there and the _must_ be sizeof(uint). That will at least give us
313             // a much better idea that these are in fact status files.
314             void* p = NULL;
315             size_t sz;
316             r =
317                 tokudb::metadata::read_realloc(
318                     status_db,
319                     txn,
320                     hatoku_new_version,
321                     &p,
322                     &sz);
323             if (r != 0) {
324                 fprintf(
325                     stderr,
326                     "TokuDB strip_frm_data : does not look like a real TokuDB "
327                     "status file, new_verion is missing, leaving alone %s \n",
328                     dname);
329 
330                 r = tokudb::metadata::close(&status_db);
331                 assert_always(r == 0);
332                 continue;
333             } else if (sz != sizeof(uint)) {
334                 fprintf(
335                     stderr,
336                     "TokuDB strip_frm_data : does not look like a real TokuDB "
337                     "status file, new_verion is the wrong size, "
338                     "leaving alone %s \n",
339                     dname);
340 
341                 tokudb::memory::free(p);
342                 r = tokudb::metadata::close(&status_db);
343                 assert_always(r == 0);
344                 continue;
345             }
346             tokudb::memory::free(p);
347             p = NULL;
348 
349             r =
350                 tokudb::metadata::read_realloc(
351                     status_db,
352                     txn,
353                     hatoku_capabilities,
354                     &p,
355                     &sz);
356             if (r != 0) {
357                 fprintf(
358                     stderr,
359                     "TokuDB strip_frm_data : does not look like a real TokuDB "
360                     "status file, capabilities is missing, leaving alone %s \n",
361                     dname);
362 
363                 r = tokudb::metadata::close(&status_db);
364                 assert_always(r == 0);
365                 continue;
366             } else if (sz != sizeof(uint)) {
367                 fprintf(
368                     stderr,
369                     "TokuDB strip_frm_data : does not look like a real TokuDB "
370                     "status file, capabilities is the wrong size, "
371                     "leaving alone %s \n",
372                     dname);
373 
374                 tokudb::memory::free(p);
375                 r = tokudb::metadata::close(&status_db);
376                 assert_always(r == 0);
377                 continue;
378             }
379             tokudb::memory::free(p);
380 
381             // OK, st this point, it is probably a status file, not 100% but
382             // it looks like it :(
383             r = tokudb::metadata::remove(status_db, hatoku_frm_data, txn);
384             if (r != 0) {
385                 fprintf(
386                     stderr,
387                     "TokuDB strip_frm_data : unable to find/strip frm data "
388                     "from status file %s, error = %d\n",
389                     dname,
390                     r);
391             }
392 
393             r = tokudb::metadata::close(&status_db);
394             assert_always(r == 0);
395         }
396     }
397     tokudb::memory::free(key.data);
398     tokudb::memory::free(val.data);
399 
400     fprintf(
401         stderr,
402         "TokuDB strip_frm_data : Stripping process complete, beginning "
403         "commit, this may take some time.\n");
404 
405     r = c->c_close(c);
406     assert_always(r == 0);
407 
408     r = txn->commit(txn, 0);
409     assert_always(r == 0);
410 
411     fprintf(
412         stderr,
413         "TokuDB strip_frm_data : Commit complete, resuming server init "
414         "process.");
415 
416     return 0;
417 }
418 
419 } // namespace metadata
420 } // namespace tokudb
421 #endif
422