Compare commits

...

50 Commits

Author SHA1 Message Date
Леонид Юрьев (Leonid Yuriev)
d47eed079e mdbx: release v0.11.2
Acknowledgements:
-----------------

 - [장세연 (Чан Се Ен)](https://github.com/sasgas) for contributing to C++ API.
 - [Alain Picard](https://github.com/castortech) for [Java bindings](https://github.com/castortech/mdbxjni).
 - [Alex Sharov](https://github.com/AskAlexSharov) for reporting and testing.
 - [Kris Zyp](https://github.com/kriszyp) for reporting and testing.
 - [Artem Vorotnikov](https://github.com/vorot93) for support [Rust wrapper](https://github.com/vorot93/libmdbx-rs).

Fixes:
------

 - [Fixed compilation](https://github.com/erthink/libmdbx/pull/239) with `devtoolset-9` on CentOS/RHEL 7.
 - [Fixed unexpected `MDBX_PROBLEM` error](https://github.com/erthink/libmdbx/issues/242) because of update an obsolete meta-page.
 - [Fixed returning `MDBX_NOTFOUND` error](https://github.com/erthink/libmdbx/issues/243) in case an inexact value found for `MDBX_GET_BOTH` operation.
 - [Fixed compilation](https://github.com/erthink/libmdbx/issues/245) without kernel/libc-devel headers.

Minors:
-------

 - Fixed `constexpr`-related macros for legacy compilers.
 - Allowed to define 'CMAKE_CXX_STANDARD` using an environment variable.
 - Simplified collection statistics of page operation .
 - Added `MDBX_FORCE_BUILD_AS_MAIN_PROJECT` cmake option.
 - Remove unneeded `#undef P_DIRTY`.

Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2021-12-02 21:55:52 +03:00
Леонид Юрьев (Leonid Yuriev)
d2b15b5958 mdbx: more unlikely(). 2021-12-02 20:22:37 +03:00
Леонид Юрьев (Leonid Yuriev)
d96bc98244 mdbx: minor refine/fix MDBX_ENODATA for compatibility.
Related to https://github.com/erthink/libmdbx/issues/243
2021-12-02 20:22:37 +03:00
Леонид Юрьев (Leonid Yuriev)
c2cab7a6a8 mdbx: using clang-format-13 (cosmetics). 2021-12-02 20:22:31 +03:00
Леонид Юрьев (Leonid Yuriev)
38df3ca5ad mdbx: update/fix new libmdbx-rs library name. 2021-12-02 15:10:06 +03:00
Leonid Yuriev
5e4b2c9ddf mdbx: add BTC address for donations/sponsorship. 2021-11-27 17:02:27 +03:00
Leonid Yuriev
d777f5bb38 mdbx: update ChangeLog. 2021-11-27 00:29:36 +03:00
Leonid Yuriev
e912f87b2a mdbx: clarify notes about custom comparators usage. 2021-11-26 23:49:38 +03:00
Leonid Yuriev
0cc3aa3af8 mdbx: remove unneeded #undef P_DIRTY. 2021-11-26 17:51:37 +03:00
Leonid Yuriev
adac03729d mdbx: remove unneeded usage of <linux/sysctl.h>.
Fixed https://github.com/erthink/libmdbx/issues/245
2021-11-26 17:34:04 +03:00
Leonid Yuriev
17a14ec25f mdbx: update/fix new lmdbx-js library name. 2021-11-26 00:31:00 +03:00
Leonid Yuriev
4e73cdf9c6 mdbx: update ChangeLog. 2021-11-26 00:24:14 +03:00
Leonid Yuriev
66c354baff mdbx-test: add seek-test for MDBX_GET_BOTH.
Related to https://github.com/erthink/libmdbx/issues/243
2021-11-25 19:19:15 +03:00
Leonid Yuriev
76399bd643 mdbx: fix returning MDBX_NOTFOUND for non-exact seek case of MDBX_GET_BOTH.
Fixed https://github.com/erthink/libmdbx/issues/243
2021-11-25 19:19:04 +03:00
Leonid Yuriev
4f2aba2d22 mdbx: minor fix formatting (cosmetics). 2021-11-25 19:15:02 +03:00
Leonid Yuriev
085a97f835 mdbx: define MDBX_ENODATA == 9919 on systems without ENODATA.
As workaround for incompatibility C and C++ code using LLVM's C++ libraries/headers on on systems without native `ENODATA`.
2021-11-22 16:09:08 +03:00
Leonid Yuriev
a2141ceaac mdbx: slightly more cases to return MDBX_ENODATA. 2021-11-22 13:48:08 +03:00
Leonid Yuriev
9d55d06a20 mdbx-test: add check for MDBX_ENODATA condition. 2021-11-22 13:43:51 +03:00
Leonid Yuriev
e93d53ed92 mdbx: update ChangeLog. 2021-11-21 02:38:14 +03:00
Leonid Yuriev
8cb7c0f4fb mdbx: fix MDBX_PROBLEM while update an obsolete meta-page.
Fixes https://github.com/erthink/libmdbx/issues/242
2021-11-21 02:37:56 +03:00
Leonid Yuriev
773172cc99 mdbx: minor clarify descriptions of the MDBX_commit_latency fields. 2021-11-19 18:36:14 +03:00
Leonid Yuriev
937b38371f mdbx: remove obsolete remark from the reference to Java bindings. 2021-11-19 17:00:29 +03:00
Леонид Юрьев (Leonid Yuriev)
c312fead97 mdbx: update ChangeLog. 2021-11-19 16:21:44 +03:00
Леонид Юрьев (Leonid Yuriev)
bfff6cfe85 mdbx: fix nasty sizeof(bytes) typo. 2021-11-19 16:21:44 +03:00
Leonid Yuriev
86c735637e mdbx-cmake: add MDBX_FORCE_BUILD_AS_MAIN_PROJECT. 2021-11-19 16:21:40 +03:00
Leonid Yuriev
ff26d30362 mdbx: minor clarity enum MDBX_db_flags_t (database flags) descriptions.
Related to https://github.com/erthink/libmdbx/issues/241
2021-11-12 19:38:48 +03:00
Leonid Yuriev
79a5802ad4 mdbx: add the note about "Visual Studio 2015 Update 3". 2021-11-11 20:38:39 +03:00
Leonid Yuriev
0e2ca3eb51 mdbx: more parallelable CMP2INT for E2K. 2021-11-10 03:17:59 +03:00
Leonid Yuriev
e488604448 mdbx: minor fix mdbx_jitter4testing() for case MDBX_DEBUG >= 2. 2021-11-10 03:05:51 +03:00
Leonid Yuriev
eaa77c91cd mdbx: drop obsolete internal mm_flags macro. 2021-11-10 02:43:43 +03:00
Leonid Yuriev
8ed0a5946b mdbx: update ChangeLog. 2021-11-10 00:34:45 +03:00
Leonid Yuriev
0cd7dfb465 mdbx-ci: add dll-path-workaround to MinGW scenario with minor cleanup. 2021-11-10 00:23:01 +03:00
Leonid Yuriev
f38b1dab13 mdbx-ci: avoid spelling warnings. 2021-11-09 13:31:23 +03:00
sasgas
74d5a42578 mdbx: fix compilation with devtoolset-9 on CentOS/RHEL 7.
devtoolset is always using the old ABI
https://bugzilla.redhat.com/show_bug.cgi?id=1546704
https://stackoverflow.com/questions/49393888/how-can-i-use-the-new-c-11-abi-with-devtoolset-7-on-centos-rhel
2021-11-09 13:29:23 +03:00
Leonid Yuriev
6ecadba69a mdbx: update ChangeLog. 2021-11-08 18:37:21 +03:00
Leonid Yuriev
b46e5c4ce8 mdbx: simplify collection of page-ops statistic. 2021-11-07 22:14:33 +03:00
Leonid Yuriev
96139eef48 mdbx: a whole snapshot inside mdbx_env_info_ex(). 2021-11-07 21:18:10 +03:00
Leonid Yuriev
0c3a5da3cb mdbx: refine visibility of internal mdbx_osal_jitter(). 2021-11-07 19:48:14 +03:00
Leonid Yuriev
c456625ef2 mdbx-cmake: initial support for C++23. 2021-11-07 02:18:56 +03:00
Leonid Yuriev
630ef98951 mdbx-cmake: allow to define CMAKE_CXX_STANDARD by the environment variable. 2021-11-07 02:18:56 +03:00
Leonid Yuriev
7179823d56 mdbx-make: extpanded list of c++std for probing. 2021-11-07 02:18:56 +03:00
Leonid Yuriev
8870d33fcd mdbx++: refine MDBX_CXX01_CONSTEXPR for legacy compilers.
Enable `constexpr` via `MDBX_CXX01_CONSTEXPR` if __cplusplus == 201103L but __cpp_constexpr is undefined.
2021-11-07 02:18:56 +03:00
Leonid Yuriev
710fc95d9a mdbx: update patch for old buildroot versions. 2021-10-24 20:43:37 +03:00
Leonid Yuriev
93a24abbab mdbx: fix minor/paranoid MSVC warning. 2021-10-24 02:28:28 +03:00
Leonid Yuriev
113162b651 mdbx: release v0.11.1
Backward compatibility break:
-----------------------------

The database format signature has been changed to prevent
forward-interoperability with an previous releases, which may lead to a
[false positive diagnosis of database corruption](https://github.com/erthink/libmdbx/issues/238)
due to flaws of an old library versions.

This change is mostly invisible:

 - previously versions are unable to read/write a new DBs;
 - but the new release is able to handle an old DBs and will silently upgrade ones.

Acknowledgements:
-----------------
 - [Alex Sharov](https://github.com/AskAlexSharov) for reporting and testing.

Signed-off-by: Leonid Yuriev <leo@yuriev.ru>
2021-10-23 20:15:50 +03:00
Leonid Yuriev
5babf0872e mdbx++: add ifndef-guard for _CRT_SECURE_NO_WARNINGS. 2021-10-22 20:14:12 +03:00
Leonid Yuriev
331232858a adds updating db-format signature during DB open.
Fixes https://github.com/erthink/libmdbx/issues/238.
2021-10-22 20:13:57 +03:00
Leonid Yuriev
fcb8cd2145 mdbx: alter DB-format' signature and change version to v0.11.x (not a release).
Related to https://github.com/erthink/libmdbx/issues/238

Signed-off-by: Leonid Yuriev <leo@yuriev.ru>
2021-10-21 15:17:18 +03:00
Leonid Yuriev
514910621e mdbx: return MDBX_CORRUPTED instead of MDBX_PAGE_NOTFOUND for invalid pages. 2021-10-15 01:11:20 +03:00
Leonid Yuriev
7251f47d5b mdbx: fix typo which lead any error conversion to the 1. 2021-10-14 20:03:37 +03:00
20 changed files with 559 additions and 360 deletions

2
.github/FUNDING.yml vendored
View File

@@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://sobe.ru/na/libmdbx', 'https://paypal.me/erthink', 'https://etherscan.io/address/0x19291d8658f762f3baceae1700c0b9466572ceab']
custom: ['https://sobe.ru/na/libmdbx', 'https://paypal.me/erthink', 'https://www.blockchain.com/en/eth/address/0x19291d8658f762f3baceae1700c0b9466572ceab', 'https://www.blockchain.com/en/btc/address/152u2KXNWWGHQS3qiBEoQaveWyPvaSWYGC']

View File

@@ -68,6 +68,7 @@ asprintf
aspx
assection
AstraLinux
astralinux
atal
atexit
atfork
@@ -99,6 +100,7 @@ bfin
biarch
bibtex
BIGDATA
bigdata
bindir
binfmt
binutils
@@ -600,6 +602,7 @@ github
githubusercontent
gitignore
glibc
GLIBCXX
globals
gmail
gmake
@@ -709,6 +712,7 @@ inprocess
INSTEADOF
integerdup
integerkey
interoperability
interprocedural
intlimits
intptr
@@ -978,6 +982,7 @@ microsoft
Mikkelson
minflt
MINGW
mingw
minimalistic
minkeys
minlen
@@ -1276,6 +1281,7 @@ pmax
pmccntr
pmcntenset
pmedvedev
pmeta
pmr
pmuseren
pmwkaa
@@ -1315,6 +1321,7 @@ proba
proces
procfs
progname
programdata
PROGRAMLISTING
projectbrief
projectlogo
@@ -1403,11 +1410,13 @@ REALMEM
REALPATH
realtime
Rebuffo
rebuffo
RECO
redis
reedom
reefont
refname
refreshenv
REGEX
regexp
reinited

View File

@@ -27,38 +27,39 @@ jobs:
- uses: actions/checkout@v2
- name: fetch tags
run: git fetch --unshallow --tags --prune --force
- name: append PATH
run: echo 'C:/ProgramData/chocolatey/lib/mingw/tools/install/mingw64/bin' >> $GITHUB_PATH
- name: update mingw64
# wanna version >= 10.2
run: choco upgrade mingw -y --no-progress
run: choco upgrade mingw -y --no-progress && refreshenv
- name: configure-dll
run: mkdir build-dll && cd build-dll && cmake -G "MinGW Makefiles" -DMDBX_BUILD_SHARED_LIBRARY:BOOL=ON -DMDBX_INSTALL_STATIC:BOOL=OFF ..
shell: bash
run: |
mkdir build-dll && cd build-dll && \
cmake -G "MinGW Makefiles" -DMDBX_BUILD_SHARED_LIBRARY:BOOL=ON -DMDBX_INSTALL_STATIC:BOOL=OFF -DMDBX_ENABLE_TESTS:BOOL=ON ..
- name: build-dll
shell: bash
run: cd build-dll && cmake --build .
- name: test-dll
shell: pwsh
shell: bash
run: |
cd build-dll
ls
./mdbx_test --progress --console=no --pathname=test.db --dont-cleanup-after basic > test.log
Get-Content test.log | Select-Object -last 42
if ($LastExitCode -ne 0) {
throw "Exec: $ErrorMessage"
} else {
./mdbx_chk -nvv test.db | Tee-Object -file chk.log | Select-Object -last 42
}
export "PATH=/c/programdata/chocolatey/lib/mingw/tools/install/mingw64/bin:$PATH" && \
cd build-dll && \
./mdbx_test.exe --progress --console=no --pathname=test.db --dont-cleanup-after basic && \
./mdbx_chk.exe -nvv test.db
- name: configure-static
run: mkdir build-static && cd build-static && cmake -G "MinGW Makefiles" -DMDBX_BUILD_SHARED_LIBRARY:BOOL=OFF -DMDBX_INSTALL_STATIC:BOOL=ON ..
- name: build-static
run: cd build-static && cmake --build .
- name: test-static
shell: pwsh
shell: bash
run: |
cd build-static
ls
./mdbx_test --progress --console=no --pathname=test.db --dont-cleanup-after basic > test.log
Get-Content test.log | Select-Object -last 42
if ($LastExitCode -ne 0) {
throw "Exec: $ErrorMessage"
} else {
./mdbx_chk -nvv test.db | Tee-Object -file chk.log | Select-Object -last 42
}
mkdir build-static && cd build-static && \
cmake -G "MinGW Makefiles" -DMDBX_BUILD_SHARED_LIBRARY:BOOL=OFF -DMDBX_INSTALL_STATIC:BOOL=ON -DMDBX_ENABLE_TESTS:BOOL=ON ..
- name: build-static
shell: bash
run: |
cd build-static && cmake --build .
- name: test-static
shell: bash
run: |
export "PATH=/c/programdata/chocolatey/lib/mingw/tools/install/mingw64/bin:$PATH" && \
cd build-static && \
./mdbx_test.exe --progress --console=no --pathname=test.db --dont-cleanup-after basic && \
./mdbx_chk.exe -nvv test.db

View File

@@ -93,6 +93,10 @@ else()
endif()
if(DEFINED PROJECT_NAME)
option(MDBX_FORCE_BUILD_AS_MAIN_PROJECT "Force libmdbx to full control build options even it added as a subdirectory to your project." OFF)
endif()
if(DEFINED PROJECT_NAME AND NOT MDBX_FORCE_BUILD_AS_MAIN_PROJECT)
set(SUBPROJECT ON)
set(NOT_SUBPROJECT OFF)
if(NOT MDBX_AMALGAMATED_SOURCE AND NOT DEFINED BUILD_TESTING)
@@ -294,11 +298,11 @@ else()
if(UNIX)
find_program(CLANG_FORMAT
NAMES clang-format-12 clang-format-11 clang-format-10 clang-format)
NAMES clang-format-13 clang-format)
if(CLANG_FORMAT)
execute_process(COMMAND ${CLANG_FORMAT} "--version" OUTPUT_VARIABLE clang_format_version_info)
string(REGEX MATCH "version ([0-9]+)\\.([0-9]+)\\.([0-9]+)(.*)?" clang_format_version_info CLANG_FORMAT_VERSION)
if(clang_format_version_info AND NOT CLANG_FORMAT_VERSION VERSION_LESS 10.0)
if(clang_format_version_info AND NOT CLANG_FORMAT_VERSION VERSION_LESS 13.0)
# Enable 'make reformat' target.
add_custom_target(reformat
VERBATIM
@@ -334,9 +338,16 @@ list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_11 HAS_CXX11)
list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_14 HAS_CXX14)
list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_17 HAS_CXX17)
list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_20 HAS_CXX20)
list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_23 HAS_CXX23)
if(NOT DEFINED MDBX_CXX_STANDARD)
if(DEFINED ENV{CMAKE_CXX_STANDARD})
set(CMAKE_CXX_STANDARD $ENV{CMAKE_CXX_STANDARD})
endif()
if(DEFINED CMAKE_CXX_STANDARD)
set(MDBX_CXX_STANDARD ${CMAKE_CXX_STANDARD})
elseif(NOT HAS_CXX23 LESS 0
AND NOT (CMAKE_COMPILER_IS_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12))
set(MDBX_CXX_STANDARD 23)
elseif(NOT HAS_CXX20 LESS 0
AND NOT (CMAKE_COMPILER_IS_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9))
set(MDBX_CXX_STANDARD 20)

View File

@@ -1,8 +1,6 @@
ChangeLog
---------
## v0.11.x (in development)
### TODO
- [Engage an "overlapped I/O" on Windows](https://github.com/erthink/libmdbx/issues/224).
@@ -16,11 +14,64 @@ ChangeLog
- [Support for RAW devices](https://github.com/erthink/libmdbx/issues/124).
- [Support MessagePack for Keys & Values](https://github.com/erthink/libmdbx/issues/115).
- [Engage new terminology](https://github.com/erthink/libmdbx/issues/137).
- Finalize C++ API (few typos and bugs are still maybe for now).
- Finalize C++ API (there is still a small chance for a few typos and bugs).
- Packages for [Astra Linux](https://astralinux.ru/), [ALT Linux](https://www.altlinux.org/), [ROSA Linux](https://www.rosalinux.ru/), etc.
## v0.10.5 at 2021-10-13
## v0.11.2 at 2021-12-02
Acknowledgements:
- [장세연 (Чан Се Ен)](https://github.com/sasgas) for contributing to C++ API.
- [Alain Picard](https://github.com/castortech) for [Java bindings](https://github.com/castortech/mdbxjni).
- [Alex Sharov](https://github.com/AskAlexSharov) for reporting and testing.
- [Kris Zyp](https://github.com/kriszyp) for reporting and testing.
- [Artem Vorotnikov](https://github.com/vorot93) for support [Rust wrapper](https://github.com/vorot93/libmdbx-rs).
Fixes:
- [Fixed compilation](https://github.com/erthink/libmdbx/pull/239) with `devtoolset-9` on CentOS/RHEL 7.
- [Fixed unexpected `MDBX_PROBLEM` error](https://github.com/erthink/libmdbx/issues/242) because of update an obsolete meta-page.
- [Fixed returning `MDBX_NOTFOUND` error](https://github.com/erthink/libmdbx/issues/243) in case an inexact value found for `MDBX_GET_BOTH` operation.
- [Fixed compilation](https://github.com/erthink/libmdbx/issues/245) without kernel/libc-devel headers.
Minors:
- Fixed `constexpr`-related macros for legacy compilers.
- Allowed to define 'CMAKE_CXX_STANDARD` using an environment variable.
- Simplified collection statistics of page operation .
- Added `MDBX_FORCE_BUILD_AS_MAIN_PROJECT` cmake option.
- Remove unneeded `#undef P_DIRTY`.
## v0.11.1 at 2021-10-23
### Backward compatibility break:
The database format signature has been changed to prevent
forward-interoperability with an previous releases, which may lead to a
[false positive diagnosis of database corruption](https://github.com/erthink/libmdbx/issues/238)
due to flaws of an old library versions.
This change is mostly invisible:
- previously versions are unable to read/write a new DBs;
- but the new release is able to handle an old DBs and will silently upgrade ones.
Acknowledgements:
- [Alex Sharov](https://github.com/AskAlexSharov) for reporting and testing.
## v0.10.5 at 2021-10-13 (obsolete, please use v0.11.1)
Unfortunately, the `v0.10.5` accidentally comes not full-compatible with previous releases:
- `v0.10.5` can read/processing DBs created by previous releases, i.e. the backward-compatibility is provided;
- however, previous releases may lead to false-corrupted state with DB that was touched by `v0.10.5`, i.e. the forward-compatibility is broken for `v0.10.4` and earlier.
This cannot be fixed, as it requires fixing past versions, which as a result we will just get a current version.
Therefore, it is recommended to use `v0.11.1` instead of `v0.10.5`.
Acknowledgements:
@@ -42,7 +93,7 @@ Minors:
Acknowledgements:
- [Artem Vorotnikov](https://github.com/vorot93) for support [Rust wrapper](https://github.com/vorot93/mdbx-rs).
- [Artem Vorotnikov](https://github.com/vorot93) for support [Rust wrapper](https://github.com/vorot93/libmdbx-rs).
- [Andrew Ashikhmin](https://github.com/yperbasis) for contributing to C++ API.
Fixes:

View File

@@ -35,7 +35,7 @@ CFLAGS ?= -std=gnu11 -O2 -g -Wall -Werror -Wextra -Wpedantic -ffunction-section
# -Wno-tautological-compare
CXX ?= g++
# Choosing C++ standard with deferred simple variable expansion trick
CXXSTD ?= $(eval CXXSTD := $$(shell PROBE=$$$$([ -f mdbx.c++ ] && echo mdbx.c++ || echo src/mdbx.c++); for std in gnu++20 c++20 gnu++2a c++2a gnu++17 c++17 gnu++14 c++14 gnu+11 c++11; do $(CXX) -std=$$$${std} -c $$$${PROBE} -o /dev/null 2>/dev/null >/dev/null && echo "-std=$$$${std}" && exit; done))$(CXXSTD)
CXXSTD ?= $(eval CXXSTD := $$(shell PROBE=$$$$([ -f mdbx.c++ ] && echo mdbx.c++ || echo src/mdbx.c++); for std in gnu++23 c++23 gnu++2b c++2b gnu++20 c++20 gnu++2a c++2a gnu++17 c++17 gnu++1z c++1z gnu++14 c++14 gnu++1y c++1y gnu+11 c++11 gnu++0x c++0x; do $(CXX) -std=$$$${std} -c $$$${PROBE} -o /dev/null 2>std-$$$${std}.err >/dev/null && echo "-std=$$$${std}" && exit; done))$(CXXSTD)
CXXFLAGS = $(CXXSTD) $(filter-out -std=gnu11,$(CFLAGS))
# TIP: Try append '--no-as-needed,-lrt' for ability to built with modern glibc, but then use with the old.

View File

@@ -566,13 +566,13 @@ Bindings
| ------- | ------ | ------ |
| Haskell | [libmdbx-hs](https://hackage.haskell.org/package/libmdbx) | [Francisco Vallarino](https://github.com/fjvallarino) |
| Python (draft) | [python-bindings](https://github.com/erthink/libmdbx/commits/python-bindings) branch | [Noel Kuntze](https://github.com/Thermi)
| NodeJS | [lmdbx-store](https://github.com/kriszyp/lmdbx-store) | [Kris Zyp](https://github.com/kriszyp/)
| NodeJS | [lmdbx-js](https://github.com/kriszyp/lmdbx-js) | [Kris Zyp](https://github.com/kriszyp/)
| NodeJS | [node-mdbx](https://www.npmjs.com/package/node-mdbx/) | [Сергей Федотов](mailto:sergey.fedotov@corp.mail.ru) |
| Ruby | [ruby-mdbx](https://rubygems.org/gems/mdbx/) | [Mahlon E. Smith](https://github.com/mahlonsmith) |
| Go | [mdbx-go](https://github.com/torquem-ch/mdbx-go) | [Alex Sharov](https://github.com/AskAlexSharov) |
| [Nim](https://en.wikipedia.org/wiki/Nim_(programming_language)) | [NimDBX](https://github.com/snej/nimdbx) | [Jens Alfke](https://github.com/snej)
| Rust | [mdbx-rs](https://github.com/vorot93/mdbx-rs) | [Artem Vorotnikov](https://github.com/vorot93) |
| Java (obsolete) | [mdbxjni](https://github.com/castortech/mdbxjni) | [Castor Technologies](https://castortech.com/) |
| Rust | [libmdbx-rs](https://github.com/vorot93/libmdbx-rs) | [Artem Vorotnikov](https://github.com/vorot93) |
| Java | [mdbxjni](https://github.com/castortech/mdbxjni) | [Castor Technologies](https://castortech.com/) |
| .NET (obsolete) | [mdbx.NET](https://github.com/wangjia184/mdbx.NET) | [Jerry Wang](https://github.com/wangjia184) |
<!-- section-end -->

View File

@@ -1,4 +1,4 @@
version: 0.10.5.{build}
version: 0.11.2.{build}
environment:
matrix:

42
mdbx.h
View File

@@ -393,7 +393,8 @@ typedef mode_t mdbx_mode_t;
#define MDBX_CXX01_CONSTEXPR __inline
#define MDBX_CXX01_CONSTEXPR_VAR const
#elif !defined(DOXYGEN) && \
(!defined(__cpp_constexpr) || __cpp_constexpr < 200704L || \
((__cplusplus < 201103L && defined(__cpp_constexpr) && \
__cpp_constexpr < 200704L) || \
(defined(__LCC__) && __LCC__ < 124) || \
(defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ < 407) && \
!defined(__clang__) && !defined(__LCC__)) || \
@@ -410,7 +411,7 @@ typedef mode_t mdbx_mode_t;
#define MDBX_CXX11_CONSTEXPR __inline
#define MDBX_CXX11_CONSTEXPR_VAR const
#elif !defined(DOXYGEN) && \
(!defined(__cpp_constexpr) || __cpp_constexpr < 201304 || \
(!defined(__cpp_constexpr) || __cpp_constexpr < 201304L || \
(defined(__LCC__) && __LCC__ < 124) || \
(defined(__GNUC__) && __GNUC__ < 6 && !defined(__clang__) && \
!defined(__LCC__)) || \
@@ -568,9 +569,9 @@ typedef mode_t mdbx_mode_t;
extern "C" {
#endif
/* MDBX version 0.10.x */
/* MDBX version 0.11.x */
#define MDBX_VERSION_MAJOR 0
#define MDBX_VERSION_MINOR 10
#define MDBX_VERSION_MINOR 11
#ifndef LIBMDBX_API
#if defined(LIBMDBX_EXPORTS)
@@ -1382,10 +1383,10 @@ DEFINE_ENUM_FLAG_OPERATORS(MDBX_txn_flags_t)
enum MDBX_db_flags_t {
MDBX_DB_DEFAULTS = 0,
/** Use reverse string keys */
/** Use reverse string comparison for keys. */
MDBX_REVERSEKEY = UINT32_C(0x02),
/** Use sorted duplicates, i.e. allow multi-values */
/** Use sorted duplicates, i.e. allow multi-values for a keys. */
MDBX_DUPSORT = UINT32_C(0x04),
/** Numeric keys in native byte order either uint32_t or uint64_t. The keys
@@ -1393,18 +1394,19 @@ enum MDBX_db_flags_t {
* arguments. */
MDBX_INTEGERKEY = UINT32_C(0x08),
/** With \ref MDBX_DUPSORT; sorted dup items have fixed size */
/** With \ref MDBX_DUPSORT; sorted dup items have fixed size. The data values
* must all be of the same size. */
MDBX_DUPFIXED = UINT32_C(0x10),
/** With \ref MDBX_DUPSORT and with \ref MDBX_DUPFIXED; dups are fixed size
* \ref MDBX_INTEGERKEY -style integers. The data values must all be of the
* same size and must be aligned while passing as arguments. */
* like \ref MDBX_INTEGERKEY -style integers. The data values must all be of
* the same size and must be aligned while passing as arguments. */
MDBX_INTEGERDUP = UINT32_C(0x20),
/** With \ref MDBX_DUPSORT; use reverse string comparison */
/** With \ref MDBX_DUPSORT; use reverse string comparison for data values. */
MDBX_REVERSEDUP = UINT32_C(0x40),
/** Create DB if not already existing */
/** Create DB if not already existing. */
MDBX_CREATE = UINT32_C(0x40000),
/** Opens an existing sub-database created with unknown flags.
@@ -1735,7 +1737,7 @@ enum MDBX_error_t {
#ifdef ENODATA
MDBX_ENODATA = ENODATA,
#else
MDBX_ENODATA = -1,
MDBX_ENODATA = 9919 /* for compatibility with LLVM's C++ libraries/headers */,
#endif /* ENODATA */
MDBX_EINVAL = EINVAL,
MDBX_EACCESS = EACCES,
@@ -3284,9 +3286,11 @@ struct MDBX_commit_latency {
uint32_t gc;
/** \brief Duration of internal audit if enabled. */
uint32_t audit;
/** \brief Duration of writing dirty/modified data pages. */
/** \brief Duration of writing dirty/modified data pages to a filesystem,
* i.e. the summary duration of a `write()` syscalls during commit. */
uint32_t write;
/** \brief Duration of syncing written data to the dist/storage. */
/** \brief Duration of syncing written data to the disk/storage, i.e.
* the duration of a `fdatasync()` or a `msync()` syscall during commit. */
uint32_t sync;
/** \brief Duration of transaction ending (releasing resources). */
uint32_t ending;
@@ -3509,10 +3513,14 @@ LIBMDBX_API int mdbx_canary_get(const MDBX_txn *txn, MDBX_canary *canary);
* The reasons to not using custom comparators are:
* - The order of records could not be validated without your code.
* So `mdbx_chk` utility will reports "wrong order" errors
* and the `-i` option is required to ignore ones.
* and the `-i` option is required to suppress ones.
* - A records could not be ordered or sorted without your code.
* So mdbx_load utility should be used with `-a` option to preserve
* input data order. */
* So `mdbx_load` utility should be used with `-a` option to preserve
* input data order.
* - However, the custom comparators feature will never be removed.
* You have been warned but still can use custom comparators knowing
* about the issues noted above. In this case you should ignore `deprecated`
* warnings or define `MDBX_DEPRECATED` macro to empty to avoid ones. */
typedef int(MDBX_cmp_func)(const MDBX_val *a,
const MDBX_val *b) MDBX_CXX17_NOEXCEPT;

View File

@@ -286,7 +286,8 @@ class cursor;
class cursor_managed;
#if defined(DOXYGEN) || \
defined(__cpp_lib_memory_resource) && __cpp_lib_memory_resource >= 201603L
(defined(__cpp_lib_memory_resource) && \
__cpp_lib_memory_resource >= 201603L && _GLIBCXX_USE_CXX11_ABI)
/// \brief Default polymorphic allocator for modern code.
using polymorphic_allocator = ::std::pmr::string::allocator_type;
#endif /* __cpp_lib_memory_resource >= 201603L */
@@ -476,30 +477,23 @@ static MDBX_CXX20_CONSTEXPR void *memcpy(void *dest, const void *src,
template <typename T>
concept MutableByteProducer = requires(T a, char array[42]) {
{ a.is_empty() }
->std::same_as<bool>;
{ a.envisage_result_length() }
->std::same_as<size_t>;
{ a.write_bytes(&array[0], size_t(42)) }
->std::same_as<char *>;
{ a.is_empty() } -> std::same_as<bool>;
{ a.envisage_result_length() } -> std::same_as<size_t>;
{ a.write_bytes(&array[0], size_t(42)) } -> std::same_as<char *>;
};
template <typename T>
concept ImmutableByteProducer = requires(const T &a, char array[42]) {
{ a.is_empty() }
->std::same_as<bool>;
{ a.envisage_result_length() }
->std::same_as<size_t>;
{ a.write_bytes(&array[0], size_t(42)) }
->std::same_as<char *>;
{ a.is_empty() } -> std::same_as<bool>;
{ a.envisage_result_length() } -> std::same_as<size_t>;
{ a.write_bytes(&array[0], size_t(42)) } -> std::same_as<char *>;
};
template <typename T>
concept SliceTranscoder =
ImmutableByteProducer<T> &&requires(const slice &source, const T &a) {
concept SliceTranscoder = ImmutableByteProducer<T> &&
requires(const slice &source, const T &a) {
T(source);
{ a.is_erroneous() }
->std::same_as<bool>;
{ a.is_erroneous() } -> std::same_as<bool>;
};
#endif /* __cpp_concepts >= 201907L*/
@@ -4404,9 +4398,9 @@ MDBX_CXX14_CONSTEXPR intptr_t slice::compare_fast(const slice &a,
const slice &b) noexcept {
const intptr_t diff = intptr_t(a.length()) - intptr_t(b.length());
return diff ? diff
: MDBX_UNLIKELY(a.length() == 0 || a.data() == b.data())
? 0
: memcmp(a.data(), b.data(), a.length());
: MDBX_UNLIKELY(a.length() == 0 || a.data() == b.data())
? 0
: memcmp(a.data(), b.data(), a.length());
}
MDBX_CXX14_CONSTEXPR intptr_t

View File

@@ -1,9 +1,9 @@
From ce7b70a572cf77d8f37e5047d5d9c8361b2675ae Mon Sep 17 00:00:00 2001
From f732fc79456f3b296543ab2625d35eeef2655618 Mon Sep 17 00:00:00 2001
From: Leonid Yuriev <leo@yuriev.ru>
Date: Sun, 10 Oct 2021 15:31:40 +0300
Date: Sun, 24 Oct 2021 20:13:33 +0300
Subject: [PATCH] package/libmdbx: new package (library/database).
This patch adds libmdbx v0.10.4:
This patch adds libmdbx v0.11.1:
- libmdbx is one of the fastest compact embeddable key-value ACID database.
- libmdbx has a specific set of properties and capabilities,
focused on creating unique lightweight solutions.
@@ -103,18 +103,18 @@ index 0000000000..d13f73938f
+ !BR2_TOOLCHAIN_GCC_AT_LEAST_4_4
diff --git a/package/libmdbx/libmdbx.hash b/package/libmdbx/libmdbx.hash
new file mode 100644
index 0000000000..326cf57bb6
index 0000000000..c8b50f9ac3
--- /dev/null
+++ b/package/libmdbx/libmdbx.hash
@@ -0,0 +1,5 @@
+# Hashes from: https://github.com/erthink/libmdbx/releases/
+sha256 e11d5339a1e1cc34407898933b62a208936fd761a2cc31e11244d581d1d2b5d0 libmdbx-amalgamated-0.10.4.tar.gz
+sha256 f954ba8c9768914a92c2b46aac0d66bec674dbb4d7b0f01e362ea2921746ddaa libmdbx-amalgamated-0.11.1.tar.gz
+
+# Locally calculated
+sha256 310fe25c858a9515fc8c8d7d1f24a67c9496f84a91e0a0e41ea9975b1371e569 LICENSE
diff --git a/package/libmdbx/libmdbx.mk b/package/libmdbx/libmdbx.mk
new file mode 100644
index 0000000000..f38e4a533b
index 0000000000..02d00b1a5a
--- /dev/null
+++ b/package/libmdbx/libmdbx.mk
@@ -0,0 +1,42 @@
@@ -124,7 +124,7 @@ index 0000000000..f38e4a533b
+#
+################################################################################
+
+LIBMDBX_VERSION = 0.10.4
+LIBMDBX_VERSION = 0.11.1
+LIBMDBX_SOURCE = libmdbx-amalgamated-$(LIBMDBX_VERSION).tar.gz
+LIBMDBX_SITE = https://github.com/erthink/libmdbx/releases/download/v$(LIBMDBX_VERSION)
+LIBMDBX_SUPPORTS_IN_SOURCE_BUILD = NO
@@ -161,5 +161,5 @@ index 0000000000..f38e4a533b
+
+$(eval $(cmake-package))
--
2.33.0
2.33.1

View File

@@ -823,6 +823,70 @@ size_t __hot mdbx_e2k_strnlen_bug_workaround(const char *s, size_t maxlen) {
/*------------------------------------------------------------------------------
* safe read/write volatile 64-bit fields on 32-bit architectures. */
MDBX_MAYBE_UNUSED static __always_inline uint64_t
atomic_store64(MDBX_atomic_uint64_t *p, const uint64_t value,
enum MDBX_memory_order order) {
STATIC_ASSERT(sizeof(MDBX_atomic_uint64_t) == 8);
#if MDBX_64BIT_ATOMIC
#ifdef MDBX_HAVE_C11ATOMICS
assert(atomic_is_lock_free(MDBX_c11a_rw(uint64_t, p)));
atomic_store_explicit(MDBX_c11a_rw(uint64_t, p), value, mo_c11_store(order));
#else /* MDBX_HAVE_C11ATOMICS */
if (order != mo_Relaxed)
mdbx_compiler_barrier();
p->weak = value;
mdbx_memory_fence(order, true);
#endif /* MDBX_HAVE_C11ATOMICS */
#else /* !MDBX_64BIT_ATOMIC */
mdbx_compiler_barrier();
atomic_store32(&p->low, (uint32_t)value, mo_Relaxed);
mdbx_jitter4testing(true);
atomic_store32(&p->high, (uint32_t)(value >> 32), order);
mdbx_jitter4testing(true);
#endif /* !MDBX_64BIT_ATOMIC */
return value;
}
MDBX_MAYBE_UNUSED static
#if MDBX_64BIT_ATOMIC
__always_inline
#endif /* MDBX_64BIT_ATOMIC */
uint64_t
atomic_load64(const MDBX_atomic_uint64_t *p,
enum MDBX_memory_order order) {
STATIC_ASSERT(sizeof(MDBX_atomic_uint64_t) == 8);
#if MDBX_64BIT_ATOMIC
#ifdef MDBX_HAVE_C11ATOMICS
assert(atomic_is_lock_free(MDBX_c11a_ro(uint64_t, p)));
return atomic_load_explicit(MDBX_c11a_ro(uint64_t, p), mo_c11_load(order));
#else /* MDBX_HAVE_C11ATOMICS */
mdbx_memory_fence(order, false);
const uint64_t value = p->weak;
if (order != mo_Relaxed)
mdbx_compiler_barrier();
return value;
#endif /* MDBX_HAVE_C11ATOMICS */
#else /* !MDBX_64BIT_ATOMIC */
mdbx_compiler_barrier();
uint64_t value = (uint64_t)atomic_load32(&p->high, order) << 32;
mdbx_jitter4testing(true);
value |= atomic_load32(&p->low, (order == mo_Relaxed) ? mo_Relaxed
: mo_AcquireRelease);
mdbx_jitter4testing(true);
for (;;) {
mdbx_compiler_barrier();
uint64_t again = (uint64_t)atomic_load32(&p->high, order) << 32;
mdbx_jitter4testing(true);
again |= atomic_load32(&p->low, (order == mo_Relaxed) ? mo_Relaxed
: mo_AcquireRelease);
mdbx_jitter4testing(true);
if (likely(value == again))
return value;
value = again;
}
#endif /* !MDBX_64BIT_ATOMIC */
}
static __always_inline void atomic_yield(void) {
#if defined(_WIN32) || defined(_WIN64)
YieldProcessor();
@@ -1509,9 +1573,6 @@ static int uniq_peek(const mdbx_mmap_t *pending, mdbx_mmap_t *scan) {
rc = MDBX_SUCCESS;
} else {
bait = 0 /* hush MSVC warning */;
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&scan_lck->mti_pgop_stat.wops, 1);
#endif /* MDBX_ENABLE_PGOP_STAT */
rc = mdbx_msync(scan, 0, sizeof(MDBX_lockinfo), MDBX_SYNC_DATA);
if (rc == MDBX_SUCCESS)
rc = mdbx_pread(pending->fd, &bait, sizeof(scan_lck->mti_bait_uniqueness),
@@ -1568,9 +1629,6 @@ __cold static int uniq_check(const mdbx_mmap_t *pending, MDBX_env **found) {
if (err == MDBX_RESULT_TRUE)
err = uniq_poke(pending, &scan->me_lck_mmap, &salt);
if (err == MDBX_RESULT_TRUE) {
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&scan->me_lck_mmap.lck->mti_pgop_stat.wops, 1);
#endif /* MDBX_ENABLE_PGOP_STAT */
(void)mdbx_msync(&scan->me_lck_mmap, 0, sizeof(MDBX_lockinfo),
MDBX_SYNC_NONE);
err = uniq_poke(pending, &scan->me_lck_mmap, &salt);
@@ -4921,7 +4979,7 @@ static int mdbx_iov_write(MDBX_txn *const txn, struct mdbx_iov_ctx *ctx) {
bytes2pgno(env, ctx->iov[i].iov_len));
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&txn->mt_env->me_lck->mti_pgop_stat.wops, ctx->iov_items);
txn->mt_env->me_lck->mti_pgop_stat.wops.weak += ctx->iov_items;
#endif /* MDBX_ENABLE_PGOP_STAT */
ctx->iov_items = 0;
ctx->iov_bytes = 0;
@@ -4986,7 +5044,7 @@ static int spill_page(MDBX_txn *txn, struct mdbx_iov_ctx *ctx, MDBX_page *dp,
err = mdbx_pnl_append_range(true, &txn->tw.spill_pages, pgno << 1, npages);
#if MDBX_ENABLE_PGOP_STAT
if (likely(err == MDBX_SUCCESS))
safe64_inc(&txn->mt_env->me_lck->mti_pgop_stat.spill, npages);
txn->mt_env->me_lck->mti_pgop_stat.spill.weak += npages;
#endif /* MDBX_ENABLE_PGOP_STAT */
}
return err;
@@ -5044,9 +5102,9 @@ static unsigned spill_prio(const MDBX_txn *txn, const unsigned i,
MDBX_page *const dp = dl->items[i].ptr;
if (dp->mp_flags & (P_LOOSE | P_SPILLED)) {
mdbx_debug("skip %s %u page %" PRIaPGNO,
(dp->mp_flags & P_LOOSE)
? "loose"
: (dp->mp_flags & P_LOOSE) ? "loose" : "parent-spilled",
(dp->mp_flags & P_LOOSE) ? "loose"
: (dp->mp_flags & P_LOOSE) ? "loose"
: "parent-spilled",
npages, pgno);
return 256;
}
@@ -5167,7 +5225,7 @@ static int mdbx_txn_spill(MDBX_txn *const txn, MDBX_cursor *const m0,
if (!MDBX_FAKE_SPILL_WRITEMAP && ctx.flush_end > ctx.flush_begin) {
MDBX_env *const env = txn->mt_env;
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&env->me_lck->mti_pgop_stat.wops, 1);
env->me_lck->mti_pgop_stat.wops.weak += 1;
#endif /* MDBX_ENABLE_PGOP_STAT */
rc = mdbx_msync(&env->me_dxb_mmap,
pgno_align2os_bytes(env, ctx.flush_begin),
@@ -5994,7 +6052,7 @@ __cold static int mdbx_mapresize(MDBX_env *env, const pgno_t used_pgno,
if ((env->me_flags & MDBX_WRITEMAP) && env->me_lck->mti_unsynced_pages.weak) {
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&env->me_lck->mti_pgop_stat.wops, 1);
env->me_lck->mti_pgop_stat.wops.weak += 1;
#endif /* MDBX_ENABLE_PGOP_STAT */
rc = mdbx_msync(&env->me_dxb_mmap, 0, pgno_align2os_bytes(env, used_pgno),
MDBX_SYNC_NONE);
@@ -6145,7 +6203,7 @@ static int mdbx_meta_unsteady(MDBX_env *env, const txnid_t last_steady,
__cold static int mdbx_wipe_steady(MDBX_env *env, const txnid_t last_steady) {
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&env->me_lck->mti_pgop_stat.wops, 1);
env->me_lck->mti_pgop_stat.wops.weak += 1;
#endif /* MDBX_ENABLE_PGOP_STAT */
const mdbx_filehandle_t fd = (env->me_dsync_fd != INVALID_HANDLE_VALUE)
? env->me_dsync_fd
@@ -6765,7 +6823,7 @@ mdbx_page_unspill(MDBX_txn *const txn, const MDBX_page *const mp) {
if (unlikely(ret.err != MDBX_SUCCESS))
return ret;
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&txn->mt_env->me_lck->mti_pgop_stat.unspill, npages);
txn->mt_env->me_lck->mti_pgop_stat.unspill.weak += npages;
#endif /* MDBX_ENABLE_PGOP_STAT */
ret.page->mp_flags |= (scan == txn) ? 0 : P_SPILLED;
ret.err = MDBX_SUCCESS;
@@ -6838,7 +6896,7 @@ __hot static int mdbx_page_touch(MDBX_cursor *mc) {
}
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&txn->mt_env->me_lck->mti_pgop_stat.cow, 1);
txn->mt_env->me_lck->mti_pgop_stat.cow.weak += 1;
#endif /* MDBX_ENABLE_PGOP_STAT */
mdbx_page_copy(np, mp, txn->mt_env->me_psize);
np->mp_pgno = pgno;
@@ -6881,7 +6939,7 @@ __hot static int mdbx_page_touch(MDBX_cursor *mc) {
goto fail;
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&txn->mt_env->me_lck->mti_pgop_stat.clone, 1);
txn->mt_env->me_lck->mti_pgop_stat.clone.weak += 1;
#endif /* MDBX_ENABLE_PGOP_STAT */
}
@@ -6959,7 +7017,7 @@ __cold static int mdbx_env_sync_internal(MDBX_env *env, bool force,
const size_t usedbytes = pgno_align2os_bytes(env, head->mm_geo.next);
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&env->me_lck->mti_pgop_stat.wops, 1);
env->me_lck->mti_pgop_stat.wops.weak += 1;
#endif /* MDBX_ENABLE_PGOP_STAT */
mdbx_txn_unlock(env);
@@ -7009,7 +7067,17 @@ fastpath:
if (atomic_load32(&env->me_lck->mti_meta_sync_txnid, mo_Relaxed) !=
(uint32_t)head_txnid) {
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&env->me_lck->mti_pgop_stat.wops, 1);
if (need_unlock)
env->me_lck->mti_pgop_stat.wops.weak += 1;
#if MDBX_64BIT_ATOMIC
else {
MDBX_atomic_uint64_t *wops = &env->me_lck->mti_pgop_stat.wops;
while (unlikely(!atomic_cas64(wops, wops->weak, wops->weak + 1)))
atomic_yield();
}
#else
/* loose the env->me_lck->mti_pgop_stat.wops.weak increment */
#endif /* MDBX_64BIT_ATOMIC */
#endif /* MDBX_ENABLE_PGOP_STAT */
rc = (flags & MDBX_WRITEMAP)
? mdbx_msync(&env->me_dxb_mmap, 0,
@@ -9136,7 +9204,8 @@ retry_noaccount:
txn->tw.lifo_reclaimed
? (unsigned)MDBX_PNL_SIZE(txn->tw.lifo_reclaimed) -
reused_gc_slot + 1
: (gc_rid < INT16_MAX) ? (unsigned)gc_rid : INT16_MAX;
: (gc_rid < INT16_MAX) ? (unsigned)gc_rid
: INT16_MAX;
if (avail_gc_slots > 1) {
if (chunk < env->me_maxgc_ov1page * 2)
chunk /= 2;
@@ -9169,10 +9238,10 @@ retry_noaccount:
}
chunk = (avail >= tail) ? tail - span
: (avail_gc_slots > 3 &&
reused_gc_slot < prefer_max_scatter - 3)
? avail - span
: tail;
: (avail_gc_slots > 3 &&
reused_gc_slot < prefer_max_scatter - 3)
? avail - span
: tail;
}
}
}
@@ -10124,7 +10193,8 @@ static int mdbx_validate_meta(MDBX_env *env, MDBX_meta *const meta,
const uint64_t magic_and_version =
unaligned_peek_u64(4, &meta->mm_magic_and_version);
if (unlikely(magic_and_version != MDBX_DATA_MAGIC &&
magic_and_version != MDBX_DATA_MAGIC_DEVEL)) {
magic_and_version != MDBX_DATA_MAGIC_LEGACY_COMPAT &&
magic_and_version != MDBX_DATA_MAGIC_LEGACY_DEVEL)) {
mdbx_error("meta[%u] has invalid magic/version %" PRIx64, meta_number,
magic_and_version);
return ((magic_and_version >> 8) != MDBX_MAGIC) ? MDBX_INVALID
@@ -10374,11 +10444,10 @@ __cold static int mdbx_read_header(MDBX_env *env, MDBX_meta *dest,
unsigned guess_pagesize = 0;
for (unsigned loop_count = 0; loop_count < loop_limit; ++loop_count) {
const unsigned meta_number = loop_count % NUM_METAS;
const unsigned offset =
(guess_pagesize
? guess_pagesize
: (loop_count > NUM_METAS) ? env->me_psize : env->me_os_psize) *
meta_number;
const unsigned offset = (guess_pagesize ? guess_pagesize
: (loop_count > NUM_METAS) ? env->me_psize
: env->me_os_psize) *
meta_number;
char buffer[MIN_PAGESIZE];
unsigned retryleft = 42;
@@ -10487,9 +10556,7 @@ __cold static MDBX_page *mdbx_meta_model(const MDBX_env *env, MDBX_page *model,
pages2pv(pv2pages(model_meta->mm_geo.shrink_pv)));
model_meta->mm_psize = env->me_psize;
model_meta->mm_flags = (uint16_t)env->me_flags & DB_PERSISTENT_FLAGS;
model_meta->mm_flags =
MDBX_INTEGERKEY; /* this is mm_dbs[FREE_DBI].md_flags */
model_meta->mm_dbs[FREE_DBI].md_flags = MDBX_INTEGERKEY;
model_meta->mm_dbs[FREE_DBI].md_root = P_INVALID;
model_meta->mm_dbs[MAIN_DBI].md_root = P_INVALID;
mdbx_meta_set_txnid(env, model_meta, MIN_TXNID + num);
@@ -10518,9 +10585,9 @@ static size_t mdbx_madvise_threshold(const MDBX_env *env,
const unsigned factor = 9;
const size_t threshold = (largest_bytes < (65536ul << factor))
? 65536 /* minimal threshold */
: (largest_bytes > (MEGABYTE * 4 << factor))
? MEGABYTE * 4 /* maximal threshold */
: largest_bytes >> factor;
: (largest_bytes > (MEGABYTE * 4 << factor))
? MEGABYTE * 4 /* maximal threshold */
: largest_bytes >> factor;
return bytes_align2os_bytes(env, threshold);
}
#endif /* MDBX_ENABLE_MADVISE */
@@ -10672,7 +10739,7 @@ static int mdbx_sync_locked(MDBX_env *env, unsigned flags,
mode_bits |= MDBX_SYNC_IODQ;
}
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&env->me_lck->mti_pgop_stat.wops, 1);
env->me_lck->mti_pgop_stat.wops.weak += 1;
#endif /* MDBX_ENABLE_PGOP_STAT */
if (flags & MDBX_WRITEMAP)
rc =
@@ -10735,17 +10802,23 @@ static int mdbx_sync_locked(MDBX_env *env, unsigned flags,
mdbx_debug("meta0: %s, %s, txn_id %" PRIaTXN ", root %" PRIaPGNO
"/%" PRIaPGNO,
(meta0 == head) ? "head" : (meta0 == target) ? "tail" : "stay",
(meta0 == head) ? "head"
: (meta0 == target) ? "tail"
: "stay",
mdbx_durable_str(meta0), mdbx_meta_txnid_fluid(env, meta0),
meta0->mm_dbs[MAIN_DBI].md_root, meta0->mm_dbs[FREE_DBI].md_root);
mdbx_debug("meta1: %s, %s, txn_id %" PRIaTXN ", root %" PRIaPGNO
"/%" PRIaPGNO,
(meta1 == head) ? "head" : (meta1 == target) ? "tail" : "stay",
(meta1 == head) ? "head"
: (meta1 == target) ? "tail"
: "stay",
mdbx_durable_str(meta1), mdbx_meta_txnid_fluid(env, meta1),
meta1->mm_dbs[MAIN_DBI].md_root, meta1->mm_dbs[FREE_DBI].md_root);
mdbx_debug("meta2: %s, %s, txn_id %" PRIaTXN ", root %" PRIaPGNO
"/%" PRIaPGNO,
(meta2 == head) ? "head" : (meta2 == target) ? "tail" : "stay",
(meta2 == head) ? "head"
: (meta2 == target) ? "tail"
: "stay",
mdbx_durable_str(meta2), mdbx_meta_txnid_fluid(env, meta2),
meta2->mm_dbs[MAIN_DBI].md_root, meta2->mm_dbs[FREE_DBI].md_root);
@@ -10758,7 +10831,7 @@ static int mdbx_sync_locked(MDBX_env *env, unsigned flags,
target == head || mdbx_meta_txnid_stable(env, target) <
unaligned_peek_u64(4, pending->mm_txnid_a));
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&env->me_lck->mti_pgop_stat.wops, 1);
env->me_lck->mti_pgop_stat.wops.weak += 1;
#endif /* MDBX_ENABLE_PGOP_STAT */
if (flags & MDBX_WRITEMAP) {
mdbx_jitter4testing(true);
@@ -10769,7 +10842,7 @@ static int mdbx_sync_locked(MDBX_env *env, unsigned flags,
unaligned_poke_u64(4, target->mm_datasync_sign, MDBX_DATASIGN_WEAK);
#ifndef NDEBUG
/* debug: provoke failure to catch a violators, but don't touch mm_psize
* and mm_flags to allow readers catch actual pagesize. */
* to allow readers catch actual pagesize. */
uint8_t *provoke_begin = (uint8_t *)&target->mm_dbs[FREE_DBI].md_root;
uint8_t *provoke_end = (uint8_t *)&target->mm_datasync_sign;
memset(provoke_begin, 0xCC, provoke_end - provoke_begin);
@@ -10817,7 +10890,7 @@ static int mdbx_sync_locked(MDBX_env *env, unsigned flags,
? env->me_dsync_fd
: env->me_lazy_fd;
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&env->me_lck->mti_pgop_stat.wops, 1);
env->me_lck->mti_pgop_stat.wops.weak += 1;
#endif /* MDBX_ENABLE_PGOP_STAT */
rc = mdbx_pwrite(fd, pending, sizeof(MDBX_meta),
(uint8_t *)target - env->me_map);
@@ -10868,12 +10941,15 @@ fail:
static void recalculate_merge_threshold(MDBX_env *env) {
const unsigned bytes = page_space(env);
env->me_merge_threshold = (uint16_t)(
bytes - (bytes * env->me_options.merge_threshold_16dot16_percent >> 16));
env->me_merge_threshold_gc = (uint16_t)(
bytes - ((env->me_options.merge_threshold_16dot16_percent > 19005)
? bytes / 3 /* 33 % */
: bytes / 4 /* 25 % */));
env->me_merge_threshold =
(uint16_t)(bytes -
(bytes * env->me_options.merge_threshold_16dot16_percent >>
16));
env->me_merge_threshold_gc =
(uint16_t)(bytes -
((env->me_options.merge_threshold_16dot16_percent > 19005)
? bytes / 3 /* 33 % */
: bytes / 4 /* 25 % */));
}
__cold static void mdbx_setup_pagesize(MDBX_env *env, const size_t pagesize) {
@@ -11867,6 +11943,31 @@ __cold static int mdbx_setup_dxb(MDBX_env *env, const int lck_rc,
atomic_store32(&env->me_lck->mti_discarded_tail,
bytes2pgno(env, used_aligned2os_bytes), mo_Relaxed);
if ((env->me_flags & MDBX_RDONLY) == 0 && env->me_stuck_meta < 0) {
for (int n = 0; n < NUM_METAS; ++n) {
MDBX_meta *const pmeta = METAPAGE(env, n);
if (unlikely(unaligned_peek_u64(4, &pmeta->mm_magic_and_version) !=
MDBX_DATA_MAGIC)) {
const txnid_t txnid = mdbx_meta_txnid_fluid(env, pmeta);
mdbx_notice("%s %s"
"meta[%u], txnid %" PRIaTXN,
"updating db-format signature for",
META_IS_STEADY(pmeta) ? "stead-" : "weak-", n, txnid);
err = mdbx_override_meta(env, n, txnid, pmeta);
if (unlikely(err != MDBX_SUCCESS) &&
/* Just ignore the MDBX_PROBLEM error, since here it is
* returned only in case of the attempt to upgrade an obsolete
* meta-page that is invalid for current state of a DB,
* e.g. after shrinking DB file */
err != MDBX_PROBLEM) {
mdbx_error("%s meta[%u], txnid %" PRIaTXN ", error %d",
"updating db-format signature for", n, txnid, err);
return err;
}
}
}
}
} /* lck exclusive, lck_rc == MDBX_RESULT_TRUE */
//---------------------------------------------------- setup madvise/readahead
@@ -12196,7 +12297,7 @@ __cold static int __must_check_result mdbx_override_meta(
return MDBX_PROBLEM;
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&env->me_lck->mti_pgop_stat.wops, 1);
env->me_lck->mti_pgop_stat.wops.weak += 1;
#endif /* MDBX_ENABLE_PGOP_STAT */
if (env->me_flags & MDBX_WRITEMAP) {
rc = mdbx_msync(&env->me_dxb_mmap, 0,
@@ -13116,10 +13217,11 @@ mdbx_page_get_ex(MDBX_cursor *const mc, const pgno_t pgno,
mdbx_tassert(txn, front <= txn->mt_front);
if (unlikely(pgno >= txn->mt_next_pgno)) {
mdbx_error("page #%" PRIaPGNO " beyond next-pgno", pgno);
notfound:
ret.page = nullptr;
corrupted:
mc->mc_txn->mt_flags |= MDBX_TXN_ERROR;
ret.err = MDBX_PAGE_NOTFOUND;
bailout:
mc->mc_txn->mt_flags |= MDBX_TXN_ERROR;
return ret;
}
@@ -13159,33 +13261,36 @@ dirty:
"mismatch actual pgno (%" PRIaPGNO ") != expected (%" PRIaPGNO
")\n",
ret.page->mp_pgno, pgno);
goto corrupted;
goto notfound;
}
#if !MDBX_DISABLE_PAGECHECKS
if (unlikely(ret.page->mp_flags & P_ILL_BITS)) {
bad_page(ret.page, "invalid page's flags (%u)\n", ret.page->mp_flags);
goto corrupted;
ret.err =
bad_page(ret.page, "invalid page's flags (%u)\n", ret.page->mp_flags);
goto bailout;
}
if (unlikely(ret.page->mp_txnid > front) &&
unlikely(ret.page->mp_txnid > txn->mt_front || front < txn->mt_txnid)) {
bad_page(ret.page,
"invalid page txnid (%" PRIaTXN ") for %s' txnid (%" PRIaTXN ")\n",
ret.page->mp_txnid,
(front == txn->mt_front && front != txn->mt_txnid) ? "front-txn"
: "parent-page",
front);
goto corrupted;
ret.err = bad_page(
ret.page,
"invalid page txnid (%" PRIaTXN ") for %s' txnid (%" PRIaTXN ")\n",
ret.page->mp_txnid,
(front == txn->mt_front && front != txn->mt_txnid) ? "front-txn"
: "parent-page",
front);
goto bailout;
}
if (unlikely((ret.page->mp_upper < ret.page->mp_lower ||
((ret.page->mp_lower | ret.page->mp_upper) & 1) ||
PAGEHDRSZ + ret.page->mp_upper > env->me_psize) &&
!IS_OVERFLOW(ret.page))) {
bad_page(ret.page, "invalid page lower(%u)/upper(%u) with limit (%u)\n",
ret.page->mp_lower, ret.page->mp_upper, page_space(env));
goto corrupted;
ret.err =
bad_page(ret.page, "invalid page lower(%u)/upper(%u) with limit (%u)\n",
ret.page->mp_lower, ret.page->mp_upper, page_space(env));
goto bailout;
}
#endif /* !MDBX_DISABLE_PAGECHECKS */
@@ -13430,7 +13535,7 @@ __hot static int mdbx_page_search(MDBX_cursor *mc, const MDBX_val *key,
break;
}
while (unlikely((scan = scan->mt_parent) != nullptr));
if (unlikely((rc = mdbx_page_get(mc, root, &mc->mc_pg[0], pp_txnid) != 0)))
if (unlikely((rc = mdbx_page_get(mc, root, &mc->mc_pg[0], pp_txnid)) != 0))
return rc;
}
@@ -13636,16 +13741,16 @@ static int mdbx_cursor_next(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
MDBX_node *node;
int rc;
if ((mc->mc_flags & C_DEL) && op == MDBX_NEXT_DUP)
if (unlikely(mc->mc_flags & C_DEL) && op == MDBX_NEXT_DUP)
return MDBX_NOTFOUND;
if (!(mc->mc_flags & C_INITIALIZED))
if (unlikely(!(mc->mc_flags & C_INITIALIZED)))
return mdbx_cursor_first(mc, key, data);
mp = mc->mc_pg[mc->mc_top];
if (mc->mc_flags & C_EOF) {
if (unlikely(mc->mc_flags & C_EOF)) {
if (mc->mc_ki[mc->mc_top] + 1u >= page_numkeys(mp))
return MDBX_NOTFOUND;
return (mc->mc_flags & C_SUB) ? MDBX_NOTFOUND : MDBX_ENODATA;
mc->mc_flags ^= C_EOF;
}
@@ -13737,10 +13842,10 @@ static int mdbx_cursor_prev(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
MDBX_node *node;
int rc;
if ((mc->mc_flags & C_DEL) && op == MDBX_PREV_DUP)
if (unlikely(mc->mc_flags & C_DEL) && op == MDBX_PREV_DUP)
return MDBX_NOTFOUND;
if (!(mc->mc_flags & C_INITIALIZED)) {
if (unlikely(!(mc->mc_flags & C_INITIALIZED))) {
rc = mdbx_cursor_last(mc, key, data);
if (unlikely(rc))
return rc;
@@ -14043,6 +14148,10 @@ got_node:
MDBX_SET_RANGE);
if (unlikely(ret.err != MDBX_SUCCESS))
return ret;
if (op == MDBX_GET_BOTH && !ret.exact) {
ret.err = MDBX_NOTFOUND;
return ret;
}
}
} else if (likely(data)) {
if (op == MDBX_GET_BOTH || op == MDBX_GET_BOTH_RANGE) {
@@ -14074,12 +14183,12 @@ got_node:
break;
}
}
MDBX_val olddata;
ret.err = mdbx_node_read(mc, node, &olddata,
MDBX_val actual_data;
ret.err = mdbx_node_read(mc, node, &actual_data,
pp_txnid4chk(mc->mc_pg[mc->mc_top], mc->mc_txn));
if (unlikely(ret.err != MDBX_SUCCESS))
return ret;
const int cmp = mc->mc_dbx->md_dcmp(&aligned_data, &olddata);
const int cmp = mc->mc_dbx->md_dcmp(&aligned_data, &actual_data);
if (cmp) {
mdbx_cassert(mc, mc->mc_ki[mc->mc_top] <
page_numkeys(mc->mc_pg[mc->mc_top]) ||
@@ -14089,7 +14198,7 @@ got_node:
return ret;
}
}
*data = olddata;
*data = actual_data;
} else {
ret.err = mdbx_node_read(mc, node, data,
pp_txnid4chk(mc->mc_pg[mc->mc_top], mc->mc_txn));
@@ -14234,6 +14343,8 @@ int mdbx_cursor_get(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
const unsigned nkeys = page_numkeys(mp);
if (mc->mc_ki[mc->mc_top] >= nkeys) {
mdbx_cassert(mc, nkeys <= UINT16_MAX);
if (mc->mc_flags & C_EOF)
return MDBX_ENODATA;
mc->mc_ki[mc->mc_top] = (uint16_t)nkeys;
mc->mc_flags |= C_EOF;
return MDBX_NOTFOUND;
@@ -14701,24 +14812,24 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
rc = MDBX_NOTFOUND;
exact = false;
} else /* checking for early exit without dirtying pages */
if (!(flags & (MDBX_RESERVE | MDBX_MULTIPLE)) &&
unlikely(mc->mc_dbx->md_dcmp(data, &olddata) == 0)) {
if (!mc->mc_xcursor)
/* the same data, nothing to update */
return MDBX_SUCCESS;
if (flags & MDBX_NODUPDATA)
return MDBX_KEYEXIST;
if (flags & MDBX_APPENDDUP)
return MDBX_EKEYMISMATCH;
if (likely(unsure_equal(mc->mc_dbx->md_dcmp, data, &olddata)))
/* data is match exactly byte-to-byte, nothing to update */
return MDBX_SUCCESS;
else {
/* The data has differences, but the user-provided comparator
* considers them equal. So continue update since called without.
* Continue to update since was called without MDBX_NODUPDATA. */
if (!(flags & (MDBX_RESERVE | MDBX_MULTIPLE)) &&
unlikely(mc->mc_dbx->md_dcmp(data, &olddata) == 0)) {
if (!mc->mc_xcursor)
/* the same data, nothing to update */
return MDBX_SUCCESS;
if (flags & MDBX_NODUPDATA)
return MDBX_KEYEXIST;
if (flags & MDBX_APPENDDUP)
return MDBX_EKEYMISMATCH;
if (likely(unsure_equal(mc->mc_dbx->md_dcmp, data, &olddata)))
/* data is match exactly byte-to-byte, nothing to update */
return MDBX_SUCCESS;
else {
/* The data has differences, but the user-provided comparator
* considers them equal. So continue update since called without.
* Continue to update since was called without MDBX_NODUPDATA. */
}
}
}
}
} else if (unlikely(rc != MDBX_NOTFOUND))
return rc;
@@ -14894,8 +15005,7 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
return err;
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&mc->mc_txn->mt_env->me_lck->mti_pgop_stat.clone,
ovpages);
mc->mc_txn->mt_env->me_lck->mti_pgop_stat.clone.weak += ovpages;
#endif /* MDBX_ENABLE_PGOP_STAT */
mdbx_cassert(mc, mdbx_dirtylist_check(mc->mc_txn));
}
@@ -15414,7 +15524,7 @@ static struct page_result mdbx_page_new(MDBX_cursor *mc, const unsigned flags,
mdbx_cassert(mc, *mc->mc_dbistate & DBI_DIRTY);
mdbx_cassert(mc, mc->mc_txn->mt_flags & MDBX_TXN_DIRTY);
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&mc->mc_txn->mt_env->me_lck->mti_pgop_stat.newly, npages);
mc->mc_txn->mt_env->me_lck->mti_pgop_stat.newly.weak += npages;
#endif /* MDBX_ENABLE_PGOP_STAT */
if (likely((flags & P_OVERFLOW) == 0)) {
@@ -16766,7 +16876,7 @@ static int mdbx_page_merge(MDBX_cursor *csrc, MDBX_cursor *cdst) {
mdbx_cassert(cdst, cdst->mc_snum == cdst->mc_top + 1);
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&cdst->mc_txn->mt_env->me_lck->mti_pgop_stat.merge, 1);
cdst->mc_txn->mt_env->me_lck->mti_pgop_stat.merge.weak += 1;
#endif /* MDBX_ENABLE_PGOP_STAT */
if (IS_LEAF(cdst->mc_pg[cdst->mc_top])) {
@@ -18262,7 +18372,7 @@ done:
newdata->iov_base = node_data(node);
}
#if MDBX_ENABLE_PGOP_STAT
safe64_inc(&env->me_lck->mti_pgop_stat.split, 1);
env->me_lck->mti_pgop_stat.split.weak += 1;
#endif /* MDBX_ENABLE_PGOP_STAT */
}
@@ -19289,31 +19399,11 @@ __cold int mdbx_env_info(const MDBX_env *env, MDBX_envinfo *info,
}
#endif /* LIBMDBX_NO_EXPORTS_LEGACY_API */
__cold int mdbx_env_info_ex(const MDBX_env *env, const MDBX_txn *txn,
MDBX_envinfo *arg, size_t bytes) {
if (unlikely((env == NULL && txn == NULL) || arg == NULL))
return MDBX_EINVAL;
if (txn) {
int err = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(err != MDBX_SUCCESS))
return err;
}
if (env) {
int err = check_env(env, false);
if (unlikely(err != MDBX_SUCCESS))
return err;
if (txn && unlikely(txn->mt_env != env))
return MDBX_EINVAL;
} else {
env = txn->mt_env;
}
__cold static int fetch_envinfo_ex(const MDBX_env *env, const MDBX_txn *txn,
MDBX_envinfo *arg, const size_t bytes) {
const size_t size_before_bootid = offsetof(MDBX_envinfo, mi_bootid);
const size_t size_before_pgop_stat = offsetof(MDBX_envinfo, mi_pgop_stat);
if (unlikely(bytes != sizeof(MDBX_envinfo)) && bytes != size_before_bootid &&
bytes != size_before_pgop_stat)
return MDBX_EINVAL;
/* is the environment open? (https://github.com/erthink/libmdbx/issues/171) */
if (unlikely(!env->me_map)) {
@@ -19343,64 +19433,48 @@ __cold int mdbx_env_info_ex(const MDBX_env *env, const MDBX_txn *txn,
const MDBX_meta *const meta0 = METAPAGE(env, 0);
const MDBX_meta *const meta1 = METAPAGE(env, 1);
const MDBX_meta *const meta2 = METAPAGE(env, 2);
pgno_t unsynced_pages;
while (1) {
if (unlikely(env->me_flags & MDBX_FATAL_ERROR))
return MDBX_PANIC;
if (unlikely(env->me_flags & MDBX_FATAL_ERROR))
return MDBX_PANIC;
const MDBX_meta *const recent_meta = mdbx_meta_head(env);
arg->mi_recent_txnid = mdbx_meta_txnid_fluid(env, recent_meta);
arg->mi_meta0_txnid = mdbx_meta_txnid_fluid(env, meta0);
arg->mi_meta0_sign = unaligned_peek_u64(4, meta0->mm_datasync_sign);
arg->mi_meta1_txnid = mdbx_meta_txnid_fluid(env, meta1);
arg->mi_meta1_sign = unaligned_peek_u64(4, meta1->mm_datasync_sign);
arg->mi_meta2_txnid = mdbx_meta_txnid_fluid(env, meta2);
arg->mi_meta2_sign = unaligned_peek_u64(4, meta2->mm_datasync_sign);
if (likely(bytes > size_before_bootid)) {
memcpy(&arg->mi_bootid.meta0, &meta0->mm_bootid, 16);
memcpy(&arg->mi_bootid.meta1, &meta1->mm_bootid, 16);
memcpy(&arg->mi_bootid.meta2, &meta2->mm_bootid, 16);
}
const MDBX_meta *txn_meta = recent_meta;
arg->mi_last_pgno = txn_meta->mm_geo.next - 1;
arg->mi_geo.current = pgno2bytes(env, txn_meta->mm_geo.now);
if (txn) {
arg->mi_last_pgno = txn->mt_next_pgno - 1;
arg->mi_geo.current = pgno2bytes(env, txn->mt_end_pgno);
const txnid_t wanna_meta_txnid = (txn->mt_flags & MDBX_TXN_RDONLY)
? txn->mt_txnid
: txn->mt_txnid - xMDBX_TXNID_STEP;
txn_meta = (arg->mi_meta0_txnid == wanna_meta_txnid) ? meta0 : txn_meta;
txn_meta = (arg->mi_meta1_txnid == wanna_meta_txnid) ? meta1 : txn_meta;
txn_meta = (arg->mi_meta2_txnid == wanna_meta_txnid) ? meta2 : txn_meta;
}
arg->mi_geo.lower = pgno2bytes(env, txn_meta->mm_geo.lower);
arg->mi_geo.upper = pgno2bytes(env, txn_meta->mm_geo.upper);
arg->mi_geo.shrink = pgno2bytes(env, pv2pages(txn_meta->mm_geo.shrink_pv));
arg->mi_geo.grow = pgno2bytes(env, pv2pages(txn_meta->mm_geo.grow_pv));
unsynced_pages =
atomic_load32(&env->me_lck->mti_unsynced_pages, mo_Relaxed) +
(atomic_load32(&env->me_lck->mti_meta_sync_txnid, mo_Relaxed) !=
(uint32_t)arg->mi_last_pgno);
arg->mi_mapsize = env->me_dxb_mmap.limit;
mdbx_compiler_barrier();
if (likely(arg->mi_meta0_txnid == mdbx_meta_txnid_fluid(env, meta0) &&
arg->mi_meta0_sign ==
unaligned_peek_u64(4, meta0->mm_datasync_sign) &&
arg->mi_meta1_txnid == mdbx_meta_txnid_fluid(env, meta1) &&
arg->mi_meta1_sign ==
unaligned_peek_u64(4, meta1->mm_datasync_sign) &&
arg->mi_meta2_txnid == mdbx_meta_txnid_fluid(env, meta2) &&
arg->mi_meta2_sign ==
unaligned_peek_u64(4, meta2->mm_datasync_sign) &&
recent_meta == mdbx_meta_head(env) &&
arg->mi_recent_txnid == mdbx_meta_txnid_fluid(env, recent_meta)))
break;
const MDBX_meta *const recent_meta = mdbx_meta_head(env);
arg->mi_recent_txnid = mdbx_meta_txnid_fluid(env, recent_meta);
arg->mi_meta0_txnid = mdbx_meta_txnid_fluid(env, meta0);
arg->mi_meta0_sign = unaligned_peek_u64(4, meta0->mm_datasync_sign);
arg->mi_meta1_txnid = mdbx_meta_txnid_fluid(env, meta1);
arg->mi_meta1_sign = unaligned_peek_u64(4, meta1->mm_datasync_sign);
arg->mi_meta2_txnid = mdbx_meta_txnid_fluid(env, meta2);
arg->mi_meta2_sign = unaligned_peek_u64(4, meta2->mm_datasync_sign);
if (likely(bytes > size_before_bootid)) {
memcpy(&arg->mi_bootid.meta0, &meta0->mm_bootid, 16);
memcpy(&arg->mi_bootid.meta1, &meta1->mm_bootid, 16);
memcpy(&arg->mi_bootid.meta2, &meta2->mm_bootid, 16);
}
const MDBX_meta *txn_meta = recent_meta;
arg->mi_last_pgno = txn_meta->mm_geo.next - 1;
arg->mi_geo.current = pgno2bytes(env, txn_meta->mm_geo.now);
if (txn) {
arg->mi_last_pgno = txn->mt_next_pgno - 1;
arg->mi_geo.current = pgno2bytes(env, txn->mt_end_pgno);
const txnid_t wanna_meta_txnid = (txn->mt_flags & MDBX_TXN_RDONLY)
? txn->mt_txnid
: txn->mt_txnid - xMDBX_TXNID_STEP;
txn_meta = (arg->mi_meta0_txnid == wanna_meta_txnid) ? meta0 : txn_meta;
txn_meta = (arg->mi_meta1_txnid == wanna_meta_txnid) ? meta1 : txn_meta;
txn_meta = (arg->mi_meta2_txnid == wanna_meta_txnid) ? meta2 : txn_meta;
}
arg->mi_geo.lower = pgno2bytes(env, txn_meta->mm_geo.lower);
arg->mi_geo.upper = pgno2bytes(env, txn_meta->mm_geo.upper);
arg->mi_geo.shrink = pgno2bytes(env, pv2pages(txn_meta->mm_geo.shrink_pv));
arg->mi_geo.grow = pgno2bytes(env, pv2pages(txn_meta->mm_geo.grow_pv));
const pgno_t unsynced_pages =
atomic_load32(&env->me_lck->mti_unsynced_pages, mo_Relaxed) +
(atomic_load32(&env->me_lck->mti_meta_sync_txnid, mo_Relaxed) !=
(uint32_t)arg->mi_last_pgno);
arg->mi_mapsize = env->me_dxb_mmap.limit;
const MDBX_lockinfo *const lck = env->me_lck;
arg->mi_maxreaders = env->me_maxreaders;
arg->mi_numreaders = env->me_lck_mmap.lck
@@ -19466,13 +19540,55 @@ __cold int mdbx_env_info_ex(const MDBX_env *env, const MDBX_txn *txn,
}
}
mdbx_compiler_barrier();
return MDBX_SUCCESS;
}
__cold int mdbx_env_info_ex(const MDBX_env *env, const MDBX_txn *txn,
MDBX_envinfo *arg, size_t bytes) {
if (unlikely((env == NULL && txn == NULL) || arg == NULL))
return MDBX_EINVAL;
if (txn) {
int err = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(err != MDBX_SUCCESS))
return err;
}
if (env) {
int err = check_env(env, false);
if (unlikely(err != MDBX_SUCCESS))
return err;
if (txn && unlikely(txn->mt_env != env))
return MDBX_EINVAL;
} else {
env = txn->mt_env;
}
const size_t size_before_bootid = offsetof(MDBX_envinfo, mi_bootid);
const size_t size_before_pgop_stat = offsetof(MDBX_envinfo, mi_pgop_stat);
if (unlikely(bytes != sizeof(MDBX_envinfo)) && bytes != size_before_bootid &&
bytes != size_before_pgop_stat)
return MDBX_EINVAL;
MDBX_envinfo snap;
int rc = fetch_envinfo_ex(env, txn, &snap, sizeof(snap));
if (unlikely(rc != MDBX_SUCCESS))
return rc;
while (1) {
rc = fetch_envinfo_ex(env, txn, arg, bytes);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
if (likely(memcmp(&snap, arg, bytes) == 0))
return MDBX_SUCCESS;
memcpy(&snap, arg, bytes);
}
}
static __inline MDBX_cmp_func *get_default_keycmp(unsigned flags) {
return (flags & MDBX_REVERSEKEY)
? cmp_reverse
: (flags & MDBX_INTEGERKEY) ? cmp_int_align2 : cmp_lexical;
return (flags & MDBX_REVERSEKEY) ? cmp_reverse
: (flags & MDBX_INTEGERKEY) ? cmp_int_align2
: cmp_lexical;
}
static __inline MDBX_cmp_func *get_default_datacmp(unsigned flags) {

View File

@@ -61,13 +61,26 @@
* Studio 2015 Update 3). But you could remove this #error and try to continue
* at your own risk. In such case please don't rise up an issues related ONLY to
* old compilers.
*
* NOTE:
* Unfortunately, there are several different builds of "Visual Studio" that
* are called "Visual Studio 2015 Update 3".
*
* The 190024234 is used here because it is minimal version of Visual Studio
* that was used for build and testing libmdbx in recent years. Soon this
* value will be increased to 19.0.24241.7, since build and testing using
* "Visual Studio 2015" will be performed only at https://ci.appveyor.com.
*
* Please ask Microsoft (but not us) for information about version differences
* and how to and where you can obtain the latest "Visual Studio 2015" build
* with all fixes.
*/
#error \
"At least \"Microsoft C/C++ Compiler\" version 19.00.24234 (Visual Studio 2015 Update 3) is required."
#endif
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#endif /* _CRT_SECURE_NO_WARNINGS */
#if _MSC_VER > 1800
#pragma warning(disable : 4464) /* relative include path contains '..' */
#endif
@@ -264,8 +277,6 @@ static __always_inline memory_order mo_c11_load(enum MDBX_memory_order fence) {
#ifndef __cplusplus
static __inline void mdbx_jitter4testing(bool tiny);
MDBX_MAYBE_UNUSED static __always_inline void
mdbx_memory_fence(enum MDBX_memory_order order, bool write) {
#ifdef MDBX_HAVE_C11ATOMICS
@@ -309,70 +320,6 @@ atomic_load32(const MDBX_atomic_uint32_t *p, enum MDBX_memory_order order) {
#endif /* MDBX_HAVE_C11ATOMICS */
}
MDBX_MAYBE_UNUSED static __always_inline uint64_t
atomic_store64(MDBX_atomic_uint64_t *p, const uint64_t value,
enum MDBX_memory_order order) {
STATIC_ASSERT(sizeof(MDBX_atomic_uint64_t) == 8);
#if MDBX_64BIT_ATOMIC
#ifdef MDBX_HAVE_C11ATOMICS
assert(atomic_is_lock_free(MDBX_c11a_rw(uint64_t, p)));
atomic_store_explicit(MDBX_c11a_rw(uint64_t, p), value, mo_c11_store(order));
#else /* MDBX_HAVE_C11ATOMICS */
if (order != mo_Relaxed)
mdbx_compiler_barrier();
p->weak = value;
mdbx_memory_fence(order, true);
#endif /* MDBX_HAVE_C11ATOMICS */
#else /* !MDBX_64BIT_ATOMIC */
mdbx_compiler_barrier();
atomic_store32(&p->low, (uint32_t)value, mo_Relaxed);
mdbx_jitter4testing(true);
atomic_store32(&p->high, (uint32_t)(value >> 32), order);
mdbx_jitter4testing(true);
#endif /* !MDBX_64BIT_ATOMIC */
return value;
}
MDBX_MAYBE_UNUSED static
#if MDBX_64BIT_ATOMIC
__always_inline
#endif /* MDBX_64BIT_ATOMIC */
uint64_t
atomic_load64(const MDBX_atomic_uint64_t *p,
enum MDBX_memory_order order) {
STATIC_ASSERT(sizeof(MDBX_atomic_uint64_t) == 8);
#if MDBX_64BIT_ATOMIC
#ifdef MDBX_HAVE_C11ATOMICS
assert(atomic_is_lock_free(MDBX_c11a_ro(uint64_t, p)));
return atomic_load_explicit(MDBX_c11a_ro(uint64_t, p), mo_c11_load(order));
#else /* MDBX_HAVE_C11ATOMICS */
mdbx_memory_fence(order, false);
const uint64_t value = p->weak;
if (order != mo_Relaxed)
mdbx_compiler_barrier();
return value;
#endif /* MDBX_HAVE_C11ATOMICS */
#else /* !MDBX_64BIT_ATOMIC */
mdbx_compiler_barrier();
uint64_t value = (uint64_t)atomic_load32(&p->high, order) << 32;
mdbx_jitter4testing(true);
value |= atomic_load32(&p->low, (order == mo_Relaxed) ? mo_Relaxed
: mo_AcquireRelease);
mdbx_jitter4testing(true);
for (;;) {
mdbx_compiler_barrier();
uint64_t again = (uint64_t)atomic_load32(&p->high, order) << 32;
mdbx_jitter4testing(true);
again |= atomic_load32(&p->low, (order == mo_Relaxed) ? mo_Relaxed
: mo_AcquireRelease);
mdbx_jitter4testing(true);
if (likely(value == again))
return value;
value = again;
}
#endif /* !MDBX_64BIT_ATOMIC */
}
#endif /* !__cplusplus */
/*----------------------------------------------------------------------------*/
@@ -384,7 +331,7 @@ MDBX_MAYBE_UNUSED static
#define MDBX_MAGIC UINT64_C(/* 56-bit prime */ 0x59659DBDEF4C11)
/* FROZEN: The version number for a database's datafile format. */
#define MDBX_DATA_VERSION 2
#define MDBX_DATA_VERSION 3
/* The version number for a database's lockfile format. */
#define MDBX_LOCK_VERSION 4
@@ -488,8 +435,6 @@ typedef struct MDBX_meta {
MDBX_db mm_dbs[CORE_DBS]; /* first is free space, 2nd is main db */
/* The size of pages used in this DB */
#define mm_psize mm_dbs[FREE_DBI].md_xsize
/* Any persistent environment flags, see mdbx_env */
#define mm_flags mm_dbs[FREE_DBI].md_flags
MDBX_canary mm_canary;
#define MDBX_DATASIGN_NONE 0u
@@ -789,7 +734,11 @@ typedef struct MDBX_lockinfo {
#define MDBX_DATA_MAGIC \
((MDBX_MAGIC << 8) + MDBX_PNL_ASCENDING * 64 + MDBX_DATA_VERSION)
#define MDBX_DATA_MAGIC_DEVEL ((MDBX_MAGIC << 8) + 255)
#define MDBX_DATA_MAGIC_LEGACY_COMPAT \
((MDBX_MAGIC << 8) + MDBX_PNL_ASCENDING * 64 + 2)
#define MDBX_DATA_MAGIC_LEGACY_DEVEL ((MDBX_MAGIC << 8) + 255)
#define MDBX_LOCK_MAGIC ((MDBX_MAGIC << 8) + MDBX_LOCK_VERSION)
@@ -1253,6 +1202,15 @@ extern uint8_t mdbx_runtime_flags;
extern uint8_t mdbx_loglevel;
extern MDBX_debug_func *mdbx_debug_logger;
MDBX_MAYBE_UNUSED static __inline void mdbx_jitter4testing(bool tiny) {
#if MDBX_DEBUG
if (MDBX_DBG_JITTER & mdbx_runtime_flags)
mdbx_osal_jitter(tiny);
#else
(void)tiny;
#endif
}
MDBX_INTERNAL_FUNC void MDBX_PRINTF_ARGS(4, 5)
mdbx_debug_log(int level, const char *function, int line, const char *fmt,
...) MDBX_PRINTF_ARGS(4, 5);
@@ -1422,15 +1380,6 @@ MDBX_INTERNAL_FUNC void mdbx_rthc_global_init(void);
MDBX_INTERNAL_FUNC void mdbx_rthc_global_dtor(void);
MDBX_INTERNAL_FUNC void mdbx_rthc_thread_dtor(void *ptr);
MDBX_MAYBE_UNUSED static __inline void mdbx_jitter4testing(bool tiny) {
#if MDBX_DEBUG
if (MDBX_DBG_JITTER & mdbx_runtime_flags)
mdbx_osal_jitter(tiny);
#else
(void)tiny;
#endif
}
#endif /* !__cplusplus */
#define MDBX_IS_ERROR(rc) \
@@ -1565,10 +1514,11 @@ typedef struct MDBX_node {
* | 1, a > b
* \
*/
#if 1
#ifndef __e2k__
/* LY: fast enough on most systems */
#define CMP2INT(a, b) (((b) > (a)) ? -1 : (a) > (b))
#else
/* LY: more parallelable on VLIW Elbrus */
#define CMP2INT(a, b) (((a) > (b)) - ((b) > (a)))
#endif

View File

@@ -5,9 +5,9 @@
// Non-inline part of the libmdbx C++ API (preliminary)
//
#ifdef _MSC_VER
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#endif /* _CRT_SECURE_NO_WARNINGS */
#include "../mdbx.h++"
@@ -992,7 +992,8 @@ bool from_base64::is_erroneous() const noexcept {
template class LIBMDBX_API_TYPE buffer<legacy_allocator>;
#if defined(__cpp_lib_memory_resource) && __cpp_lib_memory_resource >= 201603L
#if defined(__cpp_lib_memory_resource) && \
__cpp_lib_memory_resource >= 201603L && _GLIBCXX_USE_CXX11_ABI
template class LIBMDBX_API_TYPE buffer<polymorphic_allocator>;
#endif /* __cpp_lib_memory_resource >= 201603L */
@@ -1428,7 +1429,7 @@ __cold ::std::ostream &operator<<(::std::ostream &out,
}
const auto bytes = (it.bytes < 0) ? out << "-",
size_t(-it.bytes) : size_t(it.bytes);
size_t(-it.bytes) : size_t(it.bytes);
struct {
size_t one;
const char *suffix;

View File

@@ -1461,8 +1461,9 @@ MDBX_INTERNAL_FUNC int mdbx_mmap(const int flags, mdbx_mmap_t *map,
if (!NT_SUCCESS(err))
return ntstatus2errcode(err);
SIZE_T ViewSize =
(flags & MDBX_RDONLY) ? 0 : mdbx_RunningUnderWine() ? size : limit;
SIZE_T ViewSize = (flags & MDBX_RDONLY) ? 0
: mdbx_RunningUnderWine() ? size
: limit;
err = NtMapViewOfSection(
map->section, GetCurrentProcess(), &map->address,
/* ZeroBits */ 0,

View File

@@ -33,7 +33,7 @@
#if defined(_WIN32) || defined(_WIN64)
#if !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#endif /* _CRT_SECURE_NO_WARNINGS */
#if !defined(_NO_CRT_STDIO_INLINE) && MDBX_BUILD_SHARED_LIBRARY && \
!defined(xMDBX_TOOLS) && MDBX_WITHOUT_MSVC_CRT
#define _NO_CRT_STDIO_INLINE
@@ -114,11 +114,9 @@
#include <mach/mach_host.h>
#include <mach/mach_port.h>
#include <uuid/uuid.h>
#undef P_DIRTY
#endif
#if defined(__linux__) || defined(__gnu_linux__)
#include <linux/sysctl.h>
#include <sched.h>
#include <sys/sendfile.h>
#include <sys/statfs.h>
@@ -481,9 +479,10 @@ typedef union MDBX_srwlock {
} MDBX_srwlock;
#endif /* Windows */
#ifdef __cplusplus
extern void mdbx_osal_jitter(bool tiny);
#else
#ifndef __cplusplus
MDBX_MAYBE_UNUSED MDBX_INTERNAL_FUNC void mdbx_osal_jitter(bool tiny);
MDBX_MAYBE_UNUSED static __inline void mdbx_jitter4testing(bool tiny);
/*----------------------------------------------------------------------------*/
/* Atomics */
@@ -735,7 +734,6 @@ MDBX_MAYBE_UNUSED static __inline uintptr_t mdbx_thread_self(void) {
return (uintptr_t)thunk;
}
MDBX_MAYBE_UNUSED MDBX_INTERNAL_FUNC void mdbx_osal_jitter(bool tiny);
MDBX_INTERNAL_FUNC uint64_t mdbx_osal_monotime(void);
MDBX_INTERNAL_FUNC uint64_t
mdbx_osal_16dot16_to_monotime(uint32_t seconds_16dot16);

View File

@@ -23,7 +23,9 @@
#define _WIN32_WINNT 0x0601 /* Windows 7 */
#endif
#ifdef _MSC_VER
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif /* _CRT_SECURE_NO_WARNINGS */
#pragma warning(push, 1)
#pragma warning(disable : 4548) /* expression before comma has no effect; \
expected expression with side - effect */

View File

@@ -73,7 +73,12 @@ bool testcase_hill::run() {
uint64_t committed_serial = serial_count;
unsigned txn_nops = 0;
bool rc = false;
bool rc = speculum_verify();
if (!rc) {
log_notice("uphill: bailout before main loop");
goto bailout;
}
while (should_continue()) {
const keygen::serial_t a_serial = serial_count;
if (unlikely(!keyvalue_maker.increment(serial_count, 1))) {

View File

@@ -641,7 +641,8 @@ enum speculum_cursors : int {
prev = 1,
prev_prev = 2,
next = 3,
next_next = 4
next_next = 4,
seek_check = 5
};
bool testcase::is_same(const Item &a, const Item &b) const {
@@ -715,7 +716,7 @@ void testcase::speculum_check_cursor(const char *where, const char *stage,
// verbose(where, stage, cursor_key, cursor_data, cursor_err);
// verbose(where, stage, it);
if (cursor_err != MDBX_SUCCESS && cursor_err != MDBX_NOTFOUND &&
cursor_err != MDBX_RESULT_TRUE)
cursor_err != MDBX_RESULT_TRUE && cursor_err != MDBX_ENODATA)
failure("speculum-%s: %s %s %d %s", where, stage, "cursor-get", cursor_err,
mdbx_strerror(cursor_err));
@@ -852,11 +853,26 @@ int testcase::insert(const keygen::buffer &akey, const keygen::buffer &adata,
int err;
bool rc = true;
Item item;
#if SPECULUM_CURSORS
MDBX_cursor *check_seek_cursor = nullptr;
MDBX_val seek_check_key, seek_check_data;
int seek_check_err = 42;
#endif /* SPECULUM_CURSORS */
if (config.params.speculum) {
item.first = iov2dataview(akey);
item.second = iov2dataview(adata);
#if SPECULUM_CURSORS
speculum_prepare_cursors(item);
check_seek_cursor = speculum_cursors[seek_check].get();
seek_check_key = akey->value;
seek_check_data = adata->value;
seek_check_err = mdbx_cursor_get(
check_seek_cursor, &seek_check_key, &seek_check_data,
(config.params.table_flags & MDBX_DUPSORT) ? MDBX_GET_BOTH
: MDBX_SET_KEY);
if (seek_check_err != MDBX_SUCCESS && seek_check_err != MDBX_NOTFOUND)
failure("speculum-%s: %s pre-insert %d %s", "insert", "seek",
seek_check_err, mdbx_strerror(seek_check_err));
#endif /* SPECULUM_CURSORS */
}
@@ -881,6 +897,26 @@ int testcase::insert(const keygen::buffer &akey, const keygen::buffer &adata,
}
#if SPECULUM_CURSORS
if (insertion_result.second) {
if (seek_check_err != MDBX_NOTFOUND) {
log_error(
"speculum.pre-insert-seek: unexpected %d {%s, %s}", seek_check_err,
mdbx_dump_val(&seek_check_key, dump_key, sizeof(dump_key)),
mdbx_dump_val(&seek_check_data, dump_value, sizeof(dump_value)));
rc = false;
}
} else {
if (seek_check_err != MDBX_SUCCESS) {
log_error(
"speculum.pre-insert-seek: unexpected %d {%s, %s}", seek_check_err,
mdbx_dump_val(&seek_check_key, dump_key, sizeof(dump_key)),
mdbx_dump_val(&seek_check_data, dump_value, sizeof(dump_value)));
speculum_check_iterator("insert", "pre-seek", insertion_result.first,
seek_check_key, seek_check_data);
rc = false;
}
}
if (insertion_result.first != speculum.begin()) {
const auto cursor_prev = speculum_cursors[prev].get();
auto it_prev = insertion_result.first;
@@ -1050,6 +1086,15 @@ bool testcase::speculum_verify() {
MDBX_val akey, avalue;
MDBX_val mkey, mvalue;
err = mdbx_cursor_get(cursor, &akey, &avalue, MDBX_FIRST);
if (err == MDBX_NOTFOUND) {
err = mdbx_cursor_get(cursor, &akey, &avalue, MDBX_GET_CURRENT);
if (err == MDBX_ENODATA)
err = MDBX_NOTFOUND;
else {
log_error("unexpected %d for MDBX_GET_CURRENT on empty DB", err);
rc = false;
}
}
unsigned extra = 0, lost = 0, n = 0;
assert(std::is_sorted(speculum.cbegin(), speculum.cend(), ItemCompare(this)));
@@ -1128,6 +1173,13 @@ bool testcase::speculum_verify() {
log_error("false-negative cursor-eof: %u, rc %i", n, eof);
rc = false;
}
err = mdbx_cursor_get(cursor, &akey, &avalue, MDBX_GET_CURRENT);
if (err == MDBX_SUCCESS)
err = mdbx_cursor_get(cursor, &akey, &avalue, MDBX_NEXT);
if (err != MDBX_ENODATA) {
log_error("unexpected %d for MDBX_GET_CURRENT at EOF", err);
rc = false;
}
}
mdbx_cursor_close(cursor);
return rc;

View File

@@ -202,7 +202,7 @@ protected:
#define SPECULUM_CURSORS 1
#endif /* SPECULUM_CURSORS */
#if SPECULUM_CURSORS
scoped_cursor_guard speculum_cursors[5];
scoped_cursor_guard speculum_cursors[5 + 1];
void speculum_prepare_cursors(const Item &item);
void speculum_check_cursor(const char *where, const char *stage,
const testcase::SET::const_iterator &it,