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/*======
5This file is part of TokuDB
6
7
8Copyright (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
32typedef 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
45namespace tokudb {
46namespace 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.
51int 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.
76int 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.
102int 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.
124int 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.
144int 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.
160int 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
172int 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
183int 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
215int 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
252int 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