mirror of
https://gitflic.ru/project/erthink/libmdbx.git
synced 2026-07-14 18:46:08 +00:00
81eaf88944
2026-07-13 mdbx: Update ChangeLog. 2026-07-12 mdbx-tools: fix/remove leftover usage of float point in `mdbx_stat`. 2026-07-12 mdbx: don't fail transaction on `MDBX_UNABLE_EXTEND_MAPSIZE`. 2026-07-12 mdbx: replace `reasonable_db_maxsize()` with `globals.assume_ram_pages` and `globals.reasonable_db_maxsize`. 2026-07-12 mdbx: avoid false-positive warning from Coverity. 2026-07-12 mdbx-make: more MDBX_CHECKING for `test-*` targets. 2026-07-12 mdbx-doc: more minor typos fixing. 2026-07-12 mdbx++: provide copy content for move-assignment of `mdbx::buffer` for non-equal allocators case. 2026-07-11 mdbx: introduce `globals.mmap_limit` and adjusting mmap-limit for ASAN/Valgrind. 2026-07-11 mdbx-tools: refine `mdbx_load`. 2026-07-11 mdbx-tools: fix 2Gb ram consumption by `mdbx_load` due leak of debug changes. 2026-07-11 mdbx-make: refine `test-*` and `smoke-*` targets. 2026-07-10 mdbx: more `LOG_IFERR(err)` into the opening path. 2026-07-10 mdbx-test: refine `copy-db` testcases, add assertions for `copy_pathname.empty()`. 2026-07-10 mdbx: fix dummy `EINVAL` inside `mdbx_chk`. 2026-07-10 mdbx: introduce `osal_fseek_shut()` with workaround for `EINVAL` from `lseek()` with huge offsets. 2026-07-10 mdbx: fix ODR-warnings from modern GCC while both LTO and UBSAN enabled. 2026-07-10 mdbx-make: fix `CTEST_OPT` for `test-memcheck` target. 2026-07-10 mdbx-test: move `valgrind_suppress.txt` and corresponding cmake-definitions.
478 lines
18 KiB
C
478 lines
18 KiB
C
/* This file is part of the libmdbx amalgamated source code (v0.14.2-293-g43618122 at 2026-07-13T02:40:29+03:00).
|
|
*
|
|
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
|
|
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight
|
|
* solutions. Please visit https://libmdbx.dqdkfa.ru for more information, changelog, documentation, C++ API description
|
|
* and links to the original git repo with the source code. Questions, feedback and suggestions are welcome to the
|
|
* Telegram' group https://t.me/libmdbx, MAX' chat https://max.ru/join/dKckvyuARxp1vRK-wnPur8zYCEkbR3OUOmpPWkWxp78.
|
|
*
|
|
* The libmdbx code will forever remain open and with high-quality free support, as far as the life circumstances of the
|
|
* project participants allow. Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`,
|
|
* BTC `bc1qzvl9uegf2ea6cwlytnanrscyv8snwsvrc0xfsu`, SOL `FTCTgbHajoLVZGr8aEFWMzx3NDMyS5wXJgfeMTmJznRi`.
|
|
* Всё будет хорошо!
|
|
*
|
|
* For ease of use and to eliminate potential limitations in both distribution and obstacles in technology development,
|
|
* libmdbx is distributed as an amalgamated source code starting at the end of 2025. The source code of the tests, as
|
|
* well as the internal documentation, will be available only to the team directly involved in the development.
|
|
*
|
|
* Copyright 2015-2026 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* For notes about the license change, credits and acknowledgments, please refer to the COPYRIGHT file. */
|
|
|
|
/* clang-format off */
|
|
|
|
#define xMDBX_TOOLS /* Avoid using internal ASSERT(), etc */
|
|
#include "mdbx-internals.h"
|
|
|
|
enum { MDBX_STAT_MAXDBS = 2 };
|
|
|
|
#if IS_WINDOWS
|
|
|
|
/* Bit of madness for Windows console */
|
|
#define mdbx_strerror mdbx_strerror_ANSI2OEM
|
|
#define mdbx_strerror_r mdbx_strerror_r_ANSI2OEM
|
|
|
|
#include "mdbx-wingetopt.h"
|
|
|
|
static volatile BOOL user_break;
|
|
static BOOL WINAPI ConsoleBreakHandlerRoutine(DWORD dwCtrlType) {
|
|
(void)dwCtrlType;
|
|
user_break = true;
|
|
return true;
|
|
}
|
|
|
|
#else /* WINDOWS */
|
|
|
|
static volatile sig_atomic_t user_break;
|
|
static void signal_handler(int sig) {
|
|
(void)sig;
|
|
user_break = 1;
|
|
}
|
|
|
|
#endif /* !WINDOWS */
|
|
|
|
static void print_stat(const MDBX_stat *ms) {
|
|
printf(" Pagesize: %u\n", ms->ms_psize);
|
|
printf(" Tree depth: %u\n", ms->ms_depth);
|
|
printf(" Branch pages: %" PRIu64 "\n", ms->ms_branch_pages);
|
|
printf(" Leaf pages: %" PRIu64 "\n", ms->ms_leaf_pages);
|
|
printf(" Large/Overflow pages: %" PRIu64 "\n", ms->ms_overflow_pages);
|
|
printf(" Entries: %" PRIu64 "\n", ms->ms_entries);
|
|
}
|
|
|
|
static void usage(const char *prog) {
|
|
fprintf(stderr,
|
|
"usage: %s [-V] [-q] [-e] [-f[f[f]]] [-r[r]] [-a|-s table] dbpath\n"
|
|
" -V\t\tprint version and exit\n"
|
|
" -q\t\tbe quiet\n"
|
|
" -p\t\tshow statistics of page operations for current session\n"
|
|
" -e\t\tshow whole DB info\n"
|
|
" -f\t\tshow GC info\n"
|
|
" -r\t\tshow readers\n"
|
|
" -a\t\tprint stat of main DB and all tables\n"
|
|
" -s table\tprint stat of only the specified named table\n"
|
|
" \t\tby default print stat of only the main DB\n",
|
|
prog);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
static int reader_list_func(void *ctx, int num, int slot, mdbx_pid_t pid, mdbx_tid_t thread, uint64_t txnid,
|
|
uint64_t lag, size_t bytes_used, size_t bytes_retained) {
|
|
(void)ctx;
|
|
if (num == 1)
|
|
printf("Reader Table\n"
|
|
" #\tslot\t%10s %*s %20s %10s %13s %13s\n",
|
|
"pid", (int)sizeof(size_t) * 2, "thread", "txnid", "lag", "used", "retained");
|
|
|
|
char thread_buf[32], *thread_str;
|
|
if (thread == (mdbx_tid_t)((uintptr_t)MDBX_TID_TXN_OUSTED))
|
|
thread_str = "ousted";
|
|
else if (thread == (mdbx_tid_t)((uintptr_t)MDBX_TID_TXN_PARKED))
|
|
thread_str = "parked";
|
|
else
|
|
snprintf(thread_str = thread_buf, sizeof(thread_buf), "%" PRIxPTR, (uintptr_t)thread);
|
|
|
|
printf(" %3d)\t[%d]\t%10" PRIdSIZE " %*s", num, slot, (size_t)pid, (int)sizeof(size_t) * 2, thread_str);
|
|
|
|
if (txnid) {
|
|
char buf_bytes_used[42], buf_bytes_retained[42];
|
|
printf(" %20" PRIu64 " %10" PRIu64 " %12sM %12sM\n", txnid, lag,
|
|
mdbx_ratio2digits(bytes_used, 1048576, 1, buf_bytes_used, sizeof(buf_bytes_used)),
|
|
mdbx_ratio2digits(bytes_retained, 1048576, 1, buf_bytes_retained, sizeof(buf_bytes_retained)));
|
|
} else
|
|
printf(" %20s %10s %13s %13s\n", "-", "0", "0", "0");
|
|
|
|
return user_break ? MDBX_RESULT_TRUE : MDBX_RESULT_FALSE;
|
|
}
|
|
|
|
static int table_enum_func(void *ctx, const MDBX_txn *txn, const MDBX_val *name, MDBX_db_flags_t db_flags,
|
|
const struct MDBX_stat *stat, MDBX_dbi dbi) {
|
|
(void)ctx;
|
|
(void)txn;
|
|
(void)db_flags;
|
|
(void)dbi;
|
|
printf("Status of %.*s\n", (int)name->iov_len, (const char *)name->iov_base);
|
|
print_stat(stat);
|
|
return user_break ? MDBX_RESULT_TRUE : MDBX_RESULT_FALSE;
|
|
}
|
|
|
|
static int gc_list_func(void *ctx, const MDBX_txn *txn, uint64_t span_txnid, size_t span_pgno, size_t span_length,
|
|
bool span_is_reclaimable) {
|
|
(void)ctx;
|
|
(void)txn;
|
|
(void)span_txnid;
|
|
(void)span_pgno;
|
|
(void)span_length;
|
|
(void)span_is_reclaimable;
|
|
return user_break ? MDBX_RESULT_TRUE : MDBX_RESULT_FALSE;
|
|
}
|
|
|
|
static const char *prog;
|
|
static bool quiet = false;
|
|
static void error(const char *func, int rc) {
|
|
if (!quiet)
|
|
fprintf(stderr, "%s: %s() error %d %s\n", prog, func, rc, mdbx_strerror(rc));
|
|
}
|
|
|
|
static void logger(MDBX_log_level_t level, const char *function, int line, const char *fmt, va_list args) {
|
|
static const char *const prefixes[] = {
|
|
"!!!fatal: ", // 0 fatal
|
|
" ! ", // 1 error
|
|
" ~ ", // 2 warning
|
|
" ", // 3 notice
|
|
" //", // 4 verbose
|
|
};
|
|
if (level < MDBX_LOG_DEBUG) {
|
|
if (function && line)
|
|
fprintf(stderr, "%s", prefixes[level]);
|
|
vfprintf(stderr, fmt, args);
|
|
}
|
|
}
|
|
|
|
static void print_pages_percentage(const char *caption, size_t value, size_t backed, size_t total) {
|
|
char buf[42];
|
|
const char *suffix = " pages";
|
|
printf(" %s: %" PRIuSIZE, caption, value);
|
|
if (value && value < backed) {
|
|
printf(", %s%% of %s", mdbx_ratio2percents(value, backed, buf, sizeof(buf)), "backed");
|
|
suffix = "";
|
|
}
|
|
if (value && value < total) {
|
|
printf(", %s%% of %s", mdbx_ratio2percents(value, total, buf, sizeof(buf)), "total");
|
|
suffix = "";
|
|
}
|
|
puts(suffix);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int opt, rc;
|
|
MDBX_env *env;
|
|
MDBX_txn *txn = nullptr;
|
|
MDBX_dbi dbi;
|
|
MDBX_envinfo mei;
|
|
prog = argv[0];
|
|
char *envname;
|
|
char *table = nullptr;
|
|
bool alltbl = false, show_env_info = false, show_page_ops = false;
|
|
short show_gc = 0, show_readers = 0;
|
|
|
|
if (argc < 2)
|
|
usage(prog);
|
|
|
|
while ((opt = getopt(argc, argv,
|
|
"V"
|
|
"q"
|
|
"p"
|
|
"a"
|
|
"e"
|
|
"f"
|
|
"n"
|
|
"r"
|
|
"s:")) != EOF) {
|
|
switch (opt) {
|
|
case 'V':
|
|
printf("mdbx_stat version %d.%d.%d.%d\n"
|
|
" - source: %s %s, commit %s, tree %s\n"
|
|
" - anchor: %s\n"
|
|
" - build: %s for %s by %s\n"
|
|
" - flags: %s\n"
|
|
" - options: %s\n",
|
|
mdbx_version.major, mdbx_version.minor, mdbx_version.patch, mdbx_version.tweak, mdbx_version.git.describe,
|
|
mdbx_version.git.datetime, mdbx_version.git.commit, mdbx_version.git.tree, mdbx_sourcery_anchor,
|
|
mdbx_build.datetime, mdbx_build.target, mdbx_build.compiler, mdbx_build.flags, mdbx_build.options);
|
|
return EXIT_SUCCESS;
|
|
case 'q':
|
|
quiet = true;
|
|
break;
|
|
case 'p':
|
|
show_page_ops = true;
|
|
break;
|
|
case 'a':
|
|
if (table)
|
|
usage(prog);
|
|
alltbl = true;
|
|
break;
|
|
case 'e':
|
|
show_env_info = true;
|
|
break;
|
|
case 'f':
|
|
show_gc += 1;
|
|
break;
|
|
case 'n':
|
|
break;
|
|
case 'r':
|
|
show_readers += 1;
|
|
break;
|
|
case 's':
|
|
if (alltbl)
|
|
usage(prog);
|
|
table = optarg;
|
|
break;
|
|
default:
|
|
usage(prog);
|
|
}
|
|
}
|
|
|
|
if (optind != argc - 1)
|
|
usage(prog);
|
|
|
|
#if IS_WINDOWS
|
|
SetConsoleCtrlHandler(ConsoleBreakHandlerRoutine, true);
|
|
#else
|
|
#ifdef SIGPIPE
|
|
signal(SIGPIPE, signal_handler);
|
|
#endif
|
|
#ifdef SIGHUP
|
|
signal(SIGHUP, signal_handler);
|
|
#endif
|
|
signal(SIGINT, signal_handler);
|
|
signal(SIGTERM, signal_handler);
|
|
#endif /* !WINDOWS */
|
|
|
|
envname = argv[optind];
|
|
if (!quiet) {
|
|
printf("mdbx_stat %s (%s, T-%s)\nRunning for %s...\n", mdbx_version.git.describe, mdbx_version.git.datetime,
|
|
mdbx_version.git.tree, envname);
|
|
fflush(nullptr);
|
|
mdbx_setup_debug(MDBX_LOG_NOTICE, MDBX_DBG_DONTCHANGE, logger);
|
|
}
|
|
|
|
rc = mdbx_env_create(&env);
|
|
if (unlikely(rc != MDBX_SUCCESS)) {
|
|
error("mdbx_env_create", rc);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (alltbl || table) {
|
|
rc = mdbx_env_set_maxdbs(env, MDBX_STAT_MAXDBS);
|
|
if (unlikely(rc != MDBX_SUCCESS)) {
|
|
error("mdbx_env_set_maxdbs", rc);
|
|
goto env_close;
|
|
}
|
|
}
|
|
|
|
rc = mdbx_env_open(env, envname, MDBX_RDONLY, 0);
|
|
if (unlikely(rc != MDBX_SUCCESS)) {
|
|
error("mdbx_env_open", rc);
|
|
goto env_close;
|
|
}
|
|
|
|
rc = mdbx_txn_begin(env, nullptr, MDBX_TXN_RDONLY, &txn);
|
|
if (unlikely(rc != MDBX_SUCCESS)) {
|
|
error("mdbx_txn_begin", rc);
|
|
goto txn_abort;
|
|
}
|
|
|
|
if (show_env_info || show_gc || show_page_ops) {
|
|
rc = mdbx_env_info_ex(env, txn, &mei, sizeof(mei));
|
|
if (unlikely(rc != MDBX_SUCCESS)) {
|
|
error("mdbx_env_info_ex", rc);
|
|
goto txn_abort;
|
|
}
|
|
} else {
|
|
/* LY: zap warnings from gcc */
|
|
memset(&mei, 0, sizeof(mei));
|
|
}
|
|
|
|
if (show_page_ops) {
|
|
printf("Page Operations (for current session):\n");
|
|
printf(" New: %8" PRIu64 "\t// quantity of a new pages added\n", mei.mi_pgop_stat.newly);
|
|
printf(" CoW: %8" PRIu64 "\t// quantity of pages copied for altering\n", mei.mi_pgop_stat.cow);
|
|
printf(" Clone: %8" PRIu64 "\t// quantity of parent's dirty pages "
|
|
"clones for nested transactions\n",
|
|
mei.mi_pgop_stat.clone);
|
|
printf(" Split: %8" PRIu64 "\t// page splits during insertions or updates\n", mei.mi_pgop_stat.split);
|
|
printf(" Merge: %8" PRIu64 "\t// page merges during deletions or updates\n", mei.mi_pgop_stat.merge);
|
|
printf(" Spill: %8" PRIu64 "\t// quantity of spilled/ousted `dirty` "
|
|
"pages during large transactions\n",
|
|
mei.mi_pgop_stat.spill);
|
|
printf(" Unspill: %8" PRIu64 "\t// quantity of unspilled/redone `dirty` "
|
|
"pages during large transactions\n",
|
|
mei.mi_pgop_stat.unspill);
|
|
printf(" WOP: %8" PRIu64 "\t// number of explicit write operations (not a pages) to a disk\n",
|
|
mei.mi_pgop_stat.wops);
|
|
printf(" PreFault: %8" PRIu64 "\t// number of prefault write operations (not a pages)\n",
|
|
mei.mi_pgop_stat.prefault);
|
|
printf(" mInCore: %8" PRIu64 "\t// number of mincore() calls\n", mei.mi_pgop_stat.mincore);
|
|
printf(" mSync: %8" PRIu64 "\t// number of explicit msync-to-disk operations (not a pages)\n",
|
|
mei.mi_pgop_stat.msync);
|
|
printf(" fSync: %8" PRIu64 "\t// number of explicit fsync-to-disk operations (not a pages)\n",
|
|
mei.mi_pgop_stat.fsync);
|
|
}
|
|
|
|
if (show_env_info) {
|
|
printf("Environment Info\n");
|
|
printf(" Pagesize: %u\n", mei.mi_dxb_pagesize);
|
|
if (mei.mi_geo.lower != mei.mi_geo.upper) {
|
|
printf(" Dynamic datafile: %" PRIu64 "..%" PRIu64 " bytes (+%" PRIu64 "/-%" PRIu64 "), %" PRIu64 "..%" PRIu64
|
|
" pages (+%" PRIu64 "/-%" PRIu64 ")\n",
|
|
mei.mi_geo.lower, mei.mi_geo.upper, mei.mi_geo.grow, mei.mi_geo.shrink,
|
|
mei.mi_geo.lower / mei.mi_dxb_pagesize, mei.mi_geo.upper / mei.mi_dxb_pagesize,
|
|
mei.mi_geo.grow / mei.mi_dxb_pagesize, mei.mi_geo.shrink / mei.mi_dxb_pagesize);
|
|
printf(" Current mapsize: %" PRIu64 " bytes, %" PRIu64 " pages \n", mei.mi_mapsize,
|
|
mei.mi_mapsize / mei.mi_dxb_pagesize);
|
|
printf(" Current datafile: %" PRIu64 " bytes, %" PRIu64 " pages\n", mei.mi_geo.current,
|
|
mei.mi_geo.current / mei.mi_dxb_pagesize);
|
|
#if IS_WINDOWS
|
|
if (mei.mi_geo.shrink && mei.mi_geo.current != mei.mi_geo.upper)
|
|
printf(" WARNING: Due Windows system limitations a "
|
|
"file couldn't\n be truncated while database "
|
|
"is opened. So, the size of\n database file "
|
|
"may be large than the database itself,\n "
|
|
"until it will be closed or reopened in read-write mode.\n");
|
|
#endif
|
|
} else {
|
|
printf(" Fixed datafile: %" PRIu64 " bytes, %" PRIu64 " pages\n", mei.mi_geo.current,
|
|
mei.mi_geo.current / mei.mi_dxb_pagesize);
|
|
}
|
|
printf(" Last transaction ID: %" PRIu64 "\n", mei.mi_recent_txnid);
|
|
printf(" Latter reader transaction ID: %" PRIu64 " (%" PRIi64 ")\n", mei.mi_latter_reader_txnid,
|
|
mei.mi_latter_reader_txnid - mei.mi_recent_txnid);
|
|
printf(" Max readers: %u\n", mei.mi_maxreaders);
|
|
printf(" Number of reader slots in use: %u\n", mei.mi_numreaders);
|
|
}
|
|
|
|
if (show_readers) {
|
|
rc = mdbx_reader_list(env, reader_list_func, nullptr);
|
|
if (MDBX_IS_ERROR(rc)) {
|
|
error("mdbx_reader_list", rc);
|
|
goto txn_abort;
|
|
}
|
|
if (rc == MDBX_RESULT_TRUE)
|
|
printf("Reader Table is absent\n");
|
|
else if (rc == MDBX_SUCCESS && show_readers > 1) {
|
|
int dead;
|
|
rc = mdbx_reader_check(env, &dead);
|
|
if (MDBX_IS_ERROR(rc)) {
|
|
error("mdbx_reader_check", rc);
|
|
goto txn_abort;
|
|
}
|
|
if (rc == MDBX_RESULT_TRUE) {
|
|
printf(" %d stale readers cleared.\n", dead);
|
|
rc = mdbx_reader_list(env, reader_list_func, nullptr);
|
|
if (rc == MDBX_RESULT_TRUE)
|
|
printf(" Now Reader Table is empty\n");
|
|
} else
|
|
printf(" No stale readers.\n");
|
|
}
|
|
if (!(table || alltbl || show_gc))
|
|
goto txn_abort;
|
|
}
|
|
|
|
if (show_gc) {
|
|
printf("Page Usage & Garbage Collection%s\n",
|
|
(show_gc > 1) ? " (please use `mdbx_chk` tool for detailed GC information instead)" : "");
|
|
MDBX_gc_info_t info;
|
|
rc = mdbx_gc_info(txn, &info, sizeof(info), gc_list_func, nullptr);
|
|
|
|
switch (rc) {
|
|
case MDBX_SUCCESS:
|
|
break;
|
|
case MDBX_EINTR:
|
|
if (!quiet)
|
|
fprintf(stderr, "Interrupted by signal/user\n");
|
|
goto txn_abort;
|
|
default:
|
|
error("mdbx_gc_info", rc);
|
|
goto txn_abort;
|
|
}
|
|
|
|
const size_t remained_pages =
|
|
info.pages_total - info.pages_allocated; /* the pages_allocated cannot be greater than the pages_total */
|
|
const size_t used_pages =
|
|
info.pages_allocated - info.pages_gc; /* the pages_gc cannot be greater than the pages_allocated */
|
|
const size_t gc_retained = info.pages_gc - info.gc_reclaimable.pages;
|
|
const size_t available_pages = info.gc_reclaimable.pages + remained_pages;
|
|
print_pages_percentage("Total", info.pages_total, info.pages_backed, info.pages_total);
|
|
print_pages_percentage("Backed", info.pages_backed, info.pages_backed, info.pages_total);
|
|
print_pages_percentage("Allocated", info.pages_allocated, info.pages_backed, info.pages_total);
|
|
print_pages_percentage("Remained", remained_pages, info.pages_backed, info.pages_total);
|
|
print_pages_percentage("Used", used_pages, info.pages_backed, info.pages_total);
|
|
print_pages_percentage("GC|whole", info.pages_gc, info.pages_backed, info.pages_total);
|
|
print_pages_percentage("GC|reclaimable", info.gc_reclaimable.pages, info.pages_backed, info.pages_total);
|
|
if (show_gc > 1) {
|
|
printf(" %s: ", "GC|reclaimable span-length distribution");
|
|
const char *suffix = "empty";
|
|
if (info.gc_reclaimable.span_histogram.amount) {
|
|
printf("single %zu", info.gc_reclaimable.span_histogram.le1_count);
|
|
for (size_t i = 0; i < ARRAY_LENGTH(info.gc_reclaimable.span_histogram.ranges); ++i) {
|
|
if (info.gc_reclaimable.span_histogram.ranges[i].count) {
|
|
printf(", %zu", info.gc_reclaimable.span_histogram.ranges[i].begin);
|
|
if (info.gc_reclaimable.span_histogram.ranges[i].end !=
|
|
info.gc_reclaimable.span_histogram.ranges[i].begin + 1)
|
|
printf("-%zu", info.gc_reclaimable.span_histogram.ranges[i].end);
|
|
printf("x%zu", info.gc_reclaimable.span_histogram.ranges[i].count);
|
|
}
|
|
}
|
|
suffix = " pages";
|
|
}
|
|
puts(suffix);
|
|
}
|
|
print_pages_percentage("GC|retained", gc_retained, info.pages_backed, info.pages_total);
|
|
print_pages_percentage("Available", available_pages, info.pages_backed, info.pages_total);
|
|
if (info.max_retained_pages || info.max_reader_lag) {
|
|
printf(" max reader lag %zu\n", info.max_reader_lag);
|
|
printf(" max retained pages %zu\n", info.max_retained_pages);
|
|
}
|
|
}
|
|
|
|
rc = mdbx_dbi_open(txn, table, MDBX_DB_ACCEDE, &dbi);
|
|
if (unlikely(rc != MDBX_SUCCESS)) {
|
|
error("mdbx_dbi_open", rc);
|
|
goto txn_abort;
|
|
}
|
|
|
|
MDBX_stat mst;
|
|
rc = mdbx_dbi_stat(txn, dbi, &mst, sizeof(mst));
|
|
if (unlikely(rc != MDBX_SUCCESS)) {
|
|
error("mdbx_dbi_stat", rc);
|
|
goto txn_abort;
|
|
}
|
|
printf("Status of %s\n", table ? table : "Main DB");
|
|
print_stat(&mst);
|
|
mdbx_dbi_close(env, dbi);
|
|
|
|
if (alltbl) {
|
|
rc = mdbx_enumerate_tables(txn, table_enum_func, nullptr);
|
|
switch (rc) {
|
|
case MDBX_SUCCESS:
|
|
case MDBX_NOTFOUND:
|
|
break;
|
|
case MDBX_EINTR:
|
|
if (!quiet)
|
|
fprintf(stderr, "Interrupted by signal/user\n");
|
|
break;
|
|
default:
|
|
error("mdbx_enumerate_tables", rc);
|
|
}
|
|
}
|
|
|
|
txn_abort:
|
|
mdbx_txn_abort(txn);
|
|
env_close:
|
|
mdbx_env_close(env);
|
|
|
|
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
|
}
|