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.
Files changed (220) hide show
  1. package/.github/workflows/ci.yml +32 -0
  2. package/.github/workflows/publish.yml +27 -0
  3. package/.gitmodules +3 -0
  4. package/CMakeLists.txt +53 -0
  5. package/LICENSE +201 -0
  6. package/README.md +639 -0
  7. package/build.js +11 -0
  8. package/deps/libmdbx/.clang-format +3 -0
  9. package/deps/libmdbx/.cmake-format.yaml +3 -0
  10. package/deps/libmdbx/.le.ini +40 -0
  11. package/deps/libmdbx/CMakeLists.txt +1269 -0
  12. package/deps/libmdbx/COPYRIGHT +159 -0
  13. package/deps/libmdbx/ChangeLog.md +2786 -0
  14. package/deps/libmdbx/GNUmakefile +950 -0
  15. package/deps/libmdbx/LICENSE +177 -0
  16. package/deps/libmdbx/Makefile +16 -0
  17. package/deps/libmdbx/NOTICE +39 -0
  18. package/deps/libmdbx/README.md +863 -0
  19. package/deps/libmdbx/TODO.md +43 -0
  20. package/deps/libmdbx/cmake/compiler.cmake +1221 -0
  21. package/deps/libmdbx/cmake/profile.cmake +58 -0
  22. package/deps/libmdbx/cmake/utils.cmake +524 -0
  23. package/deps/libmdbx/conanfile.py +323 -0
  24. package/deps/libmdbx/docs/Doxyfile.in +2734 -0
  25. package/deps/libmdbx/docs/_preface.md +47 -0
  26. package/deps/libmdbx/docs/_restrictions.md +248 -0
  27. package/deps/libmdbx/docs/_starting.md +245 -0
  28. package/deps/libmdbx/docs/_toc.md +34 -0
  29. package/deps/libmdbx/docs/header.html +96 -0
  30. package/deps/libmdbx/example/CMakeLists.txt +6 -0
  31. package/deps/libmdbx/example/README.md +1 -0
  32. package/deps/libmdbx/example/example-mdbx.c +154 -0
  33. package/deps/libmdbx/example/sample-bdb.txt +77 -0
  34. package/deps/libmdbx/mdbx.h +6655 -0
  35. package/deps/libmdbx/mdbx.h++ +6428 -0
  36. package/deps/libmdbx/packages/buildroot/0001-package-libmdbx-new-package-library-database.patch +173 -0
  37. package/deps/libmdbx/src/alloy.c +54 -0
  38. package/deps/libmdbx/src/api-cold.c +543 -0
  39. package/deps/libmdbx/src/api-copy.c +912 -0
  40. package/deps/libmdbx/src/api-cursor.c +754 -0
  41. package/deps/libmdbx/src/api-dbi.c +315 -0
  42. package/deps/libmdbx/src/api-env.c +1434 -0
  43. package/deps/libmdbx/src/api-extra.c +165 -0
  44. package/deps/libmdbx/src/api-key-transform.c +197 -0
  45. package/deps/libmdbx/src/api-misc.c +286 -0
  46. package/deps/libmdbx/src/api-opts.c +575 -0
  47. package/deps/libmdbx/src/api-range-estimate.c +365 -0
  48. package/deps/libmdbx/src/api-txn-data.c +454 -0
  49. package/deps/libmdbx/src/api-txn.c +921 -0
  50. package/deps/libmdbx/src/atomics-ops.h +364 -0
  51. package/deps/libmdbx/src/atomics-types.h +97 -0
  52. package/deps/libmdbx/src/audit.c +109 -0
  53. package/deps/libmdbx/src/bits.md +34 -0
  54. package/deps/libmdbx/src/chk.c +1796 -0
  55. package/deps/libmdbx/src/cogs.c +309 -0
  56. package/deps/libmdbx/src/cogs.h +506 -0
  57. package/deps/libmdbx/src/coherency.c +170 -0
  58. package/deps/libmdbx/src/config.h.in +88 -0
  59. package/deps/libmdbx/src/cursor.c +2396 -0
  60. package/deps/libmdbx/src/cursor.h +391 -0
  61. package/deps/libmdbx/src/dbi.c +717 -0
  62. package/deps/libmdbx/src/dbi.h +142 -0
  63. package/deps/libmdbx/src/debug_begin.h +36 -0
  64. package/deps/libmdbx/src/debug_end.h +15 -0
  65. package/deps/libmdbx/src/dpl.c +486 -0
  66. package/deps/libmdbx/src/dpl.h +134 -0
  67. package/deps/libmdbx/src/dxb.c +1335 -0
  68. package/deps/libmdbx/src/env.c +607 -0
  69. package/deps/libmdbx/src/essentials.h +125 -0
  70. package/deps/libmdbx/src/gc-get.c +1345 -0
  71. package/deps/libmdbx/src/gc-put.c +970 -0
  72. package/deps/libmdbx/src/gc.h +40 -0
  73. package/deps/libmdbx/src/global.c +474 -0
  74. package/deps/libmdbx/src/internals.h +585 -0
  75. package/deps/libmdbx/src/layout-dxb.h +288 -0
  76. package/deps/libmdbx/src/layout-lck.h +289 -0
  77. package/deps/libmdbx/src/lck-posix.c +859 -0
  78. package/deps/libmdbx/src/lck-windows.c +607 -0
  79. package/deps/libmdbx/src/lck.c +174 -0
  80. package/deps/libmdbx/src/lck.h +110 -0
  81. package/deps/libmdbx/src/logging_and_debug.c +250 -0
  82. package/deps/libmdbx/src/logging_and_debug.h +159 -0
  83. package/deps/libmdbx/src/man1/mdbx_chk.1 +106 -0
  84. package/deps/libmdbx/src/man1/mdbx_copy.1 +95 -0
  85. package/deps/libmdbx/src/man1/mdbx_drop.1 +48 -0
  86. package/deps/libmdbx/src/man1/mdbx_dump.1 +101 -0
  87. package/deps/libmdbx/src/man1/mdbx_load.1 +105 -0
  88. package/deps/libmdbx/src/man1/mdbx_stat.1 +86 -0
  89. package/deps/libmdbx/src/mdbx.c++ +1837 -0
  90. package/deps/libmdbx/src/meta.c +656 -0
  91. package/deps/libmdbx/src/meta.h +168 -0
  92. package/deps/libmdbx/src/mvcc-readers.c +414 -0
  93. package/deps/libmdbx/src/node.c +365 -0
  94. package/deps/libmdbx/src/node.h +102 -0
  95. package/deps/libmdbx/src/ntdll.def +1246 -0
  96. package/deps/libmdbx/src/options.h +534 -0
  97. package/deps/libmdbx/src/osal.c +3485 -0
  98. package/deps/libmdbx/src/osal.h +587 -0
  99. package/deps/libmdbx/src/page-get.c +483 -0
  100. package/deps/libmdbx/src/page-iov.c +185 -0
  101. package/deps/libmdbx/src/page-iov.h +34 -0
  102. package/deps/libmdbx/src/page-ops.c +744 -0
  103. package/deps/libmdbx/src/page-ops.h +142 -0
  104. package/deps/libmdbx/src/pnl.c +236 -0
  105. package/deps/libmdbx/src/pnl.h +146 -0
  106. package/deps/libmdbx/src/preface.h +990 -0
  107. package/deps/libmdbx/src/proto.h +105 -0
  108. package/deps/libmdbx/src/refund.c +212 -0
  109. package/deps/libmdbx/src/sort.h +484 -0
  110. package/deps/libmdbx/src/spill.c +431 -0
  111. package/deps/libmdbx/src/spill.h +74 -0
  112. package/deps/libmdbx/src/table.c +107 -0
  113. package/deps/libmdbx/src/tls.c +551 -0
  114. package/deps/libmdbx/src/tls.h +43 -0
  115. package/deps/libmdbx/src/tools/chk.c +673 -0
  116. package/deps/libmdbx/src/tools/copy.c +166 -0
  117. package/deps/libmdbx/src/tools/drop.c +199 -0
  118. package/deps/libmdbx/src/tools/dump.c +515 -0
  119. package/deps/libmdbx/src/tools/load.c +831 -0
  120. package/deps/libmdbx/src/tools/stat.c +516 -0
  121. package/deps/libmdbx/src/tools/wingetopt.c +87 -0
  122. package/deps/libmdbx/src/tools/wingetopt.h +30 -0
  123. package/deps/libmdbx/src/tree-ops.c +1554 -0
  124. package/deps/libmdbx/src/tree-search.c +140 -0
  125. package/deps/libmdbx/src/txl.c +99 -0
  126. package/deps/libmdbx/src/txl.h +26 -0
  127. package/deps/libmdbx/src/txn.c +1083 -0
  128. package/deps/libmdbx/src/unaligned.h +205 -0
  129. package/deps/libmdbx/src/utils.c +32 -0
  130. package/deps/libmdbx/src/utils.h +76 -0
  131. package/deps/libmdbx/src/version.c.in +44 -0
  132. package/deps/libmdbx/src/walk.c +290 -0
  133. package/deps/libmdbx/src/walk.h +20 -0
  134. package/deps/libmdbx/src/windows-import.c +152 -0
  135. package/deps/libmdbx/src/windows-import.h +128 -0
  136. package/deps/libmdbx/test/CMakeLists.txt +317 -0
  137. package/deps/libmdbx/test/append.c++ +237 -0
  138. package/deps/libmdbx/test/base.h++ +92 -0
  139. package/deps/libmdbx/test/battery-tmux.sh +64 -0
  140. package/deps/libmdbx/test/cases.c++ +118 -0
  141. package/deps/libmdbx/test/chrono.c++ +134 -0
  142. package/deps/libmdbx/test/chrono.h++ +85 -0
  143. package/deps/libmdbx/test/config.c++ +643 -0
  144. package/deps/libmdbx/test/config.h++ +334 -0
  145. package/deps/libmdbx/test/copy.c++ +62 -0
  146. package/deps/libmdbx/test/dead.c++ +39 -0
  147. package/deps/libmdbx/test/dump-load.sh +40 -0
  148. package/deps/libmdbx/test/extra/crunched_delete.c++ +409 -0
  149. package/deps/libmdbx/test/extra/cursor_closing.c++ +410 -0
  150. package/deps/libmdbx/test/extra/dbi.c++ +229 -0
  151. package/deps/libmdbx/test/extra/doubtless_positioning.c++ +253 -0
  152. package/deps/libmdbx/test/extra/dupfix_addodd.c +94 -0
  153. package/deps/libmdbx/test/extra/dupfix_multiple.c++ +311 -0
  154. package/deps/libmdbx/test/extra/early_close_dbi.c++ +137 -0
  155. package/deps/libmdbx/test/extra/hex_base64_base58.c++ +118 -0
  156. package/deps/libmdbx/test/extra/maindb_ordinal.c++ +61 -0
  157. package/deps/libmdbx/test/extra/open.c++ +96 -0
  158. package/deps/libmdbx/test/extra/pcrf/README.md +2 -0
  159. package/deps/libmdbx/test/extra/pcrf/pcrf_test.c +380 -0
  160. package/deps/libmdbx/test/extra/probe.c++ +10 -0
  161. package/deps/libmdbx/test/extra/txn.c++ +407 -0
  162. package/deps/libmdbx/test/extra/upsert_alldups.c +193 -0
  163. package/deps/libmdbx/test/fork.c++ +263 -0
  164. package/deps/libmdbx/test/hill.c++ +447 -0
  165. package/deps/libmdbx/test/jitter.c++ +197 -0
  166. package/deps/libmdbx/test/keygen.c++ +393 -0
  167. package/deps/libmdbx/test/keygen.h++ +130 -0
  168. package/deps/libmdbx/test/log.c++ +358 -0
  169. package/deps/libmdbx/test/log.h++ +91 -0
  170. package/deps/libmdbx/test/main.c++ +706 -0
  171. package/deps/libmdbx/test/nested.c++ +318 -0
  172. package/deps/libmdbx/test/osal-unix.c++ +647 -0
  173. package/deps/libmdbx/test/osal-windows.c++ +440 -0
  174. package/deps/libmdbx/test/osal.h++ +41 -0
  175. package/deps/libmdbx/test/stochastic.sh +690 -0
  176. package/deps/libmdbx/test/stub/LICENSE +24 -0
  177. package/deps/libmdbx/test/stub/README.md +8 -0
  178. package/deps/libmdbx/test/stub/pthread_barrier.c +104 -0
  179. package/deps/libmdbx/test/stub/pthread_barrier.h +77 -0
  180. package/deps/libmdbx/test/test.c++ +1551 -0
  181. package/deps/libmdbx/test/test.h++ +298 -0
  182. package/deps/libmdbx/test/tmux.conf +3 -0
  183. package/deps/libmdbx/test/try.c++ +30 -0
  184. package/deps/libmdbx/test/ttl.c++ +240 -0
  185. package/deps/libmdbx/test/utils.c++ +203 -0
  186. package/deps/libmdbx/test/utils.h++ +326 -0
  187. package/deps/libmdbx/test/valgrind_suppress.txt +536 -0
  188. package/lib/mdbx_evn_async.js +211 -0
  189. package/lib/mdbx_worker.js +195 -0
  190. package/lib/nativemou.js +6 -0
  191. package/package.json +38 -0
  192. package/src/async/envmou_close.cpp +34 -0
  193. package/src/async/envmou_close.hpp +32 -0
  194. package/src/async/envmou_copy_to.cpp +29 -0
  195. package/src/async/envmou_copy_to.hpp +38 -0
  196. package/src/async/envmou_keys.cpp +201 -0
  197. package/src/async/envmou_keys.hpp +50 -0
  198. package/src/async/envmou_open.cpp +38 -0
  199. package/src/async/envmou_open.hpp +33 -0
  200. package/src/async/envmou_query.cpp +167 -0
  201. package/src/async/envmou_query.hpp +53 -0
  202. package/src/dbimou.cpp +522 -0
  203. package/src/dbimou.hpp +82 -0
  204. package/src/env_arg0.hpp +24 -0
  205. package/src/envmou.cpp +445 -0
  206. package/src/envmou.hpp +116 -0
  207. package/src/modulemou.cpp +113 -0
  208. package/src/querymou.cpp +177 -0
  209. package/src/querymou.hpp +93 -0
  210. package/src/txnmou.cpp +254 -0
  211. package/src/txnmou.hpp +122 -0
  212. package/src/typemou.hpp +239 -0
  213. package/src/valuemou.hpp +194 -0
  214. package/test/async.js +67 -0
  215. package/test/e3.js +38 -0
  216. package/test/e4.js +89 -0
  217. package/test/e5.js +162 -0
  218. package/test/test-batch-ops.js +243 -0
  219. package/test/test-cursor-mode.js +84 -0
  220. package/test/test-multi-mode.js +87 -0
@@ -0,0 +1,717 @@
1
+ /// \copyright SPDX-License-Identifier: Apache-2.0
2
+ /// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
3
+
4
+ #include "internals.h"
5
+
6
+ #if MDBX_ENABLE_DBI_SPARSE
7
+ size_t dbi_bitmap_ctz_fallback(const MDBX_txn *txn, intptr_t bmi) {
8
+ tASSERT(txn, bmi > 0);
9
+ bmi &= -bmi;
10
+ if (sizeof(txn->dbi_sparse[0]) > 4) {
11
+ static const uint8_t debruijn_ctz64[64] = {0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28,
12
+ 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11,
13
+ 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10,
14
+ 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12};
15
+ return debruijn_ctz64[(UINT64_C(0x022FDD63CC95386D) * (uint64_t)bmi) >> 58];
16
+ } else {
17
+ static const uint8_t debruijn_ctz32[32] = {0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
18
+ 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9};
19
+ return debruijn_ctz32[(UINT32_C(0x077CB531) * (uint32_t)bmi) >> 27];
20
+ }
21
+ }
22
+ #endif /* MDBX_ENABLE_DBI_SPARSE */
23
+
24
+ struct dbi_snap_result dbi_snap(const MDBX_env *env, const size_t dbi) {
25
+ eASSERT(env, dbi < env->n_dbi);
26
+ struct dbi_snap_result r;
27
+ uint32_t snap = atomic_load32(&env->dbi_seqs[dbi], mo_AcquireRelease);
28
+ do {
29
+ r.sequence = snap;
30
+ r.flags = env->dbs_flags[dbi];
31
+ snap = atomic_load32(&env->dbi_seqs[dbi], mo_AcquireRelease);
32
+ } while (unlikely(snap != r.sequence));
33
+ return r;
34
+ }
35
+
36
+ __noinline int dbi_import(MDBX_txn *txn, const size_t dbi) {
37
+ const MDBX_env *const env = txn->env;
38
+ if (dbi >= env->n_dbi || !env->dbs_flags[dbi])
39
+ return MDBX_BAD_DBI;
40
+
41
+ #if MDBX_ENABLE_DBI_SPARSE
42
+ const size_t bitmap_chunk = CHAR_BIT * sizeof(txn->dbi_sparse[0]);
43
+ const size_t bitmap_indx = dbi / bitmap_chunk;
44
+ const size_t bitmap_mask = (size_t)1 << dbi % bitmap_chunk;
45
+ if (dbi >= txn->n_dbi) {
46
+ for (size_t i = (txn->n_dbi + bitmap_chunk - 1) / bitmap_chunk; bitmap_indx >= i; ++i)
47
+ txn->dbi_sparse[i] = 0;
48
+ eASSERT(env, (txn->dbi_sparse[bitmap_indx] & bitmap_mask) == 0);
49
+ MDBX_txn *scan = txn;
50
+ do {
51
+ eASSERT(env, scan->dbi_sparse == txn->dbi_sparse);
52
+ eASSERT(env, scan->n_dbi < dbi + 1);
53
+ scan->n_dbi = (unsigned)dbi + 1;
54
+ scan->dbi_state[dbi] = 0;
55
+ scan = scan->parent;
56
+ } while (scan /* && scan->dbi_sparse == txn->dbi_sparse */);
57
+ txn->dbi_sparse[bitmap_indx] |= bitmap_mask;
58
+ goto lindo;
59
+ }
60
+ if ((txn->dbi_sparse[bitmap_indx] & bitmap_mask) == 0) {
61
+ MDBX_txn *scan = txn;
62
+ do {
63
+ eASSERT(env, scan->dbi_sparse == txn->dbi_sparse);
64
+ eASSERT(env, scan->n_dbi == txn->n_dbi);
65
+ scan->dbi_state[dbi] = 0;
66
+ scan = scan->parent;
67
+ } while (scan /* && scan->dbi_sparse == txn->dbi_sparse */);
68
+ txn->dbi_sparse[bitmap_indx] |= bitmap_mask;
69
+ goto lindo;
70
+ }
71
+ #else
72
+ if (dbi >= txn->n_dbi) {
73
+ size_t i = txn->n_dbi;
74
+ do
75
+ txn->dbi_state[i] = 0;
76
+ while (dbi >= ++i);
77
+ txn->n_dbi = i;
78
+ goto lindo;
79
+ }
80
+ #endif /* MDBX_ENABLE_DBI_SPARSE */
81
+
82
+ if (!txn->dbi_state[dbi]) {
83
+ lindo:
84
+ /* dbi-слот еще не инициализирован в транзакции, а хендл не использовался */
85
+ txn->cursors[dbi] = nullptr;
86
+ MDBX_txn *const parent = txn->parent;
87
+ if (unlikely(parent)) {
88
+ /* вложенная пишущая транзакция */
89
+ int rc = dbi_check(parent, dbi);
90
+ /* копируем состояние table очищая new-флаги. */
91
+ eASSERT(env, txn->dbi_seqs == parent->dbi_seqs);
92
+ txn->dbi_state[dbi] = parent->dbi_state[dbi] & ~(DBI_FRESH | DBI_CREAT | DBI_DIRTY);
93
+ if (likely(rc == MDBX_SUCCESS)) {
94
+ txn->dbs[dbi] = parent->dbs[dbi];
95
+ if (parent->cursors[dbi]) {
96
+ rc = cursor_shadow(parent->cursors[dbi], txn, dbi);
97
+ if (unlikely(rc != MDBX_SUCCESS)) {
98
+ /* не получилось забекапить курсоры */
99
+ txn->dbi_state[dbi] = DBI_OLDEN | DBI_LINDO | DBI_STALE;
100
+ txn->flags |= MDBX_TXN_ERROR;
101
+ }
102
+ }
103
+ }
104
+ return rc;
105
+ }
106
+ txn->dbi_seqs[dbi] = 0;
107
+ txn->dbi_state[dbi] = DBI_LINDO;
108
+ } else {
109
+ eASSERT(env, txn->dbi_seqs[dbi] != env->dbi_seqs[dbi].weak);
110
+ if (unlikely(txn->cursors[dbi])) {
111
+ /* хендл уже использовался в транзакции и остались висячие курсоры */
112
+ txn->dbi_seqs[dbi] = env->dbi_seqs[dbi].weak;
113
+ txn->dbi_state[dbi] = DBI_OLDEN | DBI_LINDO;
114
+ return MDBX_DANGLING_DBI;
115
+ }
116
+ if (unlikely(txn->dbi_state[dbi] & (DBI_OLDEN | DBI_VALID))) {
117
+ /* хендл уже использовался в транзакции, но был закрыт или переоткрыт,
118
+ * висячих курсоров нет */
119
+ txn->dbi_seqs[dbi] = env->dbi_seqs[dbi].weak;
120
+ txn->dbi_state[dbi] = DBI_OLDEN | DBI_LINDO;
121
+ return MDBX_BAD_DBI;
122
+ }
123
+ }
124
+
125
+ /* хендл не использовался в транзакции, либо явно пере-отрывается при
126
+ * отсутствии висячих курсоров */
127
+ eASSERT(env, (txn->dbi_state[dbi] & (DBI_LINDO | DBI_VALID)) == DBI_LINDO && !txn->cursors[dbi]);
128
+
129
+ /* читаем актуальные флаги и sequence */
130
+ struct dbi_snap_result snap = dbi_snap(env, dbi);
131
+ txn->dbi_seqs[dbi] = snap.sequence;
132
+ if (snap.flags & DB_VALID) {
133
+ txn->dbs[dbi].flags = snap.flags & DB_PERSISTENT_FLAGS;
134
+ txn->dbi_state[dbi] = (dbi >= CORE_DBS) ? DBI_LINDO | DBI_VALID | DBI_STALE : DBI_LINDO | DBI_VALID;
135
+ return MDBX_SUCCESS;
136
+ }
137
+ return MDBX_BAD_DBI;
138
+ }
139
+
140
+ int dbi_defer_release(MDBX_env *const env, defer_free_item_t *const chain) {
141
+ size_t length = 0;
142
+ defer_free_item_t *obsolete_chain = nullptr;
143
+ #if MDBX_ENABLE_DBI_LOCKFREE
144
+ const uint64_t now = osal_monotime();
145
+ defer_free_item_t **scan = &env->defer_free;
146
+ if (env->defer_free) {
147
+ const uint64_t threshold_1second = osal_16dot16_to_monotime(1 * 65536);
148
+ do {
149
+ defer_free_item_t *item = *scan;
150
+ if (now - item->timestamp < threshold_1second) {
151
+ scan = &item->next;
152
+ length += 1;
153
+ } else {
154
+ *scan = item->next;
155
+ item->next = obsolete_chain;
156
+ obsolete_chain = item;
157
+ }
158
+ } while (*scan);
159
+ }
160
+
161
+ eASSERT(env, *scan == nullptr);
162
+ if (chain) {
163
+ defer_free_item_t *item = chain;
164
+ do {
165
+ item->timestamp = now;
166
+ item = item->next;
167
+ } while (item);
168
+ *scan = chain;
169
+ }
170
+ #else /* MDBX_ENABLE_DBI_LOCKFREE */
171
+ obsolete_chain = chain;
172
+ #endif /* MDBX_ENABLE_DBI_LOCKFREE */
173
+
174
+ ENSURE(env, osal_fastmutex_release(&env->dbi_lock) == MDBX_SUCCESS);
175
+ if (length > 42) {
176
+ #if defined(_WIN32) || defined(_WIN64)
177
+ SwitchToThread();
178
+ #else
179
+ sched_yield();
180
+ #endif /* Windows */
181
+ }
182
+ while (obsolete_chain) {
183
+ defer_free_item_t *item = obsolete_chain;
184
+ obsolete_chain = obsolete_chain->next;
185
+ osal_free(item);
186
+ }
187
+ return chain ? MDBX_SUCCESS : MDBX_BAD_DBI;
188
+ }
189
+
190
+ /* Export or close DBI handles opened in this txn. */
191
+ int dbi_update(MDBX_txn *txn, int keep) {
192
+ MDBX_env *const env = txn->env;
193
+ tASSERT(txn, !txn->parent && txn == env->basal_txn);
194
+ bool locked = false;
195
+ defer_free_item_t *defer_chain = nullptr;
196
+ TXN_FOREACH_DBI_USER(txn, dbi) {
197
+ if (likely((txn->dbi_state[dbi] & DBI_CREAT) == 0))
198
+ continue;
199
+ if (!locked) {
200
+ int err = osal_fastmutex_acquire(&env->dbi_lock);
201
+ if (unlikely(err != MDBX_SUCCESS))
202
+ return err;
203
+ locked = true;
204
+ if (dbi >= env->n_dbi)
205
+ /* хендл был закрыт из другого потока пока захватывали блокировку */
206
+ continue;
207
+ }
208
+ tASSERT(txn, dbi < env->n_dbi);
209
+ if (keep) {
210
+ env->dbs_flags[dbi] = txn->dbs[dbi].flags | DB_VALID;
211
+ } else {
212
+ uint32_t seq = dbi_seq_next(env, dbi);
213
+ defer_free_item_t *item = env->kvs[dbi].name.iov_base;
214
+ if (item) {
215
+ env->dbs_flags[dbi] = 0;
216
+ env->kvs[dbi].name.iov_len = 0;
217
+ env->kvs[dbi].name.iov_base = nullptr;
218
+ atomic_store32(&env->dbi_seqs[dbi], seq, mo_AcquireRelease);
219
+ osal_flush_incoherent_cpu_writeback();
220
+ item->next = defer_chain;
221
+ defer_chain = item;
222
+ } else {
223
+ eASSERT(env, env->kvs[dbi].name.iov_len == 0);
224
+ eASSERT(env, env->dbs_flags[dbi] == 0);
225
+ }
226
+ }
227
+ }
228
+
229
+ if (locked) {
230
+ size_t i = env->n_dbi;
231
+ while ((env->dbs_flags[i - 1] & DB_VALID) == 0) {
232
+ --i;
233
+ eASSERT(env, i >= CORE_DBS);
234
+ eASSERT(env, !env->dbs_flags[i] && !env->kvs[i].name.iov_len && !env->kvs[i].name.iov_base);
235
+ }
236
+ env->n_dbi = (unsigned)i;
237
+ dbi_defer_release(env, defer_chain);
238
+ }
239
+ return MDBX_SUCCESS;
240
+ }
241
+
242
+ int dbi_bind(MDBX_txn *txn, const size_t dbi, unsigned user_flags, MDBX_cmp_func *keycmp, MDBX_cmp_func *datacmp) {
243
+ const MDBX_env *const env = txn->env;
244
+ eASSERT(env, dbi < txn->n_dbi && dbi < env->n_dbi);
245
+ eASSERT(env, dbi_state(txn, dbi) & DBI_LINDO);
246
+ eASSERT(env, env->dbs_flags[dbi] != DB_POISON);
247
+ if ((env->dbs_flags[dbi] & DB_VALID) == 0) {
248
+ eASSERT(env, !env->kvs[dbi].clc.k.cmp && !env->kvs[dbi].clc.v.cmp && !env->kvs[dbi].name.iov_len &&
249
+ !env->kvs[dbi].name.iov_base && !env->kvs[dbi].clc.k.lmax && !env->kvs[dbi].clc.k.lmin &&
250
+ !env->kvs[dbi].clc.v.lmax && !env->kvs[dbi].clc.v.lmin);
251
+ } else {
252
+ eASSERT(env, !(txn->dbi_state[dbi] & DBI_VALID) || (txn->dbs[dbi].flags | DB_VALID) == env->dbs_flags[dbi]);
253
+ eASSERT(env, env->kvs[dbi].name.iov_base || dbi < CORE_DBS);
254
+ }
255
+
256
+ /* Если dbi уже использовался, то корректными считаем четыре варианта:
257
+ * 1) user_flags равны MDBX_DB_ACCEDE
258
+ * = предполагаем что пользователь открывает существующую table,
259
+ * при этом код проверки не позволит установить другие компараторы.
260
+ * 2) user_flags нулевые, а оба компаратора пустые/нулевые или равны текущим
261
+ * = предполагаем что пользователь открывает существующую table
262
+ * старым способом с нулевыми с флагами по-умолчанию.
263
+ * 3) user_flags совпадают, а компараторы не заданы или те же
264
+ * = предполагаем что пользователь открывает table указывая все параметры;
265
+ * 4) user_flags отличаются, но table пустая и задан флаг MDBX_CREATE
266
+ * = предполагаем что пользователь пересоздает table;
267
+ */
268
+ if ((user_flags & ~MDBX_CREATE) != (unsigned)(env->dbs_flags[dbi] & DB_PERSISTENT_FLAGS)) {
269
+ /* flags are differs, check other conditions */
270
+ if ((!user_flags && (!keycmp || keycmp == env->kvs[dbi].clc.k.cmp) &&
271
+ (!datacmp || datacmp == env->kvs[dbi].clc.v.cmp)) ||
272
+ user_flags == MDBX_DB_ACCEDE) {
273
+ user_flags = env->dbs_flags[dbi] & DB_PERSISTENT_FLAGS;
274
+ } else if ((user_flags & MDBX_CREATE) == 0)
275
+ return /* FIXME: return extended info */ MDBX_INCOMPATIBLE;
276
+ else {
277
+ if (txn->dbi_state[dbi] & DBI_STALE) {
278
+ eASSERT(env, env->dbs_flags[dbi] & DB_VALID);
279
+ int err = tbl_fetch(txn, dbi);
280
+ if (unlikely(err == MDBX_SUCCESS))
281
+ return err;
282
+ }
283
+ eASSERT(env, ((env->dbs_flags[dbi] ^ txn->dbs[dbi].flags) & DB_PERSISTENT_FLAGS) == 0);
284
+ eASSERT(env, (txn->dbi_state[dbi] & (DBI_LINDO | DBI_VALID | DBI_STALE)) == (DBI_LINDO | DBI_VALID));
285
+ if (unlikely(txn->dbs[dbi].leaf_pages))
286
+ return /* FIXME: return extended info */ MDBX_INCOMPATIBLE;
287
+
288
+ /* Пересоздаём table если там пусто */
289
+ if (unlikely(txn->cursors[dbi]))
290
+ return MDBX_DANGLING_DBI;
291
+ env->dbs_flags[dbi] = DB_POISON;
292
+ atomic_store32(&env->dbi_seqs[dbi], dbi_seq_next(env, dbi), mo_AcquireRelease);
293
+
294
+ const uint32_t seq = dbi_seq_next(env, dbi);
295
+ const uint16_t db_flags = user_flags & DB_PERSISTENT_FLAGS;
296
+ eASSERT(env, txn->dbs[dbi].height == 0 && txn->dbs[dbi].items == 0 && txn->dbs[dbi].root == P_INVALID);
297
+ env->kvs[dbi].clc.k.cmp = keycmp ? keycmp : builtin_keycmp(user_flags);
298
+ env->kvs[dbi].clc.v.cmp = datacmp ? datacmp : builtin_datacmp(user_flags);
299
+ txn->dbs[dbi].flags = db_flags;
300
+ txn->dbs[dbi].dupfix_size = 0;
301
+ if (unlikely(tbl_setup(env, &env->kvs[dbi], &txn->dbs[dbi]))) {
302
+ txn->dbi_state[dbi] = DBI_LINDO;
303
+ txn->flags |= MDBX_TXN_ERROR;
304
+ return MDBX_PROBLEM;
305
+ }
306
+
307
+ env->dbs_flags[dbi] = db_flags | DB_VALID;
308
+ atomic_store32(&env->dbi_seqs[dbi], seq, mo_AcquireRelease);
309
+ txn->dbi_seqs[dbi] = seq;
310
+ txn->dbi_state[dbi] = DBI_LINDO | DBI_VALID | DBI_CREAT | DBI_DIRTY;
311
+ txn->flags |= MDBX_TXN_DIRTY;
312
+ }
313
+ }
314
+
315
+ if (!keycmp)
316
+ keycmp = (env->dbs_flags[dbi] & DB_VALID) ? env->kvs[dbi].clc.k.cmp : builtin_keycmp(user_flags);
317
+ if (env->kvs[dbi].clc.k.cmp != keycmp) {
318
+ if (env->dbs_flags[dbi] & DB_VALID)
319
+ return MDBX_EINVAL;
320
+ env->kvs[dbi].clc.k.cmp = keycmp;
321
+ }
322
+
323
+ if (!datacmp)
324
+ datacmp = (env->dbs_flags[dbi] & DB_VALID) ? env->kvs[dbi].clc.v.cmp : builtin_datacmp(user_flags);
325
+ if (env->kvs[dbi].clc.v.cmp != datacmp) {
326
+ if (env->dbs_flags[dbi] & DB_VALID)
327
+ return MDBX_EINVAL;
328
+ env->kvs[dbi].clc.v.cmp = datacmp;
329
+ }
330
+
331
+ return MDBX_SUCCESS;
332
+ }
333
+
334
+ static inline size_t dbi_namelen(const MDBX_val name) {
335
+ return (name.iov_len > sizeof(defer_free_item_t)) ? name.iov_len : sizeof(defer_free_item_t);
336
+ }
337
+
338
+ static int dbi_open_locked(MDBX_txn *txn, unsigned user_flags, MDBX_dbi *dbi, MDBX_cmp_func *keycmp,
339
+ MDBX_cmp_func *datacmp, MDBX_val name) {
340
+ MDBX_env *const env = txn->env;
341
+
342
+ /* Cannot mix named table(s) with DUPSORT flags */
343
+ tASSERT(txn, (txn->dbi_state[MAIN_DBI] & (DBI_LINDO | DBI_VALID | DBI_STALE)) == (DBI_LINDO | DBI_VALID));
344
+ if (unlikely(txn->dbs[MAIN_DBI].flags & MDBX_DUPSORT)) {
345
+ if (unlikely((user_flags & MDBX_CREATE) == 0))
346
+ return MDBX_NOTFOUND;
347
+ if (unlikely(txn->dbs[MAIN_DBI].leaf_pages))
348
+ /* В MainDB есть записи, либо она уже использовалась. */
349
+ return MDBX_INCOMPATIBLE;
350
+
351
+ /* Пересоздаём MainDB когда там пусто. */
352
+ tASSERT(txn,
353
+ txn->dbs[MAIN_DBI].height == 0 && txn->dbs[MAIN_DBI].items == 0 && txn->dbs[MAIN_DBI].root == P_INVALID);
354
+ if (unlikely(txn->cursors[MAIN_DBI]))
355
+ return MDBX_DANGLING_DBI;
356
+ env->dbs_flags[MAIN_DBI] = DB_POISON;
357
+ atomic_store32(&env->dbi_seqs[MAIN_DBI], dbi_seq_next(env, MAIN_DBI), mo_AcquireRelease);
358
+
359
+ const uint32_t seq = dbi_seq_next(env, MAIN_DBI);
360
+ const uint16_t main_flags = txn->dbs[MAIN_DBI].flags & (MDBX_REVERSEKEY | MDBX_INTEGERKEY);
361
+ env->kvs[MAIN_DBI].clc.k.cmp = builtin_keycmp(main_flags);
362
+ env->kvs[MAIN_DBI].clc.v.cmp = builtin_datacmp(main_flags);
363
+ txn->dbs[MAIN_DBI].flags = main_flags;
364
+ txn->dbs[MAIN_DBI].dupfix_size = 0;
365
+ int err = tbl_setup(env, &env->kvs[MAIN_DBI], &txn->dbs[MAIN_DBI]);
366
+ if (unlikely(err != MDBX_SUCCESS)) {
367
+ txn->dbi_state[MAIN_DBI] = DBI_LINDO;
368
+ txn->flags |= MDBX_TXN_ERROR;
369
+ env->flags |= ENV_FATAL_ERROR;
370
+ return err;
371
+ }
372
+ env->dbs_flags[MAIN_DBI] = main_flags | DB_VALID;
373
+ txn->dbi_seqs[MAIN_DBI] = atomic_store32(&env->dbi_seqs[MAIN_DBI], seq, mo_AcquireRelease);
374
+ txn->dbi_state[MAIN_DBI] |= DBI_DIRTY;
375
+ txn->flags |= MDBX_TXN_DIRTY;
376
+ }
377
+
378
+ tASSERT(txn, env->kvs[MAIN_DBI].clc.k.cmp);
379
+
380
+ /* Is the DB already open? */
381
+ size_t slot = env->n_dbi;
382
+ for (size_t scan = CORE_DBS; scan < env->n_dbi; ++scan) {
383
+ if ((env->dbs_flags[scan] & DB_VALID) == 0) {
384
+ /* Remember this free slot */
385
+ slot = (slot < scan) ? slot : scan;
386
+ continue;
387
+ }
388
+ if (env->kvs[MAIN_DBI].clc.k.cmp(&name, &env->kvs[scan].name) == 0) {
389
+ slot = scan;
390
+ int err = dbi_check(txn, slot);
391
+ if (err == MDBX_BAD_DBI && txn->dbi_state[slot] == (DBI_OLDEN | DBI_LINDO)) {
392
+ /* хендл использовался, стал невалидным,
393
+ * но теперь явно пере-открывается в этой транзакци */
394
+ eASSERT(env, !txn->cursors[slot]);
395
+ txn->dbi_state[slot] = DBI_LINDO;
396
+ err = dbi_check(txn, slot);
397
+ }
398
+ if (err == MDBX_SUCCESS) {
399
+ err = dbi_bind(txn, slot, user_flags, keycmp, datacmp);
400
+ if (likely(err == MDBX_SUCCESS)) {
401
+ goto done;
402
+ }
403
+ }
404
+ return err;
405
+ }
406
+ }
407
+
408
+ /* Fail, if no free slot and max hit */
409
+ if (unlikely(slot >= env->max_dbi))
410
+ return MDBX_DBS_FULL;
411
+
412
+ if (env->n_dbi == slot)
413
+ eASSERT(env, !env->dbs_flags[slot] && !env->kvs[slot].name.iov_len && !env->kvs[slot].name.iov_base);
414
+
415
+ env->dbs_flags[slot] = DB_POISON;
416
+ atomic_store32(&env->dbi_seqs[slot], dbi_seq_next(env, slot), mo_AcquireRelease);
417
+ memset(&env->kvs[slot], 0, sizeof(env->kvs[slot]));
418
+ if (env->n_dbi == slot)
419
+ env->n_dbi = (unsigned)slot + 1;
420
+ eASSERT(env, slot < env->n_dbi);
421
+
422
+ int err = dbi_check(txn, slot);
423
+ eASSERT(env, err == MDBX_BAD_DBI);
424
+ if (err != MDBX_BAD_DBI)
425
+ return MDBX_PROBLEM;
426
+
427
+ /* Find the DB info */
428
+ MDBX_val body;
429
+ cursor_couple_t cx;
430
+ int rc = cursor_init(&cx.outer, txn, MAIN_DBI);
431
+ if (unlikely(rc != MDBX_SUCCESS))
432
+ return rc;
433
+ rc = cursor_seek(&cx.outer, &name, &body, MDBX_SET).err;
434
+ if (unlikely(rc != MDBX_SUCCESS)) {
435
+ if (rc != MDBX_NOTFOUND || !(user_flags & MDBX_CREATE))
436
+ return rc;
437
+ } else {
438
+ /* make sure this is actually a table */
439
+ node_t *node = page_node(cx.outer.pg[cx.outer.top], cx.outer.ki[cx.outer.top]);
440
+ if (unlikely((node_flags(node) & (N_DUP | N_TREE)) != N_TREE))
441
+ return MDBX_INCOMPATIBLE;
442
+ if (!MDBX_DISABLE_VALIDATION && unlikely(body.iov_len != sizeof(tree_t))) {
443
+ ERROR("%s/%d: %s %zu", "MDBX_CORRUPTED", MDBX_CORRUPTED, "invalid table node size", body.iov_len);
444
+ return MDBX_CORRUPTED;
445
+ }
446
+ memcpy(&txn->dbs[slot], body.iov_base, sizeof(tree_t));
447
+ }
448
+
449
+ /* Done here so we cannot fail after creating a new DB */
450
+ defer_free_item_t *const clone = osal_malloc(dbi_namelen(name));
451
+ if (unlikely(!clone))
452
+ return MDBX_ENOMEM;
453
+ memcpy(clone, name.iov_base, name.iov_len);
454
+ name.iov_base = clone;
455
+
456
+ uint8_t dbi_state = DBI_LINDO | DBI_VALID | DBI_FRESH;
457
+ if (unlikely(rc)) {
458
+ /* MDBX_NOTFOUND and MDBX_CREATE: Create new DB */
459
+ tASSERT(txn, rc == MDBX_NOTFOUND);
460
+ body.iov_base = memset(&txn->dbs[slot], 0, body.iov_len = sizeof(tree_t));
461
+ txn->dbs[slot].root = P_INVALID;
462
+ txn->dbs[slot].mod_txnid = txn->txnid;
463
+ txn->dbs[slot].flags = user_flags & DB_PERSISTENT_FLAGS;
464
+ cx.outer.next = txn->cursors[MAIN_DBI];
465
+ txn->cursors[MAIN_DBI] = &cx.outer;
466
+ rc = cursor_put_checklen(&cx.outer, &name, &body, N_TREE | MDBX_NOOVERWRITE);
467
+ txn->cursors[MAIN_DBI] = cx.outer.next;
468
+ if (unlikely(rc != MDBX_SUCCESS))
469
+ goto bailout;
470
+
471
+ dbi_state |= DBI_DIRTY | DBI_CREAT;
472
+ txn->flags |= MDBX_TXN_DIRTY;
473
+ tASSERT(txn, (txn->dbi_state[MAIN_DBI] & DBI_DIRTY) != 0);
474
+ }
475
+
476
+ /* Got info, register DBI in this txn */
477
+ const uint32_t seq = dbi_seq_next(env, slot);
478
+ eASSERT(env, env->dbs_flags[slot] == DB_POISON && !txn->cursors[slot] &&
479
+ (txn->dbi_state[slot] & (DBI_LINDO | DBI_VALID)) == DBI_LINDO);
480
+ txn->dbi_state[slot] = dbi_state;
481
+ memcpy(&txn->dbs[slot], body.iov_base, sizeof(txn->dbs[slot]));
482
+ env->dbs_flags[slot] = txn->dbs[slot].flags;
483
+ rc = dbi_bind(txn, slot, user_flags, keycmp, datacmp);
484
+ if (unlikely(rc != MDBX_SUCCESS))
485
+ goto bailout;
486
+
487
+ env->kvs[slot].name = name;
488
+ env->dbs_flags[slot] = txn->dbs[slot].flags | DB_VALID;
489
+ txn->dbi_seqs[slot] = atomic_store32(&env->dbi_seqs[slot], seq, mo_AcquireRelease);
490
+
491
+ done:
492
+ *dbi = (MDBX_dbi)slot;
493
+ tASSERT(txn, slot < txn->n_dbi && (env->dbs_flags[slot] & DB_VALID) != 0);
494
+ eASSERT(env, dbi_check(txn, slot) == MDBX_SUCCESS);
495
+ return MDBX_SUCCESS;
496
+
497
+ bailout:
498
+ eASSERT(env, !txn->cursors[slot] && !env->kvs[slot].name.iov_len && !env->kvs[slot].name.iov_base);
499
+ txn->dbi_state[slot] &= DBI_LINDO | DBI_OLDEN;
500
+ env->dbs_flags[slot] = 0;
501
+ osal_free(clone);
502
+ if (slot + 1 == env->n_dbi)
503
+ txn->n_dbi = env->n_dbi = (unsigned)slot;
504
+ return rc;
505
+ }
506
+
507
+ int dbi_open(MDBX_txn *txn, const MDBX_val *const name, unsigned user_flags, MDBX_dbi *dbi, MDBX_cmp_func *keycmp,
508
+ MDBX_cmp_func *datacmp) {
509
+ if (unlikely(!dbi))
510
+ return MDBX_EINVAL;
511
+ *dbi = 0;
512
+
513
+ if (user_flags != MDBX_ACCEDE && unlikely(!check_table_flags(user_flags & ~MDBX_CREATE)))
514
+ return MDBX_EINVAL;
515
+
516
+ int rc = check_txn(txn, MDBX_TXN_BLOCKED);
517
+ if (unlikely(rc != MDBX_SUCCESS))
518
+ return rc;
519
+
520
+ if ((user_flags & MDBX_CREATE) && unlikely(txn->flags & MDBX_TXN_RDONLY))
521
+ return MDBX_EACCESS;
522
+
523
+ /* main table? */
524
+ if (unlikely(name == MDBX_CHK_MAIN || name->iov_base == MDBX_CHK_MAIN)) {
525
+ rc = dbi_bind(txn, MAIN_DBI, user_flags, keycmp, datacmp);
526
+ if (likely(rc == MDBX_SUCCESS))
527
+ *dbi = MAIN_DBI;
528
+ return rc;
529
+ }
530
+ if (unlikely(name == MDBX_CHK_GC || name->iov_base == MDBX_CHK_GC)) {
531
+ rc = dbi_bind(txn, FREE_DBI, user_flags, keycmp, datacmp);
532
+ if (likely(rc == MDBX_SUCCESS))
533
+ *dbi = FREE_DBI;
534
+ return rc;
535
+ }
536
+ if (unlikely(name == MDBX_CHK_META || name->iov_base == MDBX_CHK_META))
537
+ return MDBX_EINVAL;
538
+ if (unlikely(name->iov_len > txn->env->leaf_nodemax - NODESIZE - sizeof(tree_t)))
539
+ return MDBX_EINVAL;
540
+
541
+ #if MDBX_ENABLE_DBI_LOCKFREE
542
+ /* Is the DB already open? */
543
+ const MDBX_env *const env = txn->env;
544
+ bool have_free_slot = env->n_dbi < env->max_dbi;
545
+ for (size_t i = CORE_DBS; i < env->n_dbi; ++i) {
546
+ if ((env->dbs_flags[i] & DB_VALID) == 0) {
547
+ have_free_slot = true;
548
+ continue;
549
+ }
550
+
551
+ struct dbi_snap_result snap = dbi_snap(env, i);
552
+ const MDBX_val snap_name = env->kvs[i].name;
553
+ const uint32_t main_seq = atomic_load32(&env->dbi_seqs[MAIN_DBI], mo_AcquireRelease);
554
+ MDBX_cmp_func *const snap_cmp = env->kvs[MAIN_DBI].clc.k.cmp;
555
+ if (unlikely(!(snap.flags & DB_VALID) || !snap_name.iov_base || !snap_name.iov_len || !snap_cmp))
556
+ /* похоже на столкновение с параллельно работающим обновлением */
557
+ goto slowpath_locking;
558
+
559
+ const bool name_match = snap_cmp(&snap_name, name) == 0;
560
+ if (unlikely(snap.sequence != atomic_load32(&env->dbi_seqs[i], mo_AcquireRelease) ||
561
+ main_seq != atomic_load32(&env->dbi_seqs[MAIN_DBI], mo_AcquireRelease) ||
562
+ snap.flags != env->dbs_flags[i] || snap_name.iov_base != env->kvs[i].name.iov_base ||
563
+ snap_name.iov_len != env->kvs[i].name.iov_len))
564
+ /* похоже на столкновение с параллельно работающим обновлением */
565
+ goto slowpath_locking;
566
+
567
+ if (!name_match)
568
+ continue;
569
+
570
+ osal_flush_incoherent_cpu_writeback();
571
+ if (user_flags != MDBX_ACCEDE &&
572
+ (((user_flags ^ snap.flags) & DB_PERSISTENT_FLAGS) || (keycmp && keycmp != env->kvs[i].clc.k.cmp) ||
573
+ (datacmp && datacmp != env->kvs[i].clc.v.cmp)))
574
+ /* есть подозрение что пользователь открывает таблицу с другими флагами/атрибутами
575
+ * или другими компараторами, поэтому уходим в безопасный режим */
576
+ goto slowpath_locking;
577
+
578
+ rc = dbi_check(txn, i);
579
+ if (rc == MDBX_BAD_DBI && txn->dbi_state[i] == (DBI_OLDEN | DBI_LINDO)) {
580
+ /* хендл использовался, стал невалидным,
581
+ * но теперь явно пере-открывается в этой транзакци */
582
+ eASSERT(env, !txn->cursors[i]);
583
+ txn->dbi_state[i] = DBI_LINDO;
584
+ rc = dbi_check(txn, i);
585
+ }
586
+ if (likely(rc == MDBX_SUCCESS)) {
587
+ if (unlikely(snap.sequence != atomic_load32(&env->dbi_seqs[i], mo_AcquireRelease) ||
588
+ main_seq != atomic_load32(&env->dbi_seqs[MAIN_DBI], mo_AcquireRelease) ||
589
+ snap.flags != env->dbs_flags[i] || snap_name.iov_base != env->kvs[i].name.iov_base ||
590
+ snap_name.iov_len != env->kvs[i].name.iov_len))
591
+ /* похоже на столкновение с параллельно работающим обновлением */
592
+ goto slowpath_locking;
593
+ rc = dbi_bind(txn, i, user_flags, keycmp, datacmp);
594
+ if (likely(rc == MDBX_SUCCESS))
595
+ *dbi = (MDBX_dbi)i;
596
+ }
597
+ return rc;
598
+ }
599
+
600
+ /* Fail, if no free slot and max hit */
601
+ if (unlikely(!have_free_slot))
602
+ return MDBX_DBS_FULL;
603
+
604
+ slowpath_locking:
605
+
606
+ #endif /* MDBX_ENABLE_DBI_LOCKFREE */
607
+
608
+ rc = osal_fastmutex_acquire(&txn->env->dbi_lock);
609
+ if (likely(rc == MDBX_SUCCESS)) {
610
+ rc = dbi_open_locked(txn, user_flags, dbi, keycmp, datacmp, *name);
611
+ ENSURE(txn->env, osal_fastmutex_release(&txn->env->dbi_lock) == MDBX_SUCCESS);
612
+ }
613
+ return rc;
614
+ }
615
+
616
+ __cold struct dbi_rename_result dbi_rename_locked(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val new_name) {
617
+ struct dbi_rename_result pair;
618
+ pair.defer = nullptr;
619
+ pair.err = dbi_check(txn, dbi);
620
+ if (unlikely(pair.err != MDBX_SUCCESS))
621
+ return pair;
622
+
623
+ MDBX_env *const env = txn->env;
624
+ MDBX_val old_name = env->kvs[dbi].name;
625
+ if (env->kvs[MAIN_DBI].clc.k.cmp(&new_name, &old_name) == 0 && MDBX_DEBUG == 0)
626
+ return pair;
627
+
628
+ cursor_couple_t cx;
629
+ pair.err = cursor_init(&cx.outer, txn, MAIN_DBI);
630
+ if (unlikely(pair.err != MDBX_SUCCESS))
631
+ return pair;
632
+ pair.err = cursor_seek(&cx.outer, &new_name, nullptr, MDBX_SET).err;
633
+ if (unlikely(pair.err != MDBX_NOTFOUND)) {
634
+ pair.err = (pair.err == MDBX_SUCCESS) ? MDBX_KEYEXIST : pair.err;
635
+ return pair;
636
+ }
637
+
638
+ pair.defer = osal_malloc(dbi_namelen(new_name));
639
+ if (unlikely(!pair.defer)) {
640
+ pair.err = MDBX_ENOMEM;
641
+ return pair;
642
+ }
643
+ new_name.iov_base = memcpy(pair.defer, new_name.iov_base, new_name.iov_len);
644
+
645
+ cx.outer.next = txn->cursors[MAIN_DBI];
646
+ txn->cursors[MAIN_DBI] = &cx.outer;
647
+
648
+ MDBX_val data = {&txn->dbs[dbi], sizeof(tree_t)};
649
+ pair.err = cursor_put_checklen(&cx.outer, &new_name, &data, N_TREE | MDBX_NOOVERWRITE);
650
+ if (likely(pair.err == MDBX_SUCCESS)) {
651
+ pair.err = cursor_seek(&cx.outer, &old_name, nullptr, MDBX_SET).err;
652
+ if (likely(pair.err == MDBX_SUCCESS))
653
+ pair.err = cursor_del(&cx.outer, N_TREE);
654
+ if (likely(pair.err == MDBX_SUCCESS)) {
655
+ pair.defer = env->kvs[dbi].name.iov_base;
656
+ env->kvs[dbi].name = new_name;
657
+ } else
658
+ txn->flags |= MDBX_TXN_ERROR;
659
+ }
660
+
661
+ txn->cursors[MAIN_DBI] = cx.outer.next;
662
+ return pair;
663
+ }
664
+
665
+ static defer_free_item_t *dbi_close_locked(MDBX_env *env, MDBX_dbi dbi) {
666
+ eASSERT(env, dbi >= CORE_DBS);
667
+ if (unlikely(dbi >= env->n_dbi))
668
+ return nullptr;
669
+
670
+ const uint32_t seq = dbi_seq_next(env, dbi);
671
+ defer_free_item_t *defer_item = env->kvs[dbi].name.iov_base;
672
+ if (likely(defer_item)) {
673
+ env->dbs_flags[dbi] = 0;
674
+ env->kvs[dbi].name.iov_len = 0;
675
+ env->kvs[dbi].name.iov_base = nullptr;
676
+ atomic_store32(&env->dbi_seqs[dbi], seq, mo_AcquireRelease);
677
+ osal_flush_incoherent_cpu_writeback();
678
+ defer_item->next = nullptr;
679
+
680
+ if (env->n_dbi == dbi + 1) {
681
+ size_t i = env->n_dbi;
682
+ do {
683
+ --i;
684
+ eASSERT(env, i >= CORE_DBS);
685
+ eASSERT(env, !env->dbs_flags[i] && !env->kvs[i].name.iov_len && !env->kvs[i].name.iov_base);
686
+ } while (i > CORE_DBS && !env->kvs[i - 1].name.iov_base);
687
+ env->n_dbi = (unsigned)i;
688
+ }
689
+ }
690
+
691
+ return defer_item;
692
+ }
693
+
694
+ __cold const tree_t *dbi_dig(const MDBX_txn *txn, const size_t dbi, tree_t *fallback) {
695
+ const MDBX_txn *dig = txn;
696
+ do {
697
+ tASSERT(txn, txn->n_dbi == dig->n_dbi);
698
+ const uint8_t state = dbi_state(dig, dbi);
699
+ if (state & DBI_LINDO)
700
+ switch (state & (DBI_VALID | DBI_STALE | DBI_OLDEN)) {
701
+ case DBI_VALID:
702
+ case DBI_OLDEN:
703
+ return dig->dbs + dbi;
704
+ case 0:
705
+ return fallback;
706
+ case DBI_VALID | DBI_STALE:
707
+ case DBI_OLDEN | DBI_STALE:
708
+ break;
709
+ default:
710
+ tASSERT(txn, !!"unexpected dig->dbi_state[dbi]");
711
+ }
712
+ dig = dig->parent;
713
+ } while (dig);
714
+ return fallback;
715
+ }
716
+
717
+ int dbi_close_release(MDBX_env *env, MDBX_dbi dbi) { return dbi_defer_release(env, dbi_close_locked(env, dbi)); }