mdbx-tools: extends dump/load to support all mdbx attributes.

Change-Id: I04772ce0083c46b70f5342e92c7509c9a425724d
This commit is contained in:
Leonid Yuriev 2020-05-15 09:11:11 +03:00
parent 8f31aad0fb
commit 3f0d2a6ac2
2 changed files with 650 additions and 356 deletions

View File

@ -26,7 +26,8 @@
#include <ctype.h>
#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:

View File

@ -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);