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. |
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 | 0); |
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 | 0); |
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 | |