diff --git a/src/mdbx_dump.c b/src/mdbx_dump.c index f97d50aa..1a7a5c9f 100644 --- a/src/mdbx_dump.c +++ b/src/mdbx_dump.c @@ -26,7 +26,8 @@ #include #define PRINT 1 -static int mode; +#define GLOBAL 2 +static int mode = GLOBAL; typedef struct flagbit { int bit; @@ -39,7 +40,7 @@ flagbit dbflags[] = {{MDBX_REVERSEKEY, "reversekey"}, {MDBX_DUPFIXED, "dupfixed"}, {MDBX_INTEGERDUP, "integerdup"}, {MDBX_REVERSEDUP, "reversedup"}, - {0, NULL}}; + {0, nullptr}}; #if defined(_WIN32) || defined(_WIN64) #include "wingetopt.h" @@ -92,53 +93,105 @@ static void dumpval(MDBX_val *v) { putchar(' '); c = v->iov_base; end = c + v->iov_len; - while (c < end) { + while (c < end) dumpbyte(*c++); - } putchar('\n'); } +bool quiet = false, rescue = false; +const char *prog; +static void error(const char *func, int rc) { + fprintf(stderr, "%s: %s() error %d %s\n", prog, func, rc, mdbx_strerror(rc)); +} + /* Dump in BDB-compatible format */ -static int dumpit(MDBX_txn *txn, MDBX_dbi dbi, char *name) { - MDBX_cursor *mc; - MDBX_stat ms; - MDBX_val key, data; - MDBX_envinfo info; +static int dump_sdb(MDBX_txn *txn, MDBX_dbi dbi, char *name) { unsigned int flags; - int rc, i; - - rc = mdbx_dbi_flags(txn, dbi, &flags); - if (rc) + int rc = mdbx_dbi_flags(txn, dbi, &flags); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_dbi_flags", rc); return rc; + } + MDBX_stat ms; rc = mdbx_dbi_stat(txn, dbi, &ms, sizeof(ms)); - if (rc) + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_dbi_stat", rc); return rc; + } + MDBX_envinfo info; rc = mdbx_env_info_ex(mdbx_txn_env(txn), txn, &info, sizeof(info)); - if (rc) + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_env_info_ex", rc); return rc; + } printf("VERSION=3\n"); + if (mode & GLOBAL) { + mode -= GLOBAL; + if (info.mi_geo.upper != info.mi_geo.lower) + printf("geometry=l%" PRIu64 ",c%" PRIu64 ",u%" PRIu64 ",s%" PRIu64 + ",g%" PRIu64 "\n", + info.mi_geo.lower, info.mi_geo.current, info.mi_geo.upper, + info.mi_geo.shrink, info.mi_geo.grow); + printf("mapsize=%" PRIu64 "\n", info.mi_geo.upper); + printf("maxreaders=%u\n", info.mi_maxreaders); + + mdbx_canary canary; + rc = mdbx_canary_get(txn, &canary); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_canary_get", rc); + return rc; + } + if (canary.v) + printf("canary=v%" PRIu64 ",x%" PRIu64 ",y%" PRIu64 ",z%" PRIu64 "\n", + canary.v, canary.x, canary.y, canary.z); + } printf("format=%s\n", mode & PRINT ? "print" : "bytevalue"); if (name) printf("database=%s\n", name); printf("type=btree\n"); - printf("mapsize=%" PRIu64 "\n", info.mi_geo.upper); - printf("maxreaders=%u\n", info.mi_maxreaders); + printf("db_pagesize=%u\n", ms.ms_psize); + /* if (ms.ms_mod_txnid) + printf("txnid=%" PRIaTXN "\n", ms.ms_mod_txnid); + else if (!name) + printf("txnid=%" PRIaTXN "\n", mdbx_txn_id(txn)); */ - for (i = 0; dbflags[i].bit; i++) + printf("duplicates=%d\n", (flags & (MDBX_DUPSORT | MDBX_DUPFIXED | + MDBX_INTEGERDUP | MDBX_REVERSEDUP)) + ? 1 + : 0); + for (int i = 0; dbflags[i].bit; i++) if (flags & dbflags[i].bit) printf("%s=1\n", dbflags[i].name); - printf("db_pagesize=%d\n", ms.ms_psize); - printf("HEADER=END\n"); - - rc = mdbx_cursor_open(txn, dbi, &mc); - if (rc) + uint64_t sequence; + rc = mdbx_dbi_sequence(txn, dbi, &sequence, 0); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_dbi_sequence", rc); return rc; + } + if (sequence) + printf("sequence=%" PRIu64 "\n", sequence); - while ((rc = mdbx_cursor_get(mc, &key, &data, MDBX_NEXT)) == MDBX_SUCCESS) { + printf("HEADER=END\n"); /*-------------------------------------------------*/ + + MDBX_cursor *cursor; + MDBX_val key, data; + rc = mdbx_cursor_open(txn, dbi, &cursor); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_cursor_open", rc); + return rc; + } + if (MDBX_DEBUG > 0 && rescue) { + cursor->mc_flags |= C_SKIPORD; + if (cursor->mc_xcursor) + cursor->mc_xcursor->mx_cursor.mc_flags |= C_SKIPORD; + } + + while ((rc = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) == + MDBX_SUCCESS) { if (user_break) { rc = MDBX_EINTR; break; @@ -154,11 +207,12 @@ static int dumpit(MDBX_txn *txn, MDBX_dbi dbi, char *name) { printf("DATA=END\n"); if (rc == MDBX_NOTFOUND) rc = MDBX_SUCCESS; - + if (unlikely(rc != MDBX_SUCCESS)) + error("mdbx_cursor_get", rc); return rc; } -static void usage(char *prog) { +static void usage(void) { fprintf(stderr, "usage: %s [-V] [-q] [-f file] [-l] [-p] [-a|-s subdb] [-r] " "dbpath\n" @@ -175,18 +229,25 @@ static void usage(char *prog) { exit(EXIT_FAILURE); } +static int anyway_greater(const MDBX_val *a, const MDBX_val *b) { + (void)a; + (void)b; + return 1; +} + int main(int argc, char *argv[]) { int i, rc; MDBX_env *env; MDBX_txn *txn; MDBX_dbi dbi; - char *prog = argv[0]; + prog = argv[0]; char *envname; - char *subname = NULL; - int alldbs = 0, envflags = 0, list = 0, quiet = 0, rescue = 0; + char *subname = nullptr; + unsigned envflags = 0; + bool alldbs = false, list = false; if (argc < 2) - usage(prog); + usage(); while ((i = getopt(argc, argv, "af:lnps:Vrq")) != EOF) { switch (i) { @@ -205,16 +266,16 @@ int main(int argc, char *argv[]) { mdbx_build.options); return EXIT_SUCCESS; case 'l': - list = 1; + list = true; /*FALLTHROUGH*/; __fallthrough; case 'a': if (subname) - usage(prog); - alldbs++; + usage(); + alldbs = true; break; case 'f': - if (freopen(optarg, "w", stdout) == NULL) { + if (freopen(optarg, "w", stdout) == nullptr) { fprintf(stderr, "%s: %s: reopen: %s\n", prog, optarg, mdbx_strerror(errno)); exit(EXIT_FAILURE); @@ -228,22 +289,22 @@ int main(int argc, char *argv[]) { break; case 's': if (alldbs) - usage(prog); + usage(); subname = optarg; break; case 'q': - quiet = 1; + quiet = true; break; case 'r': - rescue = 1; + rescue = true; break; default: - usage(prog); + usage(); } } if (optind != argc - 1) - usage(prog); + usage(); #if defined(_WIN32) || defined(_WIN64) SetConsoleCtrlHandler(ConsoleBreakHandlerRoutine, true); @@ -263,78 +324,97 @@ int main(int argc, char *argv[]) { fprintf(stderr, "mdbx_dump %s (%s, T-%s)\nRunning for %s...\n", mdbx_version.git.describe, mdbx_version.git.datetime, mdbx_version.git.tree, envname); - fflush(NULL); + fflush(nullptr); } rc = mdbx_env_create(&env); - if (rc) { - fprintf(stderr, "mdbx_env_create failed, error %d %s\n", rc, - mdbx_strerror(rc)); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_env_create", rc); return EXIT_FAILURE; } if (alldbs || subname) { - mdbx_env_set_maxdbs(env, 2); + rc = mdbx_env_set_maxdbs(env, 2); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_env_set_maxdbs", rc); + goto env_close; + } } rc = mdbx_env_open( env, envname, envflags | (rescue ? MDBX_RDONLY | MDBX_EXCLUSIVE : MDBX_RDONLY), 0); - if (rc) { - fprintf(stderr, "mdbx_env_open failed, error %d %s\n", rc, - mdbx_strerror(rc)); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_env_open", rc); goto env_close; } - rc = mdbx_txn_begin(env, NULL, MDBX_RDONLY, &txn); - if (rc) { - fprintf(stderr, "mdbx_txn_begin failed, error %d %s\n", rc, - mdbx_strerror(rc)); + rc = mdbx_txn_begin(env, nullptr, MDBX_RDONLY, &txn); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_txn_begin", rc); goto env_close; } rc = mdbx_dbi_open(txn, subname, 0, &dbi); - if (rc) { - fprintf(stderr, "mdbx_open failed, error %d %s\n", rc, mdbx_strerror(rc)); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_dbi_open", rc); goto txn_abort; } if (alldbs) { - MDBX_cursor *cursor; - MDBX_val key; - int count = 0; + assert(dbi == MAIN_DBI); - rc = mdbx_cursor_open(txn, dbi, &cursor); - if (rc) { - fprintf(stderr, "mdbx_cursor_open failed, error %d %s\n", rc, - mdbx_strerror(rc)); + MDBX_cursor *cursor; + rc = mdbx_cursor_open(txn, MAIN_DBI, &cursor); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_cursor_open", rc); goto txn_abort; } - while ((rc = mdbx_cursor_get(cursor, &key, NULL, MDBX_NEXT_NODUP)) == 0) { + if (MDBX_DEBUG > 0 && rescue) { + cursor->mc_flags |= C_SKIPORD; + if (cursor->mc_xcursor) + cursor->mc_xcursor->mx_cursor.mc_flags |= C_SKIPORD; + } + + bool have_raw = false; + int count = 0; + MDBX_val key; + while (MDBX_SUCCESS == + (rc = mdbx_cursor_get(cursor, &key, nullptr, MDBX_NEXT_NODUP))) { if (user_break) { rc = MDBX_EINTR; break; } - char *str; - MDBX_dbi db2; + if (memchr(key.iov_base, '\0', key.iov_len)) continue; - count++; - str = mdbx_malloc(key.iov_len + 1); - memcpy(str, key.iov_base, key.iov_len); - str[key.iov_len] = '\0'; - rc = mdbx_dbi_open(txn, str, 0, &db2); - if (rc == MDBX_SUCCESS) { + subname = mdbx_malloc(key.iov_len + 1); + memcpy(subname, key.iov_base, key.iov_len); + subname[key.iov_len] = '\0'; + + MDBX_dbi sub_dbi; + rc = mdbx_dbi_open_ex(txn, subname, 0, &sub_dbi, + rescue ? anyway_greater : nullptr, + rescue ? anyway_greater : nullptr); + if (unlikely(rc != MDBX_SUCCESS)) { + if (rc == MDBX_INCOMPATIBLE) { + have_raw = true; + continue; + } + error("mdbx_dbi_open", rc); + if (!rescue) + break; + } else { + count++; if (list) { - printf("%s\n", str); - list++; + printf("%s\n", subname); } else { - rc = dumpit(txn, db2, str); - if (rc) { + rc = dump_sdb(txn, sub_dbi, subname); + if (unlikely(rc != MDBX_SUCCESS)) { if (!rescue) break; fprintf(stderr, "%s: %s: ignore %s for `%s` and continue\n", prog, - envname, mdbx_strerror(rc), str); + envname, mdbx_strerror(rc), subname); /* Here is a hack for rescue mode, don't do that: * - we should restart transaction in case error due * database corruption; @@ -344,39 +424,51 @@ int main(int argc, char *argv[]) { * mode and transaction is the same, i.e. has the same address * and so on. */ rc = mdbx_txn_reset(txn); - if (rc) { - fprintf(stderr, "mdbx_txn_reset failed, error %d %s\n", rc, - mdbx_strerror(rc)); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_txn_reset", rc); goto env_close; } rc = mdbx_txn_renew(txn); - if (rc) { - fprintf(stderr, "mdbx_txn_renew failed, error %d %s\n", rc, - mdbx_strerror(rc)); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_txn_renew", rc); goto env_close; } } } - mdbx_dbi_close(env, db2); + rc = mdbx_dbi_close(env, sub_dbi); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_dbi_close", rc); + break; + } } - mdbx_free(str); - if (rc) - continue; + mdbx_free(subname); } mdbx_cursor_close(cursor); - if (!count) { + cursor = nullptr; + + if (have_raw && (!count /* || rescue */)) + rc = dump_sdb(txn, MAIN_DBI, nullptr); + else if (!count) { fprintf(stderr, "%s: %s does not contain multiple databases\n", prog, envname); rc = MDBX_NOTFOUND; - } else if (rc == MDBX_INCOMPATIBLE) { - /* LY: the record it not a named sub-db. */ - rc = MDBX_SUCCESS; } } else { - rc = dumpit(txn, dbi, subname); + rc = dump_sdb(txn, dbi, subname); + } + + switch (rc) { + case MDBX_NOTFOUND: + rc = MDBX_SUCCESS; + case MDBX_SUCCESS: + break; + case MDBX_EINTR: + fprintf(stderr, "Interrupted by signal/user\n"); + break; + default: + if (unlikely(rc != MDBX_SUCCESS)) + error("mdbx_cursor_get", rc); } - if (rc && rc != MDBX_NOTFOUND) - fprintf(stderr, "%s: %s: %s\n", prog, envname, mdbx_strerror(rc)); mdbx_dbi_close(env, dbi); txn_abort: diff --git a/src/mdbx_load.c b/src/mdbx_load.c index ee193352..52e454e6 100644 --- a/src/mdbx_load.c +++ b/src/mdbx_load.c @@ -45,178 +45,305 @@ static void signal_handler(int sig) { #endif /* !WINDOWS */ +static char *prog; +static size_t lineno; +static void error(const char *func, int rc) { + if (lineno) + fprintf(stderr, "%s: at input line %" PRIiSIZE ": %s() error %d, %s\n", + prog, lineno, func, rc, mdbx_strerror(rc)); + else + fprintf(stderr, "%s: %s() error %d %s\n", prog, func, rc, + mdbx_strerror(rc)); +} + +static char *valstr(char *line, const char *item) { + const size_t len = strlen(item); + if (strncmp(line, item, len) != 0) + return nullptr; + if (line[len] != '=') { + if (line[len] > ' ') + return nullptr; + fprintf(stderr, "%s: line %" PRIiSIZE ": unexpected line format for '%s'\n", + prog, lineno, item); + exit(EXIT_FAILURE); + } + char *ptr = strchr(line, '\n'); + if (ptr) + *ptr = '\0'; + return line + len + 1; +} + +static bool valnum(char *line, const char *item, uint64_t *value) { + char *str = valstr(line, item); + if (!str) + return false; + + char *end = nullptr; + *value = strtoull(str, &end, 0); + if (end && *end) { + fprintf(stderr, + "%s: line %" PRIiSIZE ": unexpected number format for '%s'\n", prog, + lineno, item); + exit(EXIT_FAILURE); + } + return true; +} + +static bool valbool(char *line, const char *item, bool *value) { + uint64_t u64; + if (!valnum(line, item, &u64)) + return false; + + if (u64 > 1) { + fprintf(stderr, "%s: line %" PRIiSIZE ": unexpected value for '%s'\n", prog, + lineno, item); + exit(EXIT_FAILURE); + } + *value = u64 != 0; + return true; +} + +/*----------------------------------------------------------------------------*/ + +static char *subname = nullptr; +static int dbi_flags; +static txnid_t txnid; +static uint64_t sequence; +static mdbx_canary canary; +static MDBX_envinfo envinfo; + #define PRINT 1 #define NOHDR 2 -static int mode; +#define GLOBAL 4 +static int mode = GLOBAL; -static char *subname = NULL; -static size_t lineno; -static int version; - -static int dbi_flags; -static char *prog; -static bool Eof; - -static MDBX_envinfo envinfo; static MDBX_val kbuf, dbuf; static MDBX_val k0buf; #define STRLENOF(s) (sizeof(s) - 1) typedef struct flagbit { - int bit; + unsigned bit; char *name; - int len; + unsigned len; } flagbit; #define S(s) s, STRLENOF(s) -flagbit dbflags[] = {{MDBX_REVERSEKEY, S("reversekey")}, - {MDBX_DUPSORT, S("dupsort")}, - {MDBX_INTEGERKEY, S("integerkey")}, - {MDBX_DUPFIXED, S("dupfixed")}, - {MDBX_INTEGERDUP, S("integerdup")}, - {MDBX_REVERSEDUP, S("reversedup")}, - {0, NULL, 0}}; +flagbit dbflags[] = { + {MDBX_REVERSEKEY, S("reversekey")}, {MDBX_DUPSORT, S("duplicates")}, + {MDBX_DUPSORT, S("dupsort")}, {MDBX_INTEGERKEY, S("integerkey")}, + {MDBX_DUPFIXED, S("dupfixed")}, {MDBX_INTEGERDUP, S("integerdup")}, + {MDBX_REVERSEDUP, S("reversedup")}, {0, nullptr, 0}}; -static void readhdr(void) { - char *ptr; - - dbi_flags = 0; - while (fgets(dbuf.iov_base, (int)dbuf.iov_len, stdin) != NULL) { - lineno++; - - if (!strncmp(dbuf.iov_base, "db_pagesize=", STRLENOF("db_pagesize="))) { - envinfo.mi_dxb_pagesize = - atoi((char *)dbuf.iov_base + STRLENOF("db_pagesize=")); - continue; - } - - if (!strncmp(dbuf.iov_base, "duplicates=", STRLENOF("duplicates="))) { - dbi_flags |= MDBX_DUPSORT; - continue; - } - - if (!strncmp(dbuf.iov_base, "VERSION=", STRLENOF("VERSION="))) { - version = atoi((char *)dbuf.iov_base + STRLENOF("VERSION=")); - if (version > 3) { - fprintf(stderr, "%s: line %" PRIiSIZE ": unsupported VERSION %d\n", - prog, lineno, version); - exit(EXIT_FAILURE); - } - continue; - } - - if (!strncmp(dbuf.iov_base, "HEADER=END", STRLENOF("HEADER=END"))) - return; - - if (!strncmp(dbuf.iov_base, "format=", STRLENOF("format="))) { - if (!strncmp((char *)dbuf.iov_base + STRLENOF("FORMAT="), "print", - STRLENOF("print"))) - mode |= PRINT; - else if (strncmp((char *)dbuf.iov_base + STRLENOF("FORMAT="), "bytevalue", - STRLENOF("bytevalue"))) { - fprintf(stderr, "%s: line %" PRIiSIZE ": unsupported FORMAT %s\n", prog, - lineno, (char *)dbuf.iov_base + STRLENOF("FORMAT=")); - exit(EXIT_FAILURE); - } - continue; - } - - if (!strncmp(dbuf.iov_base, "database=", STRLENOF("database="))) { - ptr = memchr(dbuf.iov_base, '\n', dbuf.iov_len); - if (ptr) - *ptr = '\0'; - if (subname) - mdbx_free(subname); - subname = mdbx_strdup((char *)dbuf.iov_base + STRLENOF("database=")); - continue; - } - - if (!strncmp(dbuf.iov_base, "type=", STRLENOF("type="))) { - if (strncmp((char *)dbuf.iov_base + STRLENOF("type="), "btree", - STRLENOF("btree"))) { - fprintf(stderr, "%s: line %" PRIiSIZE ": unsupported type %s\n", prog, - lineno, (char *)dbuf.iov_base + STRLENOF("type=")); - exit(EXIT_FAILURE); - } - continue; - } - - if (!strncmp(dbuf.iov_base, "mapaddr=", STRLENOF("mapaddr="))) { - int i; - ptr = memchr(dbuf.iov_base, '\n', dbuf.iov_len); - if (ptr) - *ptr = '\0'; - void *unused; - i = sscanf((char *)dbuf.iov_base + STRLENOF("mapaddr="), "%p", &unused); - if (i != 1) { - fprintf(stderr, "%s: line %" PRIiSIZE ": invalid mapaddr %s\n", prog, - lineno, (char *)dbuf.iov_base + STRLENOF("mapaddr=")); - exit(EXIT_FAILURE); - } - continue; - } - - if (!strncmp(dbuf.iov_base, "mapsize=", STRLENOF("mapsize="))) { - int i; - ptr = memchr(dbuf.iov_base, '\n', dbuf.iov_len); - if (ptr) - *ptr = '\0'; - i = sscanf((char *)dbuf.iov_base + STRLENOF("mapsize="), "%" PRIu64, - &envinfo.mi_mapsize); - if (i != 1) { - fprintf(stderr, "%s: line %" PRIiSIZE ": invalid mapsize %s\n", prog, - lineno, (char *)dbuf.iov_base + STRLENOF("mapsize=")); - exit(EXIT_FAILURE); - } - continue; - } - - if (!strncmp(dbuf.iov_base, "maxreaders=", STRLENOF("maxreaders="))) { - int i; - ptr = memchr(dbuf.iov_base, '\n', dbuf.iov_len); - if (ptr) - *ptr = '\0'; - i = sscanf((char *)dbuf.iov_base + STRLENOF("maxreaders="), "%u", - &envinfo.mi_maxreaders); - if (i != 1) { - fprintf(stderr, "%s: line %" PRIiSIZE ": invalid maxreaders %s\n", prog, - lineno, (char *)dbuf.iov_base + STRLENOF("maxreaders=")); - exit(EXIT_FAILURE); - } - continue; - } - - int i; - for (i = 0; dbflags[i].bit; i++) { - if (!strncmp(dbuf.iov_base, dbflags[i].name, dbflags[i].len) && - ((char *)dbuf.iov_base)[dbflags[i].len] == '=') { - if (((char *)dbuf.iov_base)[dbflags[i].len + 1] == '1') - dbi_flags |= dbflags[i].bit; - break; - } - } - if (!dbflags[i].bit) { - ptr = memchr(dbuf.iov_base, '=', dbuf.iov_len); - if (!ptr) { - fprintf(stderr, "%s: line %" PRIiSIZE ": unexpected format\n", prog, - lineno); - exit(EXIT_FAILURE); - } else { - *ptr = '\0'; - fprintf(stderr, - "%s: line %" PRIiSIZE ": unrecognized keyword ignored: %s\n", - prog, lineno, (char *)dbuf.iov_base); - } - } +static int readhdr(void) { + /* reset parameters */ + if (subname) { + free(subname); + subname = nullptr; } - Eof = true; + dbi_flags = 0; + txnid = 0; + sequence = 0; + + while (true) { + errno = 0; + if (fgets(dbuf.iov_base, (int)dbuf.iov_len, stdin) == nullptr) + return errno ? errno : EOF; + if (user_break) + return MDBX_EINTR; + + lineno++; + uint64_t u64; + + if (valnum(dbuf.iov_base, "VERSION", &u64)) { + if (u64 != 3) { + fprintf(stderr, + "%s: line %" PRIiSIZE ": unsupported value %" PRIu64 + " for %s\n", + prog, lineno, u64, "VERSION"); + exit(EXIT_FAILURE); + } + continue; + } + + if (valnum(dbuf.iov_base, "db_pagesize", &u64)) { + if (!(mode & GLOBAL) && envinfo.mi_dxb_pagesize != u64) + fprintf(stderr, + "%s: line %" PRIiSIZE ": ignore value %" PRIu64 + " for '%s' in non-global context\n", + prog, lineno, u64, "db_pagesize"); + else if (u64 < MDBX_MIN_PAGESIZE || u64 > MDBX_MAX_PAGESIZE) + fprintf(stderr, + "%s: line %" PRIiSIZE ": ignore unsupported value %" PRIu64 + " for %s\n", + prog, lineno, u64, "db_pagesize"); + else + envinfo.mi_dxb_pagesize = (uint32_t)u64; + continue; + } + + char *str = valstr(dbuf.iov_base, "format"); + if (str) { + if (strcmp(str, "print") == 0) { + mode |= PRINT; + continue; + } + if (strcmp(str, "bytevalue") == 0) { + mode &= ~PRINT; + continue; + } + fprintf(stderr, "%s: line %" PRIiSIZE ": unsupported value '%s' for %s\n", + prog, lineno, str, "format"); + exit(EXIT_FAILURE); + } + + str = valstr(dbuf.iov_base, "database"); + if (str) { + if (*str) { + subname = mdbx_strdup(str); + if (!subname) { + perror("strdup()"); + exit(EXIT_FAILURE); + } + } + continue; + } + + str = valstr(dbuf.iov_base, "type"); + if (str) { + if (strcmp(str, "btree") != 0) { + fprintf(stderr, + "%s: line %" PRIiSIZE ": unsupported value '%s' for %s\n", prog, + lineno, str, "type"); + exit(EXIT_FAILURE); + } + continue; + } + + if (valnum(dbuf.iov_base, "mapaddr", &u64)) { + if (u64) + fprintf(stderr, + "%s: line %" PRIiSIZE ": ignore unsupported value 0x%" PRIx64 + " for %s\n", + prog, lineno, u64, "mapaddr"); + continue; + } + + if (valnum(dbuf.iov_base, "mapsize", &u64)) { + if (!(mode & GLOBAL)) + fprintf(stderr, + "%s: line %" PRIiSIZE ": ignore value %" PRIu64 + " for '%s' in non-global context\n", + prog, lineno, u64, "mapsize"); + else if (u64 < MIN_MAPSIZE || u64 > MAX_MAPSIZE64) + fprintf(stderr, + "%s: line %" PRIiSIZE ": ignore unsupported value 0x%" PRIx64 + " for %s\n", + prog, lineno, u64, "mapsize"); + else + envinfo.mi_mapsize = (size_t)u64; + continue; + } + + if (valnum(dbuf.iov_base, "maxreaders", &u64)) { + if (!(mode & GLOBAL)) + fprintf(stderr, + "%s: line %" PRIiSIZE ": ignore value %" PRIu64 + " for '%s' in non-global context\n", + prog, lineno, u64, "maxreaders"); + else if (u64 < 1 || u64 > MDBX_READERS_LIMIT) + fprintf(stderr, + "%s: line %" PRIiSIZE ": ignore unsupported value 0x%" PRIx64 + " for %s\n", + prog, lineno, u64, "maxreaders"); + else + envinfo.mi_maxreaders = (int)u64; + continue; + } + + if (valnum(dbuf.iov_base, "txnid", &u64)) { + if (u64 < MIN_TXNID || u64 > MAX_TXNID) + fprintf(stderr, + "%s: line %" PRIiSIZE ": ignore unsupported value 0x%" PRIx64 + " for %s\n", + prog, lineno, u64, "txnid"); + txnid = u64; + continue; + } + + if (valnum(dbuf.iov_base, "sequence", &u64)) { + sequence = u64; + continue; + } + + str = valstr(dbuf.iov_base, "geometry"); + if (str) { + if (!(mode & GLOBAL)) + fprintf(stderr, + "%s: line %" PRIiSIZE ": ignore values %s" + " for '%s' in non-global context\n", + prog, lineno, str, "geometry"); + else if (sscanf(str, + "l%" PRIu64 ",c%" PRIu64 ",u%" PRIu64 ",s%" PRIu64 + ",g%" PRIu64, + &envinfo.mi_geo.lower, &envinfo.mi_geo.current, + &envinfo.mi_geo.upper, &envinfo.mi_geo.shrink, + &envinfo.mi_geo.grow) != 5) { + fprintf(stderr, + "%s: line %" PRIiSIZE ": unexpected line format for '%s'\n", + prog, lineno, "geometry"); + exit(EXIT_FAILURE); + } + continue; + } + + str = valstr(dbuf.iov_base, "canary"); + if (str) { + if (!(mode & GLOBAL)) + fprintf(stderr, + "%s: line %" PRIiSIZE ": ignore values %s" + " for '%s' in non-global context\n", + prog, lineno, str, "canary"); + else if (sscanf(str, "v%" PRIu64 ",x%" PRIu64 ",y%" PRIu64 ",z%" PRIu64, + &canary.v, &canary.x, &canary.y, &canary.z) != 4) { + fprintf(stderr, + "%s: line %" PRIiSIZE ": unexpected line format for '%s'\n", + prog, lineno, "canary"); + exit(EXIT_FAILURE); + } + continue; + } + + for (int i = 0; dbflags[i].bit; i++) { + bool value; + if (valbool(dbuf.iov_base, dbflags[i].name, &value)) { + if (value) + dbi_flags |= dbflags[i].bit; + else + dbi_flags &= ~dbflags[i].bit; + goto next; + } + } + + str = valstr(dbuf.iov_base, "HEADER"); + if (str) { + if (strcmp(str, "END") == 0) + return MDBX_SUCCESS; + } + + fprintf(stderr, + "%s: line %" PRIiSIZE ": unrecognized keyword ignored: %s\n", prog, + lineno, (char *)dbuf.iov_base); + next:; + } + return EOF; } -static void badend(void) { +static int badend(void) { fprintf(stderr, "%s: line %" PRIiSIZE ": unexpected end of input\n", prog, lineno); + return errno ? errno : MDBX_ENODATA; } static int unhex(unsigned char *c2) { @@ -237,29 +364,26 @@ static int readline(MDBX_val *out, MDBX_val *buf) { size_t len, l2; int c; + if (user_break) + return MDBX_EINTR; + + errno = 0; if (!(mode & NOHDR)) { c = fgetc(stdin); - if (c == EOF) { - Eof = true; - return EOF; - } + if (c == EOF) + return errno ? errno : EOF; if (c != ' ') { lineno++; - if (fgets(buf->iov_base, (int)buf->iov_len, stdin) == NULL) { - badend: - Eof = true; - badend(); - return EOF; + errno = 0; + if (fgets(buf->iov_base, (int)buf->iov_len, stdin)) { + if (c == 'D' && !strncmp(buf->iov_base, "ATA=END", STRLENOF("ATA=END"))) + return EOF; } - if (c == 'D' && !strncmp(buf->iov_base, "ATA=END", STRLENOF("ATA=END"))) - return EOF; - goto badend; + return badend(); } } - if (fgets(buf->iov_base, (int)buf->iov_len, stdin) == NULL) { - Eof = true; - return EOF; - } + if (fgets(buf->iov_base, (int)buf->iov_len, stdin) == nullptr) + return errno ? errno : EOF; lineno++; c1 = buf->iov_base; @@ -270,18 +394,15 @@ static int readline(MDBX_val *out, MDBX_val *buf) { while (c1[len - 1] != '\n') { buf->iov_base = mdbx_realloc(buf->iov_base, buf->iov_len * 2); if (!buf->iov_base) { - Eof = true; fprintf(stderr, "%s: line %" PRIiSIZE ": out of memory, line too long\n", prog, lineno); - return EOF; + return MDBX_ENOMEM; } c1 = buf->iov_base; c1 += l2; - if (fgets((char *)c1, (int)buf->iov_len + 1, stdin) == NULL) { - Eof = true; - badend(); - return EOF; - } + errno = 0; + if (fgets((char *)c1, (int)buf->iov_len + 1, stdin) == nullptr) + return errno ? errno : EOF; buf->iov_len *= 2; len = strlen((char *)c1); l2 += len; @@ -297,11 +418,8 @@ static int readline(MDBX_val *out, MDBX_val *buf) { if (c2[1] == '\\') { *c1++ = '\\'; } else { - if (c2 + 3 > end || !isxdigit(c2[1]) || !isxdigit(c2[2])) { - Eof = true; - badend(); - return EOF; - } + if (c2 + 3 > end || !isxdigit(c2[1]) || !isxdigit(c2[2])) + return badend(); *c1++ = (char)unhex(++c2); } c2 += 2; @@ -312,17 +430,11 @@ static int readline(MDBX_val *out, MDBX_val *buf) { } } else { /* odd length not allowed */ - if (len & 1) { - Eof = true; - badend(); - return EOF; - } + if (len & 1) + return badend(); while (c2 < end) { - if (!isxdigit(*c2) || !isxdigit(c2[1])) { - Eof = true; - badend(); - return EOF; - } + if (!isxdigit(*c2) || !isxdigit(c2[1])) + return badend(); *c1++ = (char)unhex(c2); c2 += 2; } @@ -330,7 +442,7 @@ static int readline(MDBX_val *out, MDBX_val *buf) { c2 = out->iov_base = buf->iov_base; out->iov_len = c1 - c2; - return 0; + return MDBX_SUCCESS; } static void usage(void) { @@ -358,15 +470,15 @@ static int anyway_greater(const MDBX_val *a, const MDBX_val *b) { int main(int argc, char *argv[]) { int i, rc; - MDBX_env *env = NULL; - MDBX_txn *txn = NULL; - MDBX_cursor *mc = NULL; + MDBX_env *env = nullptr; + MDBX_txn *txn = nullptr; + MDBX_cursor *mc = nullptr; MDBX_dbi dbi; - char *envname = NULL; + char *envname = nullptr; int envflags = MDBX_UTTERLY_NOSYNC, putflags = 0; - int append = 0; - int quiet = 0; - int rescue = 0; + bool append = false; + bool quiet = false; + bool rescue = false; MDBX_val prevk; prog = argv[0]; @@ -390,10 +502,10 @@ int main(int argc, char *argv[]) { mdbx_build.options); return EXIT_SUCCESS; case 'a': - append = 1; + append = true; break; case 'f': - if (freopen(optarg, "r", stdin) == NULL) { + if (freopen(optarg, "r", stdin) == nullptr) { fprintf(stderr, "%s: %s: open: %s\n", prog, optarg, mdbx_strerror(errno)); exit(EXIT_FAILURE); @@ -412,10 +524,10 @@ int main(int argc, char *argv[]) { mode |= NOHDR | PRINT; break; case 'q': - quiet = 1; + quiet = true; break; case 'r': - rescue = 1; + rescue = true; break; default: usage(); @@ -443,56 +555,73 @@ int main(int argc, char *argv[]) { printf("mdbx_load %s (%s, T-%s)\nRunning for %s...\n", mdbx_version.git.describe, mdbx_version.git.datetime, mdbx_version.git.tree, envname); - fflush(NULL); + fflush(nullptr); dbuf.iov_len = 4096; dbuf.iov_base = mdbx_malloc(dbuf.iov_len); /* read first header for mapsize= */ - if (!(mode & NOHDR)) - readhdr(); + if (!(mode & NOHDR)) { + rc = readhdr(); + if (unlikely(rc != MDBX_SUCCESS)) { + if (rc == EOF) + rc = ENODATA; + error("readheader", rc); + goto env_close; + } + } rc = mdbx_env_create(&env); - if (rc) { - fprintf(stderr, "mdbx_env_create failed, error %d %s\n", rc, - mdbx_strerror(rc)); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_env_create", rc); return EXIT_FAILURE; } mdbx_env_set_maxdbs(env, 2); - -#ifdef MDBX_FIXEDMAP - if (info.mi_mapaddr) - envflags |= MDBX_FIXEDMAP; -#endif - - if (envinfo.mi_mapsize) { - if (envinfo.mi_mapsize > INTPTR_MAX) { - fprintf(stderr, - "Database size is too large for current system (mapsize=%" PRIu64 - " is great than system-limit %zi)\n", - envinfo.mi_mapsize, INTPTR_MAX); + if (envinfo.mi_maxreaders) { + rc = mdbx_env_set_maxreaders(env, envinfo.mi_maxreaders); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_env_set_maxreaders", rc); goto env_close; } - rc = mdbx_env_set_geometry(env, 0, 0, (intptr_t)envinfo.mi_mapsize, -1, -1, - -1); - if (rc) { - fprintf(stderr, "mdbx_env_set_geometry failed, error %d %s\n", rc, - mdbx_strerror(rc)); + } + + if (envinfo.mi_mapsize) { + if (envinfo.mi_geo.current) { + rc = mdbx_env_set_geometry( + env, (intptr_t)envinfo.mi_geo.lower, (intptr_t)envinfo.mi_geo.current, + (intptr_t)envinfo.mi_geo.upper, (intptr_t)envinfo.mi_geo.shrink, + (intptr_t)envinfo.mi_geo.grow, + envinfo.mi_dxb_pagesize ? (intptr_t)envinfo.mi_dxb_pagesize : -1); + } else { + if (envinfo.mi_mapsize > MAX_MAPSIZE) { + fprintf( + stderr, + "Database size is too large for current system (mapsize=%" PRIu64 + " is great than system-limit %zu)\n", + envinfo.mi_mapsize, (size_t)MAX_MAPSIZE); + goto env_close; + } + rc = mdbx_env_set_geometry( + env, (intptr_t)envinfo.mi_mapsize, (intptr_t)envinfo.mi_mapsize, + (intptr_t)envinfo.mi_mapsize, 0, 0, + envinfo.mi_dxb_pagesize ? (intptr_t)envinfo.mi_dxb_pagesize : -1); + } + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_env_set_geometry", rc); goto env_close; } } rc = mdbx_env_open(env, envname, envflags, 0664); - if (rc) { - fprintf(stderr, "mdbx_env_open failed, error %d %s\n", rc, - mdbx_strerror(rc)); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_env_open", rc); goto env_close; } kbuf.iov_len = mdbx_env_get_maxvalsize_ex(env, MDBX_DUPSORT); if (kbuf.iov_len >= INTPTR_MAX / 4) { - fprintf(stderr, "mdbx_env_get_maxkeysize failed, returns %zu\n", + fprintf(stderr, "mdbx_env_get_maxkeysize() failed, returns %zu\n", kbuf.iov_len); goto env_close; } @@ -502,42 +631,84 @@ int main(int argc, char *argv[]) { k0buf.iov_base = (char *)kbuf.iov_base + kbuf.iov_len; prevk.iov_base = k0buf.iov_base; - while (!Eof) { + while (rc == MDBX_SUCCESS) { if (user_break) { rc = MDBX_EINTR; break; } - rc = mdbx_txn_begin(env, NULL, 0, &txn); - if (rc) { - fprintf(stderr, "mdbx_txn_begin failed, error %d %s\n", rc, - mdbx_strerror(rc)); + rc = mdbx_txn_begin(env, nullptr, 0, &txn); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_txn_begin", rc); goto env_close; } + if (mode & GLOBAL) { + mode -= GLOBAL; + if (canary.v | canary.x | canary.y | canary.z) { + rc = mdbx_canary_put(txn, &canary); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_canary_put", rc); + goto txn_abort; + } + } + } + + const char *const dbi_name = subname ? subname : "@MAIN"; rc = mdbx_dbi_open_ex(txn, subname, dbi_flags | MDBX_CREATE, &dbi, - append ? anyway_greater : NULL, - append ? anyway_greater : NULL); - if (rc) { - fprintf(stderr, "mdbx_open failed, error %d %s\n", rc, mdbx_strerror(rc)); + append ? anyway_greater : nullptr, + append ? anyway_greater : nullptr); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_dbi_open_ex", rc); goto txn_abort; } + uint64_t present_sequence; + rc = mdbx_dbi_sequence(txn, dbi, &present_sequence, 0); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_dbi_sequence", rc); + goto txn_abort; + } + if (present_sequence > sequence) { + fprintf(stderr, + "present sequence for '%s' value (%" PRIu64 + ") is greated than loaded (%" PRIu64 ")\n", + dbi_name, present_sequence, sequence); + rc = MDBX_RESULT_TRUE; + goto txn_abort; + } + if (present_sequence < sequence) { + rc = mdbx_dbi_sequence(txn, dbi, nullptr, sequence - present_sequence); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_dbi_sequence", rc); + goto txn_abort; + } + } + rc = mdbx_cursor_open(txn, dbi, &mc); - if (rc) { - fprintf(stderr, "mdbx_cursor_open failed, error %d %s\n", rc, - mdbx_strerror(rc)); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_cursor_open", rc); goto txn_abort; } + /* if (append) { + mc->mc_flags |= C_SKIPORD; + if (mc->mc_xcursor) + mc->mc_xcursor->mx_cursor.mc_flags |= C_SKIPORD; + } */ int batch = 0; prevk.iov_len = 0; - while (1) { + while (rc == MDBX_SUCCESS) { MDBX_val key; rc = readline(&key, &kbuf); - if (rc) /* rc == EOF */ + if (rc != MDBX_SUCCESS) /* rc == EOF */ break; + if (user_break) { + rc = MDBX_EINTR; + break; + } + MDBX_val data; rc = readline(&data, &dbuf); if (rc) { @@ -565,50 +736,81 @@ int main(int argc, char *argv[]) { mdbx_strerror(rc)); continue; } - if (rc) { - fprintf(stderr, "mdbx_cursor_put failed, error %d %s\n", rc, - mdbx_strerror(rc)); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_cursor_put", rc); goto txn_abort; } batch++; - if (batch == 100) { + + MDBX_txn_info txn_info; + rc = mdbx_txn_info(txn, &txn_info, false); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_txn_info", rc); + goto txn_abort; + } + + if (batch == 10000 || txn_info.txn_space_dirty > MEGABYTE * 16) { + mdbx_cursor_close(mc); + mc = nullptr; rc = mdbx_txn_commit(txn); - if (rc) { - fprintf(stderr, "%s: line %" PRIiSIZE ": txn_commit: %s\n", prog, - lineno, mdbx_strerror(rc)); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_txn_commit", rc); goto env_close; } - rc = mdbx_txn_begin(env, NULL, 0, &txn); - if (rc) { - fprintf(stderr, "mdbx_txn_begin failed, error %d %s\n", rc, - mdbx_strerror(rc)); + batch = 0; + + rc = mdbx_txn_begin(env, nullptr, 0, &txn); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_txn_begin", rc); goto env_close; } rc = mdbx_cursor_open(txn, dbi, &mc); - if (rc) { - fprintf(stderr, "mdbx_cursor_open failed, error %d %s\n", rc, - mdbx_strerror(rc)); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_cursor_open", rc); goto txn_abort; } - batch = 0; + /* if (append) { + mc->mc_flags |= C_SKIPORD; + if (mc->mc_xcursor) + mc->mc_xcursor->mx_cursor.mc_flags |= C_SKIPORD; + } */ } } + + mdbx_cursor_close(mc); + mc = nullptr; rc = mdbx_txn_commit(txn); - txn = NULL; - if (rc) { - fprintf(stderr, "%s: line %" PRIiSIZE ": txn_commit: %s\n", prog, lineno, - mdbx_strerror(rc)); + txn = nullptr; + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_txn_commit", rc); + goto env_close; + } + rc = mdbx_dbi_close(env, dbi); + if (unlikely(rc != MDBX_SUCCESS)) { + error("mdbx_dbi_close", rc); goto env_close; } - mdbx_dbi_close(env, dbi); - subname = NULL; /* try read next header */ if (!(mode & NOHDR)) - readhdr(); + rc = readhdr(); + } + + switch (rc) { + case EOF: + rc = MDBX_SUCCESS; + case MDBX_SUCCESS: + break; + case MDBX_EINTR: + fprintf(stderr, "Interrupted by signal/user\n"); + break; + default: + if (unlikely(rc != MDBX_SUCCESS)) + error("readline", rc); } txn_abort: + mdbx_cursor_close(mc); mdbx_txn_abort(txn); env_close: mdbx_env_close(env);