| 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 PerconaFT. |
| 6 | |
| 7 | |
| 8 | Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved. |
| 9 | |
| 10 | PerconaFT 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 | PerconaFT 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 PerconaFT. If not, see <http://www.gnu.org/licenses/>. |
| 21 | |
| 22 | ---------------------------------------- |
| 23 | |
| 24 | PerconaFT is free software: you can redistribute it and/or modify |
| 25 | it under the terms of the GNU Affero General Public License, version 3, |
| 26 | as published by the Free Software Foundation. |
| 27 | |
| 28 | PerconaFT is distributed in the hope that it will be useful, |
| 29 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 30 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 31 | GNU Affero General Public License for more details. |
| 32 | |
| 33 | You should have received a copy of the GNU Affero General Public License |
| 34 | along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. |
| 35 | ======= */ |
| 36 | |
| 37 | #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved." |
| 38 | |
| 39 | #include <db.h> |
| 40 | |
| 41 | #include <toku_stdlib.h> |
| 42 | #include <toku_stdint.h> |
| 43 | #include <toku_portability.h> |
| 44 | #include <toku_assert.h> |
| 45 | #include <stdio.h> |
| 46 | #include <sys/types.h> |
| 47 | #include <unistd.h> |
| 48 | #include <string.h> |
| 49 | #include <ctype.h> |
| 50 | #include <errno.h> |
| 51 | #include <getopt.h> |
| 52 | #include <signal.h> |
| 53 | #include <memory.h> |
| 54 | |
| 55 | typedef struct { |
| 56 | bool leadingspace; |
| 57 | bool plaintext; |
| 58 | bool ; |
| 59 | bool ; |
| 60 | bool is_private; |
| 61 | bool recovery_and_txn; |
| 62 | char* progname; |
| 63 | char* homedir; |
| 64 | char* database; |
| 65 | char* subdatabase; |
| 66 | int exitcode; |
| 67 | int recover_flags; |
| 68 | DBTYPE dbtype; |
| 69 | DBTYPE opened_dbtype; |
| 70 | DB* db; |
| 71 | DB_ENV* dbenv; |
| 72 | } dump_globals; |
| 73 | |
| 74 | dump_globals g; |
| 75 | |
| 76 | #define SET_BITS(bitvector, bits) ((bitvector) |= (bits)) |
| 77 | #define REMOVE_BITS(bitvector, bits) ((bitvector) &= ~(bits)) |
| 78 | #define IS_SET_ANY(bitvector, bits) ((bitvector) & (bits)) |
| 79 | #define IS_SET_ALL(bitvector, bits) (((bitvector) & (bits)) == (bits)) |
| 80 | |
| 81 | #define IS_POWER_OF_2(num) ((num) > 0 && ((num) & ((num) - 1)) == 0) |
| 82 | |
| 83 | //DB_ENV->err disabled since it does not use db_strerror |
| 84 | #define PRINT_ERROR(retval, ...) \ |
| 85 | do { \ |
| 86 | if (0) g.dbenv->err(g.dbenv, retval, __VA_ARGS__); \ |
| 87 | else { \ |
| 88 | fprintf(stderr, "\tIn %s:%d %s()\n", __FILE__, __LINE__, __FUNCTION__); \ |
| 89 | fprintf(stderr, "%s: %s:", g.progname, db_strerror(retval)); \ |
| 90 | fprintf(stderr, __VA_ARGS__); \ |
| 91 | fprintf(stderr, "\n"); \ |
| 92 | fflush(stderr); \ |
| 93 | } \ |
| 94 | } while (0) |
| 95 | |
| 96 | //DB_ENV->err disabled since it does not use db_strerror, errx does not exist. |
| 97 | #define PRINT_ERRORX(...) \ |
| 98 | do { \ |
| 99 | if (0) g.dbenv->err(g.dbenv, 0, __VA_ARGS__); \ |
| 100 | else { \ |
| 101 | fprintf(stderr, "\tIn %s:%d %s()\n", __FILE__, __LINE__, __FUNCTION__); \ |
| 102 | fprintf(stderr, "%s: ", g.progname); \ |
| 103 | fprintf(stderr, __VA_ARGS__); \ |
| 104 | fprintf(stderr, "\n"); \ |
| 105 | fflush(stderr); \ |
| 106 | } \ |
| 107 | } while (0) |
| 108 | |
| 109 | int strtoint32 (char* str, int32_t* num, int32_t min, int32_t max, int base); |
| 110 | int strtouint32 (char* str, uint32_t* num, uint32_t min, uint32_t max, int base); |
| 111 | int strtoint64 (char* str, int64_t* num, int64_t min, int64_t max, int base); |
| 112 | int strtouint64 (char* str, uint64_t* num, uint64_t min, uint64_t max, int base); |
| 113 | |
| 114 | /* |
| 115 | * Convert a string to an integer of type "type". |
| 116 | * |
| 117 | * |
| 118 | * Sets errno and returns: |
| 119 | * EINVAL: str == NULL, num == NULL, or string not of the form [ \t]*[+-]?[0-9]+ |
| 120 | * ERANGE: value out of range specified. (Range of [min, max]) |
| 121 | * |
| 122 | * *num is unchanged on error. |
| 123 | * Returns: |
| 124 | * |
| 125 | */ |
| 126 | #define DEF_STR_TO(name, type, bigtype, strtofunc, frmt) \ |
| 127 | int name(char* str, type* num, type min, type max, int base) \ |
| 128 | { \ |
| 129 | char* test; \ |
| 130 | bigtype value; \ |
| 131 | \ |
| 132 | assert(str); \ |
| 133 | assert(num); \ |
| 134 | assert(min <= max); \ |
| 135 | assert(g.dbenv || g.progname); \ |
| 136 | assert(base == 0 || (base >= 2 && base <= 36)); \ |
| 137 | \ |
| 138 | errno = 0; \ |
| 139 | while (isspace(*str)) str++; \ |
| 140 | value = strtofunc(str, &test, base); \ |
| 141 | if ((*test != '\0' && *test != '\n') || test == str) { \ |
| 142 | PRINT_ERRORX("%s: Invalid numeric argument\n", str); \ |
| 143 | errno = EINVAL; \ |
| 144 | goto error; \ |
| 145 | } \ |
| 146 | if (errno != 0) { \ |
| 147 | PRINT_ERROR(errno, "%s\n", str); \ |
| 148 | } \ |
| 149 | if (value < min) { \ |
| 150 | PRINT_ERRORX("%s: Less than minimum value (%" frmt ")\n", str, min); \ |
| 151 | goto error; \ |
| 152 | } \ |
| 153 | if (value > max) { \ |
| 154 | PRINT_ERRORX("%s: Greater than maximum value (%" frmt ")\n", str, max); \ |
| 155 | goto error; \ |
| 156 | } \ |
| 157 | *num = value; \ |
| 158 | return EXIT_SUCCESS; \ |
| 159 | error: \ |
| 160 | return errno; \ |
| 161 | } |
| 162 | |
| 163 | DEF_STR_TO(strtoint32, int32_t, int64_t, strtoll, PRId32) |
| 164 | DEF_STR_TO(strtouint32, uint32_t, uint64_t, strtoull, PRIu32) |
| 165 | DEF_STR_TO(strtoint64, int64_t, int64_t, strtoll, PRId64) |
| 166 | DEF_STR_TO(strtouint64, uint64_t, uint64_t, strtoull, PRIu64) |
| 167 | |
| 168 | static inline void |
| 169 | outputbyte(uint8_t ch) |
| 170 | { |
| 171 | if (g.plaintext) { |
| 172 | if (ch == '\\') printf("\\\\" ); |
| 173 | else if (isprint(ch)) printf("%c" , ch); |
| 174 | else printf("\\%02x" , ch); |
| 175 | } |
| 176 | else printf("%02x" , ch); |
| 177 | } |
| 178 | |
| 179 | static inline void |
| 180 | outputstring(char* str) |
| 181 | { |
| 182 | char* p; |
| 183 | |
| 184 | for (p = str; *p != '\0'; p++) { |
| 185 | outputbyte((uint8_t)*p); |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | static inline void |
| 190 | outputplaintextstring(char* str) |
| 191 | { |
| 192 | bool old_plaintext = g.plaintext; |
| 193 | g.plaintext = true; |
| 194 | outputstring(str); |
| 195 | g.plaintext = old_plaintext; |
| 196 | } |
| 197 | |
| 198 | static inline int |
| 199 | verify_library_version(void) |
| 200 | { |
| 201 | int major; |
| 202 | int minor; |
| 203 | |
| 204 | db_version(&major, &minor, NULL); |
| 205 | if (major != DB_VERSION_MAJOR || minor != DB_VERSION_MINOR) { |
| 206 | PRINT_ERRORX("version %d.%d doesn't match library version %d.%d\n" , |
| 207 | DB_VERSION_MAJOR, DB_VERSION_MINOR, major, minor); |
| 208 | return EXIT_FAILURE; |
| 209 | } |
| 210 | return EXIT_SUCCESS; |
| 211 | } |
| 212 | |
| 213 | static int last_caught = 0; |
| 214 | |
| 215 | static void catch_signal(int which_signal) { |
| 216 | last_caught = which_signal; |
| 217 | if (last_caught == 0) last_caught = SIGINT; |
| 218 | } |
| 219 | |
| 220 | static inline void |
| 221 | init_catch_signals(void) { |
| 222 | signal(SIGINT, catch_signal); |
| 223 | signal(SIGTERM, catch_signal); |
| 224 | #ifdef SIGHUP |
| 225 | signal(SIGHUP, catch_signal); |
| 226 | #endif |
| 227 | #ifdef SIGPIPE |
| 228 | signal(SIGPIPE, catch_signal); |
| 229 | #endif |
| 230 | } |
| 231 | |
| 232 | static inline int |
| 233 | caught_any_signals(void) { |
| 234 | return last_caught != 0; |
| 235 | } |
| 236 | |
| 237 | static inline void |
| 238 | resend_signals(void) { |
| 239 | if (last_caught) { |
| 240 | signal(last_caught, SIG_DFL); |
| 241 | raise(last_caught); |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | static int usage (void); |
| 246 | static int create_init_env(void); |
| 247 | static int dump_database (void); |
| 248 | static int open_database (void); |
| 249 | static int dump_pairs (void); |
| 250 | static int dump_footer (void); |
| 251 | static int dump_header (void); |
| 252 | static int close_database (void); |
| 253 | |
| 254 | int main(int argc, char *const argv[]) { |
| 255 | int ch; |
| 256 | int retval; |
| 257 | |
| 258 | /* Set up the globals. */ |
| 259 | memset(&g, 0, sizeof(g)); |
| 260 | g.leadingspace = true; |
| 261 | //TODO: Uncomment when DB_UNKNOWN + db->get_type are implemented. |
| 262 | g.dbtype = DB_UNKNOWN; |
| 263 | //g.dbtype = DB_BTREE; |
| 264 | g.progname = argv[0]; |
| 265 | g.header = true; |
| 266 | g.footer = true; |
| 267 | g.recovery_and_txn = true; |
| 268 | |
| 269 | if (verify_library_version() != 0) goto error; |
| 270 | |
| 271 | while ((ch = getopt(argc, argv, "d:f:h:klNP:ps:RrVTx" )) != EOF) { |
| 272 | switch (ch) { |
| 273 | case ('d'): { |
| 274 | PRINT_ERRORX("-%c option not supported.\n" , ch); |
| 275 | goto error; |
| 276 | } |
| 277 | case ('f'): { |
| 278 | if (freopen(optarg, "w" , stdout) == NULL) { |
| 279 | fprintf(stderr, |
| 280 | "%s: %s: reopen: %s\n" , |
| 281 | g.progname, optarg, strerror(errno)); |
| 282 | goto error; |
| 283 | } |
| 284 | break; |
| 285 | } |
| 286 | case ('h'): { |
| 287 | g.homedir = optarg; |
| 288 | break; |
| 289 | } |
| 290 | case ('k'): { |
| 291 | PRINT_ERRORX("-%c option not supported.\n" , ch); |
| 292 | goto error; |
| 293 | } |
| 294 | case ('l'): { |
| 295 | //TODO: Implement (Requires master database support) |
| 296 | PRINT_ERRORX("-%c option not supported.\n" , ch); //YET! |
| 297 | goto error; |
| 298 | } |
| 299 | case ('N'): { |
| 300 | PRINT_ERRORX("-%c option not supported.\n" , ch); |
| 301 | goto error; |
| 302 | } |
| 303 | case ('P'): { |
| 304 | /* Clear password. */ |
| 305 | memset(optarg, 0, strlen(optarg)); |
| 306 | PRINT_ERRORX("-%c option not supported.\n" , ch); |
| 307 | goto error; |
| 308 | } |
| 309 | case ('p'): { |
| 310 | g.plaintext = true; |
| 311 | break; |
| 312 | } |
| 313 | case ('R'): { |
| 314 | //TODO: Uncomment when DB_SALVAGE,DB_AGGRESSIVE are implemented. |
| 315 | /*g.recover_flags |= DB_SALVAGE | DB_AGGRESSIVE;*/ |
| 316 | |
| 317 | //TODO: Implement aggressive recovery (requires db->verify()) |
| 318 | PRINT_ERRORX("-%c option not supported.\n" , ch); |
| 319 | goto error; |
| 320 | } |
| 321 | case ('r'): { |
| 322 | //TODO: Uncomment when DB_SALVAGE,DB_AGGRESSIVE are implemented. |
| 323 | /*g.recover_flags |= DB_SALVAGE;*/ |
| 324 | |
| 325 | //TODO: Implement recovery (requires db->verify()) |
| 326 | PRINT_ERRORX("-%c option not supported.\n" , ch); |
| 327 | goto error; |
| 328 | } |
| 329 | case ('s'): { |
| 330 | g.subdatabase = optarg; |
| 331 | break; |
| 332 | } |
| 333 | case ('V'): { |
| 334 | printf("%s\n" , db_version(NULL, NULL, NULL)); |
| 335 | goto cleanup; |
| 336 | } |
| 337 | case ('T'): { |
| 338 | g.plaintext = true; |
| 339 | g.leadingspace = false; |
| 340 | g.header = false; |
| 341 | g.footer = false; |
| 342 | break; |
| 343 | } |
| 344 | case ('x'): { |
| 345 | g.recovery_and_txn = false; |
| 346 | break; |
| 347 | } |
| 348 | case ('?'): |
| 349 | default: { |
| 350 | g.exitcode = usage(); |
| 351 | goto cleanup; |
| 352 | } |
| 353 | } |
| 354 | } |
| 355 | argc -= optind; |
| 356 | argv += optind; |
| 357 | |
| 358 | //TODO: Uncomment when DB_SALVAGE,DB_AGGRESSIVE,DB_PRINTABLE,db->verify are implemented. |
| 359 | /* |
| 360 | if (g.plaintext) g.recover_flags |= DB_PRINTABLE; |
| 361 | |
| 362 | if (g.subdatabase != NULL && IS_SET_ALL(g.recover_flags, DB_SALVAGE)) { |
| 363 | if (IS_SET_ALL(g.recover_flags, DB_AGGRESSIVE)) { |
| 364 | PRINT_ERRORX("The -s and -R options may not both be specified.\n"); |
| 365 | goto error; |
| 366 | } |
| 367 | PRINT_ERRORX("The -s and -r options may not both be specified.\n"); |
| 368 | goto error; |
| 369 | |
| 370 | } |
| 371 | */ |
| 372 | |
| 373 | if (argc != 1) { |
| 374 | g.exitcode = usage(); |
| 375 | goto cleanup; |
| 376 | } |
| 377 | |
| 378 | init_catch_signals(); |
| 379 | |
| 380 | g.database = argv[0]; |
| 381 | if (caught_any_signals()) goto cleanup; |
| 382 | if (create_init_env() != 0) goto error; |
| 383 | if (caught_any_signals()) goto cleanup; |
| 384 | if (dump_database() != 0) goto error; |
| 385 | if (false) { |
| 386 | error: |
| 387 | g.exitcode = EXIT_FAILURE; |
| 388 | fprintf(stderr, "%s: Quitting out due to errors.\n" , g.progname); |
| 389 | } |
| 390 | cleanup: |
| 391 | if (g.dbenv && (retval = g.dbenv->close(g.dbenv, 0)) != 0) { |
| 392 | g.exitcode = EXIT_FAILURE; |
| 393 | fprintf(stderr, "%s: %s: dbenv->close\n" , g.progname, db_strerror(retval)); |
| 394 | } |
| 395 | // if (g.subdatabase) free(g.subdatabase); |
| 396 | resend_signals(); |
| 397 | |
| 398 | return g.exitcode; |
| 399 | } |
| 400 | |
| 401 | int dump_database() |
| 402 | { |
| 403 | int retval; |
| 404 | |
| 405 | /* Create a database handle. */ |
| 406 | retval = db_create(&g.db, g.dbenv, 0); |
| 407 | if (retval != 0) { |
| 408 | PRINT_ERROR(retval, "db_create" ); |
| 409 | return EXIT_FAILURE; |
| 410 | } |
| 411 | |
| 412 | /* |
| 413 | TODO: If/when supporting encryption |
| 414 | if (g.password && (retval = db->set_flags(db, DB_ENCRYPT))) { |
| 415 | PRINT_ERROR(ret, "DB->set_flags: DB_ENCRYPT"); |
| 416 | goto error; |
| 417 | } |
| 418 | */ |
| 419 | if (open_database() != 0) goto error; |
| 420 | if (caught_any_signals()) goto cleanup; |
| 421 | if (g.header && dump_header() != 0) goto error; |
| 422 | if (caught_any_signals()) goto cleanup; |
| 423 | if (dump_pairs() != 0) goto error; |
| 424 | if (caught_any_signals()) goto cleanup; |
| 425 | if (g.footer && dump_footer() != 0) goto error; |
| 426 | |
| 427 | if (false) { |
| 428 | error: |
| 429 | g.exitcode = EXIT_FAILURE; |
| 430 | } |
| 431 | cleanup: |
| 432 | |
| 433 | if (close_database() != 0) g.exitcode = EXIT_FAILURE; |
| 434 | |
| 435 | return g.exitcode; |
| 436 | } |
| 437 | |
| 438 | int usage() |
| 439 | { |
| 440 | fprintf(stderr, |
| 441 | "usage: %s [-pVT] [-x] [-f output] [-h home] [-s database] db_file\n" , |
| 442 | g.progname); |
| 443 | return EXIT_FAILURE; |
| 444 | } |
| 445 | |
| 446 | int create_init_env() |
| 447 | { |
| 448 | int retval; |
| 449 | DB_ENV* dbenv; |
| 450 | int flags; |
| 451 | //TODO: Experiments to determine right cache size for tokudb, or maybe command line argument. |
| 452 | |
| 453 | retval = db_env_create(&dbenv, 0); |
| 454 | if (retval) { |
| 455 | fprintf(stderr, "%s: db_dbenv_create: %s\n" , g.progname, db_strerror(retval)); |
| 456 | goto error; |
| 457 | } |
| 458 | ///TODO: UNCOMMENT/IMPLEMENT dbenv->set_errfile(dbenv, stderr); |
| 459 | dbenv->set_errpfx(dbenv, g.progname); |
| 460 | /* |
| 461 | TODO: Anything for encryption? |
| 462 | */ |
| 463 | |
| 464 | /* Open the dbenvironment. */ |
| 465 | g.is_private = false; |
| 466 | //flags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_USE_ENVIRON; |
| 467 | flags = DB_INIT_LOCK | DB_INIT_MPOOL; ///TODO: UNCOMMENT/IMPLEMENT | DB_USE_ENVIRON; |
| 468 | if (g.recovery_and_txn) { |
| 469 | SET_BITS(flags, DB_INIT_LOG | DB_INIT_TXN | DB_RECOVER); |
| 470 | } |
| 471 | |
| 472 | /* |
| 473 | ///TODO: UNCOMMENT/IMPLEMENT Notes: We require DB_PRIVATE |
| 474 | if (!dbenv->open(dbenv, g.homedir, flags, 0)) goto success; |
| 475 | */ |
| 476 | |
| 477 | /* |
| 478 | ///TODO: UNCOMMENT/IMPLEMENT |
| 479 | retval = dbenv->set_cachesize(dbenv, 0, cache, 1); |
| 480 | if (retval) { |
| 481 | PRINT_ERROR(retval, "DB_ENV->set_cachesize"); |
| 482 | goto error; |
| 483 | } |
| 484 | */ |
| 485 | g.is_private = true; |
| 486 | //TODO: Do we want to support transactions even in single-process mode? |
| 487 | //Logging is not necessary.. this is read-only. |
| 488 | //However, do we need to use DB_INIT_LOG to join a logging environment? |
| 489 | //REMOVE_BITS(flags, DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN); |
| 490 | SET_BITS(flags, DB_CREATE | DB_PRIVATE); |
| 491 | |
| 492 | retval = dbenv->open(dbenv, g.homedir, flags, 0); |
| 493 | if (retval) { |
| 494 | PRINT_ERROR(retval, "DB_ENV->open" ); |
| 495 | goto error; |
| 496 | } |
| 497 | g.dbenv = dbenv; |
| 498 | return EXIT_SUCCESS; |
| 499 | |
| 500 | error: |
| 501 | return EXIT_FAILURE; |
| 502 | } |
| 503 | |
| 504 | #define DUMP_FLAG(bit, dump) if (IS_SET_ALL(flags, bit)) printf(dump); |
| 505 | |
| 506 | #define DUMP_IGNORED_FLAG(bit, dump) |
| 507 | |
| 508 | |
| 509 | int () |
| 510 | { |
| 511 | uint32_t flags; |
| 512 | int retval; |
| 513 | DB* db = g.db; |
| 514 | |
| 515 | assert(g.header); |
| 516 | printf("VERSION=3\n" ); |
| 517 | printf("format=%s\n" , g.plaintext ? "print" : "bytevalue" ); |
| 518 | //TODO: Uncomment when DB_UNKNOWN + db->get_type are implemented. |
| 519 | /*assert(g.dbtype == DB_BTREE || (g.dbtype == DB_UNKNOWN && g.opened_dbtype == DB_BTREE));*/ |
| 520 | printf("type=btree\n" ); |
| 521 | //TODO: Get page size from db. Currently tokudb does not support db->get_pagesize. |
| 522 | //Don't print this out //printf("db_pagesize=4096\n"); |
| 523 | if (g.subdatabase) { |
| 524 | printf("subdatabase=" ); |
| 525 | outputplaintextstring(g.subdatabase); |
| 526 | printf("\n" ); |
| 527 | } |
| 528 | //TODO: Uncomment when db->get_flags is implemented |
| 529 | if ((retval = db->get_flags(db, &flags)) != 0) { |
| 530 | PRINT_ERROR(retval, "DB->get_flags" ); |
| 531 | goto error; |
| 532 | } |
| 533 | DUMP_IGNORED_FLAG(DB_CHKSUM, "chksum=1\n" ); |
| 534 | DUMP_IGNORED_FLAG(DB_RECNUM, "recnum=1\n" ); |
| 535 | printf("HEADER=END\n" ); |
| 536 | |
| 537 | if (ferror(stdout)) goto error; |
| 538 | return EXIT_SUCCESS; |
| 539 | |
| 540 | error: |
| 541 | return EXIT_FAILURE; |
| 542 | } |
| 543 | |
| 544 | int () |
| 545 | { |
| 546 | printf("DATA=END\n" ); |
| 547 | if (ferror(stdout)) goto error; |
| 548 | |
| 549 | return EXIT_SUCCESS; |
| 550 | error: |
| 551 | return EXIT_FAILURE; |
| 552 | } |
| 553 | |
| 554 | int open_database() |
| 555 | { |
| 556 | DB* db = g.db; |
| 557 | int retval; |
| 558 | |
| 559 | int open_flags = 0;//|DB_RDONLY; |
| 560 | //TODO: Transaction auto commit stuff |
| 561 | SET_BITS(open_flags, DB_AUTO_COMMIT); |
| 562 | |
| 563 | retval = db->open(db, NULL, g.database, g.subdatabase, g.dbtype, open_flags, 0666); |
| 564 | if (retval != 0) { |
| 565 | PRINT_ERROR(retval, "DB->open: %s" , g.database); |
| 566 | goto error; |
| 567 | } |
| 568 | //TODO: Uncomment when DB_UNKNOWN + db->get_type are implemented. |
| 569 | /* |
| 570 | retval = db->get_type(db, &g.opened_dbtype); |
| 571 | if (retval != 0) { |
| 572 | PRINT_ERROR(retval, "DB->get_type"); |
| 573 | goto error; |
| 574 | } |
| 575 | if (g.opened_dbtype != DB_BTREE) { |
| 576 | PRINT_ERRORX("Unsupported db type %d\n", g.opened_dbtype); |
| 577 | goto error; |
| 578 | } |
| 579 | if (g.dbtype != DB_UNKNOWN && g.opened_dbtype != g.dbtype) { |
| 580 | PRINT_ERRORX("DBTYPE %d does not match opened DBTYPE %d.\n", g.dbtype, g.opened_dbtype); |
| 581 | goto error; |
| 582 | }*/ |
| 583 | return EXIT_SUCCESS; |
| 584 | error: |
| 585 | fprintf(stderr, "Quitting out due to errors.\n" ); |
| 586 | return EXIT_FAILURE; |
| 587 | } |
| 588 | |
| 589 | static int dump_dbt(DBT* dbt) |
| 590 | { |
| 591 | char* str; |
| 592 | uint32_t idx; |
| 593 | |
| 594 | assert(dbt); |
| 595 | str = (char*)dbt->data; |
| 596 | if (g.leadingspace) printf(" " ); |
| 597 | if (dbt->size > 0) { |
| 598 | assert(dbt->data); |
| 599 | for (idx = 0; idx < dbt->size; idx++) { |
| 600 | outputbyte(str[idx]); |
| 601 | if (ferror(stdout)) { |
| 602 | perror("stdout" ); |
| 603 | goto error; |
| 604 | } |
| 605 | } |
| 606 | } |
| 607 | printf("\n" ); |
| 608 | if (false) { |
| 609 | error: |
| 610 | g.exitcode = EXIT_FAILURE; |
| 611 | } |
| 612 | return g.exitcode; |
| 613 | } |
| 614 | |
| 615 | int dump_pairs() |
| 616 | { |
| 617 | int retval; |
| 618 | DBT key; |
| 619 | DBT data; |
| 620 | DB* db = g.db; |
| 621 | DBC* dbc = NULL; |
| 622 | |
| 623 | memset(&key, 0, sizeof(key)); |
| 624 | memset(&data, 0, sizeof(data)); |
| 625 | |
| 626 | DB_TXN* txn = NULL; |
| 627 | if (g.recovery_and_txn) { |
| 628 | retval = g.dbenv->txn_begin(g.dbenv, NULL, &txn, 0); |
| 629 | if (retval) { |
| 630 | PRINT_ERROR(retval, "DB_ENV->txn_begin" ); |
| 631 | goto error; |
| 632 | } |
| 633 | } |
| 634 | |
| 635 | if ((retval = db->cursor(db, txn, &dbc, 0)) != 0) { |
| 636 | PRINT_ERROR(retval, "DB->cursor" ); |
| 637 | goto error; |
| 638 | } |
| 639 | while ((retval = dbc->c_get(dbc, &key, &data, DB_NEXT)) == 0) { |
| 640 | if (caught_any_signals()) goto cleanup; |
| 641 | if (dump_dbt(&key) != 0) goto error; |
| 642 | if (dump_dbt(&data) != 0) goto error; |
| 643 | } |
| 644 | if (retval != DB_NOTFOUND) { |
| 645 | PRINT_ERROR(retval, "DBC->c_get" ); |
| 646 | goto error; |
| 647 | } |
| 648 | |
| 649 | |
| 650 | if (false) { |
| 651 | error: |
| 652 | g.exitcode = EXIT_FAILURE; |
| 653 | } |
| 654 | cleanup: |
| 655 | if (dbc && (retval = dbc->c_close(dbc)) != 0) { |
| 656 | PRINT_ERROR(retval, "DBC->c_close" ); |
| 657 | g.exitcode = EXIT_FAILURE; |
| 658 | } |
| 659 | if (txn) { |
| 660 | if (retval) { |
| 661 | int r2 = txn->abort(txn); |
| 662 | if (r2) PRINT_ERROR(r2, "DB_TXN->abort" ); |
| 663 | } |
| 664 | else { |
| 665 | retval = txn->commit(txn, 0); |
| 666 | if (retval) PRINT_ERROR(retval, "DB_TXN->abort" ); |
| 667 | } |
| 668 | } |
| 669 | return g.exitcode; |
| 670 | } |
| 671 | |
| 672 | int close_database() |
| 673 | { |
| 674 | DB* db = g.db; |
| 675 | int retval; |
| 676 | |
| 677 | assert(db); |
| 678 | if ((retval = db->close(db, 0)) != 0) { |
| 679 | PRINT_ERROR(retval, "DB->close" ); |
| 680 | goto error; |
| 681 | } |
| 682 | return EXIT_SUCCESS; |
| 683 | error: |
| 684 | return EXIT_FAILURE; |
| 685 | } |
| 686 | |