mdbxmou 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +32 -0
- package/.github/workflows/publish.yml +27 -0
- package/.gitmodules +3 -0
- package/CMakeLists.txt +53 -0
- package/LICENSE +201 -0
- package/README.md +639 -0
- package/build.js +11 -0
- package/deps/libmdbx/.clang-format +3 -0
- package/deps/libmdbx/.cmake-format.yaml +3 -0
- package/deps/libmdbx/.le.ini +40 -0
- package/deps/libmdbx/CMakeLists.txt +1269 -0
- package/deps/libmdbx/COPYRIGHT +159 -0
- package/deps/libmdbx/ChangeLog.md +2786 -0
- package/deps/libmdbx/GNUmakefile +950 -0
- package/deps/libmdbx/LICENSE +177 -0
- package/deps/libmdbx/Makefile +16 -0
- package/deps/libmdbx/NOTICE +39 -0
- package/deps/libmdbx/README.md +863 -0
- package/deps/libmdbx/TODO.md +43 -0
- package/deps/libmdbx/cmake/compiler.cmake +1221 -0
- package/deps/libmdbx/cmake/profile.cmake +58 -0
- package/deps/libmdbx/cmake/utils.cmake +524 -0
- package/deps/libmdbx/conanfile.py +323 -0
- package/deps/libmdbx/docs/Doxyfile.in +2734 -0
- package/deps/libmdbx/docs/_preface.md +47 -0
- package/deps/libmdbx/docs/_restrictions.md +248 -0
- package/deps/libmdbx/docs/_starting.md +245 -0
- package/deps/libmdbx/docs/_toc.md +34 -0
- package/deps/libmdbx/docs/header.html +96 -0
- package/deps/libmdbx/example/CMakeLists.txt +6 -0
- package/deps/libmdbx/example/README.md +1 -0
- package/deps/libmdbx/example/example-mdbx.c +154 -0
- package/deps/libmdbx/example/sample-bdb.txt +77 -0
- package/deps/libmdbx/mdbx.h +6655 -0
- package/deps/libmdbx/mdbx.h++ +6428 -0
- package/deps/libmdbx/packages/buildroot/0001-package-libmdbx-new-package-library-database.patch +173 -0
- package/deps/libmdbx/src/alloy.c +54 -0
- package/deps/libmdbx/src/api-cold.c +543 -0
- package/deps/libmdbx/src/api-copy.c +912 -0
- package/deps/libmdbx/src/api-cursor.c +754 -0
- package/deps/libmdbx/src/api-dbi.c +315 -0
- package/deps/libmdbx/src/api-env.c +1434 -0
- package/deps/libmdbx/src/api-extra.c +165 -0
- package/deps/libmdbx/src/api-key-transform.c +197 -0
- package/deps/libmdbx/src/api-misc.c +286 -0
- package/deps/libmdbx/src/api-opts.c +575 -0
- package/deps/libmdbx/src/api-range-estimate.c +365 -0
- package/deps/libmdbx/src/api-txn-data.c +454 -0
- package/deps/libmdbx/src/api-txn.c +921 -0
- package/deps/libmdbx/src/atomics-ops.h +364 -0
- package/deps/libmdbx/src/atomics-types.h +97 -0
- package/deps/libmdbx/src/audit.c +109 -0
- package/deps/libmdbx/src/bits.md +34 -0
- package/deps/libmdbx/src/chk.c +1796 -0
- package/deps/libmdbx/src/cogs.c +309 -0
- package/deps/libmdbx/src/cogs.h +506 -0
- package/deps/libmdbx/src/coherency.c +170 -0
- package/deps/libmdbx/src/config.h.in +88 -0
- package/deps/libmdbx/src/cursor.c +2396 -0
- package/deps/libmdbx/src/cursor.h +391 -0
- package/deps/libmdbx/src/dbi.c +717 -0
- package/deps/libmdbx/src/dbi.h +142 -0
- package/deps/libmdbx/src/debug_begin.h +36 -0
- package/deps/libmdbx/src/debug_end.h +15 -0
- package/deps/libmdbx/src/dpl.c +486 -0
- package/deps/libmdbx/src/dpl.h +134 -0
- package/deps/libmdbx/src/dxb.c +1335 -0
- package/deps/libmdbx/src/env.c +607 -0
- package/deps/libmdbx/src/essentials.h +125 -0
- package/deps/libmdbx/src/gc-get.c +1345 -0
- package/deps/libmdbx/src/gc-put.c +970 -0
- package/deps/libmdbx/src/gc.h +40 -0
- package/deps/libmdbx/src/global.c +474 -0
- package/deps/libmdbx/src/internals.h +585 -0
- package/deps/libmdbx/src/layout-dxb.h +288 -0
- package/deps/libmdbx/src/layout-lck.h +289 -0
- package/deps/libmdbx/src/lck-posix.c +859 -0
- package/deps/libmdbx/src/lck-windows.c +607 -0
- package/deps/libmdbx/src/lck.c +174 -0
- package/deps/libmdbx/src/lck.h +110 -0
- package/deps/libmdbx/src/logging_and_debug.c +250 -0
- package/deps/libmdbx/src/logging_and_debug.h +159 -0
- package/deps/libmdbx/src/man1/mdbx_chk.1 +106 -0
- package/deps/libmdbx/src/man1/mdbx_copy.1 +95 -0
- package/deps/libmdbx/src/man1/mdbx_drop.1 +48 -0
- package/deps/libmdbx/src/man1/mdbx_dump.1 +101 -0
- package/deps/libmdbx/src/man1/mdbx_load.1 +105 -0
- package/deps/libmdbx/src/man1/mdbx_stat.1 +86 -0
- package/deps/libmdbx/src/mdbx.c++ +1837 -0
- package/deps/libmdbx/src/meta.c +656 -0
- package/deps/libmdbx/src/meta.h +168 -0
- package/deps/libmdbx/src/mvcc-readers.c +414 -0
- package/deps/libmdbx/src/node.c +365 -0
- package/deps/libmdbx/src/node.h +102 -0
- package/deps/libmdbx/src/ntdll.def +1246 -0
- package/deps/libmdbx/src/options.h +534 -0
- package/deps/libmdbx/src/osal.c +3485 -0
- package/deps/libmdbx/src/osal.h +587 -0
- package/deps/libmdbx/src/page-get.c +483 -0
- package/deps/libmdbx/src/page-iov.c +185 -0
- package/deps/libmdbx/src/page-iov.h +34 -0
- package/deps/libmdbx/src/page-ops.c +744 -0
- package/deps/libmdbx/src/page-ops.h +142 -0
- package/deps/libmdbx/src/pnl.c +236 -0
- package/deps/libmdbx/src/pnl.h +146 -0
- package/deps/libmdbx/src/preface.h +990 -0
- package/deps/libmdbx/src/proto.h +105 -0
- package/deps/libmdbx/src/refund.c +212 -0
- package/deps/libmdbx/src/sort.h +484 -0
- package/deps/libmdbx/src/spill.c +431 -0
- package/deps/libmdbx/src/spill.h +74 -0
- package/deps/libmdbx/src/table.c +107 -0
- package/deps/libmdbx/src/tls.c +551 -0
- package/deps/libmdbx/src/tls.h +43 -0
- package/deps/libmdbx/src/tools/chk.c +673 -0
- package/deps/libmdbx/src/tools/copy.c +166 -0
- package/deps/libmdbx/src/tools/drop.c +199 -0
- package/deps/libmdbx/src/tools/dump.c +515 -0
- package/deps/libmdbx/src/tools/load.c +831 -0
- package/deps/libmdbx/src/tools/stat.c +516 -0
- package/deps/libmdbx/src/tools/wingetopt.c +87 -0
- package/deps/libmdbx/src/tools/wingetopt.h +30 -0
- package/deps/libmdbx/src/tree-ops.c +1554 -0
- package/deps/libmdbx/src/tree-search.c +140 -0
- package/deps/libmdbx/src/txl.c +99 -0
- package/deps/libmdbx/src/txl.h +26 -0
- package/deps/libmdbx/src/txn.c +1083 -0
- package/deps/libmdbx/src/unaligned.h +205 -0
- package/deps/libmdbx/src/utils.c +32 -0
- package/deps/libmdbx/src/utils.h +76 -0
- package/deps/libmdbx/src/version.c.in +44 -0
- package/deps/libmdbx/src/walk.c +290 -0
- package/deps/libmdbx/src/walk.h +20 -0
- package/deps/libmdbx/src/windows-import.c +152 -0
- package/deps/libmdbx/src/windows-import.h +128 -0
- package/deps/libmdbx/test/CMakeLists.txt +317 -0
- package/deps/libmdbx/test/append.c++ +237 -0
- package/deps/libmdbx/test/base.h++ +92 -0
- package/deps/libmdbx/test/battery-tmux.sh +64 -0
- package/deps/libmdbx/test/cases.c++ +118 -0
- package/deps/libmdbx/test/chrono.c++ +134 -0
- package/deps/libmdbx/test/chrono.h++ +85 -0
- package/deps/libmdbx/test/config.c++ +643 -0
- package/deps/libmdbx/test/config.h++ +334 -0
- package/deps/libmdbx/test/copy.c++ +62 -0
- package/deps/libmdbx/test/dead.c++ +39 -0
- package/deps/libmdbx/test/dump-load.sh +40 -0
- package/deps/libmdbx/test/extra/crunched_delete.c++ +409 -0
- package/deps/libmdbx/test/extra/cursor_closing.c++ +410 -0
- package/deps/libmdbx/test/extra/dbi.c++ +229 -0
- package/deps/libmdbx/test/extra/doubtless_positioning.c++ +253 -0
- package/deps/libmdbx/test/extra/dupfix_addodd.c +94 -0
- package/deps/libmdbx/test/extra/dupfix_multiple.c++ +311 -0
- package/deps/libmdbx/test/extra/early_close_dbi.c++ +137 -0
- package/deps/libmdbx/test/extra/hex_base64_base58.c++ +118 -0
- package/deps/libmdbx/test/extra/maindb_ordinal.c++ +61 -0
- package/deps/libmdbx/test/extra/open.c++ +96 -0
- package/deps/libmdbx/test/extra/pcrf/README.md +2 -0
- package/deps/libmdbx/test/extra/pcrf/pcrf_test.c +380 -0
- package/deps/libmdbx/test/extra/probe.c++ +10 -0
- package/deps/libmdbx/test/extra/txn.c++ +407 -0
- package/deps/libmdbx/test/extra/upsert_alldups.c +193 -0
- package/deps/libmdbx/test/fork.c++ +263 -0
- package/deps/libmdbx/test/hill.c++ +447 -0
- package/deps/libmdbx/test/jitter.c++ +197 -0
- package/deps/libmdbx/test/keygen.c++ +393 -0
- package/deps/libmdbx/test/keygen.h++ +130 -0
- package/deps/libmdbx/test/log.c++ +358 -0
- package/deps/libmdbx/test/log.h++ +91 -0
- package/deps/libmdbx/test/main.c++ +706 -0
- package/deps/libmdbx/test/nested.c++ +318 -0
- package/deps/libmdbx/test/osal-unix.c++ +647 -0
- package/deps/libmdbx/test/osal-windows.c++ +440 -0
- package/deps/libmdbx/test/osal.h++ +41 -0
- package/deps/libmdbx/test/stochastic.sh +690 -0
- package/deps/libmdbx/test/stub/LICENSE +24 -0
- package/deps/libmdbx/test/stub/README.md +8 -0
- package/deps/libmdbx/test/stub/pthread_barrier.c +104 -0
- package/deps/libmdbx/test/stub/pthread_barrier.h +77 -0
- package/deps/libmdbx/test/test.c++ +1551 -0
- package/deps/libmdbx/test/test.h++ +298 -0
- package/deps/libmdbx/test/tmux.conf +3 -0
- package/deps/libmdbx/test/try.c++ +30 -0
- package/deps/libmdbx/test/ttl.c++ +240 -0
- package/deps/libmdbx/test/utils.c++ +203 -0
- package/deps/libmdbx/test/utils.h++ +326 -0
- package/deps/libmdbx/test/valgrind_suppress.txt +536 -0
- package/lib/mdbx_evn_async.js +211 -0
- package/lib/mdbx_worker.js +195 -0
- package/lib/nativemou.js +6 -0
- package/package.json +38 -0
- package/src/async/envmou_close.cpp +34 -0
- package/src/async/envmou_close.hpp +32 -0
- package/src/async/envmou_copy_to.cpp +29 -0
- package/src/async/envmou_copy_to.hpp +38 -0
- package/src/async/envmou_keys.cpp +201 -0
- package/src/async/envmou_keys.hpp +50 -0
- package/src/async/envmou_open.cpp +38 -0
- package/src/async/envmou_open.hpp +33 -0
- package/src/async/envmou_query.cpp +167 -0
- package/src/async/envmou_query.hpp +53 -0
- package/src/dbimou.cpp +522 -0
- package/src/dbimou.hpp +82 -0
- package/src/env_arg0.hpp +24 -0
- package/src/envmou.cpp +445 -0
- package/src/envmou.hpp +116 -0
- package/src/modulemou.cpp +113 -0
- package/src/querymou.cpp +177 -0
- package/src/querymou.hpp +93 -0
- package/src/txnmou.cpp +254 -0
- package/src/txnmou.hpp +122 -0
- package/src/typemou.hpp +239 -0
- package/src/valuemou.hpp +194 -0
- package/test/async.js +67 -0
- package/test/e3.js +38 -0
- package/test/e4.js +89 -0
- package/test/e5.js +162 -0
- package/test/test-batch-ops.js +243 -0
- package/test/test-cursor-mode.js +84 -0
- package/test/test-multi-mode.js +87 -0
|
@@ -0,0 +1,912 @@
|
|
|
1
|
+
/// \copyright SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/// \note Please refer to the COPYRIGHT file for explanations license change,
|
|
3
|
+
/// credits and acknowledgments.
|
|
4
|
+
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
|
|
5
|
+
|
|
6
|
+
#include "internals.h"
|
|
7
|
+
|
|
8
|
+
typedef struct compacting_context {
|
|
9
|
+
MDBX_env *env;
|
|
10
|
+
MDBX_txn *txn;
|
|
11
|
+
MDBX_copy_flags_t flags;
|
|
12
|
+
pgno_t first_unallocated;
|
|
13
|
+
osal_condpair_t condpair;
|
|
14
|
+
volatile unsigned head;
|
|
15
|
+
volatile unsigned tail;
|
|
16
|
+
uint8_t *write_buf[2];
|
|
17
|
+
size_t write_len[2];
|
|
18
|
+
/* Error code. Never cleared if set. Both threads can set nonzero
|
|
19
|
+
* to fail the copy. Not mutex-protected, expects atomic int. */
|
|
20
|
+
volatile int error;
|
|
21
|
+
mdbx_filehandle_t fd;
|
|
22
|
+
} ctx_t;
|
|
23
|
+
|
|
24
|
+
__cold static int compacting_walk_tree(ctx_t *ctx, tree_t *tree);
|
|
25
|
+
|
|
26
|
+
/* Dedicated writer thread for compacting copy. */
|
|
27
|
+
__cold static THREAD_RESULT THREAD_CALL compacting_write_thread(void *arg) {
|
|
28
|
+
ctx_t *const ctx = arg;
|
|
29
|
+
|
|
30
|
+
#if defined(EPIPE) && !(defined(_WIN32) || defined(_WIN64))
|
|
31
|
+
sigset_t sigset;
|
|
32
|
+
sigemptyset(&sigset);
|
|
33
|
+
sigaddset(&sigset, SIGPIPE);
|
|
34
|
+
ctx->error = pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
|
|
35
|
+
#endif /* EPIPE */
|
|
36
|
+
|
|
37
|
+
osal_condpair_lock(&ctx->condpair);
|
|
38
|
+
while (!ctx->error) {
|
|
39
|
+
while (ctx->tail == ctx->head && !ctx->error) {
|
|
40
|
+
int err = osal_condpair_wait(&ctx->condpair, true);
|
|
41
|
+
if (err != MDBX_SUCCESS) {
|
|
42
|
+
ctx->error = err;
|
|
43
|
+
goto bailout;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const unsigned toggle = ctx->tail & 1;
|
|
47
|
+
size_t wsize = ctx->write_len[toggle];
|
|
48
|
+
if (wsize == 0) {
|
|
49
|
+
ctx->tail += 1;
|
|
50
|
+
break /* EOF */;
|
|
51
|
+
}
|
|
52
|
+
ctx->write_len[toggle] = 0;
|
|
53
|
+
uint8_t *ptr = ctx->write_buf[toggle];
|
|
54
|
+
if (!ctx->error) {
|
|
55
|
+
int err = osal_write(ctx->fd, ptr, wsize);
|
|
56
|
+
if (err != MDBX_SUCCESS) {
|
|
57
|
+
#if defined(EPIPE) && !(defined(_WIN32) || defined(_WIN64))
|
|
58
|
+
if (err == EPIPE) {
|
|
59
|
+
/* Collect the pending SIGPIPE,
|
|
60
|
+
* otherwise at least OS X gives it to the process on thread-exit. */
|
|
61
|
+
int unused;
|
|
62
|
+
sigwait(&sigset, &unused);
|
|
63
|
+
}
|
|
64
|
+
#endif /* EPIPE */
|
|
65
|
+
ctx->error = err;
|
|
66
|
+
goto bailout;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
ctx->tail += 1;
|
|
70
|
+
osal_condpair_signal(&ctx->condpair, false);
|
|
71
|
+
}
|
|
72
|
+
bailout:
|
|
73
|
+
osal_condpair_unlock(&ctx->condpair);
|
|
74
|
+
return (THREAD_RESULT)0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Give buffer and/or MDBX_EOF to writer thread, await unused buffer. */
|
|
78
|
+
__cold static int compacting_toggle_write_buffers(ctx_t *ctx) {
|
|
79
|
+
osal_condpair_lock(&ctx->condpair);
|
|
80
|
+
eASSERT(ctx->env, ctx->head - ctx->tail < 2 || ctx->error);
|
|
81
|
+
ctx->head += 1;
|
|
82
|
+
osal_condpair_signal(&ctx->condpair, true);
|
|
83
|
+
while (!ctx->error && ctx->head - ctx->tail == 2 /* both buffers in use */) {
|
|
84
|
+
if (ctx->flags & MDBX_CP_THROTTLE_MVCC)
|
|
85
|
+
mdbx_txn_park(ctx->txn, false);
|
|
86
|
+
int err = osal_condpair_wait(&ctx->condpair, false);
|
|
87
|
+
if (err == MDBX_SUCCESS && (ctx->flags & MDBX_CP_THROTTLE_MVCC) != 0)
|
|
88
|
+
err = mdbx_txn_unpark(ctx->txn, false);
|
|
89
|
+
if (err != MDBX_SUCCESS)
|
|
90
|
+
ctx->error = err;
|
|
91
|
+
}
|
|
92
|
+
osal_condpair_unlock(&ctx->condpair);
|
|
93
|
+
return ctx->error;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
static int compacting_put_bytes(ctx_t *ctx, const void *src, size_t bytes, pgno_t pgno, pgno_t npages) {
|
|
97
|
+
assert(pgno == 0 || bytes > PAGEHDRSZ);
|
|
98
|
+
while (bytes > 0) {
|
|
99
|
+
const size_t side = ctx->head & 1;
|
|
100
|
+
const size_t left = MDBX_ENVCOPY_WRITEBUF - ctx->write_len[side];
|
|
101
|
+
if (left < (pgno ? PAGEHDRSZ : 1)) {
|
|
102
|
+
int err = compacting_toggle_write_buffers(ctx);
|
|
103
|
+
if (unlikely(err != MDBX_SUCCESS))
|
|
104
|
+
return err;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const size_t chunk = (bytes < left) ? bytes : left;
|
|
108
|
+
void *const dst = ctx->write_buf[side] + ctx->write_len[side];
|
|
109
|
+
if (src) {
|
|
110
|
+
memcpy(dst, src, chunk);
|
|
111
|
+
if (pgno) {
|
|
112
|
+
assert(chunk > PAGEHDRSZ);
|
|
113
|
+
page_t *mp = dst;
|
|
114
|
+
mp->pgno = pgno;
|
|
115
|
+
if (mp->txnid == 0)
|
|
116
|
+
mp->txnid = ctx->txn->txnid;
|
|
117
|
+
if (mp->flags == P_LARGE) {
|
|
118
|
+
assert(bytes <= pgno2bytes(ctx->env, npages));
|
|
119
|
+
mp->pages = npages;
|
|
120
|
+
}
|
|
121
|
+
pgno = 0;
|
|
122
|
+
}
|
|
123
|
+
src = ptr_disp(src, chunk);
|
|
124
|
+
} else
|
|
125
|
+
memset(dst, 0, chunk);
|
|
126
|
+
bytes -= chunk;
|
|
127
|
+
ctx->write_len[side] += chunk;
|
|
128
|
+
}
|
|
129
|
+
return MDBX_SUCCESS;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
static int compacting_put_page(ctx_t *ctx, const page_t *mp, const size_t head_bytes, const size_t tail_bytes,
|
|
133
|
+
const pgno_t npages) {
|
|
134
|
+
if (tail_bytes) {
|
|
135
|
+
assert(head_bytes + tail_bytes <= ctx->env->ps);
|
|
136
|
+
assert(npages == 1 && (page_type(mp) == P_BRANCH || page_type(mp) == P_LEAF));
|
|
137
|
+
} else {
|
|
138
|
+
assert(head_bytes <= pgno2bytes(ctx->env, npages));
|
|
139
|
+
assert((npages == 1 && page_type(mp) == (P_LEAF | P_DUPFIX)) || page_type(mp) == P_LARGE);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const pgno_t pgno = ctx->first_unallocated;
|
|
143
|
+
ctx->first_unallocated += npages;
|
|
144
|
+
int err = compacting_put_bytes(ctx, mp, head_bytes, pgno, npages);
|
|
145
|
+
if (unlikely(err != MDBX_SUCCESS))
|
|
146
|
+
return err;
|
|
147
|
+
err = compacting_put_bytes(ctx, nullptr, pgno2bytes(ctx->env, npages) - (head_bytes + tail_bytes), 0, 0);
|
|
148
|
+
if (unlikely(err != MDBX_SUCCESS))
|
|
149
|
+
return err;
|
|
150
|
+
return compacting_put_bytes(ctx, ptr_disp(mp, ctx->env->ps - tail_bytes), tail_bytes, 0, 0);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
__cold static int compacting_walk(ctx_t *ctx, MDBX_cursor *mc, pgno_t *const parent_pgno, txnid_t parent_txnid) {
|
|
154
|
+
mc->top = 0;
|
|
155
|
+
mc->ki[0] = 0;
|
|
156
|
+
int rc = page_get(mc, *parent_pgno, &mc->pg[0], parent_txnid);
|
|
157
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
158
|
+
return rc;
|
|
159
|
+
|
|
160
|
+
rc = tree_search_finalize(mc, nullptr, Z_FIRST);
|
|
161
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
162
|
+
return rc;
|
|
163
|
+
|
|
164
|
+
/* Make cursor pages writable */
|
|
165
|
+
const intptr_t deep_limit = mc->top + 1;
|
|
166
|
+
void *const buf = osal_malloc(pgno2bytes(ctx->env, deep_limit + 1));
|
|
167
|
+
if (buf == nullptr)
|
|
168
|
+
return MDBX_ENOMEM;
|
|
169
|
+
|
|
170
|
+
void *ptr = buf;
|
|
171
|
+
for (intptr_t i = 0; i <= mc->top; i++) {
|
|
172
|
+
page_copy(ptr, mc->pg[i], ctx->env->ps);
|
|
173
|
+
mc->pg[i] = ptr;
|
|
174
|
+
ptr = ptr_disp(ptr, ctx->env->ps);
|
|
175
|
+
}
|
|
176
|
+
/* This is writable space for a leaf page. Usually not needed. */
|
|
177
|
+
page_t *const leaf = ptr;
|
|
178
|
+
|
|
179
|
+
while (mc->top >= 0) {
|
|
180
|
+
page_t *mp = mc->pg[mc->top];
|
|
181
|
+
const size_t nkeys = page_numkeys(mp);
|
|
182
|
+
if (is_leaf(mp)) {
|
|
183
|
+
if (!(mc->flags & z_inner) /* may have nested N_TREE or N_BIG nodes */) {
|
|
184
|
+
for (size_t i = 0; i < nkeys; i++) {
|
|
185
|
+
node_t *node = page_node(mp, i);
|
|
186
|
+
if (node_flags(node) == N_BIG) {
|
|
187
|
+
/* Need writable leaf */
|
|
188
|
+
if (mp != leaf) {
|
|
189
|
+
mc->pg[mc->top] = leaf;
|
|
190
|
+
page_copy(leaf, mp, ctx->env->ps);
|
|
191
|
+
mp = leaf;
|
|
192
|
+
node = page_node(mp, i);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const pgr_t lp = page_get_large(mc, node_largedata_pgno(node), mp->txnid);
|
|
196
|
+
if (unlikely((rc = lp.err) != MDBX_SUCCESS))
|
|
197
|
+
goto bailout;
|
|
198
|
+
const size_t datasize = node_ds(node);
|
|
199
|
+
const pgno_t npages = largechunk_npages(ctx->env, datasize);
|
|
200
|
+
poke_pgno(node_data(node), ctx->first_unallocated);
|
|
201
|
+
rc = compacting_put_page(ctx, lp.page, PAGEHDRSZ + datasize, 0, npages);
|
|
202
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
203
|
+
goto bailout;
|
|
204
|
+
} else if (node_flags(node) & N_TREE) {
|
|
205
|
+
if (!MDBX_DISABLE_VALIDATION && unlikely(node_ds(node) != sizeof(tree_t))) {
|
|
206
|
+
ERROR("%s/%d: %s %u", "MDBX_CORRUPTED", MDBX_CORRUPTED, "invalid dupsort sub-tree node size",
|
|
207
|
+
(unsigned)node_ds(node));
|
|
208
|
+
rc = MDBX_CORRUPTED;
|
|
209
|
+
goto bailout;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Need writable leaf */
|
|
213
|
+
if (mp != leaf) {
|
|
214
|
+
mc->pg[mc->top] = leaf;
|
|
215
|
+
page_copy(leaf, mp, ctx->env->ps);
|
|
216
|
+
mp = leaf;
|
|
217
|
+
node = page_node(mp, i);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
tree_t *nested = nullptr;
|
|
221
|
+
if (node_flags(node) & N_DUP) {
|
|
222
|
+
rc = cursor_dupsort_setup(mc, node, mp);
|
|
223
|
+
if (likely(rc == MDBX_SUCCESS)) {
|
|
224
|
+
nested = &mc->subcur->nested_tree;
|
|
225
|
+
rc = compacting_walk(ctx, &mc->subcur->cursor, &nested->root, mp->txnid);
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
cASSERT(mc, (mc->flags & z_inner) == 0 && mc->subcur == 0);
|
|
229
|
+
cursor_couple_t *couple = container_of(mc, cursor_couple_t, outer);
|
|
230
|
+
nested = &couple->inner.nested_tree;
|
|
231
|
+
memcpy(nested, node_data(node), sizeof(tree_t));
|
|
232
|
+
rc = compacting_walk_tree(ctx, nested);
|
|
233
|
+
}
|
|
234
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
235
|
+
goto bailout;
|
|
236
|
+
memcpy(node_data(node), nested, sizeof(tree_t));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
mc->ki[mc->top]++;
|
|
242
|
+
if (mc->ki[mc->top] < nkeys) {
|
|
243
|
+
for (;;) {
|
|
244
|
+
const node_t *node = page_node(mp, mc->ki[mc->top]);
|
|
245
|
+
rc = page_get(mc, node_pgno(node), &mp, mp->txnid);
|
|
246
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
247
|
+
goto bailout;
|
|
248
|
+
mc->top += 1;
|
|
249
|
+
if (unlikely(mc->top >= deep_limit)) {
|
|
250
|
+
rc = MDBX_CURSOR_FULL;
|
|
251
|
+
goto bailout;
|
|
252
|
+
}
|
|
253
|
+
mc->ki[mc->top] = 0;
|
|
254
|
+
if (!is_branch(mp)) {
|
|
255
|
+
mc->pg[mc->top] = mp;
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
/* Whenever we advance to a sibling branch page,
|
|
259
|
+
* we must proceed all the way down to its first leaf. */
|
|
260
|
+
page_copy(mc->pg[mc->top], mp, ctx->env->ps);
|
|
261
|
+
}
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const pgno_t pgno = ctx->first_unallocated;
|
|
267
|
+
if (likely(!is_dupfix_leaf(mp))) {
|
|
268
|
+
rc = compacting_put_page(ctx, mp, PAGEHDRSZ + mp->lower, ctx->env->ps - (PAGEHDRSZ + mp->upper), 1);
|
|
269
|
+
} else {
|
|
270
|
+
rc = compacting_put_page(ctx, mp, PAGEHDRSZ + page_numkeys(mp) * mp->dupfix_ksize, 0, 1);
|
|
271
|
+
}
|
|
272
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
273
|
+
goto bailout;
|
|
274
|
+
|
|
275
|
+
if (mc->top) {
|
|
276
|
+
/* Update parent if there is one */
|
|
277
|
+
node_set_pgno(page_node(mc->pg[mc->top - 1], mc->ki[mc->top - 1]), pgno);
|
|
278
|
+
cursor_pop(mc);
|
|
279
|
+
} else {
|
|
280
|
+
/* Otherwise we're done */
|
|
281
|
+
*parent_pgno = pgno;
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
bailout:
|
|
287
|
+
osal_free(buf);
|
|
288
|
+
return rc;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
__cold static int compacting_walk_tree(ctx_t *ctx, tree_t *tree) {
|
|
292
|
+
if (unlikely(tree->root == P_INVALID))
|
|
293
|
+
return MDBX_SUCCESS; /* empty db */
|
|
294
|
+
|
|
295
|
+
cursor_couple_t couple;
|
|
296
|
+
memset(&couple, 0, sizeof(couple));
|
|
297
|
+
couple.inner.cursor.signature = ~cur_signature_live;
|
|
298
|
+
kvx_t kvx = {.clc = {.k = {.lmin = INT_MAX}, .v = {.lmin = INT_MAX}}};
|
|
299
|
+
int rc = cursor_init4walk(&couple, ctx->txn, tree, &kvx);
|
|
300
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
301
|
+
return rc;
|
|
302
|
+
|
|
303
|
+
couple.outer.checking |= z_ignord | z_pagecheck;
|
|
304
|
+
couple.inner.cursor.checking |= z_ignord | z_pagecheck;
|
|
305
|
+
if (!tree->mod_txnid)
|
|
306
|
+
tree->mod_txnid = ctx->txn->txnid;
|
|
307
|
+
return compacting_walk(ctx, &couple.outer, &tree->root, tree->mod_txnid);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
__cold static void compacting_fixup_meta(MDBX_env *env, meta_t *meta) {
|
|
311
|
+
eASSERT(env, meta->trees.gc.mod_txnid || meta->trees.gc.root == P_INVALID);
|
|
312
|
+
eASSERT(env, meta->trees.main.mod_txnid || meta->trees.main.root == P_INVALID);
|
|
313
|
+
|
|
314
|
+
/* Calculate filesize taking in account shrink/growing thresholds */
|
|
315
|
+
if (meta->geometry.first_unallocated != meta->geometry.now) {
|
|
316
|
+
meta->geometry.now = meta->geometry.first_unallocated;
|
|
317
|
+
const size_t aligner = pv2pages(meta->geometry.grow_pv ? meta->geometry.grow_pv : meta->geometry.shrink_pv);
|
|
318
|
+
if (aligner) {
|
|
319
|
+
const pgno_t aligned = pgno_align2os_pgno(env, meta->geometry.first_unallocated + aligner -
|
|
320
|
+
meta->geometry.first_unallocated % aligner);
|
|
321
|
+
meta->geometry.now = aligned;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (meta->geometry.now < meta->geometry.lower)
|
|
326
|
+
meta->geometry.now = meta->geometry.lower;
|
|
327
|
+
if (meta->geometry.now > meta->geometry.upper)
|
|
328
|
+
meta->geometry.now = meta->geometry.upper;
|
|
329
|
+
|
|
330
|
+
/* Update signature */
|
|
331
|
+
assert(meta->geometry.now >= meta->geometry.first_unallocated);
|
|
332
|
+
meta_sign_as_steady(meta);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/* Make resizable */
|
|
336
|
+
__cold static void meta_make_sizeable(meta_t *meta) {
|
|
337
|
+
meta->geometry.lower = MIN_PAGENO;
|
|
338
|
+
if (meta->geometry.grow_pv == 0) {
|
|
339
|
+
const pgno_t step = 1 + (meta->geometry.upper - meta->geometry.lower) / 42;
|
|
340
|
+
meta->geometry.grow_pv = pages2pv(step);
|
|
341
|
+
}
|
|
342
|
+
if (meta->geometry.shrink_pv == 0) {
|
|
343
|
+
const pgno_t step = pv2pages(meta->geometry.grow_pv) << 1;
|
|
344
|
+
meta->geometry.shrink_pv = pages2pv(step);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
__cold static int copy_with_compacting(MDBX_env *env, MDBX_txn *txn, mdbx_filehandle_t fd, uint8_t *buffer,
|
|
349
|
+
const bool dest_is_pipe, const MDBX_copy_flags_t flags) {
|
|
350
|
+
const size_t meta_bytes = pgno2bytes(env, NUM_METAS);
|
|
351
|
+
uint8_t *const data_buffer = buffer + ceil_powerof2(meta_bytes, globals.sys_pagesize);
|
|
352
|
+
meta_t *const meta = meta_init_triplet(env, buffer);
|
|
353
|
+
meta_set_txnid(env, meta, txn->txnid);
|
|
354
|
+
|
|
355
|
+
if (flags & MDBX_CP_FORCE_DYNAMIC_SIZE)
|
|
356
|
+
meta_make_sizeable(meta);
|
|
357
|
+
|
|
358
|
+
/* copy canary sequences if present */
|
|
359
|
+
if (txn->canary.v) {
|
|
360
|
+
meta->canary = txn->canary;
|
|
361
|
+
meta->canary.v = constmeta_txnid(meta);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (txn->dbs[MAIN_DBI].root == P_INVALID) {
|
|
365
|
+
/* When the DB is empty, handle it specially to
|
|
366
|
+
* fix any breakage like page leaks from ITS#8174. */
|
|
367
|
+
meta->trees.main.flags = txn->dbs[MAIN_DBI].flags;
|
|
368
|
+
compacting_fixup_meta(env, meta);
|
|
369
|
+
if (dest_is_pipe) {
|
|
370
|
+
if (flags & MDBX_CP_THROTTLE_MVCC)
|
|
371
|
+
mdbx_txn_park(txn, false);
|
|
372
|
+
int rc = osal_write(fd, buffer, meta_bytes);
|
|
373
|
+
if (likely(rc == MDBX_SUCCESS) && (flags & MDBX_CP_THROTTLE_MVCC) != 0)
|
|
374
|
+
rc = mdbx_txn_unpark(txn, false);
|
|
375
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
376
|
+
return rc;
|
|
377
|
+
}
|
|
378
|
+
} else {
|
|
379
|
+
/* Count free pages + GC pages. */
|
|
380
|
+
cursor_couple_t couple;
|
|
381
|
+
int rc = cursor_init(&couple.outer, txn, FREE_DBI);
|
|
382
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
383
|
+
return rc;
|
|
384
|
+
pgno_t gc_npages = txn->dbs[FREE_DBI].branch_pages + txn->dbs[FREE_DBI].leaf_pages + txn->dbs[FREE_DBI].large_pages;
|
|
385
|
+
MDBX_val key, data;
|
|
386
|
+
rc = outer_first(&couple.outer, &key, &data);
|
|
387
|
+
while (rc == MDBX_SUCCESS) {
|
|
388
|
+
const pnl_t pnl = data.iov_base;
|
|
389
|
+
if (unlikely(data.iov_len % sizeof(pgno_t) || data.iov_len < MDBX_PNL_SIZEOF(pnl))) {
|
|
390
|
+
ERROR("%s/%d: %s %zu", "MDBX_CORRUPTED", MDBX_CORRUPTED, "invalid GC-record length", data.iov_len);
|
|
391
|
+
return MDBX_CORRUPTED;
|
|
392
|
+
}
|
|
393
|
+
if (unlikely(!pnl_check(pnl, txn->geo.first_unallocated))) {
|
|
394
|
+
ERROR("%s/%d: %s", "MDBX_CORRUPTED", MDBX_CORRUPTED, "invalid GC-record content");
|
|
395
|
+
return MDBX_CORRUPTED;
|
|
396
|
+
}
|
|
397
|
+
gc_npages += MDBX_PNL_GETSIZE(pnl);
|
|
398
|
+
rc = outer_next(&couple.outer, &key, &data, MDBX_NEXT);
|
|
399
|
+
}
|
|
400
|
+
if (unlikely(rc != MDBX_NOTFOUND))
|
|
401
|
+
return rc;
|
|
402
|
+
|
|
403
|
+
meta->geometry.first_unallocated = txn->geo.first_unallocated - gc_npages;
|
|
404
|
+
meta->trees.main = txn->dbs[MAIN_DBI];
|
|
405
|
+
|
|
406
|
+
ctx_t ctx;
|
|
407
|
+
memset(&ctx, 0, sizeof(ctx));
|
|
408
|
+
rc = osal_condpair_init(&ctx.condpair);
|
|
409
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
410
|
+
return rc;
|
|
411
|
+
|
|
412
|
+
memset(data_buffer, 0, 2 * (size_t)MDBX_ENVCOPY_WRITEBUF);
|
|
413
|
+
ctx.write_buf[0] = data_buffer;
|
|
414
|
+
ctx.write_buf[1] = data_buffer + (size_t)MDBX_ENVCOPY_WRITEBUF;
|
|
415
|
+
ctx.first_unallocated = NUM_METAS;
|
|
416
|
+
ctx.env = env;
|
|
417
|
+
ctx.fd = fd;
|
|
418
|
+
ctx.txn = txn;
|
|
419
|
+
ctx.flags = flags;
|
|
420
|
+
|
|
421
|
+
osal_thread_t thread;
|
|
422
|
+
int thread_err = osal_thread_create(&thread, compacting_write_thread, &ctx);
|
|
423
|
+
if (likely(thread_err == MDBX_SUCCESS)) {
|
|
424
|
+
if (dest_is_pipe) {
|
|
425
|
+
if (!meta->trees.main.mod_txnid)
|
|
426
|
+
meta->trees.main.mod_txnid = txn->txnid;
|
|
427
|
+
compacting_fixup_meta(env, meta);
|
|
428
|
+
if (flags & MDBX_CP_THROTTLE_MVCC)
|
|
429
|
+
mdbx_txn_park(txn, false);
|
|
430
|
+
rc = osal_write(fd, buffer, meta_bytes);
|
|
431
|
+
if (likely(rc == MDBX_SUCCESS) && (flags & MDBX_CP_THROTTLE_MVCC) != 0)
|
|
432
|
+
rc = mdbx_txn_unpark(txn, false);
|
|
433
|
+
}
|
|
434
|
+
if (likely(rc == MDBX_SUCCESS))
|
|
435
|
+
rc = compacting_walk_tree(&ctx, &meta->trees.main);
|
|
436
|
+
if (ctx.write_len[ctx.head & 1])
|
|
437
|
+
/* toggle to flush non-empty buffers */
|
|
438
|
+
compacting_toggle_write_buffers(&ctx);
|
|
439
|
+
|
|
440
|
+
if (likely(rc == MDBX_SUCCESS) && unlikely(meta->geometry.first_unallocated != ctx.first_unallocated)) {
|
|
441
|
+
if (ctx.first_unallocated > meta->geometry.first_unallocated) {
|
|
442
|
+
ERROR("the source DB %s: post-compactification used pages %" PRIaPGNO " %c expected %" PRIaPGNO,
|
|
443
|
+
"has double-used pages or other corruption", ctx.first_unallocated, '>',
|
|
444
|
+
meta->geometry.first_unallocated);
|
|
445
|
+
rc = MDBX_CORRUPTED; /* corrupted DB */
|
|
446
|
+
}
|
|
447
|
+
if (ctx.first_unallocated < meta->geometry.first_unallocated) {
|
|
448
|
+
WARNING("the source DB %s: post-compactification used pages %" PRIaPGNO " %c expected %" PRIaPGNO,
|
|
449
|
+
"has page leak(s)", ctx.first_unallocated, '<', meta->geometry.first_unallocated);
|
|
450
|
+
if (dest_is_pipe)
|
|
451
|
+
/* the root within already written meta-pages is wrong */
|
|
452
|
+
rc = MDBX_CORRUPTED;
|
|
453
|
+
}
|
|
454
|
+
/* fixup meta */
|
|
455
|
+
meta->geometry.first_unallocated = ctx.first_unallocated;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/* toggle with empty buffers to exit thread's loop */
|
|
459
|
+
eASSERT(env, (ctx.write_len[ctx.head & 1]) == 0);
|
|
460
|
+
compacting_toggle_write_buffers(&ctx);
|
|
461
|
+
thread_err = osal_thread_join(thread);
|
|
462
|
+
eASSERT(env, (ctx.tail == ctx.head && ctx.write_len[ctx.head & 1] == 0) || ctx.error);
|
|
463
|
+
osal_condpair_destroy(&ctx.condpair);
|
|
464
|
+
}
|
|
465
|
+
if (unlikely(thread_err != MDBX_SUCCESS))
|
|
466
|
+
return thread_err;
|
|
467
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
468
|
+
return rc;
|
|
469
|
+
if (unlikely(ctx.error != MDBX_SUCCESS))
|
|
470
|
+
return ctx.error;
|
|
471
|
+
if (!dest_is_pipe)
|
|
472
|
+
compacting_fixup_meta(env, meta);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (flags & MDBX_CP_THROTTLE_MVCC)
|
|
476
|
+
mdbx_txn_park(txn, false);
|
|
477
|
+
|
|
478
|
+
/* Extend file if required */
|
|
479
|
+
if (meta->geometry.now != meta->geometry.first_unallocated) {
|
|
480
|
+
const size_t whole_size = pgno2bytes(env, meta->geometry.now);
|
|
481
|
+
if (!dest_is_pipe)
|
|
482
|
+
return osal_ftruncate(fd, whole_size);
|
|
483
|
+
|
|
484
|
+
const size_t used_size = pgno2bytes(env, meta->geometry.first_unallocated);
|
|
485
|
+
memset(data_buffer, 0, (size_t)MDBX_ENVCOPY_WRITEBUF);
|
|
486
|
+
for (size_t offset = used_size; offset < whole_size;) {
|
|
487
|
+
const size_t chunk =
|
|
488
|
+
((size_t)MDBX_ENVCOPY_WRITEBUF < whole_size - offset) ? (size_t)MDBX_ENVCOPY_WRITEBUF : whole_size - offset;
|
|
489
|
+
int rc = osal_write(fd, data_buffer, chunk);
|
|
490
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
491
|
+
return rc;
|
|
492
|
+
offset += chunk;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return MDBX_SUCCESS;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
//----------------------------------------------------------------------------
|
|
499
|
+
|
|
500
|
+
__cold static int copy_asis(MDBX_env *env, MDBX_txn *txn, mdbx_filehandle_t fd, uint8_t *buffer,
|
|
501
|
+
const bool dest_is_pipe, const MDBX_copy_flags_t flags) {
|
|
502
|
+
bool should_unlock = false;
|
|
503
|
+
if ((txn->flags & MDBX_TXN_RDONLY) != 0 && (flags & MDBX_CP_RENEW_TXN) != 0) {
|
|
504
|
+
/* Try temporarily block writers until we snapshot the meta pages */
|
|
505
|
+
int err = lck_txn_lock(env, true);
|
|
506
|
+
if (likely(err == MDBX_SUCCESS))
|
|
507
|
+
should_unlock = true;
|
|
508
|
+
else if (unlikely(err != MDBX_BUSY))
|
|
509
|
+
return err;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
jitter4testing(false);
|
|
513
|
+
int rc = MDBX_SUCCESS;
|
|
514
|
+
const size_t meta_bytes = pgno2bytes(env, NUM_METAS);
|
|
515
|
+
troika_t troika = meta_tap(env);
|
|
516
|
+
/* Make a snapshot of meta-pages,
|
|
517
|
+
* but writing ones after the data was flushed */
|
|
518
|
+
retry_snap_meta:
|
|
519
|
+
memcpy(buffer, env->dxb_mmap.base, meta_bytes);
|
|
520
|
+
const meta_ptr_t recent = meta_recent(env, &troika);
|
|
521
|
+
meta_t *headcopy = /* LY: get pointer to the snapshot copy */
|
|
522
|
+
ptr_disp(buffer, ptr_dist(recent.ptr_c, env->dxb_mmap.base));
|
|
523
|
+
jitter4testing(false);
|
|
524
|
+
if (txn->flags & MDBX_TXN_RDONLY) {
|
|
525
|
+
if (recent.txnid != txn->txnid) {
|
|
526
|
+
if (flags & MDBX_CP_RENEW_TXN)
|
|
527
|
+
rc = mdbx_txn_renew(txn);
|
|
528
|
+
else {
|
|
529
|
+
rc = MDBX_MVCC_RETARDED;
|
|
530
|
+
for (size_t n = 0; n < NUM_METAS; ++n) {
|
|
531
|
+
meta_t *const meta = page_meta(ptr_disp(buffer, pgno2bytes(env, n)));
|
|
532
|
+
if (troika.txnid[n] == txn->txnid && ((/* is_steady */ (troika.fsm >> n) & 1) || rc != MDBX_SUCCESS)) {
|
|
533
|
+
rc = MDBX_SUCCESS;
|
|
534
|
+
headcopy = meta;
|
|
535
|
+
} else if (troika.txnid[n] > txn->txnid)
|
|
536
|
+
meta_set_txnid(env, meta, 0);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (should_unlock)
|
|
541
|
+
lck_txn_unlock(env);
|
|
542
|
+
else {
|
|
543
|
+
troika_t snap = meta_tap(env);
|
|
544
|
+
if (memcmp(&troika, &snap, sizeof(troika_t)) && rc == MDBX_SUCCESS) {
|
|
545
|
+
troika = snap;
|
|
546
|
+
goto retry_snap_meta;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
551
|
+
return rc;
|
|
552
|
+
|
|
553
|
+
if (txn->flags & MDBX_TXN_RDONLY)
|
|
554
|
+
eASSERT(env, meta_txnid(headcopy) == txn->txnid);
|
|
555
|
+
if (flags & MDBX_CP_FORCE_DYNAMIC_SIZE)
|
|
556
|
+
meta_make_sizeable(headcopy);
|
|
557
|
+
/* Update signature to steady */
|
|
558
|
+
meta_sign_as_steady(headcopy);
|
|
559
|
+
|
|
560
|
+
/* Copy the data */
|
|
561
|
+
const size_t whole_size = pgno_align2os_bytes(env, txn->geo.end_pgno);
|
|
562
|
+
const size_t used_size = pgno2bytes(env, txn->geo.first_unallocated);
|
|
563
|
+
jitter4testing(false);
|
|
564
|
+
|
|
565
|
+
if (flags & MDBX_CP_THROTTLE_MVCC)
|
|
566
|
+
mdbx_txn_park(txn, false);
|
|
567
|
+
|
|
568
|
+
if (dest_is_pipe)
|
|
569
|
+
rc = osal_write(fd, buffer, meta_bytes);
|
|
570
|
+
|
|
571
|
+
uint8_t *const data_buffer = buffer + ceil_powerof2(meta_bytes, globals.sys_pagesize);
|
|
572
|
+
#if MDBX_USE_COPYFILERANGE
|
|
573
|
+
static bool copyfilerange_unavailable;
|
|
574
|
+
#if (defined(__linux__) || defined(__gnu_linux__))
|
|
575
|
+
if (globals.linux_kernel_version >= 0x05030000 && globals.linux_kernel_version < 0x05130000)
|
|
576
|
+
copyfilerange_unavailable = true;
|
|
577
|
+
#endif /* linux */
|
|
578
|
+
bool not_the_same_filesystem = false;
|
|
579
|
+
if (!copyfilerange_unavailable) {
|
|
580
|
+
struct statfs statfs_info;
|
|
581
|
+
if (fstatfs(fd, &statfs_info) || statfs_info.f_type == /* ECRYPTFS_SUPER_MAGIC */ 0xf15f)
|
|
582
|
+
/* avoid use copyfilerange_unavailable() to ecryptfs due bugs */
|
|
583
|
+
not_the_same_filesystem = true;
|
|
584
|
+
}
|
|
585
|
+
#endif /* MDBX_USE_COPYFILERANGE */
|
|
586
|
+
|
|
587
|
+
for (size_t offset = meta_bytes; rc == MDBX_SUCCESS && offset < used_size;) {
|
|
588
|
+
if (flags & MDBX_CP_THROTTLE_MVCC) {
|
|
589
|
+
rc = mdbx_txn_unpark(txn, false);
|
|
590
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
591
|
+
break;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
#if MDBX_USE_SENDFILE
|
|
595
|
+
static bool sendfile_unavailable;
|
|
596
|
+
if (dest_is_pipe && likely(!sendfile_unavailable)) {
|
|
597
|
+
off_t in_offset = offset;
|
|
598
|
+
const ssize_t written = sendfile(fd, env->lazy_fd, &in_offset, used_size - offset);
|
|
599
|
+
if (likely(written > 0)) {
|
|
600
|
+
offset = in_offset;
|
|
601
|
+
if (flags & MDBX_CP_THROTTLE_MVCC)
|
|
602
|
+
rc = mdbx_txn_park(txn, false);
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
rc = MDBX_ENODATA;
|
|
606
|
+
if (written == 0 || ignore_enosys_and_eagain(rc = errno) != MDBX_RESULT_TRUE)
|
|
607
|
+
break;
|
|
608
|
+
sendfile_unavailable = true;
|
|
609
|
+
}
|
|
610
|
+
#endif /* MDBX_USE_SENDFILE */
|
|
611
|
+
|
|
612
|
+
#if MDBX_USE_COPYFILERANGE
|
|
613
|
+
if (!dest_is_pipe && !not_the_same_filesystem && likely(!copyfilerange_unavailable)) {
|
|
614
|
+
off_t in_offset = offset, out_offset = offset;
|
|
615
|
+
ssize_t bytes_copied = copy_file_range(env->lazy_fd, &in_offset, fd, &out_offset, used_size - offset, 0);
|
|
616
|
+
if (likely(bytes_copied > 0)) {
|
|
617
|
+
offset = in_offset;
|
|
618
|
+
if (flags & MDBX_CP_THROTTLE_MVCC)
|
|
619
|
+
rc = mdbx_txn_park(txn, false);
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
rc = MDBX_ENODATA;
|
|
623
|
+
if (bytes_copied == 0)
|
|
624
|
+
break;
|
|
625
|
+
rc = errno;
|
|
626
|
+
if (rc == EXDEV || rc == /* workaround for ecryptfs bug(s),
|
|
627
|
+
maybe useful for others FS */
|
|
628
|
+
EINVAL)
|
|
629
|
+
not_the_same_filesystem = true;
|
|
630
|
+
else if (ignore_enosys_and_eagain(rc) == MDBX_RESULT_TRUE)
|
|
631
|
+
copyfilerange_unavailable = true;
|
|
632
|
+
else
|
|
633
|
+
break;
|
|
634
|
+
}
|
|
635
|
+
#endif /* MDBX_USE_COPYFILERANGE */
|
|
636
|
+
|
|
637
|
+
/* fallback to portable */
|
|
638
|
+
const size_t chunk =
|
|
639
|
+
((size_t)MDBX_ENVCOPY_WRITEBUF < used_size - offset) ? (size_t)MDBX_ENVCOPY_WRITEBUF : used_size - offset;
|
|
640
|
+
/* copy to avoid EFAULT in case swapped-out */
|
|
641
|
+
memcpy(data_buffer, ptr_disp(env->dxb_mmap.base, offset), chunk);
|
|
642
|
+
if (flags & MDBX_CP_THROTTLE_MVCC)
|
|
643
|
+
mdbx_txn_park(txn, false);
|
|
644
|
+
rc = osal_write(fd, data_buffer, chunk);
|
|
645
|
+
offset += chunk;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/* Extend file if required */
|
|
649
|
+
if (likely(rc == MDBX_SUCCESS) && whole_size != used_size) {
|
|
650
|
+
if (!dest_is_pipe)
|
|
651
|
+
rc = osal_ftruncate(fd, whole_size);
|
|
652
|
+
else {
|
|
653
|
+
memset(data_buffer, 0, (size_t)MDBX_ENVCOPY_WRITEBUF);
|
|
654
|
+
for (size_t offset = used_size; rc == MDBX_SUCCESS && offset < whole_size;) {
|
|
655
|
+
const size_t chunk =
|
|
656
|
+
((size_t)MDBX_ENVCOPY_WRITEBUF < whole_size - offset) ? (size_t)MDBX_ENVCOPY_WRITEBUF : whole_size - offset;
|
|
657
|
+
rc = osal_write(fd, data_buffer, chunk);
|
|
658
|
+
offset += chunk;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
return rc;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
//----------------------------------------------------------------------------
|
|
667
|
+
|
|
668
|
+
__cold static int copy2fd(MDBX_txn *txn, mdbx_filehandle_t fd, MDBX_copy_flags_t flags) {
|
|
669
|
+
if (unlikely(txn->flags & MDBX_TXN_DIRTY))
|
|
670
|
+
return MDBX_BAD_TXN;
|
|
671
|
+
|
|
672
|
+
int rc = MDBX_SUCCESS;
|
|
673
|
+
if (txn->flags & MDBX_TXN_RDONLY) {
|
|
674
|
+
if (flags & MDBX_CP_THROTTLE_MVCC) {
|
|
675
|
+
rc = mdbx_txn_park(txn, true);
|
|
676
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
677
|
+
return rc;
|
|
678
|
+
}
|
|
679
|
+
} else if (unlikely(flags & (MDBX_CP_THROTTLE_MVCC | MDBX_CP_RENEW_TXN)))
|
|
680
|
+
return MDBX_EINVAL;
|
|
681
|
+
|
|
682
|
+
const int dest_is_pipe = osal_is_pipe(fd);
|
|
683
|
+
if (MDBX_IS_ERROR(dest_is_pipe))
|
|
684
|
+
return dest_is_pipe;
|
|
685
|
+
|
|
686
|
+
if (!dest_is_pipe) {
|
|
687
|
+
rc = osal_fseek(fd, 0);
|
|
688
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
689
|
+
return rc;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
MDBX_env *const env = txn->env;
|
|
693
|
+
const size_t buffer_size =
|
|
694
|
+
pgno_align2os_bytes(env, NUM_METAS) +
|
|
695
|
+
ceil_powerof2(((flags & MDBX_CP_COMPACT) ? 2 * (size_t)MDBX_ENVCOPY_WRITEBUF : (size_t)MDBX_ENVCOPY_WRITEBUF),
|
|
696
|
+
globals.sys_pagesize);
|
|
697
|
+
|
|
698
|
+
uint8_t *buffer = nullptr;
|
|
699
|
+
rc = osal_memalign_alloc(globals.sys_pagesize, buffer_size, (void **)&buffer);
|
|
700
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
701
|
+
return rc;
|
|
702
|
+
|
|
703
|
+
if (!dest_is_pipe) {
|
|
704
|
+
/* Firstly write a stub to meta-pages.
|
|
705
|
+
* Now we sure to incomplete copy will not be used. */
|
|
706
|
+
memset(buffer, -1, pgno2bytes(env, NUM_METAS));
|
|
707
|
+
rc = osal_write(fd, buffer, pgno2bytes(env, NUM_METAS));
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
if (likely(rc == MDBX_SUCCESS))
|
|
711
|
+
rc = mdbx_txn_unpark(txn, false);
|
|
712
|
+
if (likely(rc == MDBX_SUCCESS)) {
|
|
713
|
+
memset(buffer, 0, pgno2bytes(env, NUM_METAS));
|
|
714
|
+
rc = ((flags & MDBX_CP_COMPACT) ? copy_with_compacting : copy_asis)(env, txn, fd, buffer, dest_is_pipe, flags);
|
|
715
|
+
|
|
716
|
+
if (likely(rc == MDBX_SUCCESS))
|
|
717
|
+
rc = mdbx_txn_unpark(txn, false);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if (txn->flags & MDBX_TXN_RDONLY) {
|
|
721
|
+
if (flags & MDBX_CP_THROTTLE_MVCC)
|
|
722
|
+
mdbx_txn_park(txn, true);
|
|
723
|
+
else if (flags & MDBX_CP_DISPOSE_TXN)
|
|
724
|
+
mdbx_txn_reset(txn);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
if (!dest_is_pipe) {
|
|
728
|
+
if (likely(rc == MDBX_SUCCESS) && (flags & MDBX_CP_DONT_FLUSH) == 0)
|
|
729
|
+
rc = osal_fsync(fd, MDBX_SYNC_DATA | MDBX_SYNC_SIZE);
|
|
730
|
+
|
|
731
|
+
/* Write actual meta */
|
|
732
|
+
if (likely(rc == MDBX_SUCCESS))
|
|
733
|
+
rc = osal_pwrite(fd, buffer, pgno2bytes(env, NUM_METAS), 0);
|
|
734
|
+
|
|
735
|
+
if (likely(rc == MDBX_SUCCESS) && (flags & MDBX_CP_DONT_FLUSH) == 0)
|
|
736
|
+
rc = osal_fsync(fd, MDBX_SYNC_DATA | MDBX_SYNC_IODQ);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
osal_memalign_free(buffer);
|
|
740
|
+
return rc;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
__cold static int copy2pathname(MDBX_txn *txn, const pathchar_t *dest_path, MDBX_copy_flags_t flags) {
|
|
744
|
+
if (unlikely(!dest_path || *dest_path == '\0'))
|
|
745
|
+
return MDBX_EINVAL;
|
|
746
|
+
|
|
747
|
+
/* The destination path must exist, but the destination file must not.
|
|
748
|
+
* We don't want the OS to cache the writes, since the source data is
|
|
749
|
+
* already in the OS cache. */
|
|
750
|
+
mdbx_filehandle_t newfd = INVALID_HANDLE_VALUE;
|
|
751
|
+
int rc = osal_openfile(MDBX_OPEN_COPY, txn->env, dest_path, &newfd,
|
|
752
|
+
#if defined(_WIN32) || defined(_WIN64)
|
|
753
|
+
(mdbx_mode_t)-1
|
|
754
|
+
#else
|
|
755
|
+
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
|
|
756
|
+
#endif
|
|
757
|
+
);
|
|
758
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
759
|
+
return rc;
|
|
760
|
+
|
|
761
|
+
#if defined(_WIN32) || defined(_WIN64)
|
|
762
|
+
/* no locking required since the file opened with ShareMode == 0 */
|
|
763
|
+
#else
|
|
764
|
+
MDBX_STRUCT_FLOCK lock_op;
|
|
765
|
+
memset(&lock_op, 0, sizeof(lock_op));
|
|
766
|
+
lock_op.l_type = F_WRLCK;
|
|
767
|
+
lock_op.l_whence = SEEK_SET;
|
|
768
|
+
lock_op.l_start = 0;
|
|
769
|
+
lock_op.l_len = OFF_T_MAX;
|
|
770
|
+
const int err_fcntl = MDBX_FCNTL(newfd, MDBX_F_SETLK, &lock_op) ? errno : MDBX_SUCCESS;
|
|
771
|
+
|
|
772
|
+
const int err_flock =
|
|
773
|
+
#ifdef LOCK_EX
|
|
774
|
+
flock(newfd, LOCK_EX | LOCK_NB) ? errno : MDBX_SUCCESS;
|
|
775
|
+
#else
|
|
776
|
+
MDBX_ENOSYS;
|
|
777
|
+
#endif /* LOCK_EX */
|
|
778
|
+
|
|
779
|
+
const int err_check_fs_local =
|
|
780
|
+
/* avoid call osal_check_fs_local() on success */
|
|
781
|
+
(!err_fcntl && !err_flock && !MDBX_DEBUG) ? MDBX_SUCCESS :
|
|
782
|
+
#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 24
|
|
783
|
+
osal_check_fs_local(newfd, 0);
|
|
784
|
+
#else
|
|
785
|
+
MDBX_ENOSYS;
|
|
786
|
+
#endif
|
|
787
|
+
|
|
788
|
+
const bool flock_may_fail =
|
|
789
|
+
#if defined(__linux__) || defined(__gnu_linux__)
|
|
790
|
+
err_check_fs_local != 0;
|
|
791
|
+
#else
|
|
792
|
+
true;
|
|
793
|
+
#endif /* Linux */
|
|
794
|
+
|
|
795
|
+
if (!err_fcntl &&
|
|
796
|
+
(err_flock == EWOULDBLOCK || err_flock == EAGAIN || ignore_enosys_and_eremote(err_flock) == MDBX_RESULT_TRUE)) {
|
|
797
|
+
rc = err_flock;
|
|
798
|
+
if (flock_may_fail) {
|
|
799
|
+
WARNING("ignore %s(%" MDBX_PRIsPATH ") error %d: since %s done, local/remote-fs check %d", "flock", dest_path,
|
|
800
|
+
err_flock, "fcntl-lock", err_check_fs_local);
|
|
801
|
+
rc = MDBX_SUCCESS;
|
|
802
|
+
}
|
|
803
|
+
} else if (!err_flock && err_check_fs_local == MDBX_RESULT_TRUE &&
|
|
804
|
+
ignore_enosys_and_eremote(err_fcntl) == MDBX_RESULT_TRUE) {
|
|
805
|
+
WARNING("ignore %s(%" MDBX_PRIsPATH ") error %d: since %s done, local/remote-fs check %d", "fcntl-lock", dest_path,
|
|
806
|
+
err_fcntl, "flock", err_check_fs_local);
|
|
807
|
+
} else if (err_fcntl || err_flock) {
|
|
808
|
+
ERROR("file-lock(%" MDBX_PRIsPATH ") failed: fcntl-lock %d, flock %d, local/remote-fs check %d", dest_path,
|
|
809
|
+
err_fcntl, err_flock, err_check_fs_local);
|
|
810
|
+
if (err_fcntl == ENOLCK || err_flock == ENOLCK)
|
|
811
|
+
rc = ENOLCK;
|
|
812
|
+
else if (err_fcntl == EWOULDBLOCK || err_flock == EWOULDBLOCK)
|
|
813
|
+
rc = EWOULDBLOCK;
|
|
814
|
+
else if (EWOULDBLOCK != EAGAIN && (err_fcntl == EAGAIN || err_flock == EAGAIN))
|
|
815
|
+
rc = EAGAIN;
|
|
816
|
+
else
|
|
817
|
+
rc = (err_fcntl && ignore_enosys_and_eremote(err_fcntl) != MDBX_RESULT_TRUE) ? err_fcntl : err_flock;
|
|
818
|
+
}
|
|
819
|
+
#endif /* Windows / POSIX */
|
|
820
|
+
|
|
821
|
+
if (rc == MDBX_SUCCESS)
|
|
822
|
+
rc = copy2fd(txn, newfd, flags);
|
|
823
|
+
|
|
824
|
+
if (newfd != INVALID_HANDLE_VALUE) {
|
|
825
|
+
int err = osal_closefile(newfd);
|
|
826
|
+
if (rc == MDBX_SUCCESS && err != rc)
|
|
827
|
+
rc = err;
|
|
828
|
+
if (rc != MDBX_SUCCESS)
|
|
829
|
+
(void)osal_removefile(dest_path);
|
|
830
|
+
}
|
|
831
|
+
return rc;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
//----------------------------------------------------------------------------
|
|
835
|
+
|
|
836
|
+
__cold int mdbx_txn_copy2fd(MDBX_txn *txn, mdbx_filehandle_t fd, MDBX_copy_flags_t flags) {
|
|
837
|
+
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
|
|
838
|
+
if (likely(rc == MDBX_SUCCESS))
|
|
839
|
+
rc = copy2fd(txn, fd, flags);
|
|
840
|
+
if (flags & MDBX_CP_DISPOSE_TXN)
|
|
841
|
+
mdbx_txn_abort(txn);
|
|
842
|
+
return LOG_IFERR(rc);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
__cold int mdbx_env_copy2fd(MDBX_env *env, mdbx_filehandle_t fd, MDBX_copy_flags_t flags) {
|
|
846
|
+
if (unlikely(flags & (MDBX_CP_DISPOSE_TXN | MDBX_CP_RENEW_TXN)))
|
|
847
|
+
return LOG_IFERR(MDBX_EINVAL);
|
|
848
|
+
|
|
849
|
+
int rc = check_env(env, true);
|
|
850
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
851
|
+
return LOG_IFERR(rc);
|
|
852
|
+
|
|
853
|
+
MDBX_txn *txn = nullptr;
|
|
854
|
+
rc = mdbx_txn_begin(env, nullptr, MDBX_TXN_RDONLY, &txn);
|
|
855
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
856
|
+
return LOG_IFERR(rc);
|
|
857
|
+
|
|
858
|
+
rc = copy2fd(txn, fd, flags | MDBX_CP_DISPOSE_TXN | MDBX_CP_RENEW_TXN);
|
|
859
|
+
mdbx_txn_abort(txn);
|
|
860
|
+
return LOG_IFERR(rc);
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
__cold int mdbx_txn_copy2pathname(MDBX_txn *txn, const char *dest_path, MDBX_copy_flags_t flags) {
|
|
864
|
+
#if defined(_WIN32) || defined(_WIN64)
|
|
865
|
+
wchar_t *dest_pathW = nullptr;
|
|
866
|
+
int rc = osal_mb2w(dest_path, &dest_pathW);
|
|
867
|
+
if (likely(rc == MDBX_SUCCESS)) {
|
|
868
|
+
rc = mdbx_txn_copy2pathnameW(txn, dest_pathW, flags);
|
|
869
|
+
osal_free(dest_pathW);
|
|
870
|
+
}
|
|
871
|
+
return LOG_IFERR(rc);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
__cold int mdbx_txn_copy2pathnameW(MDBX_txn *txn, const wchar_t *dest_path, MDBX_copy_flags_t flags) {
|
|
875
|
+
#endif /* Windows */
|
|
876
|
+
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
|
|
877
|
+
if (likely(rc == MDBX_SUCCESS))
|
|
878
|
+
rc = copy2pathname(txn, dest_path, flags);
|
|
879
|
+
if (flags & MDBX_CP_DISPOSE_TXN)
|
|
880
|
+
mdbx_txn_abort(txn);
|
|
881
|
+
return LOG_IFERR(rc);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
__cold int mdbx_env_copy(MDBX_env *env, const char *dest_path, MDBX_copy_flags_t flags) {
|
|
885
|
+
#if defined(_WIN32) || defined(_WIN64)
|
|
886
|
+
wchar_t *dest_pathW = nullptr;
|
|
887
|
+
int rc = osal_mb2w(dest_path, &dest_pathW);
|
|
888
|
+
if (likely(rc == MDBX_SUCCESS)) {
|
|
889
|
+
rc = mdbx_env_copyW(env, dest_pathW, flags);
|
|
890
|
+
osal_free(dest_pathW);
|
|
891
|
+
}
|
|
892
|
+
return LOG_IFERR(rc);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
__cold int mdbx_env_copyW(MDBX_env *env, const wchar_t *dest_path, MDBX_copy_flags_t flags) {
|
|
896
|
+
#endif /* Windows */
|
|
897
|
+
if (unlikely(flags & (MDBX_CP_DISPOSE_TXN | MDBX_CP_RENEW_TXN)))
|
|
898
|
+
return LOG_IFERR(MDBX_EINVAL);
|
|
899
|
+
|
|
900
|
+
int rc = check_env(env, true);
|
|
901
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
902
|
+
return LOG_IFERR(rc);
|
|
903
|
+
|
|
904
|
+
MDBX_txn *txn = nullptr;
|
|
905
|
+
rc = mdbx_txn_begin(env, nullptr, MDBX_TXN_RDONLY, &txn);
|
|
906
|
+
if (unlikely(rc != MDBX_SUCCESS))
|
|
907
|
+
return LOG_IFERR(rc);
|
|
908
|
+
|
|
909
|
+
rc = copy2pathname(txn, dest_path, flags | MDBX_CP_DISPOSE_TXN | MDBX_CP_RENEW_TXN);
|
|
910
|
+
mdbx_txn_abort(txn);
|
|
911
|
+
return LOG_IFERR(rc);
|
|
912
|
+
}
|