diff options
Diffstat (limited to 'storage/tokudb/PerconaFT/tools/tokudb_dump.cc')
-rw-r--r-- | storage/tokudb/PerconaFT/tools/tokudb_dump.cc | 685 |
1 files changed, 685 insertions, 0 deletions
diff --git a/storage/tokudb/PerconaFT/tools/tokudb_dump.cc b/storage/tokudb/PerconaFT/tools/tokudb_dump.cc new file mode 100644 index 00000000..d7362fc6 --- /dev/null +++ b/storage/tokudb/PerconaFT/tools/tokudb_dump.cc @@ -0,0 +1,685 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4: +#ident "$Id$" +/*====== +This file is part of PerconaFT. + + +Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved. + + PerconaFT is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2, + as published by the Free Software Foundation. + + PerconaFT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. + +---------------------------------------- + + PerconaFT is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License, version 3, + as published by the Free Software Foundation. + + PerconaFT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. +======= */ + +#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved." + +#include <db.h> + +#include <toku_stdlib.h> +#include <toku_stdint.h> +#include <toku_portability.h> +#include <toku_assert.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <getopt.h> +#include <signal.h> +#include <memory.h> + +typedef struct { + bool leadingspace; + bool plaintext; + bool header; + bool footer; + bool is_private; + bool recovery_and_txn; + char* progname; + char* homedir; + char* database; + char* subdatabase; + int exitcode; + int recover_flags; + DBTYPE dbtype; + DBTYPE opened_dbtype; + DB* db; + DB_ENV* dbenv; +} dump_globals; + +dump_globals g; + +#define SET_BITS(bitvector, bits) ((bitvector) |= (bits)) +#define REMOVE_BITS(bitvector, bits) ((bitvector) &= ~(bits)) +#define IS_SET_ANY(bitvector, bits) ((bitvector) & (bits)) +#define IS_SET_ALL(bitvector, bits) (((bitvector) & (bits)) == (bits)) + +#define IS_POWER_OF_2(num) ((num) > 0 && ((num) & ((num) - 1)) == 0) + +//DB_ENV->err disabled since it does not use db_strerror +#define PRINT_ERROR(retval, ...) \ +do { \ +if (0) g.dbenv->err(g.dbenv, retval, __VA_ARGS__); \ +else { \ + fprintf(stderr, "\tIn %s:%d %s()\n", __FILE__, __LINE__, __FUNCTION__); \ + fprintf(stderr, "%s: %s:", g.progname, db_strerror(retval)); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + fflush(stderr); \ +} \ +} while (0) + +//DB_ENV->err disabled since it does not use db_strerror, errx does not exist. +#define PRINT_ERRORX(...) \ +do { \ +if (0) g.dbenv->err(g.dbenv, 0, __VA_ARGS__); \ +else { \ + fprintf(stderr, "\tIn %s:%d %s()\n", __FILE__, __LINE__, __FUNCTION__); \ + fprintf(stderr, "%s: ", g.progname); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + fflush(stderr); \ +} \ +} while (0) + +int strtoint32 (char* str, int32_t* num, int32_t min, int32_t max, int base); +int strtouint32 (char* str, uint32_t* num, uint32_t min, uint32_t max, int base); +int strtoint64 (char* str, int64_t* num, int64_t min, int64_t max, int base); +int strtouint64 (char* str, uint64_t* num, uint64_t min, uint64_t max, int base); + +/* + * Convert a string to an integer of type "type". + * + * + * Sets errno and returns: + * EINVAL: str == NULL, num == NULL, or string not of the form [ \t]*[+-]?[0-9]+ + * ERANGE: value out of range specified. (Range of [min, max]) + * + * *num is unchanged on error. + * Returns: + * + */ +#define DEF_STR_TO(name, type, bigtype, strtofunc, frmt) \ +int name(char* str, type* num, type min, type max, int base) \ +{ \ + char* test; \ + bigtype value; \ + \ + assert(str); \ + assert(num); \ + assert(min <= max); \ + assert(g.dbenv || g.progname); \ + assert(base == 0 || (base >= 2 && base <= 36)); \ + \ + errno = 0; \ + while (isspace(*str)) str++; \ + value = strtofunc(str, &test, base); \ + if ((*test != '\0' && *test != '\n') || test == str) { \ + PRINT_ERRORX("%s: Invalid numeric argument\n", str); \ + errno = EINVAL; \ + goto error; \ + } \ + if (errno != 0) { \ + PRINT_ERROR(errno, "%s\n", str); \ + } \ + if (value < min) { \ + PRINT_ERRORX("%s: Less than minimum value (%" frmt ")\n", str, min); \ + goto error; \ + } \ + if (value > max) { \ + PRINT_ERRORX("%s: Greater than maximum value (%" frmt ")\n", str, max); \ + goto error; \ + } \ + *num = value; \ + return EXIT_SUCCESS; \ +error: \ + return errno; \ +} + +DEF_STR_TO(strtoint32, int32_t, int64_t, strtoll, PRId32) +DEF_STR_TO(strtouint32, uint32_t, uint64_t, strtoull, PRIu32) +DEF_STR_TO(strtoint64, int64_t, int64_t, strtoll, PRId64) +DEF_STR_TO(strtouint64, uint64_t, uint64_t, strtoull, PRIu64) + +static inline void +outputbyte(uint8_t ch) +{ + if (g.plaintext) { + if (ch == '\\') printf("\\\\"); + else if (isprint(ch)) printf("%c", ch); + else printf("\\%02x", ch); + } + else printf("%02x", ch); +} + +static inline void +outputstring(char* str) +{ + char* p; + + for (p = str; *p != '\0'; p++) { + outputbyte((uint8_t)*p); + } +} + +static inline void +outputplaintextstring(char* str) +{ + bool old_plaintext = g.plaintext; + g.plaintext = true; + outputstring(str); + g.plaintext = old_plaintext; +} + +static inline int +verify_library_version(void) +{ + int major; + int minor; + + db_version(&major, &minor, NULL); + if (major != DB_VERSION_MAJOR || minor != DB_VERSION_MINOR) { + PRINT_ERRORX("version %d.%d doesn't match library version %d.%d\n", + DB_VERSION_MAJOR, DB_VERSION_MINOR, major, minor); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +static int last_caught = 0; + +static void catch_signal(int which_signal) { + last_caught = which_signal; + if (last_caught == 0) last_caught = SIGINT; +} + +static inline void +init_catch_signals(void) { + signal(SIGINT, catch_signal); + signal(SIGTERM, catch_signal); +#ifdef SIGHUP + signal(SIGHUP, catch_signal); +#endif +#ifdef SIGPIPE + signal(SIGPIPE, catch_signal); +#endif +} + +static inline int +caught_any_signals(void) { + return last_caught != 0; +} + +static inline void +resend_signals(void) { + if (last_caught) { + signal(last_caught, SIG_DFL); + raise(last_caught); + } +} + +static int usage (void); +static int create_init_env(void); +static int dump_database (void); +static int open_database (void); +static int dump_pairs (void); +static int dump_footer (void); +static int dump_header (void); +static int close_database (void); + +int main(int argc, char *const argv[]) { + int ch; + int retval; + + /* Set up the globals. */ + memset(&g, 0, sizeof(g)); + g.leadingspace = true; + //TODO: Uncomment when DB_UNKNOWN + db->get_type are implemented. + g.dbtype = DB_UNKNOWN; + //g.dbtype = DB_BTREE; + g.progname = argv[0]; + g.header = true; + g.footer = true; + g.recovery_and_txn = true; + + if (verify_library_version() != 0) goto error; + + while ((ch = getopt(argc, argv, "d:f:h:klNP:ps:RrVTx")) != EOF) { + switch (ch) { + case ('d'): { + PRINT_ERRORX("-%c option not supported.\n", ch); + goto error; + } + case ('f'): { + if (freopen(optarg, "w", stdout) == NULL) { + fprintf(stderr, + "%s: %s: reopen: %s\n", + g.progname, optarg, strerror(errno)); + goto error; + } + break; + } + case ('h'): { + g.homedir = optarg; + break; + } + case ('k'): { + PRINT_ERRORX("-%c option not supported.\n", ch); + goto error; + } + case ('l'): { + //TODO: Implement (Requires master database support) + PRINT_ERRORX("-%c option not supported.\n", ch); //YET! + goto error; + } + case ('N'): { + PRINT_ERRORX("-%c option not supported.\n", ch); + goto error; + } + case ('P'): { + /* Clear password. */ + memset(optarg, 0, strlen(optarg)); + PRINT_ERRORX("-%c option not supported.\n", ch); + goto error; + } + case ('p'): { + g.plaintext = true; + break; + } + case ('R'): { + //TODO: Uncomment when DB_SALVAGE,DB_AGGRESSIVE are implemented. + /*g.recover_flags |= DB_SALVAGE | DB_AGGRESSIVE;*/ + + //TODO: Implement aggressive recovery (requires db->verify()) + PRINT_ERRORX("-%c option not supported.\n", ch); + goto error; + } + case ('r'): { + //TODO: Uncomment when DB_SALVAGE,DB_AGGRESSIVE are implemented. + /*g.recover_flags |= DB_SALVAGE;*/ + + //TODO: Implement recovery (requires db->verify()) + PRINT_ERRORX("-%c option not supported.\n", ch); + goto error; + } + case ('s'): { + g.subdatabase = optarg; + break; + } + case ('V'): { + printf("%s\n", db_version(NULL, NULL, NULL)); + goto cleanup; + } + case ('T'): { + g.plaintext = true; + g.leadingspace = false; + g.header = false; + g.footer = false; + break; + } + case ('x'): { + g.recovery_and_txn = false; + break; + } + case ('?'): + default: { + g.exitcode = usage(); + goto cleanup; + } + } + } + argc -= optind; + argv += optind; + + //TODO: Uncomment when DB_SALVAGE,DB_AGGRESSIVE,DB_PRINTABLE,db->verify are implemented. + /* + if (g.plaintext) g.recover_flags |= DB_PRINTABLE; + + if (g.subdatabase != NULL && IS_SET_ALL(g.recover_flags, DB_SALVAGE)) { + if (IS_SET_ALL(g.recover_flags, DB_AGGRESSIVE)) { + PRINT_ERRORX("The -s and -R options may not both be specified.\n"); + goto error; + } + PRINT_ERRORX("The -s and -r options may not both be specified.\n"); + goto error; + + } + */ + + if (argc != 1) { + g.exitcode = usage(); + goto cleanup; + } + + init_catch_signals(); + + g.database = argv[0]; + if (caught_any_signals()) goto cleanup; + if (create_init_env() != 0) goto error; + if (caught_any_signals()) goto cleanup; + if (dump_database() != 0) goto error; + if (false) { +error: + g.exitcode = EXIT_FAILURE; + fprintf(stderr, "%s: Quitting out due to errors.\n", g.progname); + } +cleanup: + if (g.dbenv && (retval = g.dbenv->close(g.dbenv, 0)) != 0) { + g.exitcode = EXIT_FAILURE; + fprintf(stderr, "%s: %s: dbenv->close\n", g.progname, db_strerror(retval)); + } + // if (g.subdatabase) free(g.subdatabase); + resend_signals(); + + return g.exitcode; +} + +int dump_database() +{ + int retval; + + /* Create a database handle. */ + retval = db_create(&g.db, g.dbenv, 0); + if (retval != 0) { + PRINT_ERROR(retval, "db_create"); + return EXIT_FAILURE; + } + + /* + TODO: If/when supporting encryption + if (g.password && (retval = db->set_flags(db, DB_ENCRYPT))) { + PRINT_ERROR(ret, "DB->set_flags: DB_ENCRYPT"); + goto error; + } + */ + if (open_database() != 0) goto error; + if (caught_any_signals()) goto cleanup; + if (g.header && dump_header() != 0) goto error; + if (caught_any_signals()) goto cleanup; + if (dump_pairs() != 0) goto error; + if (caught_any_signals()) goto cleanup; + if (g.footer && dump_footer() != 0) goto error; + + if (false) { +error: + g.exitcode = EXIT_FAILURE; + } +cleanup: + + if (close_database() != 0) g.exitcode = EXIT_FAILURE; + + return g.exitcode; +} + +int usage() +{ + fprintf(stderr, + "usage: %s [-pVT] [-x] [-f output] [-h home] [-s database] db_file\n", + g.progname); + return EXIT_FAILURE; +} + +int create_init_env() +{ + int retval; + DB_ENV* dbenv; + int flags; + //TODO: Experiments to determine right cache size for tokudb, or maybe command line argument. + + retval = db_env_create(&dbenv, 0); + if (retval) { + fprintf(stderr, "%s: db_dbenv_create: %s\n", g.progname, db_strerror(retval)); + goto error; + } + ///TODO: UNCOMMENT/IMPLEMENT dbenv->set_errfile(dbenv, stderr); + dbenv->set_errpfx(dbenv, g.progname); + /* + TODO: Anything for encryption? + */ + + /* Open the dbenvironment. */ + g.is_private = false; + //flags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_USE_ENVIRON; + flags = DB_INIT_LOCK | DB_INIT_MPOOL; ///TODO: UNCOMMENT/IMPLEMENT | DB_USE_ENVIRON; + if (g.recovery_and_txn) { + SET_BITS(flags, DB_INIT_LOG | DB_INIT_TXN | DB_RECOVER); + } + + /* + ///TODO: UNCOMMENT/IMPLEMENT Notes: We require DB_PRIVATE + if (!dbenv->open(dbenv, g.homedir, flags, 0)) goto success; + */ + + /* + ///TODO: UNCOMMENT/IMPLEMENT + retval = dbenv->set_cachesize(dbenv, 0, cache, 1); + if (retval) { + PRINT_ERROR(retval, "DB_ENV->set_cachesize"); + goto error; + } + */ + g.is_private = true; + //TODO: Do we want to support transactions even in single-process mode? + //Logging is not necessary.. this is read-only. + //However, do we need to use DB_INIT_LOG to join a logging environment? + //REMOVE_BITS(flags, DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN); + SET_BITS(flags, DB_CREATE | DB_PRIVATE); + + retval = dbenv->open(dbenv, g.homedir, flags, 0); + if (retval) { + PRINT_ERROR(retval, "DB_ENV->open"); + goto error; + } + g.dbenv = dbenv; + return EXIT_SUCCESS; + +error: + return EXIT_FAILURE; +} + +#define DUMP_FLAG(bit, dump) if (IS_SET_ALL(flags, bit)) printf(dump); + +#define DUMP_IGNORED_FLAG(bit, dump) + + +int dump_header() +{ + uint32_t flags; + int retval; + DB* db = g.db; + + assert(g.header); + printf("VERSION=3\n"); + printf("format=%s\n", g.plaintext ? "print" : "bytevalue"); + //TODO: Uncomment when DB_UNKNOWN + db->get_type are implemented. + /*assert(g.dbtype == DB_BTREE || (g.dbtype == DB_UNKNOWN && g.opened_dbtype == DB_BTREE));*/ + printf("type=btree\n"); + //TODO: Get page size from db. Currently tokudb does not support db->get_pagesize. + //Don't print this out //printf("db_pagesize=4096\n"); + if (g.subdatabase) { + printf("subdatabase="); + outputplaintextstring(g.subdatabase); + printf("\n"); + } + //TODO: Uncomment when db->get_flags is implemented + if ((retval = db->get_flags(db, &flags)) != 0) { + PRINT_ERROR(retval, "DB->get_flags"); + goto error; + } + DUMP_IGNORED_FLAG(DB_CHKSUM, "chksum=1\n"); + DUMP_IGNORED_FLAG(DB_RECNUM, "recnum=1\n"); + printf("HEADER=END\n"); + + if (ferror(stdout)) goto error; + return EXIT_SUCCESS; + +error: + return EXIT_FAILURE; +} + +int dump_footer() +{ + printf("DATA=END\n"); + if (ferror(stdout)) goto error; + + return EXIT_SUCCESS; +error: + return EXIT_FAILURE; +} + +int open_database() +{ + DB* db = g.db; + int retval; + + int open_flags = 0;//|DB_RDONLY; + //TODO: Transaction auto commit stuff + SET_BITS(open_flags, DB_AUTO_COMMIT); + + retval = db->open(db, NULL, g.database, g.subdatabase, g.dbtype, open_flags, 0666); + if (retval != 0) { + PRINT_ERROR(retval, "DB->open: %s", g.database); + goto error; + } + //TODO: Uncomment when DB_UNKNOWN + db->get_type are implemented. + /* + retval = db->get_type(db, &g.opened_dbtype); + if (retval != 0) { + PRINT_ERROR(retval, "DB->get_type"); + goto error; + } + if (g.opened_dbtype != DB_BTREE) { + PRINT_ERRORX("Unsupported db type %d\n", g.opened_dbtype); + goto error; + } + if (g.dbtype != DB_UNKNOWN && g.opened_dbtype != g.dbtype) { + PRINT_ERRORX("DBTYPE %d does not match opened DBTYPE %d.\n", g.dbtype, g.opened_dbtype); + goto error; + }*/ + return EXIT_SUCCESS; +error: + fprintf(stderr, "Quitting out due to errors.\n"); + return EXIT_FAILURE; +} + +static int dump_dbt(DBT* dbt) +{ + char* str; + uint32_t idx; + + assert(dbt); + str = (char*)dbt->data; + if (g.leadingspace) printf(" "); + if (dbt->size > 0) { + assert(dbt->data); + for (idx = 0; idx < dbt->size; idx++) { + outputbyte(str[idx]); + if (ferror(stdout)) { + perror("stdout"); + goto error; + } + } + } + printf("\n"); + if (false) { +error: + g.exitcode = EXIT_FAILURE; + } + return g.exitcode; +} + +int dump_pairs() +{ + int retval; + DBT key; + DBT data; + DB* db = g.db; + DBC* dbc = NULL; + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + DB_TXN* txn = NULL; + if (g.recovery_and_txn) { + retval = g.dbenv->txn_begin(g.dbenv, NULL, &txn, 0); + if (retval) { + PRINT_ERROR(retval, "DB_ENV->txn_begin"); + goto error; + } + } + + if ((retval = db->cursor(db, txn, &dbc, 0)) != 0) { + PRINT_ERROR(retval, "DB->cursor"); + goto error; + } + while ((retval = dbc->c_get(dbc, &key, &data, DB_NEXT)) == 0) { + if (caught_any_signals()) goto cleanup; + if (dump_dbt(&key) != 0) goto error; + if (dump_dbt(&data) != 0) goto error; + } + if (retval != DB_NOTFOUND) { + PRINT_ERROR(retval, "DBC->c_get"); + goto error; + } + + + if (false) { +error: + g.exitcode = EXIT_FAILURE; + } +cleanup: + if (dbc && (retval = dbc->c_close(dbc)) != 0) { + PRINT_ERROR(retval, "DBC->c_close"); + g.exitcode = EXIT_FAILURE; + } + if (txn) { + if (retval) { + int r2 = txn->abort(txn); + if (r2) PRINT_ERROR(r2, "DB_TXN->abort"); + } + else { + retval = txn->commit(txn, 0); + if (retval) PRINT_ERROR(retval, "DB_TXN->abort"); + } + } + return g.exitcode; +} + +int close_database() +{ + DB* db = g.db; + int retval; + + assert(db); + if ((retval = db->close(db, 0)) != 0) { + PRINT_ERROR(retval, "DB->close"); + goto error; + } + return EXIT_SUCCESS; +error: + return EXIT_FAILURE; +} |