summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/libmdbx/src/.circleci/config.yml2
-rw-r--r--libs/libmdbx/src/.cirrus.yml6
-rw-r--r--libs/libmdbx/src/.github/workflows/release-assets.yml54
-rw-r--r--libs/libmdbx/src/.travis.yml101
-rw-r--r--libs/libmdbx/src/CMakeLists.txt139
-rw-r--r--libs/libmdbx/src/ChangeLog.md113
-rw-r--r--libs/libmdbx/src/GNUmakefile99
-rw-r--r--libs/libmdbx/src/Makefile2
-rw-r--r--libs/libmdbx/src/README.md12
-rw-r--r--libs/libmdbx/src/appveyor.yml2
-rw-r--r--libs/libmdbx/src/cmake/compiler.cmake156
-rw-r--r--libs/libmdbx/src/cmake/utils.cmake83
-rw-r--r--libs/libmdbx/src/config.h11
-rw-r--r--libs/libmdbx/src/mdbx.h126
-rw-r--r--libs/libmdbx/src/src/config.h.in1
-rw-r--r--libs/libmdbx/src/src/core.c1604
-rw-r--r--libs/libmdbx/src/src/internals.h80
-rw-r--r--libs/libmdbx/src/src/lck-posix.c4
-rw-r--r--libs/libmdbx/src/src/lck-windows.c136
-rw-r--r--libs/libmdbx/src/src/man1/mdbx_chk.12
-rw-r--r--libs/libmdbx/src/src/man1/mdbx_copy.12
-rw-r--r--libs/libmdbx/src/src/man1/mdbx_dump.12
-rw-r--r--libs/libmdbx/src/src/man1/mdbx_load.12
-rw-r--r--libs/libmdbx/src/src/man1/mdbx_stat.12
-rw-r--r--libs/libmdbx/src/src/mdbx_chk.c115
-rw-r--r--libs/libmdbx/src/src/mdbx_copy.c3
-rw-r--r--libs/libmdbx/src/src/mdbx_dump.c282
-rw-r--r--libs/libmdbx/src/src/mdbx_load.c660
-rw-r--r--libs/libmdbx/src/src/mdbx_stat.c185
-rw-r--r--libs/libmdbx/src/src/options.h5
-rw-r--r--libs/libmdbx/src/src/osal.c122
-rw-r--r--libs/libmdbx/src/src/osal.h23
-rw-r--r--libs/libmdbx/src/test/config.cc22
-rw-r--r--libs/libmdbx/src/test/config.h118
-rw-r--r--libs/libmdbx/src/test/dump-load.sh40
-rw-r--r--libs/libmdbx/src/test/hill.cc41
-rw-r--r--libs/libmdbx/src/test/keygen.cc32
-rw-r--r--libs/libmdbx/src/test/keygen.h21
-rw-r--r--libs/libmdbx/src/test/long_stochastic.sh321
-rw-r--r--libs/libmdbx/src/test/main.cc21
-rw-r--r--libs/libmdbx/src/test/nested.cc130
-rw-r--r--libs/libmdbx/src/test/osal-unix.cc4
-rw-r--r--libs/libmdbx/src/test/test.cc38
-rw-r--r--libs/libmdbx/src/test/test.h59
-rw-r--r--libs/libmdbx/src/test/ttl.cc224
-rw-r--r--libs/libmdbx/src/test/utils.cc14
-rw-r--r--libs/libmdbx/src/test/utils.h4
-rw-r--r--libs/libmdbx/src/test/valgrind_suppress.txt518
-rw-r--r--libs/libmdbx/src/version.c12
49 files changed, 3928 insertions, 1827 deletions
diff --git a/libs/libmdbx/src/.circleci/config.yml b/libs/libmdbx/src/.circleci/config.yml
index 91e11a4bfe..6d46ecefd3 100644
--- a/libs/libmdbx/src/.circleci/config.yml
+++ b/libs/libmdbx/src/.circleci/config.yml
@@ -2,7 +2,7 @@ version: 2
jobs:
build:
docker:
- - image: circleci/buildpack-deps:artful
+ - image: circleci/buildpack-deps:20.04
environment:
- TESTDB: /tmp/test.db
- TESTLOG: /tmp/test.log
diff --git a/libs/libmdbx/src/.cirrus.yml b/libs/libmdbx/src/.cirrus.yml
new file mode 100644
index 0000000000..c08e316c37
--- /dev/null
+++ b/libs/libmdbx/src/.cirrus.yml
@@ -0,0 +1,6 @@
+freebsd_instance:
+ image_family: freebsd-12-1-snap
+
+task:
+ install_script: pkg install -y gmake bash git
+ script: git fetch --tags && gmake check
diff --git a/libs/libmdbx/src/.github/workflows/release-assets.yml b/libs/libmdbx/src/.github/workflows/release-assets.yml
new file mode 100644
index 0000000000..934ab93b39
--- /dev/null
+++ b/libs/libmdbx/src/.github/workflows/release-assets.yml
@@ -0,0 +1,54 @@
+# Based on the https://github.com/actions/upload-release-asset example
+
+on:
+ push:
+ # Sequence of patterns matched against refs/tags
+ tags:
+ - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
+
+name: Upload Release Asset
+
+jobs:
+ build:
+ name: Upload Release Asset
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+ - name: Build assets
+ run: |
+ make release-assets
+ - id: name
+ run: |
+ echo "::set-output name=tarball::$(ls *.tar.gz)"
+ echo "::set-output name=zip::$(ls *.zip)"
+ - name: Create Release
+ id: create_release
+ uses: actions/create-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: ${{ github.ref }}
+ release_name: Release ${{ github.ref }}
+ draft: true
+ prerelease: true
+ - name: Upload tarball
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ${{ steps.name.outputs.tarball }}
+ asset_name: amalgamated.tar.gz
+ # asset_label: Amalgamated source tarball
+ asset_content_type: application/tar+gzip
+ - name: Upload zip
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ${{ steps.name.outputs.zip }}
+ asset_name: amalgamated.zip
+ # asset_label: Amalgamated source zip-archive
+ asset_content_type: application/zip
diff --git a/libs/libmdbx/src/.travis.yml b/libs/libmdbx/src/.travis.yml
index c1bd6da5e9..e736d01341 100644
--- a/libs/libmdbx/src/.travis.yml
+++ b/libs/libmdbx/src/.travis.yml
@@ -1,61 +1,88 @@
language: c cpp
+sudo: false
+
+env:
+ global:
+ - secure: "M+W+heGGyRQJoBq2W0uqWVrpL4KBXmL0MFL7FSs7f9vmAaDyEgziUXeZRj3GOKzW4kTef3LpIeiu9SmvqSMoQivGGiomZShqPVl045o/OUgRCAT7Al1RLzEZ0efSHpIPf0PZ6byEf6GR2ML76OfuL6JxTVdnz8iVyO2sgLE1HbX1VeB+wgd/jfMeOBhCCXskfK6MLyZihfMYsiYZYSaV98ZDhDLSlzuuRIgzb0bMi8aL6AErs0WLW0NelRBeHkKPYfAUc85pdQHscgrJw6Rh/zT6+8BQ/q5f4IgWhiu4xoRg3Ngl7SNoedRQh93ADM3UG2iGl6HDFpVORaXcFWKAtuYY+kHQ0HB84BRYpQmeBuXNpltsfxQ3d1Q3u0RlE45zRvmr2+X1mFnkcNUAWISLPbsOUlriDQM8irGwRpho77/uYnRC00bJsHW//s6+uPf9zrAw1nI4f0y3PAWukGF/xs6HAI3FZPsuSSnx18Tj3Opgbc9Spop+V3hkhdiJoPGpNKTkFX4ZRXfkPgoRVJmtp4PpbpH0Ps/mCriKjMEfGGi0HcVCi0pEGLXiecdqJ5KPg5+22zNycEujQBJcNTKd9shN+R3glrbmhAxTEzGdGwxXXJ2ybwJ2PWJLMYZ7g98nLyX+uQPaA3BlsbYJHNeS5283/9pJsd9DzfHKsN2nFSc="
+
+addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - cmake
+ - clang-format
+ update: true
matrix:
include:
- os: linux
- dist: trusty
+ dist: focal
+ compiler: gcc
env: CC=cc CXX=c++
- os: linux
- dist: trusty
+ dist: focal
compiler: clang
env: CC=clang CXX=clang++
- os: linux
- dist: xenial
+ dist: bionic
compiler: gcc
- env: CC=gcc CXX=g++
+ env: CC=cc CXX=c++
- os: linux
dist: bionic
compiler: clang
env: CC=clang CXX=clang++
+ - os: linux
+ dist: xenial
+ compiler: gcc
+ env: CC=cc CXX=c++
+ - os: linux
+ dist: xenial
+ compiler: clang
+ env: CC=clang CXX=clang++
- os: osx
- osx_image: xcode11
+ osx_image: xcode11.3
env: CC=cc CXX=c++
- os: osx
osx_image: xcode9.4
env: CC=cc CXX=c++
-script: >
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then
- git fetch --unshallow --tags --prune &&
- git submodule foreach --recursive git fetch --unshallow --tags --prune &&
- (if which clang-format-6.0 > /dev/null && make reformat && [[ -n $(git diff) ]];
- then
- echo "You must run 'make reformat' before submitting a pull request";
- echo "";
- git diff;
- exit -1;
- fi) &&
+before_script: |
+ if [ "${TRAVIS_BRANCH}" = "coverity_scan" ]; then
+ # call Coverity Scan manually of addons.coverity_scan for first job only
+ if [ "${TRAVIS_JOB_NUMBER}" = "${TRAVIS_BUILD_NUMBER}.1" ]; then
+ export COVERITY_SCAN_BRANCH=1
+ echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-
+ curl -s 'https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh' -o coverity_scan.sh
+ else
+ echo 'echo "Skip CoverityScan for unrelated os/compiler"' > coverity_scan.sh
+ fi
+ fi
+
+script: |
+ ${CC} --version
+ ${CXX} --version
+ git fetch --unshallow --tags --prune || exit 1
+ if [ ! -s ./coverity_scan.sh ]; then
make --keep-going all && MALLOC_CHECK_=7 MALLOC_PERTURB_=42 make --keep-going check
else
- [ ! -s cov-int/scm_log.txt ] || cat cov-int/scm_log.txt;
- fi && sleep 3
+ COVERITY_SCAN_PROJECT_NAME="ReOpen/libmdbx" \
+ COVERITY_SCAN_NOTIFICATION_EMAIL="leo@yuriev.ru" \
+ COVERITY_SCAN_BUILD_COMMAND_PREPEND="" \
+ COVERITY_SCAN_BUILD_COMMAND="make MDBX_OPTIONS=-DMDBX_DEBUG=2 build-test" \
+ COVERITY_SCAN_BRANCH_PATTERN="$TRAVIS_BRANCH" \
+ bash ./coverity_scan.sh || cat cov-int/scm_log.txt
+ fi
-env:
- global:
- - secure: "M+W+heGGyRQJoBq2W0uqWVrpL4KBXmL0MFL7FSs7f9vmAaDyEgziUXeZRj3GOKzW4kTef3LpIeiu9SmvqSMoQivGGiomZShqPVl045o/OUgRCAT7Al1RLzEZ0efSHpIPf0PZ6byEf6GR2ML76OfuL6JxTVdnz8iVyO2sgLE1HbX1VeB+wgd/jfMeOBhCCXskfK6MLyZihfMYsiYZYSaV98ZDhDLSlzuuRIgzb0bMi8aL6AErs0WLW0NelRBeHkKPYfAUc85pdQHscgrJw6Rh/zT6+8BQ/q5f4IgWhiu4xoRg3Ngl7SNoedRQh93ADM3UG2iGl6HDFpVORaXcFWKAtuYY+kHQ0HB84BRYpQmeBuXNpltsfxQ3d1Q3u0RlE45zRvmr2+X1mFnkcNUAWISLPbsOUlriDQM8irGwRpho77/uYnRC00bJsHW//s6+uPf9zrAw1nI4f0y3PAWukGF/xs6HAI3FZPsuSSnx18Tj3Opgbc9Spop+V3hkhdiJoPGpNKTkFX4ZRXfkPgoRVJmtp4PpbpH0Ps/mCriKjMEfGGi0HcVCi0pEGLXiecdqJ5KPg5+22zNycEujQBJcNTKd9shN+R3glrbmhAxTEzGdGwxXXJ2ybwJ2PWJLMYZ7g98nLyX+uQPaA3BlsbYJHNeS5283/9pJsd9DzfHKsN2nFSc="
-
-before_install:
- - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-
- - ${CC} --version
- - ${CXX} --version
-
-addons:
- coverity_scan:
- project:
- name: "ReOpen/libmdbx"
- version: 0.1
- description: "Build submitted via Travis CI"
- notification_email: leo@yuriev.ru
- build_command_prepend: "git fetch --unshallow --tags --prune && make dist"
- build_command: "make MDBX_OPTIONS=-DMDBX_DEBUG=2 -C dist all"
- branch_pattern: coverity_scan
+after_script: |
+ if [ "${TRAVIS_BRANCH}" != "coverity_scan" -a "${TRAVIS_JOB_NUMBER}" = "${TRAVIS_BUILD_NUMBER}.1" ] && make reformat && [[ -n $(git diff) ]]; then
+ echo "You must run 'make reformat' before submitting a pull request"
+ echo "-------------------------------------------------------------------------------"
+ git diff
+ sleep 1
+ echo "-------------------------------------------------------------------------------"
+ sleep 1
+ exit -1
+ fi
+ echo "-------------------------------------------------------------------------------"
+ sleep 1
diff --git a/libs/libmdbx/src/CMakeLists.txt b/libs/libmdbx/src/CMakeLists.txt
index a17bd35ab6..40355c27d3 100644
--- a/libs/libmdbx/src/CMakeLists.txt
+++ b/libs/libmdbx/src/CMakeLists.txt
@@ -38,6 +38,9 @@ cmake_minimum_required(VERSION 3.8.2)
cmake_policy(PUSH)
cmake_policy(VERSION 3.8.2)
+if(NOT CMAKE_VERSION VERSION_LESS 3.15)
+ cmake_policy(SET CMP0091 NEW)
+endif()
if(NOT CMAKE_VERSION VERSION_LESS 3.13)
cmake_policy(SET CMP0077 NEW)
endif()
@@ -253,17 +256,23 @@ else()
add_custom_target(ctags DEPENDS tags)
endif(CTAGS)
- # Enable 'make reformat' target.
- find_program(CLANG_FORMAT
- NAMES clang-format-11.0 clang-format-10.0 clang-format-9.0 clang-format-8.0 clang-format)
- if(CLANG_FORMAT AND UNIX)
- add_custom_target(reformat
- VERBATIM
- COMMAND
- git ls-files |
- grep -E \\.\(c|cxx|cc|cpp|h|hxx|hpp\)\(\\.in\)?\$ |
- xargs ${CLANG_FORMAT} -i --style=file
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+ if(UNIX)
+ find_program(CLANG_FORMAT
+ NAMES clang-format-12 clang-format-11 clang-format-10 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)
+ # Enable 'make reformat' target.
+ add_custom_target(reformat
+ VERBATIM
+ COMMAND
+ git ls-files |
+ grep -E \\.\(c|cxx|cc|cpp|h|hxx|hpp\)\(\\.in\)?\$ |
+ xargs ${CLANG_FORMAT} -i --style=file
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
+ endif()
+ endif()
endif()
if(NOT "${PROJECT_BINARY_DIR}" STREQUAL "${PROJECT_SOURCE_DIR}")
@@ -369,6 +378,7 @@ add_mdbx_option(MDBX_BUILD_SHARED_LIBRARY "Build libmdbx as shared library (DLL)
add_mdbx_option(MDBX_BUILD_TOOLS "Build MDBX tools (mdbx_chk/stat/dump/load/copy)" ${MDBX_BUILD_TOOLS_DEFAULT})
add_mdbx_option(MDBX_TXN_CHECKOWNER "Checking transaction matches the calling thread inside libmdbx's API" ON)
add_mdbx_option(MDBX_TXN_CHECKPID "Paranoid checking PID inside libmdbx's API" AUTO)
+add_mdbx_option(MDBX_HUGE_TRANSACTIONS "Support for huge write-transactions" OFF)
mark_as_advanced(MDBX_TXN_CHECKPID)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
add_mdbx_option(MDBX_DISABLE_GNU_SOURCE "Don't use GNU/Linux libc extensions" OFF)
@@ -410,7 +420,7 @@ endif()
################################################################################
# Get version
-fetch_version(MDBX "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" FALSE)
+fetch_version(MDBX "${CMAKE_CURRENT_SOURCE_DIR}" FALSE)
message(STATUS "libmdbx version is ${MDBX_VERSION}")
# sources list
@@ -459,7 +469,7 @@ macro(libmdbx_setup_libs TARGET MODE)
target_link_libraries(${TARGET} ${MODE} ${CMAKE_THREAD_LIBS_INIT})
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
target_link_libraries(${TARGET} ${MODE} ntdll.lib)
- if(MDBX_NTDLL_EXTRA_IMPLIB)
+ if(MDBX_NTDLL_EXTRA_IMPLIB AND MDBX_AVOID_CRT)
target_link_libraries(${TARGET} ${MODE} ntdll_extra)
endif()
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "SunOS" OR ${CMAKE_SYSTEM_NAME} STREQUAL "Solaris")
@@ -498,6 +508,13 @@ if(MDBX_BUILD_SHARED_LIBRARY)
target_compile_definitions(mdbx PRIVATE LIBMDBX_EXPORTS MDBX_BUILD_SHARED_LIBRARY=1 INTERFACE LIBMDBX_IMPORTS)
target_setup_options(mdbx)
libmdbx_setup_libs(mdbx PRIVATE)
+ if(MSVC)
+ if(MDBX_NTDLL_EXTRA_IMPLIB AND MDBX_AVOID_CRT)
+ set_property(TARGET mdbx PROPERTY LINKER_FLAGS "/NODEFAULTLIB")
+ else()
+ set_property(TARGET mdbx PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
+ endif()
+ endif()
if(CC_HAS_VISIBILITY AND (LTO_ENABLED OR INTERPROCEDURAL_OPTIMIZATION))
set_target_properties(mdbx PROPERTIES LINK_FLAGS "-fvisibility=hidden")
endif()
@@ -557,45 +574,21 @@ endif()
if(MDBX_BUILD_SHARED_LIBRARY)
if(CMAKE_VERSION VERSION_LESS 3.12)
install(TARGETS mdbx EXPORT libmdbx
- RUNTIME
- DESTINATION bin
- COMPONENT runtime
- LIBRARY
- DESTINATION lib
- COMPONENT runtime
- OBJECTS
- DESTINATION lib
- COMPONENT devel
- ARCHIVE
- DESTINATION lib
- COMPONENT devel
- PUBLIC_HEADER
- DESTINATION include
- COMPONENT devel
- INCLUDES
- DESTINATION include
- COMPONENT devel)
+ RUNTIME DESTINATION bin COMPONENT runtime
+ LIBRARY DESTINATION bin COMPONENT runtime
+ OBJECTS DESTINATION lib COMPONENT devel
+ ARCHIVE DESTINATION lib COMPONENT devel
+ PUBLIC_HEADER DESTINATION include COMPONENT devel
+ INCLUDES DESTINATION include COMPONENT devel)
else()
install(TARGETS mdbx EXPORT libmdbx
- RUNTIME
- DESTINATION bin
- COMPONENT runtime
- LIBRARY
- DESTINATION lib
- COMPONENT runtime
+ RUNTIME DESTINATION bin COMPONENT runtime
+ LIBRARY DESTINATION bin COMPONENT runtime
NAMELINK_COMPONENT devel
- OBJECTS
- DESTINATION lib
- COMPONENT devel
- ARCHIVE
- DESTINATION lib
- COMPONENT devel
- PUBLIC_HEADER
- DESTINATION include
- COMPONENT devel
- INCLUDES
- DESTINATION include
- COMPONENT devel)
+ OBJECTS DESTINATION lib COMPONENT devel
+ ARCHIVE DESTINATION lib COMPONENT devel
+ PUBLIC_HEADER DESTINATION include COMPONENT devel
+ INCLUDES DESTINATION include COMPONENT devel)
endif()
endif(MDBX_BUILD_SHARED_LIBRARY)
@@ -626,45 +619,21 @@ endif(MDBX_BUILD_TOOLS)
if(MDBX_INSTALL_STATIC)
if(CMAKE_VERSION VERSION_LESS 3.12)
install(TARGETS mdbx-static EXPORT libmdbx
- RUNTIME
- DESTINATION bin
- COMPONENT runtime
- LIBRARY
- DESTINATION lib
- COMPONENT runtime
- OBJECTS
- DESTINATION lib
- COMPONENT devel
- ARCHIVE
- DESTINATION lib
- COMPONENT devel
- PUBLIC_HEADER
- DESTINATION include
- COMPONENT devel
- INCLUDES
- DESTINATION include
- COMPONENT devel)
+ RUNTIME DESTINATION bin COMPONENT runtime
+ LIBRARY DESTINATION bin COMPONENT runtime
+ OBJECTS DESTINATION lib COMPONENT devel
+ ARCHIVE DESTINATION lib COMPONENT devel
+ PUBLIC_HEADER DESTINATION include COMPONENT devel
+ INCLUDES DESTINATION include COMPONENT devel)
else()
install(TARGETS mdbx-static EXPORT libmdbx
- RUNTIME
- DESTINATION bin
- COMPONENT runtime
- LIBRARY
- DESTINATION lib
- COMPONENT runtime
+ RUNTIME DESTINATION bin COMPONENT runtime
+ LIBRARY DESTINATION bin COMPONENT runtime
NAMELINK_COMPONENT devel
- OBJECTS
- DESTINATION lib
- COMPONENT devel
- ARCHIVE
- DESTINATION lib
- COMPONENT devel
- PUBLIC_HEADER
- DESTINATION include
- COMPONENT devel
- INCLUDES
- DESTINATION include
- COMPONENT devel)
+ OBJECTS DESTINATION lib COMPONENT devel
+ ARCHIVE DESTINATION lib COMPONENT devel
+ PUBLIC_HEADER DESTINATION include COMPONENT devel
+ INCLUDES DESTINATION include COMPONENT devel)
endif()
endif(MDBX_INSTALL_STATIC)
diff --git a/libs/libmdbx/src/ChangeLog.md b/libs/libmdbx/src/ChangeLog.md
new file mode 100644
index 0000000000..1c4beb635b
--- /dev/null
+++ b/libs/libmdbx/src/ChangeLog.md
@@ -0,0 +1,113 @@
+v0.9.x (in the development):
+ - TODO: Native bindings for C++.
+ - TODO: Packages for AltLinux, Fedora/RHEL, Debian/Ubuntu.
+
+v0.8.1 2020-06-12:
+ - Minor change versioning. The last number in version now mean the number of commits since last release/tag.
+ - Provide ChangeLog file.
+ - Fix for using libmdbx as C-only sub-project with CMake.
+ - Fix `mdbx_env_set_geometry()` for case it called from opened environment outside of write transaction.
+ - Add support for huge transactions and `MDBX_HUGE_TRANSACTIONS` build-option (default `OFF`).
+ - Refine LTO (link time optimization) for clang.
+ - Force enabling exceptions handling for MSVC (`/EHsc` option).
+
+v0.8.0 2020-06-05:
+ - Support for Android/Bionic.
+ - Support for iOS.
+ - Auto-handling `MDBX_NOSUBDIR` while opening for any exists database.
+ - Engage github-actions to make release-assets.
+ - Clarify API description.
+ - Extended keygen-cases in stochastic test.
+ - Fix fetching of first/lower key from LEAF2-page during page merge.
+ - Fix missing comma in array of error messages.
+ - Fix div-by-zero while copy-with-compaction for non-resizeable environment.
+ - Fixes & enhancements for custom-comparators.
+ - Fix `MDBX_AVOID_CRT` option and missing `ntdll.def`.
+ - Fix `mdbx_env_close()` to work correctly called concurrently from several threads.
+ - Fix null-deref in an ASAN-enabled builds while opening environment with error and/or read-only.
+ - Fix AddressSanitizer errors after closing environment.
+ - Fix/workaround to avoid GCC 10.x pedantic warnings.
+ - Fix using `ENODATA` for FreeBSD.
+ - Avoid invalidation of DBI-handle(s) when it just closing.
+ - Avoid using `pwritev()` for single-writes (up to 10% speedup for some kernels & scenarios).
+ - Avoiding `MDBX_UTTERLY_NOSYNC` as result of flags merge.
+ - Add `mdbx_dbi_dupsort_depthmask()` function.
+ - Add `MDBX_CP_FORCE_RESIZEABLE` option.
+ - Add deprecated `MDBX_MAP_RESIZED` for compatibility.
+ - Add `MDBX_BUILD_TOOLS` option (default `ON`).
+ - Refine `mdbx_dbi_open_ex()` to safe concurrently opening the same handle from difference threads.
+ - Truncate clk-file during environment closing. So zero-length lck file indicates that environment was closed properly.
+ - Refine `mdbx_update_gc()` for huge transactions with small sizes of database page.
+ - Extends dump/load to support all MDBX attributes.
+ - Avoid upsertion the same key-value data, fix related assertions.
+ - Rework min/max length checking for keys & values.
+ - Checking the order of keys on all pages during checking.
+ - Support `CFLAGS_EXTRA` make-option for convenience.
+ - Preserve last txnid while copy with compactification.
+ - Auto-reset running transaction in mdbx_txn_renew().
+ - Automatically abort errored transaction in mdbx_txn_commit().
+ - Auto-choose pagesize for a large databases.
+ - Rearrange source files, rework build, options-support by CMake.
+ - Crutch for WSL1 (Windows subsystem for Linux).
+ - Refine install/uninstall targets.
+ - Support for Valgrind 3.14 and later.
+ - Add check-analyzer check-ubsan check-asan check-leak targets to Makefile.
+ - Minor fix/workaround to avoid UBSAN traps for `memcpy(ptr, NULL, 0)`.
+ - Avoid some GCC-analyzer false-positive warnings.
+
+v0.7.0 2020-03-18:
+ - Workarounds for Wine (Windows compatibility layer for Linux).
+ - `MDBX_MAP_RESIZED` renamed to `MDBX_UNABLE_EXTEND_MAPSIZE`.
+ - Clarify API description, fix typos.
+ - Speedup runtime checks in debug/checked builds.
+ - Added checking for read/write transactions overlapping for the same thread, added `MDBX_TXN_OVERLAPPING` error and `MDBX_DBG_LEGACY_OVERLAP` option.
+ - Added `mdbx_key_from_jsonInteger()`, `mdbx_key_from_double()`, `mdbx_key_from_float()`, `mdbx_key_from_int64()` and `mdbx_key_from_int32()` functions. See `mdbx.h` for description.
+ - Fix compatibility (use zero for invalid DBI).
+ - Refine/clarify error messages.
+ - Avoids extra error messages "bad txn" from mdbx_chk when DB is corrupted.
+
+v0.6.0 2020-01-21:
+ - Fix `mdbx_load` utility for custom comparators.
+ - Fix checks related to `MDBX_APPEND` flag inside `mdbx_cursor_put()`.
+ - Refine/fix dbi_bind() internals.
+ - Refine/fix handling STATUS_CONFLICTING_ADDRESSES.
+ - Rework `MDBX_DBG_DUMP` option to avoid disk I/O performance degradation.
+ - Add built-in help to test tool.
+ - Fix `mdbx_env_set_geometry()` for large page size.
+ - Fix env_set_geometry() for large pagesize.
+ - Clarify API description & comments, fix typos.
+
+v0.5.0 2019-12-31:
+ - Fix returning MDBX_RESULT_TRUE from page_alloc().
+ - Fix false-positive ASAN issue.
+ - Fix assertion for `MDBX_NOTLS` option.
+ - Rework MADV_DONTNEED threshold.
+ - Fix `mdbx_chk` utility for don't checking some numbers if walking of B-tree was disabled.
+ - Use page's mp_txnid for basic integrity checking.
+ - Add MDBX_FORCE_ASSERTIONS built-time option.
+ - Rework MDBX_DBG_DUMP to avoid performance degradation.
+ - Rename MDBX_NOSYNC to MDBX_SAFE_NOSYNC for clarity.
+ - Interpret `ERROR_ACCESS_DENIED` from `OpenProcess()` as 'process exists'.
+ - Avoid using FILE_FLAG_NO_BUFFERING for compatibility with small database pages.
+ - Added install section for CMake.
+
+v0.4.0 2019-12-02:
+ - Support for Mac OSX, FreeBSD, NetBSD, OpenBSD, DragonFly BSD, OpenSolaris, OpenIndiana (AIX and HP-UX pending).
+ - Use bootid for decisions of rollback.
+ - Counting retired pages and extended transaction info.
+ - Add MDBX_ACCEDE flag for database opening.
+ - Using OFD-locks and tracking for in-process multi-opening.
+ - Hot backup into pipe.
+ - Support for cmake & amalgamated sources.
+ - Fastest internal sort implementation.
+ - New internal dirty-list implementation with lazy sorting.
+ - Support for lazy-sync-to-disk with polling.
+ - Extended key length.
+ - Last update transaction number for each sub-database.
+ - Automatic read ahead enabling/disabling.
+ - More auto-compactification.
+ - Using -fsanitize=undefined and -Wpedantic options.
+ - Rework page merging.
+ - Nested transactions.
+ - API description.
+ - Checking for non-local filesystems to avoid DB corruption.
diff --git a/libs/libmdbx/src/GNUmakefile b/libs/libmdbx/src/GNUmakefile
index 6aeb1f92ee..22a1c7874d 100644
--- a/libs/libmdbx/src/GNUmakefile
+++ b/libs/libmdbx/src/GNUmakefile
@@ -26,7 +26,7 @@ CFLAGS ?= -O2 -g -Wall -Werror -Wextra -Wpedantic -ffunction-sections -fPIC -fv
# -Wno-tautological-compare
# HINT: Try append '--no-as-needed,-lrt' for ability to built with modern glibc, but then run with the old.
-LIBS ?= $(shell uname | grep -qi SunOS && echo "-lkstat") $(shell uname | grep -qi -e Darwin -e OpenBSD || echo "-lrt")
+LIBS ?= $(shell uname | grep -qi SunOS && echo "-lkstat") $(shell uname | grep -qi -e Darwin -e OpenBSD || echo "-lrt") $(shell uname | grep -qi Windows && echo "-lntdll")
LDFLAGS ?= $(shell $(LD) --help 2>/dev/null | grep -q -- --gc-sections && echo '-Wl,--gc-sections,-z,relro,-O1')$(shell $(LD) --help 2>/dev/null | grep -q -- -dead_strip && echo '-Wl,-dead_strip')
EXE_LDFLAGS ?= -pthread
@@ -113,64 +113,91 @@ define uname2titer
esac
endef
-DIST_EXTRA := LICENSE README.md CMakeLists.txt GNUmakefile Makefile VERSION config.h.in \
+DIST_EXTRA := LICENSE README.md CMakeLists.txt GNUmakefile Makefile ChangeLog.md VERSION config.h.in ntdll.def \
$(addprefix man1/, $(MANPAGES)) cmake/compiler.cmake cmake/profile.cmake cmake/utils.cmake
DIST_SRC := mdbx.h mdbx.c $(addsuffix .c, $(TOOLS))
TEST_DB ?= $(shell [ -d /dev/shm ] && echo /dev/shm || echo /tmp)/mdbx-test.db
-TEST_LOG ?= $(shell [ -d /dev/shm ] && echo /dev/shm || echo /tmp)/mdbx-test.log
+TEST_LOG ?= $(shell [ -d /dev/shm ] && echo /dev/shm || echo /tmp)/mdbx-test.log.gz
TEST_OSAL := $(shell $(uname2osal))
TEST_ITER := $(shell $(uname2titer))
TEST_SRC := test/osal-$(TEST_OSAL).cc $(filter-out $(wildcard test/osal-*.cc), $(wildcard test/*.cc))
TEST_INC := $(wildcard test/*.h)
TEST_OBJ := $(patsubst %.cc,%.o,$(TEST_SRC))
CXX ?= g++
-CXXSTD ?= $(shell $(CXX) -std=c++27 -c test/test.cc -o /dev/null 2>/dev/null && echo -std=c++17 || echo -std=c++11)
+CXXSTD ?= $(shell $(CXX) -std=c++17 -c test/test.cc -o /dev/null 2>/dev/null && echo -std=c++17 || echo -std=c++11)
CXXFLAGS := $(CXXSTD) $(filter-out -std=gnu11,$(CFLAGS))
TAR ?= $(shell which gnu-tar || echo tar)
+ZIP ?= $(shell which zip || echo "echo 'Please install zip'")
+CLANG_FORMAT ?= $(shell (which clang-format-12 || which clang-format-11 || which clang-format-10 || which clang-format) 2>/dev/null)
+
+reformat:
+ @if [ -n "$(CLANG_FORMAT)" ]; then \
+ git ls-files | grep -E '\.(c|cxx|cc|cpp|h|hxx|hpp)(\.in)?$$' | xargs -r $(CLANG_FORMAT) -i --style=file; \
+ else \
+ echo "clang-format version 8..12 not found for 'reformat'"; \
+ fi
MAN_SRCDIR := src/man1/
ALLOY_DEPS := $(wildcard src/*)
-MDBX_VERSION_GIT = ${shell set -o pipefail; git describe --tags | sed -n 's|^v*\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\(.*\)|\1|p' || echo 'Please fetch tags and/or install latest git version'}
-MDBX_REVISION_GIT = $(shell git rev-list --count --no-merges HEAD || echo 'Please fetch tags and/or install latest git version')
+MDBX_GIT_VERSION = ${shell set -o pipefail; git describe --tags | sed -n 's|^v*\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\(.*\)|\1|p' || echo 'Please fetch tags and/or install latest git version'}
+MDBX_GIT_REVISION = $(shell git rev-list --count HEAD ^`git tag --sort=-version:refname | sed -n '/^\(v[0-9]\+\.[0-9]\+\.[0-9]\+\)*/p;q'`)
MDBX_GIT_TIMESTAMP = $(shell git show --no-patch --format=%cI HEAD || echo 'Please install latest get version')
MDBX_GIT_DESCRIBE = $(shell git describe --tags --long --dirty=-dirty || echo 'Please fetch tags and/or install latest git version')
MDBX_VERSION_SUFFIX = $(shell set -o pipefail; echo -n '$(MDBX_GIT_DESCRIBE)' | tr -c -s '[a-zA-Z0-9]' _)
MDBX_BUILD_SOURCERY = $(shell set -o pipefail; $(MAKE) -s src/version.c && (openssl dgst -r -sha256 src/version.c || sha256sum src/version.c || shasum -a 256 src/version.c) 2>/dev/null | cut -d ' ' -f 1 || echo 'Please install openssl or sha256sum or shasum')_$(MDBX_VERSION_SUFFIX)
+MDBX_DIST_DIR = libmdbx-$(MDBX_VERSION_SUFFIX)
-check: test mdbx_example dist
+check: test dist
-test: all mdbx_example mdbx_test
+test: build-test
rm -f $(TEST_DB) $(TEST_LOG) && (set -o pipefail; \
- (./mdbx_test --progress --console=no --repeat=$(TEST_ITER) --pathname=$(TEST_DB) --dont-cleanup-after basic && \
+ (./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=$(TEST_ITER) --pathname=$(TEST_DB) --dont-cleanup-after basic && \
./mdbx_test --mode=-writemap,-mapasync,-lifo --progress --console=no --repeat=12 --pathname=$(TEST_DB) --dont-cleanup-after basic) \
- | tee -a $(TEST_LOG) | tail -n 42) \
+ | tee >(gzip --stdout > $(TEST_LOG)) | tail -n 42) \
&& ./mdbx_chk -vvn $(TEST_DB) && ./mdbx_chk -vvn $(TEST_DB)-copy
-mdbx_example: mdbx.h example/example-mdbx.c libmdbx.$(SO_SUFFIX)
- $(CC) $(CFLAGS) -I. example/example-mdbx.c ./libmdbx.$(SO_SUFFIX) -o $@
-
test-singleprocess: all mdbx_test
rm -f $(TEST_DB) $(TEST_LOG) && (set -o pipefail; \
- (./mdbx_test --progress --console=no --repeat=42 --pathname=$(TEST_DB) --dont-cleanup-after --hill && \
+ (./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=42 --pathname=$(TEST_DB) --dont-cleanup-after --hill && \
./mdbx_test --progress --console=no --repeat=2 --pathname=$(TEST_DB) --dont-cleanup-before --dont-cleanup-after --copy && \
./mdbx_test --mode=-writemap,-mapasync,-lifo --progress --console=no --repeat=42 --pathname=$(TEST_DB) --dont-cleanup-after --nested) \
- | tee -a $(TEST_LOG) | tail -n 42) \
+ | tee >(gzip --stdout > $(TEST_LOG)) | tail -n 42) \
&& ./mdbx_chk -vvn $(TEST_DB) && ./mdbx_chk -vvn $(TEST_DB)-copy
test-fault: all mdbx_test
- rm -f $(TEST_DB) $(TEST_LOG) && (set -o pipefail; ./mdbx_test --progress --console=no --pathname=$(TEST_DB) --inject-writefault=42 --dump-config --dont-cleanup-after basic | tee -a $(TEST_LOG) | tail -n 42) \
+ rm -f $(TEST_DB) $(TEST_LOG) && (set -o pipefail; ./mdbx_test --progress --console=no --pathname=$(TEST_DB) --inject-writefault=42 --dump-config --dont-cleanup-after basic \
+ | tee >(gzip --stdout > $(TEST_LOG)) | tail -n 42) \
; ./mdbx_chk -vvnw $(TEST_DB) && ([ ! -e $(TEST_DB)-copy ] || ./mdbx_chk -vvn $(TEST_DB)-copy)
VALGRIND=valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt
-memcheck test-valgrind: all mdbx_test
- @echo "$(MDBX_OPTIONS)" | grep -q MDBX_USE_VALGRIND || echo "WARNING: Please build libmdbx with -DMDBX_USE_VALGRIND to avoid false-positives from Valgrind !!!" >&2
- rm -f valgrind-*.log $(TEST_DB) $(TEST_LOG) && (set -o pipefail; \
- ($(VALGRIND) ./mdbx_test --mode=-writemap,-mapasync,-lifo --progress --console=no --repeat=4 --pathname=$(TEST_DB) --dont-cleanup-after basic && \
+memcheck test-valgrind:
+ $(MAKE) clean && $(MAKE) CFLAGS_EXTRA="-Ofast -DMDBX_USE_VALGRIND" build-test && \
+ rm -f valgrind-*.log $(TEST_DB) $(TEST_LOG) && (set -o pipefail; ( \
+ $(VALGRIND) ./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=2 --pathname=$(TEST_DB) --dont-cleanup-after basic && \
$(VALGRIND) ./mdbx_test --progress --console=no --pathname=$(TEST_DB) --dont-cleanup-before --dont-cleanup-after --copy && \
- $(VALGRIND) ./mdbx_test --progress --console=no --repeat=2 --pathname=$(TEST_DB) --dont-cleanup-after basic) \
- | tee -a $(TEST_LOG) | tail -n 42) \
- && $(VALGRIND) ./mdbx_chk -vvn $(TEST_DB) && ./mdbx_chk -vvn $(TEST_DB)-copy
+ $(VALGRIND) ./mdbx_test --mode=-writemap,-mapasync,-lifo --progress --console=no --repeat=4 --pathname=$(TEST_DB) --dont-cleanup-after basic && \
+ $(VALGRIND) ./mdbx_chk -vvn $(TEST_DB) && \
+ $(VALGRIND) ./mdbx_chk -vvn $(TEST_DB)-copy \
+ ) | tee >(gzip --stdout > $(TEST_LOG)) | tail -n 42)
+
+gcc-analyzer:
+ @echo "NOTE: There a lot of false-positive warnings at 2020-05-01 by pre-release GCC-10 (20200328, Red Hat 10.0.1-0.11)"
+ $(MAKE) --always-make CFLAGS_EXTRA="-Og -fanalyzer -Wno-error" build-test
+
+test-ubsan:
+ $(MAKE) clean && $(MAKE) CFLAGS_EXTRA="-Ofast -fsanitize=undefined -fsanitize-undefined-trap-on-error" check
+
+test-asan:
+ $(MAKE) clean && $(MAKE) CFLAGS_EXTRA="-Os -fsanitize=address" check
+
+test-leak:
+ $(MAKE) clean && $(MAKE) CFLAGS_EXTRA="-fsanitize=leak" check
+
+mdbx_example: mdbx.h example/example-mdbx.c libmdbx.$(SO_SUFFIX)
+ $(CC) $(CFLAGS) -I. example/example-mdbx.c ./libmdbx.$(SO_SUFFIX) -o $@
+
+build-test: all mdbx_example mdbx_test
define test-rule
$(patsubst %.cc,%.o,$(1)): $(1) $(TEST_INC) mdbx.h $(lastword $(MAKEFILE_LIST))
@@ -193,10 +220,10 @@ src/version.c: src/version.c.in $(lastword $(MAKEFILE_LIST)) $(git_DIR)/HEAD $(g
-e "s|@MDBX_GIT_TREE@|$(shell git show --no-patch --format=%T HEAD || echo 'Please install latest get version')|" \
-e "s|@MDBX_GIT_COMMIT@|$(shell git show --no-patch --format=%H HEAD || echo 'Please install latest get version')|" \
-e "s|@MDBX_GIT_DESCRIBE@|$(MDBX_GIT_DESCRIBE)|" \
- -e "s|\$${MDBX_VERSION_MAJOR}|$(shell echo '$(MDBX_VERSION_GIT)' | cut -d . -f 1)|" \
- -e "s|\$${MDBX_VERSION_MINOR}|$(shell echo '$(MDBX_VERSION_GIT)' | cut -d . -f 2)|" \
- -e "s|\$${MDBX_VERSION_RELEASE}|$(shell echo '$(MDBX_VERSION_GIT)' | cut -d . -f 3)|" \
- -e "s|\$${MDBX_VERSION_REVISION}|$(MDBX_REVISION_GIT)|" \
+ -e "s|\$${MDBX_VERSION_MAJOR}|$(shell echo '$(MDBX_GIT_VERSION)' | cut -d . -f 1)|" \
+ -e "s|\$${MDBX_VERSION_MINOR}|$(shell echo '$(MDBX_GIT_VERSION)' | cut -d . -f 2)|" \
+ -e "s|\$${MDBX_VERSION_RELEASE}|$(shell echo '$(MDBX_GIT_VERSION)' | cut -d . -f 3)|" \
+ -e "s|\$${MDBX_VERSION_REVISION}|$(MDBX_GIT_REVISION)|" \
src/version.c.in > $@
src/config.h: src/version.c $(lastword $(MAKEFILE_LIST))
@@ -213,13 +240,18 @@ mdbx-dylib.o: src/config.h src/version.c src/alloy.c $(ALLOY_DEPS) $(lastword $(
mdbx-static.o: src/config.h src/version.c src/alloy.c $(ALLOY_DEPS) $(lastword $(MAKEFILE_LIST))
$(CC) $(CFLAGS) $(MDBX_OPTIONS) '-DMDBX_CONFIG_H="config.h"' -ULIBMDBX_EXPORTS -c src/alloy.c -o $@
-.PHONY: dist
+.PHONY: dist release-assets
dist: libmdbx-sources-$(MDBX_VERSION_SUFFIX).tar.gz $(lastword $(MAKEFILE_LIST))
-libmdbx-sources-$(MDBX_VERSION_SUFFIX).tar.gz: $(addprefix dist/, $(DIST_SRC) $(DIST_EXTRA)) $(addprefix dist/man1/,$(MANPAGES))
- $(TAR) -c $(shell LC_ALL=C $(TAR) --help | grep -q -- '--owner' && echo '--owner=0 --group=0') -f - -C dist $(DIST_SRC) $(DIST_EXTRA) | gzip -c > $@ \
+release-assets: libmdbx-sources-$(MDBX_VERSION_SUFFIX).tar.gz libmdbx-sources-$(MDBX_VERSION_SUFFIX).zip
+
+libmdbx-sources-$(MDBX_VERSION_SUFFIX).tar.gz: $(addprefix dist/, $(DIST_SRC) $(DIST_EXTRA))
+ $(TAR) -c $(shell LC_ALL=C $(TAR) --help | grep -q -- '--owner' && echo '--owner=0 --group=0') -f - -C dist $(DIST_SRC) $(DIST_EXTRA) | gzip -c -9 > $@ \
&& rm dist/@tmp-shared_internals.inc
+libmdbx-sources-$(MDBX_VERSION_SUFFIX).zip: $(addprefix dist/, $(DIST_SRC) $(DIST_EXTRA))
+ rm -rf $@ && (cd dist && $(ZIP) -9 ../$@ $(DIST_SRC) $(DIST_EXTRA)) || rm -rf $@
+
dist/mdbx.h: mdbx.h src/version.c $(lastword $(MAKEFILE_LIST))
mkdir -p dist && cp $< $@
@@ -255,10 +287,13 @@ dist/$(1): $(1)
mkdir -p $$(dir $$@) && sed -e '/^#> dist-cutoff-begin/,/^#< dist-cutoff-end/d' $$< > $$@
endef
-$(foreach file,$(filter-out man1/% VERSION %.in,$(DIST_EXTRA)),$(eval $(call dist-extra-rule,$(file))))
+$(foreach file,$(filter-out man1/% VERSION %.in ntdll.def,$(DIST_EXTRA)),$(eval $(call dist-extra-rule,$(file))))
dist/VERSION: src/version.c
- mkdir -p dist/ && echo "$(MDBX_VERSION_GIT).$(MDBX_REVISION_GIT)" > $@
+ mkdir -p dist/ && echo "$(MDBX_GIT_VERSION).$(MDBX_GIT_REVISION)" > $@
+
+dist/ntdll.def: src/ntdll.def
+ mkdir -p dist/cmake/ && cp $< $@
dist/config.h.in: src/config.h.in
mkdir -p dist/cmake/ && cp $< $@
diff --git a/libs/libmdbx/src/Makefile b/libs/libmdbx/src/Makefile
index 0595d23524..1898655ec2 100644
--- a/libs/libmdbx/src/Makefile
+++ b/libs/libmdbx/src/Makefile
@@ -1,4 +1,4 @@
-all install mdbx tools strip clean test check dist test-singleprocess test-fault memcheck test-valgrind cross-gcc cross-qemu bench bench-quartet clean-bench:
+all bench bench-quartet build-test check clean clean-bench cross-gcc cross-qemu dist gcc-analyzer install mdbx memcheck reformat release-assets strip test test-asan test-fault test-leak test-singleprocess test-ubsan test-valgrind tools:
@CC=$(CC) \
CXX=`if test -n "$(CXX)" && which "$(CXX)" > /dev/null; then echo "$(CXX)"; elif test -n "$(CCC)" && which "$(CCC)" > /dev/null; then echo "$(CCC)"; else echo "c++"; fi` \
`which gmake || which gnumake || echo 'echo "GNU Make is required"; exit 2;'` \
diff --git a/libs/libmdbx/src/README.md b/libs/libmdbx/src/README.md
index 9685648bb4..6f02ab467d 100644
--- a/libs/libmdbx/src/README.md
+++ b/libs/libmdbx/src/README.md
@@ -61,8 +61,10 @@ _MithrilDB_ is rightly relevant name.
[![https://t.me/libmdbx](https://raw.githubusercontent.com/wiki/erthink/libmdbx/img/telegram.png)](https://t.me/libmdbx)
[![Build Status](https://travis-ci.org/erthink/libmdbx.svg?branch=master)](https://travis-ci.org/erthink/libmdbx)
-[![Build status](https://ci.appveyor.com/api/projects/status/ue94mlopn50dqiqg/branch/master?svg=true)](https://ci.appveyor.com/project/erthink/libmdbx/branch/master)
+[![Build status](https://ci.appveyor.com/api/projects/status/ue94mlopn50dqiqg/branch/master?svg=true)](https://ci.appveyor.com/project/leo-yuriev/libmdbx/branch/master)
+[![CircleCI](https://circleci.com/gh/erthink/libmdbx/tree/master.svg?style=svg)](https://circleci.com/gh/erthink/libmdbx/tree/master)
[![Coverity Scan Status](https://scan.coverity.com/projects/12915/badge.svg)](https://scan.coverity.com/projects/reopen-libmdbx)
+[![Build Status](https://api.cirrus-ci.com/github/erthink/libmdbx.svg)](https://cirrus-ci.com/github/erthink/libmdbx)
*The Future will (be) [Positive](https://www.ptsecurity.com). Всё будет хорошо.*
@@ -263,7 +265,7 @@ pre-opening is not needed.
> contrary to LMDB. Nevertheless, the `MDBX_UTTERLY_NOSYNC` mode available to match LMDB behaviour,
> and for a special use-cases.
-6. On **MacOS** the `fcntl(F_FULLFSYNC)` syscall is used _by
+6. On **MacOS & iOS** the `fcntl(F_FULLFSYNC)` syscall is used _by
default_ to synchronize data with the disk, as this is [the only way to
guarantee data
durability](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fsync.2.html)
@@ -384,8 +386,8 @@ the basic tests.
### Windows
For build _libmdbx_ on Windows the _original_ CMake and [Microsoft Visual
-Studio](https://en.wikipedia.org/wiki/Microsoft_Visual_Studio) are
-recommended.
+Studio 2019](https://en.wikipedia.org/wiki/Microsoft_Visual_Studio) are
+recommended. Otherwise do not forget add `ntdll.lib` to linking.
Building by MinGW, MSYS or Cygwin is potentially possible. However,
these scripts are not tested and will probably require you to modify the
@@ -484,7 +486,7 @@ Here showed sum of performance metrics in 3 benchmarks:
## Read Scalability
Summary performance with concurrent read/search queries in 1-2-4-8
-threads on 4 CPU cores machine.
+threads on machine with 4 logical CPU in HyperThreading mode (i.e. actually 2 physical CPU cores).
![Comparison #2: Read Scalability](https://raw.githubusercontent.com/wiki/erthink/libmdbx/img/perf-slide-2.png)
diff --git a/libs/libmdbx/src/appveyor.yml b/libs/libmdbx/src/appveyor.yml
index efdeae648d..15fd7dbb25 100644
--- a/libs/libmdbx/src/appveyor.yml
+++ b/libs/libmdbx/src/appveyor.yml
@@ -1,4 +1,4 @@
-version: 0.7.0.{build}
+version: 0.8.0.{build}
environment:
matrix:
diff --git a/libs/libmdbx/src/cmake/compiler.cmake b/libs/libmdbx/src/cmake/compiler.cmake
index a3bca1ac29..544a22d9fb 100644
--- a/libs/libmdbx/src/cmake/compiler.cmake
+++ b/libs/libmdbx/src/cmake/compiler.cmake
@@ -64,11 +64,11 @@ endmacro(check_compiler_flag)
# We support building with Clang and gcc. First check
# what we're using for build.
-if(CMAKE_C_COMPILER_LOADED AND CMAKE_C_COMPILER_ID STREQUAL "Clang")
+if(CMAKE_C_COMPILER_LOADED AND CMAKE_C_COMPILER_ID MATCHES ".*[Cc][Ll][Aa][Nn][Gg].*")
set(CMAKE_COMPILER_IS_CLANG ON)
set(CMAKE_COMPILER_IS_GNUCC OFF)
endif()
-if(CMAKE_CXX_COMPILER_LOADED AND CMAKE_CXx_COMPILER_ID STREQUAL "Clang")
+if(CMAKE_CXX_COMPILER_LOADED AND CMAKE_CXX_COMPILER_ID MATCHES ".*[Cc][Ll][Aa][Nn][Gg].*")
set(CMAKE_COMPILER_IS_CLANG ON)
set(CMAKE_COMPILER_IS_GNUCXX OFF)
endif()
@@ -161,40 +161,47 @@ if(NOT DEFINED IOS)
endif()
endif()
+if(NOT DEFINED CMAKE_TARGET_BITNESS)
+ if (CMAKE_SIZEOF_VOID_P LESS 4)
+ set(CMAKE_TARGET_BITNESS 16)
+ elseif (CMAKE_SIZEOF_VOID_P LESS 8)
+ set(CMAKE_TARGET_BITNESS 32)
+ else()
+ set(CMAKE_TARGET_BITNESS 64)
+ endif()
+endif()
+
if(CMAKE_COMPILER_IS_ELBRUSC OR CMAKE_SYSTEM_PROCESSOR MATCHES "e2k.*|E2K.*|elbrus.*|ELBRUS.*")
set(E2K TRUE)
set(CMAKE_SYSTEM_ARCH "Elbrus")
-elseif((MSVC64 OR MINGW64) AND CMAKE_SIZEOF_VOID_P EQUAL 8)
+elseif((MSVC64 OR MINGW64) AND CMAKE_TARGET_BITNESS EQUAL 64)
set(X86_64 TRUE)
set(CMAKE_SYSTEM_ARCH "x86_64")
elseif(MINGW OR (MSVC AND NOT CMAKE_CROSSCOMPILING))
set(X86_32 TRUE)
set(CMAKE_SYSTEM_ARCH "x86")
-elseif(CMAKE_COMPILER_IS_ELBRUSC OR CMAKE_SYSTEM_PROCESSOR MATCHES "e2k.*|E2K.*|elbrus.*|ELBRUS.*")
- set(E2K TRUE)
- set(CMAKE_SYSTEM_ARCH "Elbrus")
-elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*" AND CMAKE_SIZEOF_VOID_P EQUAL 8)
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*" AND CMAKE_TARGET_BITNESS EQUAL 64)
set(X86_64 TRUE)
set(CMAKE_SYSTEM_ARCH "x86_64")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i686.*|i386.*|x86.*")
set(X86_32 TRUE)
set(CMAKE_SYSTEM_ARCH "x86")
-elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*|ARM64.*)" AND CMAKE_SIZEOF_VOID_P EQUAL 8)
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*|ARM64.*)" AND CMAKE_TARGET_BITNESS EQUAL 64)
set(AARCH64 TRUE)
set(CMAKE_SYSTEM_ARCH "ARM64")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm.*|ARM.*)")
set(ARM32 TRUE)
set(CMAKE_SYSTEM_ARCH "ARM")
-elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64le.*" AND CMAKE_SIZEOF_VOID_P EQUAL 8)
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64le.*" AND CMAKE_TARGET_BITNESS EQUAL 64)
set(PPC64LE TRUE)
set(CMAKE_SYSTEM_ARCH "PPC64LE")
-elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64.*" AND CMAKE_SIZEOF_VOID_P EQUAL 8)
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64.*" AND CMAKE_TARGET_BITNESS EQUAL 64)
set(PPC64 TRUE)
set(CMAKE_SYSTEM_ARCH "PPC64")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc).*")
set(PPC32 TRUE)
set(CMAKE_SYSTEM_ARCH "PPC")
-elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(mips|MIPS)64.*" AND CMAKE_SIZEOF_VOID_P EQUAL 8)
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(mips|MIPS)64.*" AND CMAKE_TARGET_BITNESS EQUAL 64)
set(MIPS64 TRUE)
set(CMAKE_SYSTEM_ARCH "MIPS64")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(mips|MIPS).*")
@@ -213,7 +220,9 @@ else()
check_compiler_flag("-Wextra" CC_HAS_WEXTRA)
check_compiler_flag("-Werror" CC_HAS_WERROR)
check_compiler_flag("-fexceptions" CC_HAS_FEXCEPTIONS)
- check_cxx_compiler_flag("-fcxx-exceptions" CC_HAS_FCXX_EXCEPTIONS)
+ if(CMAKE_CXX_COMPILER_LOADED)
+ check_cxx_compiler_flag("-fcxx-exceptions" CC_HAS_FCXX_EXCEPTIONS)
+ endif()
check_compiler_flag("-funwind-tables" CC_HAS_FUNWIND_TABLES)
check_compiler_flag("-fno-omit-frame-pointer" CC_HAS_FNO_OMIT_FRAME_POINTER)
check_compiler_flag("-fno-common" CC_HAS_FNO_COMMON)
@@ -229,10 +238,17 @@ else()
# Check for an omp support
set(CMAKE_REQUIRED_FLAGS "-fopenmp -Werror")
- check_cxx_source_compiles("int main(void) {
- #pragma omp parallel
- return 0;
- }" HAVE_OPENMP)
+ if(CMAKE_CXX_COMPILER_LOADED)
+ check_cxx_source_compiles("int main(void) {
+ #pragma omp parallel
+ return 0;
+ }" HAVE_OPENMP)
+ else()
+ check_c_source_compiles("int main(void) {
+ #pragma omp parallel
+ return 0;
+ }" HAVE_OPENMP)
+ endif()
set(CMAKE_REQUIRED_FLAGS "")
endif()
@@ -312,61 +328,81 @@ if(CMAKE_COMPILER_IS_CLANG)
execute_process(COMMAND ${CMAKE_${CMAKE_PRIMARY_LANG}_COMPILER} -print-search-dirs
OUTPUT_VARIABLE clang_search_dirs RESULT_VARIABLE clang_probe_result ERROR_QUIET)
- unset(clang_bindir)
- unset(clang_libdir)
+ unset(clang_bindirs)
+ unset(clang_libdirs)
if(clang_probe_result EQUAL 0)
- string(REGEX MATCH "^(.*programs: =)([^:]*:)*([^:]+/llvm[-.0-9]+/bin[^:]*)(:[^:]*)*(\n.+)$" clang_bindir_valid ${clang_search_dirs})
- if(clang_bindir_valid)
- string(REGEX REPLACE "^(.*programs: =)([^:]*:)*([^:]+/llvm[-.0-9]+/bin[^:]*)(:[^:]*)*(\n.+)$" "\\3" clang_bindir ${clang_search_dirs})
- if(CMAKE_SYSTEM_NAME STREQUAL "WINDOWS")
- set(clang_libdir ${clang_bindir})
- else()
- get_filename_component(clang_libdir "${clang_bindir}/../lib" REALPATH)
- endif()
+ string(REGEX MATCH "(^|\n.*)(.*programs: =)([^\n]+)((\n.*)|$)" regexp_valid ${clang_search_dirs})
+ if(regexp_valid)
+ string(REGEX REPLACE "(^|\n.*)(.*programs: =)([^\n]+)((\n.*)|$)" "\\3" list ${clang_search_dirs})
+ string(REPLACE ":" ";" list "${list}")
+ #set(clang_bindirs "")
+ foreach(dir IN LISTS list)
+ get_filename_component(dir "${dir}" REALPATH)
+ list(APPEND clang_bindirs "${dir}")
+ endforeach()
+ list(REMOVE_DUPLICATES clang_bindirs)
+ endif()
+ string(REGEX MATCH "(^|\n.*)(.*libraries: =)([^\n]+)((\n.*)|$)" regexp_valid ${clang_search_dirs})
+ if(regexp_valid)
+ string(REGEX REPLACE "(^|\n.*)(.*libraries: =)([^\n]+)((\n.*)|$)" "\\3" list ${clang_search_dirs})
+ string(REPLACE ":" ";" list "${list}")
+ #set(clang_libdirs "")
+ foreach(dir IN LISTS list)
+ get_filename_component(dir "${dir}" REALPATH)
+ list(APPEND clang_libdirs "${dir}")
+ endforeach()
+ list(REMOVE_DUPLICATES clang_libdirs)
endif()
else()
- get_filename_component(clang_bindir ${CMAKE_${CMAKE_PRIMARY_LANG}_COMPILER} DIRECTORY)
+ get_filename_component(clang_bindirs ${CMAKE_${CMAKE_PRIMARY_LANG}_COMPILER} DIRECTORY)
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
- set(clang_libdir ${clang_bindir})
+ set(clang_libdirs ${clang_bindirs})
else()
- get_filename_component(clang_libdir "${clang_bindir}/../lib" REALPATH)
+ get_filename_component(clang_libdirs "${CMAKE_${CMAKE_PRIMARY_LANG}_COMPILER}/../lib" REALPATH)
endif()
endif()
- if(clang_bindir AND clang_libdir)
- message(STATUS "Found CLANG/LLVM directories: ${clang_bindir}, ${clang_libdir}")
+ if(clang_bindirs AND clang_libdirs)
+ message(STATUS "Found CLANG/LLVM directories: ${clang_bindirs}, ${clang_libdirs}")
else()
message(STATUS "Could NOT find CLANG/LLVM directories (bin and/or lib).")
endif()
- if(NOT CMAKE_CLANG_LD AND clang_bindir)
- find_program(CMAKE_CLANG_LD NAMES lld-link ld.lld ld64.lld lld-link llvm-link link llvm-ld ld PATHS ${clang_bindir} NO_DEFAULT_PATH)
+ if(NOT CMAKE_CLANG_LD AND clang_bindirs)
+ find_program(CMAKE_CLANG_LD NAMES lld-link ld.lld "ld${CMAKE_TARGET_BITNESS}.lld" lld llvm-link llvm-ld PATHS ${clang_bindirs} NO_DEFAULT_PATH)
endif()
- if(NOT CMAKE_CLANG_AR AND clang_bindir)
- find_program(CMAKE_CLANG_AR NAMES llvm-ar ar PATHS ${clang_bindir} NO_DEFAULT_PATH)
+ if(NOT CMAKE_CLANG_AR AND clang_bindirs)
+ find_program(CMAKE_CLANG_AR NAMES llvm-ar ar PATHS ${clang_bindirs} NO_DEFAULT_PATH)
endif()
- if(NOT CMAKE_CLANG_NM AND clang_bindir)
- find_program(CMAKE_CLANG_NM NAMES llvm-nm nm PATHS ${clang_bindir} NO_DEFAULT_PATH)
+ if(NOT CMAKE_CLANG_NM AND clang_bindirs)
+ find_program(CMAKE_CLANG_NM NAMES llvm-nm nm PATHS ${clang_bindirs} NO_DEFAULT_PATH)
endif()
- if(NOT CMAKE_CLANG_RANLIB AND clang_bindir)
- find_program(CMAKE_CLANG_RANLIB NAMES llvm-ranlib ranlib PATHS ${clang_bindir} NO_DEFAULT_PATH)
+ if(NOT CMAKE_CLANG_RANLIB AND clang_bindirs)
+ find_program(CMAKE_CLANG_RANLIB NAMES llvm-ranlib ranlib PATHS ${clang_bindirs} NO_DEFAULT_PATH)
endif()
set(clang_lto_plugin_name "LLVMgold${CMAKE_SHARED_LIBRARY_SUFFIX}")
- if(NOT CMAKE_LD_GOLD AND clang_bindir)
- find_program(CMAKE_LD_GOLD NAMES ld.gold PATHS)
+ if(NOT CMAKE_LD_GOLD AND clang_bindirs)
+ find_program(CMAKE_LD_GOLD NAMES ld.gold PATHS ${clang_bindirs})
endif()
- if(NOT CLANG_LTO_PLUGIN AND clang_libdir)
- find_file(CLANG_LTO_PLUGIN ${clang_lto_plugin_name} PATH ${clang_libdir} NO_DEFAULT_PATH)
+ if(NOT CLANG_LTO_PLUGIN AND clang_libdirs)
+ find_file(CLANG_LTO_PLUGIN ${clang_lto_plugin_name} PATHS ${clang_libdirs} NO_DEFAULT_PATH)
endif()
+
if(CLANG_LTO_PLUGIN)
message(STATUS "Found CLANG/LLVM's plugin for LTO: ${CLANG_LTO_PLUGIN}")
else()
message(STATUS "Could NOT find CLANG/LLVM's plugin (${clang_lto_plugin_name}) for LTO.")
endif()
- if(CMAKE_CLANG_LD AND CMAKE_CLANG_AR AND CMAKE_CLANG_NM AND CMAKE_CLANG_RANLIB)
- message(STATUS "Found CLANG/LLVM's binutils for LTO: ${CMAKE_CLANG_AR}, ${CMAKE_CLANG_RANLIB}")
+ if(CMAKE_CLANG_LD)
+ message(STATUS "Found CLANG/LLVM's linker for LTO: ${CMAKE_CLANG_LD}")
+ else()
+ message(STATUS "Could NOT find CLANG/LLVM's linker (lld, llvm-ld, llvm-link) for LTO.")
+ endif()
+
+ if(CMAKE_CLANG_AR AND CMAKE_CLANG_RANLIB AND CMAKE_CLANG_NM)
+ message(STATUS "Found CLANG/LLVM's binutils for LTO: ${CMAKE_CLANG_AR}, ${CMAKE_CLANG_RANLIB}, ${CMAKE_CLANG_NM}")
else()
message(STATUS "Could NOT find CLANG/LLVM's binutils (ar, ranlib, nm) for LTO.")
endif()
@@ -378,12 +414,19 @@ if(CMAKE_COMPILER_IS_CLANG)
unset(clang_search_dirs)
endif()
- if((CLANG_LTO_PLUGIN AND CMAKE_LD_GOLD) OR
- (CMAKE_CLANG_LD AND CMAKE_CLANG_AR AND CMAKE_CLANG_NM AND CMAKE_CLANG_RANLIB))
+ if (CMAKE_CLANG_AR AND CMAKE_CLANG_NM AND CMAKE_CLANG_RANLIB
+ AND ((CLANG_LTO_PLUGIN AND CMAKE_LD_GOLD)
+ OR (CMAKE_CLANG_LD
+ AND NOT (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux"
+ AND CMAKE_SYSTEM_NAME STREQUAL "Linux"))
+ OR APPLE))
set(CLANG_LTO_AVAILABLE TRUE)
message(STATUS "Link-Time Optimization by CLANG/LLVM is available")
elseif(CMAKE_TOOLCHAIN_FILE AND NOT CMAKE_${CMAKE_PRIMARY_LANG}_COMPILER_VERSION VERSION_LESS 7.0)
set(CLANG_LTO_AVAILABLE TRUE)
+ if(NOT CMAKE_CLANG_LD)
+ set(CMAKE_CLANG_LD ${CMAKE_LINKER})
+ endif()
if(NOT CMAKE_CLANG_AR)
set(CMAKE_CLANG_AR ${CMAKE_AR})
endif()
@@ -442,9 +485,6 @@ endif()
macro(setup_compile_flags)
# save initial C/CXX flags
if(NOT INITIAL_CMAKE_FLAGS_SAVED)
- if(MSVC)
- string(REGEX REPLACE "^(.*)(/EHsc)( *)(.*)$" "\\1/EHs\\3\\4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
- endif()
set(INITIAL_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} CACHE STRING "Initial CMake's flags" FORCE)
set(INITIAL_CMAKE_C_FLAGS ${CMAKE_C_FLAGS} CACHE STRING "Initial CMake's flags" FORCE)
set(INITIAL_CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} CACHE STRING "Initial CMake's flags" FORCE)
@@ -468,6 +508,22 @@ macro(setup_compile_flags)
if(CC_HAS_FCXX_EXCEPTIONS)
add_compile_flags("CXX" "-fcxx-exceptions -frtti")
endif()
+ if(MSVC)
+ # checks for /EHa or /clr options exists,
+ # i.e. is enabled structured async WinNT exceptions
+ string(REGEX MATCH "^(.* )*[-/]EHc*a( .*)*$" msvc_async_eh_enabled "${CXX_FLAGS}" "${C_FLAGS}")
+ string(REGEX MATCH "^(.* )*[-/]clr( .*)*$" msvc_clr_enabled "${CXX_FLAGS}" "${C_FLAGS}")
+ # remote any /EH? options
+ string(REGEX REPLACE "( *[-/]-*EH[csa]+ *)+" "" CXX_FLAGS "${CXX_FLAGS}")
+ string(REGEX REPLACE "( *[-/]-*EH[csa]+ *)+" "" C_FLAGS "${C_FLAGS}")
+ if (msvc_clr_enabled STREQUAL "")
+ if(NOT msvc_async_eh_enabled STREQUAL "")
+ add_compile_flags("C;CXX" "/EHa")
+ else()
+ add_compile_flags("C;CXX" "/EHsc")
+ endif()
+ endif()
+ endif(MSVC)
if(CC_HAS_WNO_ATTRIBUTES AND CMAKE_COMPILER_IS_GNU${CMAKE_PRIMARY_LANG}
AND CMAKE_${CMAKE_PRIMARY_LANG}_COMPILER_VERSION VERSION_LESS 9)
diff --git a/libs/libmdbx/src/cmake/utils.cmake b/libs/libmdbx/src/cmake/utils.cmake
index 13add097dc..8e98267f4e 100644
--- a/libs/libmdbx/src/cmake/utils.cmake
+++ b/libs/libmdbx/src/cmake/utils.cmake
@@ -61,7 +61,7 @@ macro(set_source_files_compile_flags)
unset(_lang)
endmacro(set_source_files_compile_flags)
-macro(fetch_version name version_file parent_scope)
+macro(fetch_version name source_root_directory parent_scope)
set(${name}_VERSION "")
set(${name}_GIT_DESCRIBE "")
set(${name}_GIT_TIMESTAMP "")
@@ -69,11 +69,11 @@ macro(fetch_version name version_file parent_scope)
set(${name}_GIT_COMMIT "")
set(${name}_GIT_REVISION 0)
set(${name}_GIT_VERSION "")
- if(GIT AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
+ if(GIT AND EXISTS "${source_root_directory}/.git")
execute_process(COMMAND ${GIT} describe --tags --long --dirty=-dirty
OUTPUT_VARIABLE ${name}_GIT_DESCRIBE
OUTPUT_STRIP_TRAILING_WHITESPACE
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE rc)
if(rc OR "${name}_GIT_DESCRIBE" STREQUAL "")
message(FATAL_ERROR "Please fetch tags and/or install latest version of git ('describe --tags --long --dirty' failed)")
@@ -82,13 +82,13 @@ macro(fetch_version name version_file parent_scope)
execute_process(COMMAND ${GIT} show --no-patch --format=%cI HEAD
OUTPUT_VARIABLE ${name}_GIT_TIMESTAMP
OUTPUT_STRIP_TRAILING_WHITESPACE
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE rc)
if(rc OR "${name}_GIT_TIMESTAMP" STREQUAL "%cI")
execute_process(COMMAND ${GIT} show --no-patch --format=%ci HEAD
OUTPUT_VARIABLE ${name}_GIT_TIMESTAMP
OUTPUT_STRIP_TRAILING_WHITESPACE
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE rc)
if(rc OR "${name}_GIT_TIMESTAMP" STREQUAL "%ci")
message(FATAL_ERROR "Please install latest version of git ('show --no-patch --format=%cI HEAD' failed)")
@@ -98,7 +98,7 @@ macro(fetch_version name version_file parent_scope)
execute_process(COMMAND ${GIT} show --no-patch --format=%T HEAD
OUTPUT_VARIABLE ${name}_GIT_TREE
OUTPUT_STRIP_TRAILING_WHITESPACE
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE rc)
if(rc OR "${name}_GIT_TREE" STREQUAL "")
message(FATAL_ERROR "Please install latest version of git ('show --no-patch --format=%T HEAD' failed)")
@@ -107,19 +107,36 @@ macro(fetch_version name version_file parent_scope)
execute_process(COMMAND ${GIT} show --no-patch --format=%H HEAD
OUTPUT_VARIABLE ${name}_GIT_COMMIT
OUTPUT_STRIP_TRAILING_WHITESPACE
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE rc)
if(rc OR "${name}_GIT_COMMIT" STREQUAL "")
message(FATAL_ERROR "Please install latest version of git ('show --no-patch --format=%H HEAD' failed)")
endif()
- execute_process(COMMAND ${GIT} rev-list --count --no-merges HEAD
+ execute_process(COMMAND ${GIT} tag --sort=-version:refname
+ OUTPUT_VARIABLE tag_list
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ WORKING_DIRECTORY ${source_root_directory}
+ RESULT_VARIABLE rc)
+ if(rc)
+ message(FATAL_ERROR "Please install latest version of git ('tag --sort=-version:refname' failed)")
+ endif()
+ string(REGEX REPLACE "\n" ";" tag_list "${tag_list}")
+ set(last_release_tag "")
+ set(git_revlist_arg "HEAD")
+ foreach(tag IN LISTS tag_list)
+ if(NOT last_release_tag)
+ string(REGEX MATCH "^v[0-9]+(\.[0-9]+)+" last_release_tag "${tag}")
+ set(git_revlist_arg "${tag}..HEAD")
+ endif()
+ endforeach(tag)
+ execute_process(COMMAND ${GIT} rev-list --count "${git_revlist_arg}"
OUTPUT_VARIABLE ${name}_GIT_REVISION
OUTPUT_STRIP_TRAILING_WHITESPACE
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE rc)
if(rc OR "${name}_GIT_REVISION" STREQUAL "")
- message(FATAL_ERROR "Please install latest version of git ('rev-list --count --no-merges HEAD' failed)")
+ message(FATAL_ERROR "Please install latest version of git ('rev-list --count ${git_revlist_arg}' failed)")
endif()
string(REGEX MATCH "^(v)?([0-9]+)\\.([0-9]+)\\.([0-9]+)(.*)?" git_version_valid "${${name}_GIT_DESCRIBE}")
@@ -136,8 +153,8 @@ macro(fetch_version name version_file parent_scope)
endif()
endif()
- if(NOT ${name}_GIT_VERSION OR NOT ${name}_GIT_TIMESTAMP OR NOT ${name}_GIT_REVISION)
- if(GIT AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
+ if(NOT ${name}_GIT_VERSION OR NOT ${name}_GIT_TIMESTAMP OR ${name}_GIT_REVISION STREQUAL "")
+ if(GIT AND EXISTS "${source_root_directory}/.git")
message(WARNING "Unable to retrive ${name} version from git.")
endif()
set(${name}_GIT_VERSION "0;0;0;0")
@@ -145,8 +162,9 @@ macro(fetch_version name version_file parent_scope)
set(${name}_GIT_REVISION 0)
# Try to get version from VERSION file
+ set(version_file "${source_root_directory}/VERSION")
if(EXISTS "${version_file}")
- file(STRINGS "${version_file}" ${name}_VERSION)
+ file(STRINGS "${version_file}" ${name}_VERSION LIMIT_COUNT 1 LIMIT_INPUT 42)
endif()
if(NOT ${name}_VERSION)
@@ -168,32 +186,19 @@ macro(fetch_version name version_file parent_scope)
list(GET ${name}_VERSION_LIST 2 "${name}_VERSION_RELEASE")
list(GET ${name}_VERSION_LIST 3 "${name}_VERSION_REVISION")
- if(parent_scope)
- set(${name}_VERSION_MAJOR ${${name}_VERSION_MAJOR} PARENT_SCOPE)
- set(${name}_VERSION_MINOR ${${name}_VERSION_MINOR} PARENT_SCOPE)
- set(${name}_VERSION_RELEASE ${${name}_VERSION_RELEASE} PARENT_SCOPE)
- set(${name}_VERSION_REVISION ${${name}_VERSION_REVISION} PARENT_SCOPE)
- set(${name}_VERSION ${${name}_VERSION} PARENT_SCOPE)
-
- set(${name}_GIT_DESCRIBE ${${name}_GIT_DESCRIBE} PARENT_SCOPE)
- set(${name}_GIT_TIMESTAMP ${${name}_GIT_TIMESTAMP} PARENT_SCOPE)
- set(${name}_GIT_TREE ${${name}_GIT_TREE} PARENT_SCOPE)
- set(${name}_GIT_COMMIT ${${name}_GIT_COMMIT} PARENT_SCOPE)
- set(${name}_GIT_REVISION ${${name}_GIT_REVISION} PARENT_SCOPE)
- set(${name}_GIT_VERSION ${${name}_GIT_VERSION} PARENT_SCOPE)
- else()
- set(${name}_VERSION_MAJOR ${${name}_VERSION_MAJOR})
- set(${name}_VERSION_MINOR ${${name}_VERSION_MINOR})
- set(${name}_VERSION_RELEASE ${${name}_VERSION_RELEASE})
- set(${name}_VERSION_REVISION ${${name}_VERSION_REVISION})
- set(${name}_VERSION ${${name}_VERSION})
-
- set(${name}_GIT_DESCRIBE ${${name}_GIT_DESCRIBE})
- set(${name}_GIT_TIMESTAMP ${${name}_GIT_TIMESTAMP})
- set(${name}_GIT_TREE ${${name}_GIT_TREE})
- set(${name}_GIT_COMMIT ${${name}_GIT_COMMIT})
- set(${name}_GIT_REVISION ${${name}_GIT_REVISION})
- set(${name}_GIT_VERSION ${${name}_GIT_VERSION})
+ if(${parent_scope})
+ set(${name}_VERSION_MAJOR "${${name}_VERSION_MAJOR}" PARENT_SCOPE)
+ set(${name}_VERSION_MINOR "${${name}_VERSION_MINOR}" PARENT_SCOPE)
+ set(${name}_VERSION_RELEASE "${${name}_VERSION_RELEASE}" PARENT_SCOPE)
+ set(${name}_VERSION_REVISION "${${name}_VERSION_REVISION}" PARENT_SCOPE)
+ set(${name}_VERSION "${${name}_VERSION}" PARENT_SCOPE)
+
+ set(${name}_GIT_DESCRIBE "${${name}_GIT_DESCRIBE}" PARENT_SCOPE)
+ set(${name}_GIT_TIMESTAMP "${${name}_GIT_TIMESTAMP}" PARENT_SCOPE)
+ set(${name}_GIT_TREE "${${name}_GIT_TREE}" PARENT_SCOPE)
+ set(${name}_GIT_COMMIT "${${name}_GIT_COMMIT}" PARENT_SCOPE)
+ set(${name}_GIT_REVISION "${${name}_GIT_REVISION}" PARENT_SCOPE)
+ set(${name}_GIT_VERSION "${${name}_GIT_VERSION}" PARENT_SCOPE)
endif()
endmacro(fetch_version)
diff --git a/libs/libmdbx/src/config.h b/libs/libmdbx/src/config.h
index 9d21e964d5..23fea98bd0 100644
--- a/libs/libmdbx/src/config.h
+++ b/libs/libmdbx/src/config.h
@@ -12,6 +12,7 @@
/* #undef MDBX_FORCE_ASSERTIONS */
/* Common */
+#define MDBX_HUGE_TRANSACTIONS 0
#define MDBX_TXN_CHECKOWNER 1
#define MDBX_TXN_CHECKPID_AUTO
#ifndef MDBX_TXN_CHECKPID_AUTO
@@ -27,8 +28,8 @@
#endif
/* Windows */
-#define MDBX_AVOID_CRT 0
#define MDBX_CONFIG_MANUAL_TLS_CALLBACK 1
+#define MDBX_AVOID_CRT 0
/* MacOS & iOS */
#define MDBX_OSX_SPEED_INSTEADOF_DURABILITY 0
@@ -41,12 +42,12 @@
#endif
/* Build Info */
-#define MDBX_BUILD_TIMESTAMP "2020-04-25T08:31:34Z"
+#define MDBX_BUILD_TIMESTAMP "2020-06-14T10:50:19Z"
#define MDBX_BUILD_TARGET "x86_64-Windows"
/* #undef MDBX_BUILD_TYPE */
-#define MDBX_BUILD_COMPILER "MSVC-19.25.28614.0"
-#define MDBX_BUILD_FLAGS "/DWIN32 /D_WINDOWS /W3 /Gy /W4 /utf-8 /WX /GL MDBX_BUILD_SHARED_LIBRARY=0"
-#define MDBX_BUILD_SOURCERY 51d296e8c57b5c01204315d0ea2160b23cf3eb40509644354cc038e3f67d61d4_v0_7_0_39_gca8fa31
+#define MDBX_BUILD_COMPILER "MSVC-19.26.28806.0"
+#define MDBX_BUILD_FLAGS "/DWIN32 /D_WINDOWS /W3 /EHsc /Gy /W4 /utf-8 /WX /GL MDBX_BUILD_SHARED_LIBRARY=0"
+#define MDBX_BUILD_SOURCERY d9a19ecb1e6bd3a67910c162b2e9c2dc476d665c134d041af742fa0196b19a3e_v0_8_1_1_g0c5496d
/* *INDENT-ON* */
/* clang-format on */
diff --git a/libs/libmdbx/src/mdbx.h b/libs/libmdbx/src/mdbx.h
index c7a6ab3f43..0c6b3de65b 100644
--- a/libs/libmdbx/src/mdbx.h
+++ b/libs/libmdbx/src/mdbx.h
@@ -658,9 +658,9 @@ typedef pthread_t mdbx_tid_t;
/*----------------------------------------------------------------------------*/
-/* MDBX version 0.7.0, released 2020-03-18 */
+/* MDBX version 0.8.0, released 2020-06-05 */
#define MDBX_VERSION_MAJOR 0
-#define MDBX_VERSION_MINOR 7
+#define MDBX_VERSION_MINOR 8
#ifndef LIBMDBX_API
#if defined(LIBMDBX_EXPORTS)
@@ -924,7 +924,9 @@ LIBMDBX_API const char *mdbx_dump_val(const MDBX_val *key, char *const buf,
* - without MDBX_NOSUBDIR = in a filesystem we have the MDBX-directory with
* given pathname, within that a pair of MDBX-files with predefined names.
*
- * This flag affects only at environment opening and can't be changed after. */
+ * This flag affects only at new environment creating by mdbx_env_open(),
+ * otherwise at opening an existing environment libmdbx will choice this
+ * automatically. */
#define MDBX_NOSUBDIR 0x4000u
/* MDBX_RDONLY = read only mode.
@@ -1375,6 +1377,7 @@ LIBMDBX_API const char *mdbx_dump_val(const MDBX_val *key, char *const buf,
/**** ENVIRONMENT COPY FLAGS **************************************************/
/* Compacting: Omit free space from copy, and renumber all pages sequentially */
#define MDBX_CP_COMPACT 1u
+#define MDBX_CP_FORCE_RESIZEABLE 2u
/**** CURSOR OPERATIONS ********************************************************
*
@@ -1385,7 +1388,8 @@ typedef enum MDBX_cursor_op {
MDBX_FIRST_DUP, /* MDBX_DUPSORT-only: Position at first data item
* of current key. */
MDBX_GET_BOTH, /* MDBX_DUPSORT-only: Position at key/data pair. */
- MDBX_GET_BOTH_RANGE, /* MDBX_DUPSORT-only: position at key, nearest data. */
+ MDBX_GET_BOTH_RANGE, /* MDBX_DUPSORT-only: Position at given key and at first
+ * data greater than or equal to specified data. */
MDBX_GET_CURRENT, /* Return key/data at current cursor position */
MDBX_GET_MULTIPLE, /* MDBX_DUPFIXED-only: Return up to a page of duplicate
* data items from current cursor position.
@@ -2430,20 +2434,37 @@ LIBMDBX_API uint64_t mdbx_txn_id(const MDBX_txn *txn);
/* Commit all the operations of a transaction into the database.
*
- * The transaction handle is freed. It and its cursors must not be used again
- * after this call, except with mdbx_cursor_renew() and mdbx_cursor_close().
- *
- * A cursor must be closed explicitly always, before or after its transaction
- * ends. It can be reused with mdbx_cursor_renew() before finally closing it.
+ * If the current thread is not eligible to manage the transaction then
+ * the MDBX_THREAD_MISMATCH error will returned. Otherwise the transaction
+ * will be committed and its handle is freed. If the transaction cannot
+ * be committed, it will be aborted with the corresponding error returned.
+ * Thus, a result other than MDBX_THREAD_MISMATCH means that the transaction
+ * is terminated:
+ * - Resources are released;
+ * - Transaction handle is invalid;
+ * - Cursor(s) associated with transaction must not be used, except with
+ * mdbx_cursor_renew() and mdbx_cursor_close().
+ * Such cursor(s) must be closed explicitly by mdbx_cursor_close() before
+ * or after transaction commit, either can be reused with mdbx_cursor_renew()
+ * until it will be explicitly closed by mdbx_cursor_close().
*
* [in] txn A transaction handle returned by mdbx_txn_begin().
*
* Returns A non-zero error value on failure and 0 on success, some
* possible errors are:
- * - MDBX_EINVAL = an invalid parameter was specified.
- * - MDBX_ENOSPC = no more disk space.
- * - MDBX_EIO = a low-level I/O error occurred while writing.
- * - MDBX_ENOMEM = out of memory. */
+ * - MDBX_RESULT_TRUE = transaction was aborted since it should be aborted
+ * due to previous errors.
+ * - MDBX_PANIC = a fatal error occurred earlier and the environment
+ * must be shut down.
+ * - MDBX_BAD_TXN = transaction is already fihished or never began.
+ * - MDBX_EBADSIGN = transaction object has invalid signature,
+ * e.g. transaction was already terminated
+ * or memory was corrupted.
+ * - MDBX_THREAD_MISMATCH = given transaction is not owned by current thread.
+ * - MDBX_EINVAL = transaction handle is NULL.
+ * - MDBX_ENOSPC = no more disk space.
+ * - MDBX_EIO = a system-level I/O error occurred while writing.
+ * - MDBX_ENOMEM = out of memory. */
LIBMDBX_API int mdbx_txn_commit(MDBX_txn *txn);
/* Abandon all the operations of the transaction instead of saving them.
@@ -2451,12 +2472,30 @@ LIBMDBX_API int mdbx_txn_commit(MDBX_txn *txn);
* The transaction handle is freed. It and its cursors must not be used again
* after this call, except with mdbx_cursor_renew() and mdbx_cursor_close().
*
- * A cursor must be closed explicitly always, before or after its transaction
- * ends. It can be reused with mdbx_cursor_renew() before finally closing it.
+ * If the current thread is not eligible to manage the transaction then
+ * the MDBX_THREAD_MISMATCH error will returned. Otherwise the transaction
+ * will be aborted and its handle is freed. Thus, a result other than
+ * MDBX_THREAD_MISMATCH means that the transaction is terminated:
+ * - Resources are released;
+ * - Transaction handle is invalid;
+ * - Cursor(s) associated with transaction must not be used, except with
+ * mdbx_cursor_renew() and mdbx_cursor_close().
+ * Such cursor(s) must be closed explicitly by mdbx_cursor_close() before
+ * or after transaction abort, either can be reused with mdbx_cursor_renew()
+ * until it will be explicitly closed by mdbx_cursor_close().
*
* [in] txn A transaction handle returned by mdbx_txn_begin().
*
- * Returns A non-zero error value on failure and 0 on success. */
+ * Returns A non-zero error value on failure and 0 on success, some
+ * possible errors are:
+ * - MDBX_PANIC = a fatal error occurred earlier and the environment
+ * must be shut down.
+ * - MDBX_BAD_TXN = transaction is already fihished or never began.
+ * - MDBX_EBADSIGN = transaction object has invalid signature,
+ * e.g. transaction was already terminated
+ * or memory was corrupted.
+ * - MDBX_THREAD_MISMATCH = given transaction is not owned by current thread.
+ * - MDBX_EINVAL = transaction handle is NULL. */
LIBMDBX_API int mdbx_txn_abort(MDBX_txn *txn);
/* Reset a read-only transaction.
@@ -2479,7 +2518,16 @@ LIBMDBX_API int mdbx_txn_abort(MDBX_txn *txn);
*
* [in] txn A transaction handle returned by mdbx_txn_begin().
*
- * Returns A non-zero error value on failure and 0 on success. */
+ * Returns A non-zero error value on failure and 0 on success, some
+ * possible errors are:
+ * - MDBX_PANIC = a fatal error occurred earlier and the environment
+ * must be shut down.
+ * - MDBX_BAD_TXN = transaction is already fihished or never began.
+ * - MDBX_EBADSIGN = transaction object has invalid signature,
+ * e.g. transaction was already terminated
+ * or memory was corrupted.
+ * - MDBX_THREAD_MISMATCH = given transaction is not owned by current thread.
+ * - MDBX_EINVAL = transaction handle is NULL. */
LIBMDBX_API int mdbx_txn_reset(MDBX_txn *txn);
/* Renew a read-only transaction.
@@ -2492,9 +2540,14 @@ LIBMDBX_API int mdbx_txn_reset(MDBX_txn *txn);
*
* Returns A non-zero error value on failure and 0 on success, some
* possible errors are:
- * - MDBX_PANIC = a fatal error occurred earlier and the environment
- * must be shut down.
- * - MDBX_EINVAL = an invalid parameter was specified. */
+ * - MDBX_PANIC = a fatal error occurred earlier and the environment
+ * must be shut down.
+ * - MDBX_BAD_TXN = transaction is already fihished or never began.
+ * - MDBX_EBADSIGN = transaction object has invalid signature,
+ * e.g. transaction was already terminated
+ * or memory was corrupted.
+ * - MDBX_THREAD_MISMATCH = transaction is running by other thread.
+ * - MDBX_EINVAL = transaction handle is NULL. */
LIBMDBX_API int mdbx_txn_renew(MDBX_txn *txn);
/* The fours integers markers (aka "canary") associated with the environment.
@@ -2669,6 +2722,21 @@ __inline uint32_t mdbx_key_from_int32(const int32_t i32) {
LIBMDBX_API int mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *stat,
size_t bytes);
+/* Retrieve depth (bitmask) information of nested dupsort (multi-value) B+trees
+ * for given database.
+ *
+ * [in] txn A transaction handle returned by mdbx_txn_begin().
+ * [in] dbi A database handle returned by mdbx_dbi_open().
+ * [out] mask The address of an uint32_t value where the bitmask
+ * will be stored.
+ *
+ * Returns A non-zero error value on failure and 0 on success, some
+ * possible errors are:
+ * - MDBX_EINVAL = an invalid parameter was specified.
+ * - MDBX_RESULT_TRUE = the dbi isn't a dupsort (multi-value) database. */
+LIBMDBX_API int mdbx_dbi_dupsort_depthmask(MDBX_txn *txn, MDBX_dbi dbi,
+ uint32_t *mask);
+
/* Retrieve the DB flags and status for a database handle.
*
* [in] txn A transaction handle returned by mdbx_txn_begin().
@@ -2910,7 +2978,8 @@ LIBMDBX_API int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
*
* NOTE: The data parameter is NOT ignored regardless the database does
* support sorted duplicate data items or not. If the data parameter
- * is non-NULL only the matching data item will be deleted.
+ * is non-NULL only the matching data item will be deleted. Otherwise, if data
+ * parameter is NULL, any/all value(s) for specified key will be deleted.
*
* This function will return MDBX_NOTFOUND if the specified key/data
* pair is not in the database.
@@ -3025,14 +3094,15 @@ LIBMDBX_API int mdbx_cursor_get(MDBX_cursor *cursor, MDBX_val *key,
* [in] key The key operated on.
* [in,out] data The data operated on.
* [in] flags Options for this operation. This parameter
- * must be set to 0 or one of the values described here:
+ * must be set to 0 or by bitwise OR'ing together one or more of
+ * the values described here:
*
* - MDBX_CURRENT
* Replace the item at the current cursor position. The key parameter
* must still be provided, and must match it, otherwise the function
* return MDBX_EKEYMISMATCH.
*
- * NOTE: MDBX unlike LMDB allows you to change the size of the data and
+ * NOTE: MDBX allows (unlike LMDB) you to change the size of the data and
* automatically handles reordering for sorted duplicates (MDBX_DUPSORT).
*
* - MDBX_NODUPDATA
@@ -3097,8 +3167,8 @@ LIBMDBX_API int mdbx_cursor_put(MDBX_cursor *cursor, const MDBX_val *key,
* or one of the values described here.
*
* - MDBX_NODUPDATA
- * Delete all of the data items for the current key. This flag may only
- * be specified if the database was opened with MDBX_DUPSORT.
+ * Delete all of the data items for the current key. This flag has effect
+ * only for database(s) was created with MDBX_DUPSORT.
*
* Returns A non-zero error value on failure and 0 on success, some
* possible errors are:
@@ -3286,8 +3356,8 @@ LIBMDBX_API int mdbx_is_dirty(const MDBX_txn *txn, const void *ptr);
* [in] increment Value to increase the sequence,
* must be 0 for read-only transactions.
*
- * Returns A non-zero error value on failure and 0 on success, some
- * possible errors are:
+ * Returns A non-zero error value on failure and 0 on success,
+ * some possible errors are:
* - MDBX_RESULT_TRUE = Increasing the sequence has resulted in an overflow
* and therefore cannot be executed. */
LIBMDBX_API int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result,
@@ -3483,7 +3553,7 @@ MDBX_pgvisitor_func(const uint64_t pgno, const unsigned number, void *const ctx,
/* B-tree traversal function. */
LIBMDBX_API int mdbx_env_pgwalk(MDBX_txn *txn, MDBX_pgvisitor_func *visitor,
- void *ctx);
+ void *ctx, int dont_check_keys_ordering);
/**** Attribute support functions for Nexenta *********************************/
#ifdef MDBX_NEXENTA_ATTRS
diff --git a/libs/libmdbx/src/src/config.h.in b/libs/libmdbx/src/src/config.h.in
index 12f3dc635c..871944e115 100644
--- a/libs/libmdbx/src/src/config.h.in
+++ b/libs/libmdbx/src/src/config.h.in
@@ -12,6 +12,7 @@
#cmakedefine MDBX_FORCE_ASSERTIONS
/* Common */
+#cmakedefine01 MDBX_HUGE_TRANSACTIONS
#cmakedefine01 MDBX_TXN_CHECKOWNER
#cmakedefine MDBX_TXN_CHECKPID_AUTO
#ifndef MDBX_TXN_CHECKPID_AUTO
diff --git a/libs/libmdbx/src/src/core.c b/libs/libmdbx/src/src/core.c
index 098da86f45..21a0e46fcb 100644
--- a/libs/libmdbx/src/src/core.c
+++ b/libs/libmdbx/src/src/core.c
@@ -388,16 +388,15 @@ __cold intptr_t mdbx_limits_valsize_max(intptr_t pagesize, unsigned flags) {
if (flags & MDBX_INTEGERDUP)
return 8 /* sizeof(uint64_t) */;
- if (flags &
- (MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP))
+ if (flags & (MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_REVERSEDUP))
return BRANCH_NODEMAX(pagesize) - NODESIZE;
const unsigned page_ln2 = log2n(pagesize);
const size_t hard = 0x7FF00000ul;
const size_t hard_pages = hard >> page_ln2;
- const size_t limit = (hard_pages < MDBX_DPL_TXNFULL)
+ const size_t limit = (hard_pages < MDBX_DPL_TXNFULL / 3)
? hard
- : ((size_t)MDBX_DPL_TXNFULL << page_ln2);
+ : ((size_t)MDBX_DPL_TXNFULL / 3 << page_ln2);
return (limit < MAX_MAPSIZE) ? limit / 2 : MAX_MAPSIZE / 2;
}
@@ -468,6 +467,25 @@ static __pure_function __always_inline size_t branch_size(const MDBX_env *env,
return node_bytes + sizeof(indx_t);
}
+static __pure_function __always_inline uint16_t
+flags_db2sub(uint16_t db_flags) {
+ uint16_t sub_flags = db_flags & MDBX_DUPFIXED;
+
+ /* MDBX_INTEGERDUP => MDBX_INTEGERKEY */
+#define SHIFT_INTEGERDUP_TO_INTEGERKEY 2
+ STATIC_ASSERT((MDBX_INTEGERDUP >> SHIFT_INTEGERDUP_TO_INTEGERKEY) ==
+ MDBX_INTEGERKEY);
+ sub_flags |= (db_flags & MDBX_INTEGERDUP) >> SHIFT_INTEGERDUP_TO_INTEGERKEY;
+
+ /* MDBX_REVERSEDUP => MDBX_REVERSEKEY */
+#define SHIFT_REVERSEDUP_TO_REVERSEKEY 5
+ STATIC_ASSERT((MDBX_REVERSEDUP >> SHIFT_REVERSEDUP_TO_REVERSEKEY) ==
+ MDBX_REVERSEKEY);
+ sub_flags |= (db_flags & MDBX_REVERSEDUP) >> SHIFT_REVERSEDUP_TO_REVERSEKEY;
+
+ return sub_flags;
+}
+
/*----------------------------------------------------------------------------*/
static __pure_function __always_inline size_t pgno2bytes(const MDBX_env *env,
@@ -571,6 +589,8 @@ static __pure_function __always_inline MDBX_node *page_node(const MDBX_page *mp,
* There are no node headers, keys are stored contiguously. */
static __pure_function __always_inline void *
page_leaf2key(const MDBX_page *mp, unsigned i, size_t keysize) {
+ assert((mp->mp_flags & (P_BRANCH | P_LEAF | P_LEAF2 | P_OVERFLOW | P_META)) ==
+ (P_LEAF | P_LEAF2));
assert(mp->mp_leaf2_ksize == keysize);
(void)keysize;
return (char *)mp + PAGEHDRSZ + (i * mp->mp_leaf2_ksize);
@@ -3062,14 +3082,15 @@ static int mdbx_txn_end(MDBX_txn *txn, unsigned mode);
static int __must_check_result mdbx_page_get(MDBX_cursor *mc, pgno_t pgno,
MDBX_page **mp, int *lvl);
static int __must_check_result mdbx_page_search_root(MDBX_cursor *mc,
- MDBX_val *key, int modify);
+ const MDBX_val *key,
+ int modify);
#define MDBX_PS_MODIFY 1
#define MDBX_PS_ROOTONLY 2
#define MDBX_PS_FIRST 4
#define MDBX_PS_LAST 8
-static int __must_check_result mdbx_page_search(MDBX_cursor *mc, MDBX_val *key,
- int flags);
+static int __must_check_result mdbx_page_search(MDBX_cursor *mc,
+ const MDBX_val *key, int flags);
static int __must_check_result mdbx_page_merge(MDBX_cursor *csrc,
MDBX_cursor *cdst);
static int __must_check_result mdbx_page_flush(MDBX_txn *txn,
@@ -3088,7 +3109,8 @@ static int __must_check_result mdbx_sync_locked(MDBX_env *env, unsigned flags,
MDBX_meta *const pending);
static int mdbx_env_close0(MDBX_env *env);
-static MDBX_node *mdbx_node_search(MDBX_cursor *mc, MDBX_val *key, int *exactp);
+static MDBX_node *mdbx_node_search(MDBX_cursor *mc, const MDBX_val *key,
+ int *exactp);
static int __must_check_result mdbx_node_add_branch(MDBX_cursor *mc,
unsigned indx,
@@ -3124,10 +3146,11 @@ mdbx_audit(MDBX_txn *txn) {
return mdbx_audit_ex(txn, 0, (txn->mt_flags & MDBX_RDONLY) != 0);
}
-static int __must_check_result mdbx_page_check(MDBX_env *env,
+static int __must_check_result mdbx_page_check(MDBX_cursor *const mc,
const MDBX_page *const mp,
- bool maybe_unfinished);
-static int __must_check_result mdbx_cursor_check(MDBX_cursor *mc, bool pending);
+ unsigned options);
+static int __must_check_result mdbx_cursor_check(MDBX_cursor *mc,
+ unsigned options);
static int __must_check_result mdbx_cursor_del0(MDBX_cursor *mc);
static int __must_check_result mdbx_del0(MDBX_txn *txn, MDBX_dbi dbi,
const MDBX_val *key,
@@ -3160,9 +3183,15 @@ static void mdbx_cursor_copy(const MDBX_cursor *csrc, MDBX_cursor *cdst);
static int __must_check_result mdbx_drop0(MDBX_cursor *mc, int subs);
static int __must_check_result mdbx_fetch_sdb(MDBX_txn *txn, MDBX_dbi dbi);
+static int __must_check_result mdbx_setup_dbx(MDBX_dbx *const dbx,
+ const MDBX_db *const db,
+ const unsigned pagesize);
static MDBX_cmp_func mdbx_cmp_memn, mdbx_cmp_memnr, mdbx_cmp_int_align4,
- mdbx_cmp_int_align2, mdbx_cmp_int_unaligned;
+ mdbx_cmp_int_align2, mdbx_cmp_int_unaligned, mdbx_cmp_lenfast;
+
+static MDBX_cmp_func *mdbx_default_keycmp(unsigned flags);
+static MDBX_cmp_func *mdbx_default_datacmp(unsigned flags);
static const char *__mdbx_strerr(int errnum) {
/* Table of descriptions for MDBX errors */
@@ -3185,7 +3214,7 @@ static const char *__mdbx_strerr(int errnum) {
"MDBX_PAGE_FULL: Internal error - Page has no more space",
"MDBX_UNABLE_EXTEND_MAPSIZE: Database engine was unable to extend"
" mapping, e.g. since address space is unavailable or busy,"
- " or Operation system not supported such operations"
+ " or Operation system not supported such operations",
"MDBX_INCOMPATIBLE: Environment or database is not compatible"
" with the requested operation or the specified flags",
"MDBX_BAD_RSLOT: Invalid reuse of reader locktable slot,"
@@ -3972,11 +4001,12 @@ static int mdbx_page_loose(MDBX_txn *txn, MDBX_page *mp) {
return MDBX_SUCCESS;
}
+ mp->mp_pgno = pgno;
mp->mp_flags = P_LOOSE | P_DIRTY;
mp->mp_next = txn->tw.loose_pages;
txn->tw.loose_pages = mp;
txn->tw.loose_count++;
- if (unlikely(txn->mt_next_pgno == mp->mp_pgno + 1))
+ if (unlikely(txn->mt_next_pgno == pgno + 1))
mdbx_refund(txn);
return MDBX_SUCCESS;
@@ -4034,9 +4064,19 @@ static int mdbx_page_retire(MDBX_cursor *mc, MDBX_page *mp) {
static __must_check_result __always_inline int
mdbx_retire_pgno(MDBX_cursor *mc, const pgno_t pgno) {
MDBX_page *mp;
- int rc = mdbx_page_get(mc, pgno, &mp, NULL);
- if (likely(rc == MDBX_SUCCESS))
- rc = mdbx_page_retire(mc, mp);
+ int rc;
+ if (mdbx_audit_enabled()) {
+ const unsigned save_flags = mc->mc_flags;
+ mc->mc_flags |= C_RETIRING;
+ rc = mdbx_page_get(mc, pgno, &mp, NULL);
+ if (likely(rc == MDBX_SUCCESS))
+ rc = mdbx_page_retire(mc, mp);
+ mc->mc_flags = (mc->mc_flags & ~C_RETIRING) | (save_flags & C_RETIRING);
+ } else {
+ rc = mdbx_page_get(mc, pgno, &mp, NULL);
+ if (likely(rc == MDBX_SUCCESS))
+ rc = mdbx_page_retire(mc, mp);
+ }
return rc;
}
@@ -4088,7 +4128,7 @@ mark_done:
if (all) {
/* Mark dirty root pages */
for (i = 0; i < txn->mt_numdbs; i++) {
- if (txn->mt_dbflags[i] & DB_DIRTY) {
+ if (txn->mt_dbstate[i] & DB_DIRTY) {
pgno_t pgno = txn->mt_dbs[i].md_root;
if (pgno == P_INVALID)
continue;
@@ -4161,8 +4201,17 @@ static int mdbx_page_spill(MDBX_cursor *mc, const MDBX_val *key,
if (txn->tw.dirtyroom > i)
return MDBX_SUCCESS;
+ /* Less aggressive spill - we originally spilled the entire dirty list,
+ * with a few exceptions for cursor pages and DB root pages. But this
+ * turns out to be a lot of wasted effort because in a large txn many
+ * of those pages will need to be used again. So now we spill only 1/8th
+ * of the dirty pages. Testing revealed this to be a good tradeoff,
+ * better than 1/2, 1/4, or 1/10. */
+ if (need < MDBX_DPL_TXNFULL / 8)
+ need = MDBX_DPL_TXNFULL / 8;
+
if (!txn->tw.spill_pages) {
- txn->tw.spill_pages = mdbx_pnl_alloc(MDBX_DPL_TXNFULL / 8);
+ txn->tw.spill_pages = mdbx_pnl_alloc(need);
if (unlikely(!txn->tw.spill_pages))
return MDBX_ENOMEM;
} else {
@@ -4181,15 +4230,6 @@ static int mdbx_page_spill(MDBX_cursor *mc, const MDBX_val *key,
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
- /* Less aggressive spill - we originally spilled the entire dirty list,
- * with a few exceptions for cursor pages and DB root pages. But this
- * turns out to be a lot of wasted effort because in a large txn many
- * of those pages will need to be used again. So now we spill only 1/8th
- * of the dirty pages. Testing revealed this to be a good tradeoff,
- * better than 1/2, 1/4, or 1/10. */
- if (need < MDBX_DPL_TXNFULL / 8)
- need = MDBX_DPL_TXNFULL / 8;
-
/* Save the page IDs of all the pages we're flushing */
/* flush from the tail forward, this saves a lot of shifting later on. */
for (i = dl->length; i && need; i--) {
@@ -5035,7 +5075,7 @@ skip_cache:
goto fail;
}
last = unaligned_peek_u64(4, key.iov_base);
- if (unlikely(last < 1 || last >= SAFE64_INVALID_THRESHOLD)) {
+ if (unlikely(last < MIN_TXNID || last > MAX_TXNID)) {
rc = MDBX_CORRUPTED;
goto fail;
}
@@ -5133,7 +5173,7 @@ skip_cache:
}
/* Don't try to coalesce too much. */
- if (unlikely(re_len > MDBX_DPL_TXNFULL / 4))
+ if (unlikely(re_len > MDBX_DPL_TXNFULL / 42))
break;
if (re_len /* current size */ >= env->me_maxgc_ov1page ||
(re_len > prev_re_len && re_len - prev_re_len /* delta from prev */ >=
@@ -5671,7 +5711,7 @@ static int mdbx_cursor_shadow(MDBX_txn *src, MDBX_txn *dst) {
* user may not use mc until dst ends. But we need a valid
* txn pointer here for cursor fixups to keep working. */
mc->mc_txn = dst;
- mc->mc_dbflag = &dst->mt_dbflags[i];
+ mc->mc_dbstate = &dst->mt_dbstate[i];
if ((mx = mc->mc_xcursor) != NULL) {
*(MDBX_xcursor *)(bk + 1) = *mx;
mx->mx_cursor.mc_txn = dst;
@@ -5710,7 +5750,7 @@ static void mdbx_cursors_eot(MDBX_txn *txn, unsigned merge) {
mc->mc_backup = bk->mc_backup;
mc->mc_txn = bk->mc_txn;
mc->mc_db = bk->mc_db;
- mc->mc_dbflag = bk->mc_dbflag;
+ mc->mc_dbstate = bk->mc_dbstate;
if ((mx = mc->mc_xcursor) != NULL)
mx->mx_cursor.mc_txn = bk->mc_txn;
} else {
@@ -5752,7 +5792,7 @@ static pgno_t mdbx_find_largest_this(MDBX_env *env, pgno_t largest) {
goto retry;
if (largest < snap_pages &&
lck->mti_oldest_reader <= /* ignore pending updates */ snap_txnid &&
- snap_txnid < SAFE64_INVALID_THRESHOLD)
+ snap_txnid <= MAX_TXNID)
largest = snap_pages;
}
}
@@ -5959,8 +5999,7 @@ static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) {
}
}
- if (unlikely(txn->mt_txnid == 0 ||
- txn->mt_txnid >= SAFE64_INVALID_THRESHOLD)) {
+ if (unlikely(txn->mt_txnid < MIN_TXNID || txn->mt_txnid > MAX_TXNID)) {
mdbx_error("%s", "environment corrupted by died writer, must shutdown!");
rc = MDBX_CORRUPTED;
goto bailout;
@@ -5982,7 +6021,7 @@ static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) {
if (lck->mti_readers[i].mr_pid == env->me_pid &&
unlikely(lck->mti_readers[i].mr_tid == tid)) {
const txnid_t txnid = safe64_read(&lck->mti_readers[i].mr_txnid);
- if (txnid >= MIN_TXNID && txnid < SAFE64_INVALID_THRESHOLD)
+ if (txnid >= MIN_TXNID && txnid <= MAX_TXNID)
return MDBX_TXN_OVERLAPPING;
}
}
@@ -6010,7 +6049,7 @@ static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) {
txn->mt_canary = meta->mm_canary;
const txnid_t snap = mdbx_meta_txnid_stable(env, meta);
txn->mt_txnid = safe64_txnid_next(snap);
- if (unlikely(txn->mt_txnid >= SAFE64_INVALID_THRESHOLD)) {
+ if (unlikely(txn->mt_txnid > MAX_TXNID)) {
mdbx_debug("%s", "txnid overflow!");
rc = MDBX_TXN_FULL;
goto bailout;
@@ -6043,11 +6082,11 @@ static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) {
for (unsigned i = CORE_DBS; i < txn->mt_numdbs; i++) {
unsigned x = env->me_dbflags[i];
txn->mt_dbs[i].md_flags = x & PERSISTENT_FLAGS;
- txn->mt_dbflags[i] =
+ txn->mt_dbstate[i] =
(x & MDBX_VALID) ? DB_VALID | DB_USRVALID | DB_STALE : 0;
}
- txn->mt_dbflags[MAIN_DBI] = DB_VALID | DB_USRVALID;
- txn->mt_dbflags[FREE_DBI] = DB_VALID;
+ txn->mt_dbstate[MAIN_DBI] = DB_VALID | DB_USRVALID;
+ txn->mt_dbstate[FREE_DBI] = DB_VALID;
if (unlikely(env->me_flags & MDBX_FATAL_ERROR)) {
mdbx_warning("%s", "environment had fatal error, must shutdown!");
@@ -6138,8 +6177,6 @@ static __always_inline int check_txn_rw(const MDBX_txn *txn, int bad_bits) {
}
int mdbx_txn_renew(MDBX_txn *txn) {
- int rc;
-
if (unlikely(!txn))
return MDBX_EINVAL;
@@ -6149,8 +6186,12 @@ int mdbx_txn_renew(MDBX_txn *txn) {
if (unlikely((txn->mt_flags & MDBX_RDONLY) == 0))
return MDBX_EINVAL;
- if (unlikely(txn->mt_owner != 0))
- return MDBX_THREAD_MISMATCH;
+ int rc;
+ if (unlikely(txn->mt_owner != 0 || !(txn->mt_flags & MDBX_TXN_FINISHED))) {
+ rc = mdbx_txn_reset(txn);
+ if (unlikely(rc != MDBX_SUCCESS))
+ return rc;
+ }
rc = mdbx_txn_renew0(txn, MDBX_RDONLY);
if (rc == MDBX_SUCCESS) {
@@ -6238,7 +6279,7 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags,
memset(txn, 0, tsize);
txn->mt_dbxs = env->me_dbxs; /* static */
txn->mt_dbs = (MDBX_db *)((char *)txn + tsize);
- txn->mt_dbflags = (uint8_t *)txn + size - env->me_maxdbs;
+ txn->mt_dbstate = (uint8_t *)txn + size - env->me_maxdbs;
txn->mt_flags = flags;
txn->mt_env = env;
@@ -6286,9 +6327,9 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags,
txn->mt_numdbs = parent->mt_numdbs;
txn->mt_owner = parent->mt_owner;
memcpy(txn->mt_dbs, parent->mt_dbs, txn->mt_numdbs * sizeof(MDBX_db));
- /* Copy parent's mt_dbflags, but clear DB_NEW */
+ /* Copy parent's mt_dbstate, but clear DB_NEW */
for (unsigned i = 0; i < txn->mt_numdbs; i++)
- txn->mt_dbflags[i] = parent->mt_dbflags[i] & ~(DB_FRESH | DB_CREAT);
+ txn->mt_dbstate[i] = parent->mt_dbstate[i] & ~(DB_FRESH | DB_CREAT);
mdbx_tassert(parent,
parent->mt_parent ||
parent->tw.dirtyroom + parent->tw.dirtylist->length ==
@@ -6462,7 +6503,7 @@ static void mdbx_dbis_update(MDBX_txn *txn, int keep) {
if (n) {
bool locked = false;
MDBX_env *env = txn->mt_env;
- uint8_t *tdbflags = txn->mt_dbflags;
+ uint8_t *tdbflags = txn->mt_dbstate;
for (unsigned i = n; --i >= CORE_DBS;) {
if (likely((tdbflags[i] & DB_CREAT) == 0))
@@ -6714,16 +6755,16 @@ static __cold int mdbx_audit_ex(MDBX_txn *txn, unsigned retired_stored,
mdbx_tassert(txn, rc == MDBX_NOTFOUND);
for (MDBX_dbi i = FREE_DBI; i < txn->mt_numdbs; i++)
- txn->mt_dbflags[i] &= ~DB_AUDITED;
+ txn->mt_dbstate[i] &= ~DB_AUDITED;
pgno_t count = 0;
for (MDBX_dbi i = FREE_DBI; i <= MAIN_DBI; i++) {
- if (!(txn->mt_dbflags[i] & DB_VALID))
+ if (!(txn->mt_dbstate[i] & DB_VALID))
continue;
rc = mdbx_cursor_init(&cx.outer, txn, i);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
- txn->mt_dbflags[i] |= DB_AUDITED;
+ txn->mt_dbstate[i] |= DB_AUDITED;
if (txn->mt_dbs[i].md_root == P_INVALID)
continue;
count += txn->mt_dbs[i].md_branch_pages + txn->mt_dbs[i].md_leaf_pages +
@@ -6743,14 +6784,14 @@ static __cold int mdbx_audit_ex(MDBX_txn *txn, unsigned retired_stored,
memcpy(db = &db_copy, node_data(node), sizeof(db_copy));
if ((txn->mt_flags & MDBX_RDONLY) == 0) {
for (MDBX_dbi k = txn->mt_numdbs; --k > MAIN_DBI;) {
- if ((txn->mt_dbflags[k] & DB_VALID) &&
+ if ((txn->mt_dbstate[k] & DB_VALID) &&
/* txn->mt_dbxs[k].md_name.iov_len > 0 && */
node_ks(node) == txn->mt_dbxs[k].md_name.iov_len &&
memcmp(node_key(node), txn->mt_dbxs[k].md_name.iov_base,
node_ks(node)) == 0) {
- txn->mt_dbflags[k] |= DB_AUDITED;
- if (txn->mt_dbflags[k] & DB_DIRTY) {
- mdbx_tassert(txn, (txn->mt_dbflags[k] & DB_STALE) == 0);
+ txn->mt_dbstate[k] |= DB_AUDITED;
+ if (txn->mt_dbstate[k] & DB_DIRTY) {
+ mdbx_tassert(txn, (txn->mt_dbstate[k] & DB_STALE) == 0);
db = txn->mt_dbs + k;
}
break;
@@ -6767,9 +6808,9 @@ static __cold int mdbx_audit_ex(MDBX_txn *txn, unsigned retired_stored,
}
for (MDBX_dbi i = FREE_DBI; i < txn->mt_numdbs; i++) {
- if ((txn->mt_dbflags[i] & (DB_VALID | DB_AUDITED | DB_STALE)) != DB_VALID)
+ if ((txn->mt_dbstate[i] & (DB_VALID | DB_AUDITED | DB_STALE)) != DB_VALID)
continue;
- if (F_ISSET(txn->mt_dbflags[i], DB_DIRTY | DB_CREAT)) {
+ if (F_ISSET(txn->mt_dbstate[i], DB_DIRTY | DB_CREAT)) {
count += txn->mt_dbs[i].md_branch_pages + txn->mt_dbs[i].md_leaf_pages +
txn->mt_dbs[i].md_overflow_pages;
} else {
@@ -6778,7 +6819,7 @@ static __cold int mdbx_audit_ex(MDBX_txn *txn, unsigned retired_stored,
txn->mt_parent ? "nested-" : "", txn->mt_txnid, i,
(int)txn->mt_dbxs[i].md_name.iov_len,
(const char *)txn->mt_dbxs[i].md_name.iov_base,
- txn->mt_dbflags[i]);
+ txn->mt_dbstate[i]);
}
}
@@ -6866,29 +6907,32 @@ static int mdbx_update_gc(MDBX_txn *txn) {
mdbx_trace("\n>>> @%" PRIaTXN, txn->mt_txnid);
unsigned retired_stored = 0, loop = 0;
- MDBX_cursor mc;
- int rc = mdbx_cursor_init(&mc, txn, FREE_DBI);
+ MDBX_cursor_couple couple;
+ int rc = mdbx_cursor_init(&couple.outer, txn, FREE_DBI);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout_notracking;
- mc.mc_flags |= C_RECLAIMING;
- mc.mc_next = txn->mt_cursors[FREE_DBI];
- txn->mt_cursors[FREE_DBI] = &mc;
+ couple.outer.mc_flags |= C_RECLAIMING;
+ couple.outer.mc_next = txn->mt_cursors[FREE_DBI];
+ txn->mt_cursors[FREE_DBI] = &couple.outer;
retry:
+ ++loop;
+retry_noaccount:
mdbx_trace("%s", " >> restart");
mdbx_tassert(
txn, mdbx_pnl_check4assert(txn->tw.reclaimed_pglist, txn->mt_next_pgno));
mdbx_tassert(txn, mdbx_dirtylist_check(txn));
mdbx_tassert(txn, txn->tw.dirtyroom + txn->tw.dirtylist->length ==
MDBX_DPL_TXNFULL);
- if (unlikely(/* paranoia */ ++loop > 42)) {
+ if (unlikely(/* paranoia */ loop > ((MDBX_DEBUG > 0) ? 9 : 99))) {
mdbx_error("too more loops %u, bailout", loop);
rc = MDBX_PROBLEM;
goto bailout;
}
- rc = mdbx_prep_backlog(txn, &mc, MDBX_PNL_SIZEOF(txn->tw.retired_pages));
+ rc = mdbx_prep_backlog(txn, &couple.outer,
+ MDBX_PNL_SIZEOF(txn->tw.retired_pages));
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
@@ -6915,18 +6959,18 @@ retry:
cleaned_gc_slot > 0 && cleaned_gc_id < *env->me_oldest);
key.iov_base = &cleaned_gc_id;
key.iov_len = sizeof(cleaned_gc_id);
- rc = mdbx_cursor_get(&mc, &key, NULL, MDBX_SET);
+ rc = mdbx_cursor_get(&couple.outer, &key, NULL, MDBX_SET);
if (rc == MDBX_NOTFOUND)
continue;
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
- rc = mdbx_prep_backlog(txn, &mc, 0);
+ rc = mdbx_prep_backlog(txn, &couple.outer, 0);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
mdbx_tassert(txn, cleaned_gc_id < *env->me_oldest);
mdbx_trace("%s.cleanup-reclaimed-id [%u]%" PRIaTXN, dbg_prefix_mode,
cleaned_gc_slot, cleaned_gc_id);
- rc = mdbx_cursor_del(&mc, 0);
+ rc = mdbx_cursor_del(&couple.outer, 0);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
} while (cleaned_gc_slot < MDBX_PNL_SIZE(txn->tw.lifo_reclaimed));
@@ -6938,7 +6982,7 @@ retry:
while (cleaned_gc_id <= txn->tw.last_reclaimed) {
gc_rid = cleaned_gc_id;
settled = 0;
- rc = mdbx_cursor_first(&mc, &key, NULL);
+ rc = mdbx_cursor_first(&couple.outer, &key, NULL);
if (unlikely(rc != MDBX_SUCCESS)) {
if (rc == MDBX_NOTFOUND)
break;
@@ -6949,15 +6993,14 @@ retry:
goto bailout;
}
cleaned_gc_id = unaligned_peek_u64(4, key.iov_base);
- if (unlikely(cleaned_gc_id < 1 ||
- cleaned_gc_id >= SAFE64_INVALID_THRESHOLD)) {
+ if (unlikely(cleaned_gc_id < MIN_TXNID || cleaned_gc_id > MAX_TXNID)) {
rc = MDBX_CORRUPTED;
goto bailout;
}
if (cleaned_gc_id > txn->tw.last_reclaimed)
break;
if (cleaned_gc_id < txn->tw.last_reclaimed) {
- rc = mdbx_prep_backlog(txn, &mc, 0);
+ rc = mdbx_prep_backlog(txn, &couple.outer, 0);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
}
@@ -6965,7 +7008,7 @@ retry:
mdbx_tassert(txn, cleaned_gc_id < *env->me_oldest);
mdbx_trace("%s.cleanup-reclaimed-id %" PRIaTXN, dbg_prefix_mode,
cleaned_gc_id);
- rc = mdbx_cursor_del(&mc, 0);
+ rc = mdbx_cursor_del(&couple.outer, 0);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
}
@@ -7064,9 +7107,10 @@ retry:
if (retired_stored < MDBX_PNL_SIZE(txn->tw.retired_pages)) {
if (unlikely(!retired_stored)) {
/* Make sure last page of GC is touched and on retired-list */
- mc.mc_flags &= ~C_RECLAIMING;
- rc = mdbx_page_search(&mc, NULL, MDBX_PS_LAST | MDBX_PS_MODIFY);
- mc.mc_flags |= C_RECLAIMING;
+ couple.outer.mc_flags &= ~C_RECLAIMING;
+ rc = mdbx_page_search(&couple.outer, NULL,
+ MDBX_PS_LAST | MDBX_PS_MODIFY);
+ couple.outer.mc_flags |= C_RECLAIMING;
if (unlikely(rc != MDBX_SUCCESS) && rc != MDBX_NOTFOUND)
goto bailout;
}
@@ -7075,8 +7119,8 @@ retry:
key.iov_base = &txn->mt_txnid;
do {
data.iov_len = MDBX_PNL_SIZEOF(txn->tw.retired_pages);
- mdbx_prep_backlog(txn, &mc, data.iov_len);
- rc = mdbx_cursor_put(&mc, &key, &data, MDBX_RESERVE);
+ mdbx_prep_backlog(txn, &couple.outer, data.iov_len);
+ rc = mdbx_cursor_put(&couple.outer, &key, &data, MDBX_RESERVE);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
/* Retry if tw.retired_pages[] grew during the Put() */
@@ -7102,7 +7146,9 @@ retry:
if (unlikely(amount != MDBX_PNL_SIZE(txn->tw.reclaimed_pglist))) {
mdbx_trace("%s.reclaimed-list changed %u -> %u, retry", dbg_prefix_mode,
amount, (unsigned)MDBX_PNL_SIZE(txn->tw.reclaimed_pglist));
- goto retry /* rare case, but avoids GC fragmentation and one loop. */;
+ goto retry_noaccount /* rare case, but avoids GC fragmentation and one
+ cycle. */
+ ;
}
continue;
}
@@ -7146,10 +7192,10 @@ retry:
env->me_maxgc_ov1page) {
/* LY: need just a txn-id for save page list. */
- mc.mc_flags &= ~C_RECLAIMING;
+ couple.outer.mc_flags &= ~C_RECLAIMING;
bool need_cleanup = false;
do {
- rc = mdbx_page_alloc(&mc, 0, NULL, MDBX_ALLOC_GC);
+ rc = mdbx_page_alloc(&couple.outer, 0, NULL, MDBX_ALLOC_GC);
if (likely(rc == MDBX_SUCCESS)) {
mdbx_trace("%s: took @%" PRIaTXN " from GC", dbg_prefix_mode,
MDBX_PNL_LAST(txn->tw.lifo_reclaimed));
@@ -7161,7 +7207,7 @@ retry:
left > ((unsigned)MDBX_PNL_SIZE(txn->tw.lifo_reclaimed) -
reused_gc_slot) *
env->me_maxgc_ov1page);
- mc.mc_flags |= C_RECLAIMING;
+ couple.outer.mc_flags |= C_RECLAIMING;
if (likely(rc == MDBX_SUCCESS)) {
mdbx_trace("%s: got enough from GC.", dbg_prefix_mode);
@@ -7198,7 +7244,7 @@ retry:
break;
}
- mdbx_tassert(txn, gc_rid > 1 && gc_rid < SAFE64_INVALID_THRESHOLD);
+ mdbx_tassert(txn, gc_rid >= MIN_TXNID && gc_rid <= MAX_TXNID);
rc = mdbx_txl_append(&txn->tw.lifo_reclaimed, --gc_rid);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
@@ -7234,14 +7280,14 @@ retry:
mdbx_tassert(txn, txn->tw.lifo_reclaimed == NULL);
if (unlikely(gc_rid == 0)) {
gc_rid = mdbx_find_oldest(txn) - 1;
- rc = mdbx_cursor_get(&mc, &key, NULL, MDBX_FIRST);
+ rc = mdbx_cursor_get(&couple.outer, &key, NULL, MDBX_FIRST);
if (rc == MDBX_SUCCESS) {
if (unlikely(key.iov_len != sizeof(txnid_t))) {
rc = MDBX_CORRUPTED;
goto bailout;
}
txnid_t gc_first = unaligned_peek_u64(4, key.iov_base);
- if (unlikely(gc_first < 1 || gc_first >= SAFE64_INVALID_THRESHOLD)) {
+ if (unlikely(gc_first < MIN_TXNID || gc_first > MAX_TXNID)) {
rc = MDBX_CORRUPTED;
goto bailout;
}
@@ -7330,8 +7376,9 @@ retry:
data.iov_len = (chunk + 1) * sizeof(pgno_t);
mdbx_trace("%s.reserve: %u [%u...%u] @%" PRIaTXN, dbg_prefix_mode, chunk,
settled + 1, settled + chunk + 1, reservation_gc_id);
- mdbx_prep_backlog(txn, &mc, data.iov_len);
- rc = mdbx_cursor_put(&mc, &key, &data, MDBX_RESERVE | MDBX_NOOVERWRITE);
+ mdbx_prep_backlog(txn, &couple.outer, data.iov_len);
+ rc = mdbx_cursor_put(&couple.outer, &key, &data,
+ MDBX_RESERVE | MDBX_NOOVERWRITE);
mdbx_tassert(txn, mdbx_pnl_check4assert(txn->tw.reclaimed_pglist,
txn->mt_next_pgno));
if (unlikely(rc != MDBX_SUCCESS))
@@ -7346,7 +7393,7 @@ retry:
unlikely(amount < MDBX_PNL_SIZE(txn->tw.reclaimed_pglist))) {
mdbx_notice("** restart: reclaimed-list growth %u -> %u", amount,
(unsigned)MDBX_PNL_SIZE(txn->tw.reclaimed_pglist));
- goto retry;
+ goto retry_noaccount;
}
continue;
@@ -7376,7 +7423,7 @@ retry:
unsigned left = amount;
if (txn->tw.lifo_reclaimed == nullptr) {
mdbx_tassert(txn, lifo == 0);
- rc = mdbx_cursor_first(&mc, &key, &data);
+ rc = mdbx_cursor_first(&couple.outer, &key, &data);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
} else {
@@ -7412,7 +7459,7 @@ retry:
dbg_prefix_mode, fill_gc_id, filled_gc_slot);
key.iov_base = &fill_gc_id;
key.iov_len = sizeof(fill_gc_id);
- rc = mdbx_cursor_get(&mc, &key, &data, MDBX_SET_KEY);
+ rc = mdbx_cursor_get(&couple.outer, &key, &data, MDBX_SET_KEY);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
}
@@ -7425,7 +7472,7 @@ retry:
key.iov_len = sizeof(fill_gc_id);
mdbx_tassert(txn, data.iov_len >= sizeof(pgno_t) * 2);
- mc.mc_flags |= C_GCFREEZE;
+ couple.outer.mc_flags |= C_GCFREEZE;
unsigned chunk = (unsigned)(data.iov_len / sizeof(pgno_t)) - 1;
if (unlikely(chunk > left)) {
mdbx_trace("%s: chunk %u > left %u, @%" PRIaTXN, dbg_prefix_mode, chunk,
@@ -7434,20 +7481,21 @@ retry:
chunk - left > env->me_maxgc_ov1page) {
data.iov_len = (left + 1) * sizeof(pgno_t);
if (loop < 7)
- mc.mc_flags &= ~C_GCFREEZE;
+ couple.outer.mc_flags &= ~C_GCFREEZE;
}
chunk = left;
}
- rc = mdbx_cursor_put(&mc, &key, &data, MDBX_CURRENT | MDBX_RESERVE);
- mc.mc_flags &= ~C_GCFREEZE;
+ rc = mdbx_cursor_put(&couple.outer, &key, &data,
+ MDBX_CURRENT | MDBX_RESERVE);
+ couple.outer.mc_flags &= ~C_GCFREEZE;
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
clean_reserved_gc_pnl(env, data);
if (unlikely(txn->tw.loose_count ||
amount != MDBX_PNL_SIZE(txn->tw.reclaimed_pglist))) {
- mdbx_notice("** restart: reclaimed-list changed (%u -> %u, %u)", amount,
- MDBX_PNL_SIZE(txn->tw.reclaimed_pglist),
+ mdbx_notice("** restart: reclaimed-list growth (%u -> %u, loose +%u)",
+ amount, MDBX_PNL_SIZE(txn->tw.reclaimed_pglist),
txn->tw.loose_count);
goto retry;
}
@@ -7482,7 +7530,7 @@ retry:
if (txn->tw.lifo_reclaimed == nullptr) {
mdbx_tassert(txn, lifo == 0);
- rc = mdbx_cursor_next(&mc, &key, &data, MDBX_NEXT);
+ rc = mdbx_cursor_next(&couple.outer, &key, &data, MDBX_NEXT);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
} else {
@@ -7507,7 +7555,7 @@ retry:
cleaned_gc_slot == MDBX_PNL_SIZE(txn->tw.lifo_reclaimed));
bailout:
- txn->mt_cursors[FREE_DBI] = mc.mc_next;
+ txn->mt_cursors[FREE_DBI] = couple.outer.mc_next;
bailout_notracking:
MDBX_PNL_SIZE(txn->tw.reclaimed_pglist) = 0;
@@ -7519,17 +7567,25 @@ static int mdbx_flush_iov(MDBX_txn *const txn, struct iovec *iov,
unsigned iov_items, size_t iov_off,
size_t iov_bytes) {
MDBX_env *const env = txn->mt_env;
- int rc = mdbx_pwritev(env->me_lazy_fd, iov, iov_items, iov_off, iov_bytes);
- if (unlikely(rc != MDBX_SUCCESS)) {
- mdbx_error("Write error: %s", mdbx_strerror(rc));
- txn->mt_flags |= MDBX_TXN_ERROR;
- }
-
- for (unsigned i = 0; i < iov_items; i++)
- mdbx_dpage_free(env, (MDBX_page *)iov[i].iov_base,
- bytes2pgno(env, iov[i].iov_len));
+ mdbx_assert(env, iov_items > 0);
+ if (likely(iov_items == 1)) {
+ mdbx_assert(env, iov->iov_len == iov_bytes);
+ int rc = mdbx_pwrite(env->me_lazy_fd, iov->iov_base, iov_bytes, iov_off);
+ mdbx_dpage_free(env, (MDBX_page *)iov->iov_base,
+ bytes2pgno(env, iov_bytes));
+ return rc;
+ } else {
+ int rc = mdbx_pwritev(env->me_lazy_fd, iov, iov_items, iov_off, iov_bytes);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ mdbx_error("Write error: %s", mdbx_strerror(rc));
+ txn->mt_flags |= MDBX_TXN_ERROR;
+ }
- return rc;
+ for (unsigned i = 0; i < iov_items; i++)
+ mdbx_dpage_free(env, (MDBX_page *)iov[i].iov_base,
+ bytes2pgno(env, iov[i].iov_len));
+ return rc;
+ }
}
/* Flush (some) dirty pages to the map, after clearing their dirty flag.
@@ -7645,34 +7701,41 @@ static __cold bool mdbx_txn_import_dbi(MDBX_txn *txn, MDBX_dbi dbi) {
mdbx_compiler_barrier();
for (unsigned i = CORE_DBS; i < snap_numdbs; ++i) {
if (i >= txn->mt_numdbs)
- txn->mt_dbflags[i] = 0;
- if (!(txn->mt_dbflags[i] & DB_USRVALID) &&
+ txn->mt_dbstate[i] = 0;
+ if (!(txn->mt_dbstate[i] & DB_USRVALID) &&
(env->me_dbflags[i] & MDBX_VALID)) {
txn->mt_dbs[i].md_flags = env->me_dbflags[i] & PERSISTENT_FLAGS;
- txn->mt_dbflags[i] = DB_VALID | DB_USRVALID | DB_STALE;
+ txn->mt_dbstate[i] = DB_VALID | DB_USRVALID | DB_STALE;
mdbx_tassert(txn, txn->mt_dbxs[i].md_cmp != NULL);
}
}
txn->mt_numdbs = snap_numdbs;
mdbx_ensure(env, mdbx_fastmutex_release(&env->me_dbi_lock) == MDBX_SUCCESS);
- return txn->mt_dbflags[dbi] & DB_USRVALID;
+ return txn->mt_dbstate[dbi] & DB_USRVALID;
}
/* Check txn and dbi arguments to a function */
static __always_inline bool mdbx_txn_dbi_exists(MDBX_txn *txn, MDBX_dbi dbi,
unsigned validity) {
- if (likely(dbi < txn->mt_numdbs && (txn->mt_dbflags[dbi] & validity)))
+ if (likely(dbi < txn->mt_numdbs && (txn->mt_dbstate[dbi] & validity)))
return true;
return mdbx_txn_import_dbi(txn, dbi);
}
int mdbx_txn_commit(MDBX_txn *txn) {
- int rc = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_HAS_CHILD);
+ STATIC_ASSERT(MDBX_TXN_FINISHED ==
+ MDBX_TXN_BLOCKED - MDBX_TXN_HAS_CHILD - MDBX_TXN_ERROR);
+ int rc = check_txn(txn, MDBX_TXN_FINISHED);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
+ if (unlikely(txn->mt_flags & MDBX_TXN_ERROR)) {
+ rc = MDBX_RESULT_TRUE;
+ goto fail;
+ }
+
MDBX_env *env = txn->mt_env;
#if MDBX_TXN_CHECKPID
if (unlikely(env->me_pid != mdbx_getpid())) {
@@ -7736,12 +7799,12 @@ int mdbx_txn_commit(MDBX_txn *txn) {
/* Update parent's DB table. */
memcpy(parent->mt_dbs, txn->mt_dbs, txn->mt_numdbs * sizeof(MDBX_db));
parent->mt_numdbs = txn->mt_numdbs;
- parent->mt_dbflags[FREE_DBI] = txn->mt_dbflags[FREE_DBI];
- parent->mt_dbflags[MAIN_DBI] = txn->mt_dbflags[MAIN_DBI];
+ parent->mt_dbstate[FREE_DBI] = txn->mt_dbstate[FREE_DBI];
+ parent->mt_dbstate[MAIN_DBI] = txn->mt_dbstate[MAIN_DBI];
for (unsigned i = CORE_DBS; i < txn->mt_numdbs; i++) {
/* preserve parent's DB_NEW status */
- parent->mt_dbflags[i] =
- txn->mt_dbflags[i] | (parent->mt_dbflags[i] & (DB_CREAT | DB_FRESH));
+ parent->mt_dbstate[i] =
+ txn->mt_dbstate[i] | (parent->mt_dbstate[i] & (DB_CREAT | DB_FRESH));
}
/* Remove refunded pages from parent's dirty & spill lists */
@@ -7958,7 +8021,7 @@ int mdbx_txn_commit(MDBX_txn *txn) {
if (txn->tw.dirtylist->length == 0 &&
(txn->mt_flags & (MDBX_TXN_DIRTY | MDBX_TXN_SPILLS)) == 0) {
for (int i = txn->mt_numdbs; --i >= 0;)
- mdbx_tassert(txn, (txn->mt_dbflags[i] & DB_DIRTY) == 0);
+ mdbx_tassert(txn, (txn->mt_dbstate[i] & DB_DIRTY) == 0);
rc = MDBX_SUCCESS;
goto done;
}
@@ -7970,15 +8033,15 @@ int mdbx_txn_commit(MDBX_txn *txn) {
/* Update DB root pointers */
if (txn->mt_numdbs > CORE_DBS) {
- MDBX_cursor mc;
+ MDBX_cursor_couple couple;
MDBX_val data;
data.iov_len = sizeof(MDBX_db);
- rc = mdbx_cursor_init(&mc, txn, MAIN_DBI);
+ rc = mdbx_cursor_init(&couple.outer, txn, MAIN_DBI);
if (unlikely(rc != MDBX_SUCCESS))
goto fail;
for (MDBX_dbi i = CORE_DBS; i < txn->mt_numdbs; i++) {
- if (txn->mt_dbflags[i] & DB_DIRTY) {
+ if (txn->mt_dbstate[i] & DB_DIRTY) {
if (unlikely(TXN_DBI_CHANGED(txn, i))) {
rc = MDBX_BAD_DBI;
goto fail;
@@ -7986,8 +8049,9 @@ int mdbx_txn_commit(MDBX_txn *txn) {
MDBX_db *db = &txn->mt_dbs[i];
db->md_mod_txnid = txn->mt_txnid;
data.iov_base = db;
- WITH_CURSOR_TRACKING(mc,
- rc = mdbx_cursor_put(&mc, &txn->mt_dbxs[i].md_name,
+ WITH_CURSOR_TRACKING(couple.outer,
+ rc = mdbx_cursor_put(&couple.outer,
+ &txn->mt_dbxs[i].md_name,
&data, F_SUBDATA));
if (unlikely(rc != MDBX_SUCCESS))
goto fail;
@@ -8837,7 +8901,7 @@ mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t size_now,
}
/* get untouched params from DB */
- if (pagesize < 0)
+ if (pagesize <= 0 || pagesize >= INT_MAX)
pagesize = env->me_psize;
if (size_lower < 0)
size_lower = pgno2bytes(env, head->mm_geo.lower);
@@ -8867,19 +8931,34 @@ mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t size_now,
if (unlikely(inside_txn))
return MDBX_PANIC;
- if (pagesize < 0) {
- pagesize = env->me_os_psize;
- if ((uintptr_t)pagesize > MAX_PAGESIZE)
- pagesize = MAX_PAGESIZE;
- mdbx_assert(env, (uintptr_t)pagesize >= MIN_PAGESIZE);
+ /* is requested some auto-value for pagesize ? */
+ if (pagesize >= INT_MAX /* maximal */)
+ pagesize = MAX_PAGESIZE;
+ else if (pagesize <= 0) {
+ if (pagesize < 0 /* default */) {
+ pagesize = env->me_os_psize;
+ if ((uintptr_t)pagesize > MAX_PAGESIZE)
+ pagesize = MAX_PAGESIZE;
+ mdbx_assert(env, (uintptr_t)pagesize >= MIN_PAGESIZE);
+ } else if (pagesize == 0 /* minimal */)
+ pagesize = MIN_PAGESIZE;
+
+ /* choose pagesize */
+ intptr_t max_size = (size_now > size_lower) ? size_now : size_lower;
+ max_size = (size_upper > max_size) ? size_upper : max_size;
+ if (max_size < 0 /* default */)
+ max_size = DEFAULT_MAPSIZE;
+ else if (max_size == 0 /* minimal */)
+ max_size = MIN_MAPSIZE;
+ else if (max_size >= (intptr_t)MAX_MAPSIZE /* maximal */)
+ max_size = MAX_MAPSIZE;
+
+ while (max_size > pagesize * (int64_t)MAX_PAGENO &&
+ pagesize < MAX_PAGESIZE)
+ pagesize <<= 1;
}
}
- if (pagesize == 0)
- pagesize = MIN_PAGESIZE;
- else if (pagesize == INTPTR_MAX)
- pagesize = MAX_PAGESIZE;
-
if (pagesize < (intptr_t)MIN_PAGESIZE || pagesize > (intptr_t)MAX_PAGESIZE ||
!is_powerof2(pagesize)) {
rc = MDBX_EINVAL;
@@ -8891,6 +8970,8 @@ mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t size_now,
if (MIN_MAPSIZE / pagesize < MIN_PAGENO)
size_lower = MIN_PAGENO * pagesize;
}
+ if (size_lower == INTPTR_MAX)
+ size_lower = MAX_MAPSIZE;
if (size_now <= 0) {
size_now = DEFAULT_MAPSIZE;
@@ -8899,6 +8980,8 @@ mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t size_now,
if (size_upper >= size_lower && size_now > size_upper)
size_now = size_upper;
}
+ if (size_now == INTPTR_MAX)
+ size_now = MAX_MAPSIZE;
if (size_upper <= 0) {
if ((size_t)size_now >= MAX_MAPSIZE / 2)
@@ -8932,8 +9015,8 @@ mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t size_now,
goto bailout;
}
- const size_t unit =
- (env->me_os_psize > (size_t)pagesize) ? env->me_os_psize : pagesize;
+ const size_t unit = (env->me_os_psize > (size_t)pagesize) ? env->me_os_psize
+ : (size_t)pagesize;
size_lower = ceil_powerof2(size_lower, unit);
size_upper = ceil_powerof2(size_upper, unit);
size_now = ceil_powerof2(size_now, unit);
@@ -9100,6 +9183,7 @@ mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t size_now,
env->me_txn->mt_geo = new_geo;
env->me_txn->mt_flags |= MDBX_TXN_DIRTY;
} else {
+ meta.mm_geo = new_geo;
mdbx_meta_set_txnid(
env, &meta, safe64_txnid_next(mdbx_meta_txnid_stable(env, head)));
rc = mdbx_sync_locked(env, env->me_flags, &meta);
@@ -9227,8 +9311,9 @@ static int __cold mdbx_setup_dxb(MDBX_env *env, const int lck_rc) {
meta.mm_geo.upper * pagesize, meta.mm_geo.grow * pagesize,
meta.mm_geo.shrink * pagesize, meta.mm_psize);
if (unlikely(err != MDBX_SUCCESS)) {
- mdbx_error("%s", "could not use present dbsize-params from db");
- return MDBX_INCOMPATIBLE;
+ mdbx_error("%s: err %d", "could not apply preconfigured db-geometry",
+ err);
+ return (err == MDBX_EINVAL) ? MDBX_INCOMPATIBLE : err;
}
} else if (env->me_dbgeo.now) {
/* silently growth to last used page */
@@ -9259,8 +9344,9 @@ static int __cold mdbx_setup_dxb(MDBX_env *env, const int lck_rc) {
env->me_dbgeo.upper, env->me_dbgeo.grow,
env->me_dbgeo.shrink, meta.mm_psize);
if (unlikely(err != MDBX_SUCCESS)) {
- mdbx_error("%s", "could not apply preconfigured dbsize-params to db");
- return MDBX_INCOMPATIBLE;
+ mdbx_error("%s: err %d", "could not apply preconfigured db-geometry",
+ err);
+ return (err == MDBX_EINVAL) ? MDBX_INCOMPATIBLE : err;
}
/* update meta fields */
@@ -9726,6 +9812,16 @@ static int __cold mdbx_setup_lck(MDBX_env *env, char *lck_pathname,
mdbx_jitter4testing(false);
lck->mti_magic_and_version = MDBX_LOCK_MAGIC;
lck->mti_os_and_format = MDBX_LOCK_FORMAT;
+ err = mdbx_msync(&env->me_lck_mmap, 0, (size_t)size, false);
+ if (unlikely(err != MDBX_SUCCESS)) {
+ mdbx_error("initial-%s for lck-file failed", "msync");
+ goto bailout;
+ }
+ err = mdbx_filesync(env->me_lck_mmap.fd, MDBX_SYNC_SIZE);
+ if (unlikely(err != MDBX_SUCCESS)) {
+ mdbx_error("initial-%s for lck-file failed", "fsync");
+ goto bailout;
+ }
} else {
if (lck->mti_magic_and_version != MDBX_LOCK_MAGIC) {
mdbx_error("%s", "lock region has invalid magic/version");
@@ -9896,6 +9992,19 @@ __cold int mdbx_is_readahead_reasonable(size_t volume, intptr_t redundancy) {
#error "Persistent DB flags & env flags overlap, but both go in mm_flags"
#endif
+/* Merge flags and avoid false MDBX_UTTERLY_NOSYNC */
+static uint32_t merge_flags(const uint32_t a, const uint32_t b) {
+ uint32_t r = a | b;
+ if (F_ISSET(r, MDBX_UTTERLY_NOSYNC) && !F_ISSET(a, MDBX_UTTERLY_NOSYNC) &&
+ !F_ISSET(b, MDBX_UTTERLY_NOSYNC))
+ r -= (r & MDBX_WRITEMAP) ? MDBX_UTTERLY_NOSYNC ^ MDBX_MAPASYNC
+ : MDBX_UTTERLY_NOSYNC ^ MDBX_SAFE_NOSYNC;
+ assert(!(F_ISSET(r, MDBX_UTTERLY_NOSYNC) &&
+ !F_ISSET(a, MDBX_UTTERLY_NOSYNC) &&
+ !F_ISSET(b, MDBX_UTTERLY_NOSYNC)));
+ return r;
+}
+
int __cold mdbx_env_open(MDBX_env *env, const char *pathname, unsigned flags,
mode_t mode) {
if (unlikely(!env || !pathname))
@@ -9911,6 +10020,71 @@ int __cold mdbx_env_open(MDBX_env *env, const char *pathname, unsigned flags,
(env->me_flags & MDBX_ENV_ACTIVE) != 0)
return MDBX_EPERM;
+#if defined(_WIN32) || defined(_WIN64)
+ const size_t wlen = mbstowcs(nullptr, pathname, INT_MAX);
+ if (wlen < 1 || wlen > /* MAX_PATH */ INT16_MAX)
+ return ERROR_INVALID_NAME;
+ wchar_t *const pathnameW = _alloca((wlen + 1) * sizeof(wchar_t));
+ if (wlen != mbstowcs(pathnameW, pathname, wlen + 1))
+ return ERROR_INVALID_NAME;
+#endif /* Windows */
+
+ /* pickup previously mdbx_env_set_flags(),
+ * but avoid MDBX_UTTERLY_NOSYNC by disjunction */
+ flags = merge_flags(flags, env->me_flags);
+
+#if defined(_WIN32) || defined(_WIN64)
+ const DWORD dwAttrib = GetFileAttributesW(pathnameW);
+ if (dwAttrib == INVALID_FILE_ATTRIBUTES) {
+ int rc = GetLastError();
+ if (rc != MDBX_ENOFILE)
+ return rc;
+ if (mode == 0 || (flags & MDBX_RDONLY) != 0)
+ /* can't open existing */
+ return rc;
+
+ /* auto-create directory if requested */
+ if ((flags & MDBX_NOSUBDIR) == 0 && !CreateDirectoryW(pathnameW, nullptr)) {
+ rc = GetLastError();
+ if (rc != ERROR_ALREADY_EXISTS)
+ return rc;
+ }
+ } else {
+ /* ignore passed MDBX_NOSUBDIR flag and set it automatically */
+ flags |= MDBX_NOSUBDIR;
+ if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)
+ flags -= MDBX_NOSUBDIR;
+ }
+#else
+ struct stat st;
+ if (stat(pathname, &st)) {
+ int rc = errno;
+ if (rc != MDBX_ENOFILE)
+ return rc;
+ if (mode == 0 || (flags & MDBX_RDONLY) != 0)
+ /* can't open existing */
+ return rc;
+
+ /* auto-create directory if requested */
+ const mode_t dir_mode =
+ (/* inherit read/write permissions for group and others */ mode &
+ (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) |
+ /* always add read/write/search for owner */ S_IRWXU |
+ ((mode & S_IRGRP) ? /* +search if readable by group */ S_IXGRP : 0) |
+ ((mode & S_IROTH) ? /* +search if readable by others */ S_IXOTH : 0);
+ if ((flags & MDBX_NOSUBDIR) == 0 && mkdir(pathname, dir_mode)) {
+ rc = errno;
+ if (rc != EEXIST)
+ return rc;
+ }
+ } else {
+ /* ignore passed MDBX_NOSUBDIR flag and set it automatically */
+ flags |= MDBX_NOSUBDIR;
+ if (S_ISDIR(st.st_mode))
+ flags -= MDBX_NOSUBDIR;
+ }
+#endif
+
size_t len_full, len = strlen(pathname);
if (flags & MDBX_NOSUBDIR) {
len_full = len + sizeof(MDBX_LOCK_SUFFIX) + len + 1;
@@ -9933,7 +10107,6 @@ int __cold mdbx_env_open(MDBX_env *env, const char *pathname, unsigned flags,
}
int rc = MDBX_SUCCESS;
- flags |= env->me_flags;
if (flags & MDBX_RDONLY) {
/* LY: silently ignore irrelevant flags when
* we're only getting read access */
@@ -9976,36 +10149,7 @@ int __cold mdbx_env_open(MDBX_env *env, const char *pathname, unsigned flags,
}
env->me_dbxs[FREE_DBI].md_cmp =
mdbx_cmp_int_align4; /* aligned MDBX_INTEGERKEY */
-
- if ((flags & (MDBX_RDONLY | MDBX_NOSUBDIR)) == 0 && mode != 0) {
-#if defined(_WIN32) || defined(_WIN64)
- const size_t wlen = mbstowcs(nullptr, pathname, INT_MAX);
- if (wlen < 1 || wlen > /* MAX_PATH */ INT16_MAX)
- return ERROR_INVALID_NAME;
- wchar_t *const pathnameW = _alloca((wlen + 1) * sizeof(wchar_t));
- if (wlen != mbstowcs(pathnameW, pathname, wlen + 1)) {
- rc = ERROR_INVALID_NAME;
- goto bailout;
- }
- if (!CreateDirectoryW(pathnameW, nullptr)) {
- rc = GetLastError();
- if (rc != ERROR_ALREADY_EXISTS)
- goto bailout;
- }
-#else
- const mode_t dir_mode =
- (/* inherit read/write permissions for group and others */ mode &
- (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) |
- /* always add read/write/search for owner */ S_IRWXU |
- ((mode & S_IRGRP) ? /* +search if readable by group */ S_IXGRP : 0) |
- ((mode & S_IROTH) ? /* +search if readable by others */ S_IXOTH : 0);
- if (mkdir(pathname, dir_mode)) {
- rc = errno;
- if (rc != EEXIST)
- goto bailout;
- }
-#endif
- }
+ env->me_dbxs[FREE_DBI].md_dcmp = mdbx_cmp_lenfast;
rc = mdbx_openfile(F_ISSET(flags, MDBX_RDONLY) ? MDBX_OPEN_DXB_READ
: MDBX_OPEN_DXB_LAZY,
@@ -10031,7 +10175,7 @@ int __cold mdbx_env_open(MDBX_env *env, const char *pathname, unsigned flags,
#if !(defined(_WIN32) || defined(_WIN64))
if (mode == 0) {
- struct stat st;
+ /* pickup mode for lck-file */
if (fstat(env->me_lazy_fd, &st)) {
rc = errno;
goto bailout;
@@ -10039,7 +10183,6 @@ int __cold mdbx_env_open(MDBX_env *env, const char *pathname, unsigned flags,
mode = st.st_mode;
}
#endif /* !Windows */
-
const int lck_rc = mdbx_setup_lck(env, lck_pathname, mode);
if (MDBX_IS_ERROR(lck_rc)) {
rc = lck_rc;
@@ -10123,7 +10266,7 @@ int __cold mdbx_env_open(MDBX_env *env, const char *pathname, unsigned flags,
txn->mt_dbs = (MDBX_db *)((char *)txn + tsize);
txn->mt_cursors = (MDBX_cursor **)(txn->mt_dbs + env->me_maxdbs);
txn->mt_dbiseqs = (unsigned *)(txn->mt_cursors + env->me_maxdbs);
- txn->mt_dbflags = (uint8_t *)(txn->mt_dbiseqs + env->me_maxdbs);
+ txn->mt_dbstate = (uint8_t *)(txn->mt_dbiseqs + env->me_maxdbs);
txn->mt_env = env;
txn->mt_dbxs = env->me_dbxs;
txn->mt_flags = MDBX_TXN_FINISHED;
@@ -10176,7 +10319,6 @@ static int __cold mdbx_env_close0(MDBX_env *env) {
return MDBX_SUCCESS;
}
- env->me_signature = 0;
env->me_flags &= ~MDBX_ENV_ACTIVE;
env->me_oldest = nullptr;
env->me_sync_timestamp = nullptr;
@@ -10289,6 +10431,7 @@ int __cold mdbx_env_close_ex(MDBX_env *env, int dont_sync) {
#endif
}
+ env->me_signature = 0;
rc = mdbx_env_close0(env) ? MDBX_PANIC : rc;
mdbx_ensure(env, mdbx_fastmutex_destroy(&env->me_dbi_lock) == MDBX_SUCCESS);
#if defined(_WIN32) || defined(_WIN64)
@@ -10400,13 +10543,19 @@ static int __hot mdbx_cmp_memnr(const MDBX_val *a, const MDBX_val *b) {
return CMP2INT(a->iov_len, b->iov_len);
}
+/* Fast non-lexically comparator */
+static int __hot mdbx_cmp_lenfast(const MDBX_val *a, const MDBX_val *b) {
+ int diff = CMP2INT(a->iov_len, b->iov_len);
+ return likely(diff) ? diff : memcmp(a->iov_base, b->iov_base, a->iov_len);
+}
+
/* Search for key within a page, using binary search.
* Returns the smallest entry larger or equal to the key.
* If exactp is non-null, stores whether the found entry was an exact match
* in *exactp (1 or 0).
* Updates the cursor index with the index of the found entry.
* If no entry larger or equal to the key is found, returns NULL. */
-static MDBX_node *__hot mdbx_node_search(MDBX_cursor *mc, MDBX_val *key,
+static MDBX_node *__hot mdbx_node_search(MDBX_cursor *mc, const MDBX_val *key,
int *exactp) {
MDBX_page *mp = mc->mc_pg[mc->mc_top];
const int nkeys = page_numkeys(mp);
@@ -10614,7 +10763,7 @@ dirty:
}
if (mdbx_audit_enabled()) {
- int err = mdbx_page_check(env, p, true);
+ int err = mdbx_page_check(mc, p, C_UPDATING);
if (unlikely(err != MDBX_SUCCESS))
return err;
}
@@ -10630,7 +10779,7 @@ corrupted:
/* Finish mdbx_page_search() / mdbx_page_search_lowest().
* The cursor is at the root page, set up the rest of it. */
-__hot static int mdbx_page_search_root(MDBX_cursor *mc, MDBX_val *key,
+__hot static int mdbx_page_search_root(MDBX_cursor *mc, const MDBX_val *key,
int flags) {
MDBX_page *mp = mc->mc_pg[mc->mc_top];
int rc;
@@ -10709,25 +10858,54 @@ __hot static int mdbx_page_search_root(MDBX_cursor *mc, MDBX_val *key,
return MDBX_SUCCESS;
}
+static int mdbx_setup_dbx(MDBX_dbx *const dbx, const MDBX_db *const db,
+ const unsigned pagesize) {
+ if (unlikely(!dbx->md_cmp)) {
+ dbx->md_cmp = mdbx_default_keycmp(db->md_flags);
+ dbx->md_dcmp = mdbx_default_datacmp(db->md_flags);
+ }
+
+ dbx->md_klen_min =
+ (db->md_flags & MDBX_INTEGERKEY) ? 4 /* sizeof(uint32_t) */ : 0;
+ dbx->md_klen_max = mdbx_limits_keysize_max(pagesize, db->md_flags);
+ assert(dbx->md_klen_max != (unsigned)-1);
+
+ dbx->md_vlen_min = (db->md_flags & MDBX_INTEGERDUP)
+ ? 4 /* sizeof(uint32_t) */
+ : ((db->md_flags & MDBX_DUPFIXED) ? 1 : 0);
+ dbx->md_vlen_max = mdbx_limits_valsize_max(pagesize, db->md_flags);
+ assert(dbx->md_vlen_max != (unsigned)-1);
+
+ if ((db->md_flags & (MDBX_DUPFIXED | MDBX_INTEGERDUP)) != 0 && db->md_xsize) {
+ if (unlikely(db->md_xsize < dbx->md_vlen_min ||
+ db->md_xsize > dbx->md_vlen_max))
+ return MDBX_CORRUPTED;
+ dbx->md_vlen_min = dbx->md_vlen_max = db->md_xsize;
+ }
+ return MDBX_SUCCESS;
+}
+
static int mdbx_fetch_sdb(MDBX_txn *txn, MDBX_dbi dbi) {
- MDBX_cursor mc;
+ MDBX_cursor_couple couple;
if (unlikely(TXN_DBI_CHANGED(txn, dbi)))
return MDBX_BAD_DBI;
- int rc = mdbx_cursor_init(&mc, txn, MAIN_DBI);
+ int rc = mdbx_cursor_init(&couple.outer, txn, MAIN_DBI);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
- rc = mdbx_page_search(&mc, &txn->mt_dbxs[dbi].md_name, 0);
+
+ MDBX_dbx *const dbx = &txn->mt_dbxs[dbi];
+ rc = mdbx_page_search(&couple.outer, &dbx->md_name, 0);
if (unlikely(rc != MDBX_SUCCESS))
return (rc == MDBX_NOTFOUND) ? MDBX_BAD_DBI : rc;
MDBX_val data;
int exact = 0;
- MDBX_node *node = mdbx_node_search(&mc, &txn->mt_dbxs[dbi].md_name, &exact);
+ MDBX_node *node = mdbx_node_search(&couple.outer, &dbx->md_name, &exact);
if (unlikely(!exact))
return MDBX_BAD_DBI;
if (unlikely((node_flags(node) & (F_DUPDATA | F_SUBDATA)) != F_SUBDATA))
return MDBX_INCOMPATIBLE; /* not a named DB */
- rc = mdbx_node_read(&mc, node, &data);
+ rc = mdbx_node_read(&couple.outer, node, &data);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
@@ -10737,11 +10915,16 @@ static int mdbx_fetch_sdb(MDBX_txn *txn, MDBX_dbi dbi) {
uint16_t md_flags = UNALIGNED_PEEK_16(data.iov_base, MDBX_db, md_flags);
/* The txn may not know this DBI, or another process may
* have dropped and recreated the DB with other flags. */
- if (unlikely((txn->mt_dbs[dbi].md_flags & PERSISTENT_FLAGS) != md_flags))
+ MDBX_db *const db = &txn->mt_dbs[dbi];
+ if (unlikely((db->md_flags & PERSISTENT_FLAGS) != md_flags))
return MDBX_INCOMPATIBLE;
- memcpy(&txn->mt_dbs[dbi], data.iov_base, sizeof(MDBX_db));
- txn->mt_dbflags[dbi] &= ~DB_STALE;
+ memcpy(db, data.iov_base, sizeof(MDBX_db));
+ rc = mdbx_setup_dbx(dbx, db, txn->mt_env->me_psize);
+ if (unlikely(rc != MDBX_SUCCESS))
+ return rc;
+
+ txn->mt_dbstate[dbi] &= ~DB_STALE;
return MDBX_SUCCESS;
}
@@ -10779,7 +10962,8 @@ __hot static int mdbx_page_search_lowest(MDBX_cursor *mc) {
* lookups.
*
* Returns 0 on success, non-zero on failure. */
-__hot static int mdbx_page_search(MDBX_cursor *mc, MDBX_val *key, int flags) {
+__hot static int mdbx_page_search(MDBX_cursor *mc, const MDBX_val *key,
+ int flags) {
int rc;
pgno_t root;
@@ -10791,7 +10975,7 @@ __hot static int mdbx_page_search(MDBX_cursor *mc, MDBX_val *key, int flags) {
}
/* Make sure we're using an up-to-date root */
- if (unlikely(*mc->mc_dbflag & DB_STALE)) {
+ if (unlikely(*mc->mc_dbstate & DB_STALE)) {
rc = mdbx_fetch_sdb(mc->mc_txn, mc->mc_dbi);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
@@ -11215,13 +11399,34 @@ static int mdbx_cursor_set(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
exactp = exactp ? exactp : &stub_exactp;
- if ((mc->mc_db->md_flags & MDBX_INTEGERKEY) &&
- unlikely(key->iov_len != sizeof(uint32_t) &&
- key->iov_len != sizeof(uint64_t))) {
- mdbx_cassert(mc, !"key-size is invalid for MDBX_INTEGERKEY");
+ if (unlikely(key->iov_len < mc->mc_dbx->md_klen_min ||
+ key->iov_len > mc->mc_dbx->md_klen_max)) {
+ mdbx_cassert(mc, !"Invalid key-size");
return MDBX_BAD_VALSIZE;
}
+ MDBX_val aligned_key = *key;
+ uint64_t aligned_keybytes;
+ if (mc->mc_db->md_flags & MDBX_INTEGERKEY) {
+ switch (aligned_key.iov_len) {
+ default:
+ mdbx_cassert(mc, !"key-size is invalid for MDBX_INTEGERKEY");
+ return MDBX_BAD_VALSIZE;
+ case 4:
+ if (unlikely(3 & (uintptr_t)aligned_key.iov_base))
+ /* copy instead of return error to avoid break compatibility */
+ aligned_key.iov_base =
+ memcpy(&aligned_keybytes, aligned_key.iov_base, 4);
+ break;
+ case 8:
+ if (unlikely(7 & (uintptr_t)aligned_key.iov_base))
+ /* copy instead of return error to avoid break compatibility */
+ aligned_key.iov_base =
+ memcpy(&aligned_keybytes, aligned_key.iov_base, 8);
+ break;
+ }
+ }
+
if (mc->mc_xcursor)
mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED | C_EOF);
@@ -11242,7 +11447,7 @@ static int mdbx_cursor_set(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
node = page_node(mp, 0);
get_key(node, &nodekey);
}
- rc = mc->mc_dbx->md_cmp(key, &nodekey);
+ rc = mc->mc_dbx->md_cmp(&aligned_key, &nodekey);
if (unlikely(rc == 0)) {
/* Probably happens rarely, but first node on the page
* was the one we wanted. */
@@ -11260,7 +11465,7 @@ static int mdbx_cursor_set(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
node = page_node(mp, nkeys - 1);
get_key(node, &nodekey);
}
- rc = mc->mc_dbx->md_cmp(key, &nodekey);
+ rc = mc->mc_dbx->md_cmp(&aligned_key, &nodekey);
if (rc == 0) {
/* last node was the one we wanted */
mdbx_cassert(mc, nkeys >= 1 && nkeys <= UINT16_MAX + 1);
@@ -11278,7 +11483,7 @@ static int mdbx_cursor_set(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
node = page_node(mp, mc->mc_ki[mc->mc_top]);
get_key(node, &nodekey);
}
- rc = mc->mc_dbx->md_cmp(key, &nodekey);
+ rc = mc->mc_dbx->md_cmp(&aligned_key, &nodekey);
if (rc == 0) {
/* current node was the one we wanted */
*exactp = 1;
@@ -11315,7 +11520,7 @@ static int mdbx_cursor_set(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
mc->mc_pg[0] = 0;
}
- rc = mdbx_page_search(mc, key, 0);
+ rc = mdbx_page_search(mc, &aligned_key, 0);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
@@ -11323,7 +11528,7 @@ static int mdbx_cursor_set(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
mdbx_cassert(mc, IS_LEAF(mp));
set2:
- node = mdbx_node_search(mc, key, exactp);
+ node = mdbx_node_search(mc, &aligned_key, exactp);
if (exactp != &stub_exactp && !*exactp) {
/* MDBX_SET specified and not an exact match. */
return MDBX_NOTFOUND;
@@ -11362,25 +11567,43 @@ set1:
if (op == MDBX_SET || op == MDBX_SET_KEY || op == MDBX_SET_RANGE) {
rc = mdbx_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL);
} else {
- int ex2, *ex2p;
- if (op == MDBX_GET_BOTH) {
- ex2p = &ex2;
- ex2 = 0;
- } else {
- ex2p = NULL;
- }
+ int ex2 = 0, *ex2p = (op == MDBX_GET_BOTH) ? &ex2 : NULL;
rc = mdbx_cursor_set(&mc->mc_xcursor->mx_cursor, data, NULL,
MDBX_SET_RANGE, ex2p);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
}
} else if (op == MDBX_GET_BOTH || op == MDBX_GET_BOTH_RANGE) {
+ if (unlikely(data->iov_len < mc->mc_dbx->md_vlen_min ||
+ data->iov_len > mc->mc_dbx->md_vlen_max)) {
+ mdbx_cassert(mc, !"Invalid data-size");
+ return MDBX_BAD_VALSIZE;
+ }
+ MDBX_val aligned_data = *data;
+ uint64_t aligned_databytes;
+ if (mc->mc_db->md_flags & MDBX_INTEGERDUP) {
+ switch (aligned_data.iov_len) {
+ default:
+ mdbx_cassert(mc, !"data-size is invalid for MDBX_INTEGERDUP");
+ return MDBX_BAD_VALSIZE;
+ case 4:
+ if (unlikely(3 & (uintptr_t)aligned_data.iov_base))
+ /* copy instead of return error to avoid break compatibility */
+ aligned_data.iov_base =
+ memcpy(&aligned_databytes, aligned_data.iov_base, 4);
+ break;
+ case 8:
+ if (unlikely(7 & (uintptr_t)aligned_data.iov_base))
+ /* copy instead of return error to avoid break compatibility */
+ aligned_data.iov_base =
+ memcpy(&aligned_databytes, aligned_data.iov_base, 8);
+ break;
+ }
+ }
MDBX_val olddata;
if (unlikely((rc = mdbx_node_read(mc, node, &olddata)) != MDBX_SUCCESS))
return rc;
- if (unlikely(mc->mc_dbx->md_dcmp == NULL))
- return MDBX_EINVAL;
- rc = mc->mc_dbx->md_dcmp(data, &olddata);
+ rc = mc->mc_dbx->md_dcmp(&aligned_data, &olddata);
if (rc) {
if (op != MDBX_GET_BOTH_RANGE || rc > 0)
return MDBX_NOTFOUND;
@@ -11668,7 +11891,7 @@ static int mdbx_cursor_touch(MDBX_cursor *mc) {
int rc = MDBX_SUCCESS;
if (mc->mc_dbi >= CORE_DBS &&
- (*mc->mc_dbflag & (DB_DIRTY | DB_DUPDATA)) == 0) {
+ (*mc->mc_dbstate & (DB_DIRTY | DB_DUPDATA)) == 0) {
mdbx_cassert(mc, (mc->mc_flags & C_RECLAIMING) == 0);
/* Touch DB record of named DB */
MDBX_cursor_couple cx;
@@ -11680,7 +11903,7 @@ static int mdbx_cursor_touch(MDBX_cursor *mc) {
rc = mdbx_page_search(&cx.outer, &mc->mc_dbx->md_name, MDBX_PS_MODIFY);
if (unlikely(rc))
return rc;
- *mc->mc_dbflag |= DB_DIRTY;
+ *mc->mc_dbstate |= DB_DIRTY;
}
mc->mc_top = 0;
if (mc->mc_snum) {
@@ -11727,7 +11950,8 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
}
if (flags & MDBX_RESERVE) {
- if (unlikely(mc->mc_db->md_flags & (MDBX_DUPSORT | MDBX_REVERSEDUP)))
+ if (unlikely(mc->mc_db->md_flags & (MDBX_DUPSORT | MDBX_REVERSEDUP |
+ MDBX_INTEGERDUP | MDBX_DUPFIXED)))
return MDBX_INCOMPATIBLE;
data->iov_base = nullptr;
}
@@ -11738,37 +11962,64 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
if (unlikely(mc->mc_txn->mt_flags & (MDBX_RDONLY | MDBX_TXN_BLOCKED)))
return (mc->mc_txn->mt_flags & MDBX_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN;
- if ((mc->mc_flags & C_SUB) == 0) {
- if (unlikely(key->iov_len > (size_t)((mc->mc_db->md_flags & MDBX_DUPSORT)
- ? env->me_maxkey_ds
- : env->me_maxkey_nd) ||
- data->iov_len > ((mc->mc_db->md_flags & MDBX_DUPSORT)
- ? env->me_maxval_ds
- : env->me_maxval_nd))) {
+ uint64_t aligned_keybytes, aligned_databytes;
+ MDBX_val aligned_key, aligned_data;
+ if (likely((mc->mc_flags & C_SUB) == 0)) {
+ if (unlikely(key->iov_len < mc->mc_dbx->md_klen_min ||
+ key->iov_len > mc->mc_dbx->md_klen_max)) {
+ mdbx_cassert(mc, !"Invalid key-size");
+ return MDBX_BAD_VALSIZE;
+ }
+ if (unlikely(data->iov_len < mc->mc_dbx->md_vlen_min ||
+ data->iov_len > mc->mc_dbx->md_vlen_max)) {
+ mdbx_cassert(mc, !"Invalid data-size");
return MDBX_BAD_VALSIZE;
}
- if ((mc->mc_db->md_flags & MDBX_INTEGERKEY)) {
- if (unlikely(key->iov_len != sizeof(uint32_t) &&
- key->iov_len != sizeof(uint64_t))) {
+ if (mc->mc_db->md_flags & MDBX_INTEGERKEY) {
+ switch (key->iov_len) {
+ default:
mdbx_cassert(mc, !"key-size is invalid for MDBX_INTEGERKEY");
return MDBX_BAD_VALSIZE;
- }
- if (unlikely(3 & (uintptr_t)key->iov_base)) {
- mdbx_cassert(mc, !"key-alignment is invalid for MDBX_INTEGERKEY");
- return MDBX_BAD_VALSIZE;
+ case 4:
+ if (unlikely(3 & (uintptr_t)key->iov_base)) {
+ /* copy instead of return error to avoid break compatibility */
+ aligned_key.iov_base =
+ memcpy(&aligned_keybytes, key->iov_base, aligned_key.iov_len = 4);
+ key = &aligned_key;
+ }
+ break;
+ case 8:
+ if (unlikely(7 & (uintptr_t)key->iov_base)) {
+ /* copy instead of return error to avoid break compatibility */
+ aligned_key.iov_base =
+ memcpy(&aligned_keybytes, key->iov_base, aligned_key.iov_len = 8);
+ key = &aligned_key;
+ }
+ break;
}
}
-
- if ((mc->mc_db->md_flags & MDBX_INTEGERDUP)) {
- if (unlikely(data->iov_len != sizeof(uint32_t) &&
- data->iov_len != sizeof(uint64_t))) {
- mdbx_cassert(mc, !"data-size is invalid for MDBX_INTEGERDUP");
- return MDBX_BAD_VALSIZE;
- }
- if (unlikely(3 & (uintptr_t)data->iov_base)) {
- mdbx_cassert(mc, !"data-alignment is invalid for MDBX_INTEGERDUP");
+ if (mc->mc_db->md_flags & MDBX_INTEGERDUP) {
+ switch (data->iov_len) {
+ default:
+ mdbx_cassert(mc, !"data-size is invalid for MDBX_INTEGERKEY");
return MDBX_BAD_VALSIZE;
+ case 4:
+ if (unlikely(3 & (uintptr_t)data->iov_base)) {
+ /* copy instead of return error to avoid break compatibility */
+ aligned_data.iov_base = memcpy(&aligned_databytes, data->iov_base,
+ aligned_data.iov_len = 4);
+ data = &aligned_data;
+ }
+ break;
+ case 8:
+ if (unlikely(7 & (uintptr_t)data->iov_base)) {
+ /* copy instead of return error to avoid break compatibility */
+ aligned_data.iov_base = memcpy(&aligned_databytes, data->iov_base,
+ aligned_data.iov_len = 8);
+ data = &aligned_data;
+ }
+ break;
}
}
}
@@ -11827,12 +12078,10 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
rc = MDBX_NO_ROOT;
} else if ((flags & MDBX_CURRENT) == 0) {
int exact = 0;
- MDBX_val d2;
if ((flags & MDBX_APPEND) != 0 && mc->mc_db->md_entries > 0) {
- MDBX_val k2;
- rc = mdbx_cursor_last(mc, &k2, &d2);
+ rc = mdbx_cursor_last(mc, &dkey, &olddata);
if (rc == 0) {
- rc = mc->mc_dbx->md_cmp(key, &k2);
+ rc = mc->mc_dbx->md_cmp(key, &dkey);
if (rc > 0) {
rc = MDBX_NOTFOUND;
mc->mc_ki[mc->mc_top]++;
@@ -11842,15 +12091,26 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
}
}
} else {
- rc = mdbx_cursor_set(mc, (MDBX_val *)key, &d2, MDBX_SET, &exact);
+ rc = mdbx_cursor_set(mc, (MDBX_val *)key, &olddata, MDBX_SET, &exact);
}
if ((flags & MDBX_NOOVERWRITE) &&
(rc == MDBX_SUCCESS || rc == MDBX_EKEYMISMATCH)) {
mdbx_debug("duplicate key [%s]", DKEY(key));
- *data = d2;
+ *data = olddata;
return MDBX_KEYEXIST;
}
- if (rc && unlikely(rc != MDBX_NOTFOUND))
+ if (likely(rc == MDBX_SUCCESS)) {
+ if (exact) {
+ if (mc->mc_flags & C_SUB) {
+ mdbx_assert(env, data->iov_len == 0);
+ return (flags & MDBX_NODUPDATA) ? MDBX_KEYEXIST : MDBX_SUCCESS;
+ }
+ if (!(flags & MDBX_RESERVE) &&
+ unlikely(mc->mc_dbx->md_dcmp(data, &olddata) == 0))
+ return ((flags & MDBX_NODUPDATA) && mc->mc_xcursor) ? MDBX_KEYEXIST
+ : MDBX_SUCCESS;
+ }
+ } else if (unlikely(rc != MDBX_NOTFOUND))
return rc;
}
@@ -11868,7 +12128,7 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
return rc2;
}
- if (rc == MDBX_NO_ROOT) {
+ if (unlikely(rc == MDBX_NO_ROOT)) {
MDBX_page *np;
/* new database, write a root leaf page */
mdbx_debug("%s", "allocating new root leaf page");
@@ -11880,7 +12140,22 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
return rc2;
mc->mc_db->md_root = np->mp_pgno;
mc->mc_db->md_depth++;
- *mc->mc_dbflag |= DB_DIRTY;
+ if (mc->mc_db->md_flags & MDBX_INTEGERKEY) {
+ assert(key->iov_len >= mc->mc_dbx->md_klen_min &&
+ key->iov_len <= mc->mc_dbx->md_klen_max);
+ mc->mc_dbx->md_klen_min = mc->mc_dbx->md_klen_max = key->iov_len;
+ }
+ if (mc->mc_db->md_flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED)) {
+ assert(data->iov_len >= mc->mc_dbx->md_vlen_min &&
+ data->iov_len <= mc->mc_dbx->md_vlen_max);
+ mc->mc_dbx->md_vlen_min = mc->mc_dbx->md_vlen_max = data->iov_len;
+ assert(mc->mc_xcursor != NULL);
+ mc->mc_db->md_xsize = mc->mc_xcursor->mx_db.md_xsize =
+ (unsigned)data->iov_len;
+ mc->mc_xcursor->mx_dbx.md_klen_min = mc->mc_xcursor->mx_dbx.md_klen_max =
+ data->iov_len;
+ }
+ *mc->mc_dbstate |= DB_DIRTY;
if ((mc->mc_db->md_flags & (MDBX_DUPSORT | MDBX_DUPFIXED)) == MDBX_DUPFIXED)
np->mp_flags |= P_LEAF2;
mc->mc_flags |= C_INITIALIZED;
@@ -11904,7 +12179,8 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
/* See note inside leaf_size() */ env->me_branch_nodemax) {
/* Too big for a node, insert in sub-DB. Set up an empty
* "old sub-page" for prep_subDB to expand to a full page. */
- fp->mp_leaf2_ksize = (uint16_t)data->iov_len /* used if MDBX_DUPFIXED */;
+ fp->mp_leaf2_ksize =
+ (mc->mc_db->md_flags & MDBX_DUPFIXED) ? (uint16_t)data->iov_len : 0;
fp->mp_lower = fp->mp_upper = 0;
olddata.iov_len = PAGEHDRSZ;
goto prep_subDB;
@@ -11940,7 +12216,7 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
}
if (mdbx_audit_enabled()) {
- int err = mdbx_cursor_check(mc, false);
+ int err = mdbx_cursor_check(mc, 0);
if (unlikely(err != MDBX_SUCCESS))
return err;
}
@@ -11949,7 +12225,7 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
more:;
if (mdbx_audit_enabled()) {
- int err = mdbx_cursor_check(mc, false);
+ int err = mdbx_cursor_check(mc, 0);
if (unlikely(err != MDBX_SUCCESS))
return err;
}
@@ -12021,7 +12297,7 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
memcpy(page_data(omp), data->iov_base, data->iov_len);
if (mdbx_audit_enabled()) {
- int err = mdbx_cursor_check(mc, false);
+ int err = mdbx_cursor_check(mc, 0);
if (unlikely(err != MDBX_SUCCESS))
return err;
}
@@ -12131,12 +12407,10 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
fp_flags &= ~P_SUBP;
prep_subDB:
nested_dupdb.md_xsize = 0;
- nested_dupdb.md_flags = 0;
+ nested_dupdb.md_flags = flags_db2sub(mc->mc_db->md_flags);
if (mc->mc_db->md_flags & MDBX_DUPFIXED) {
fp_flags |= P_LEAF2;
nested_dupdb.md_xsize = fp->mp_leaf2_ksize;
- if (mc->mc_db->md_flags & MDBX_INTEGERDUP)
- nested_dupdb.md_flags = MDBX_INTEGERKEY;
}
nested_dupdb.md_depth = 1;
nested_dupdb.md_branch_pages = 0;
@@ -12214,7 +12488,7 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
}
if (mdbx_audit_enabled()) {
- int err = mdbx_cursor_check(mc, false);
+ int err = mdbx_cursor_check(mc, 0);
if (unlikely(err != MDBX_SUCCESS))
return err;
}
@@ -12237,7 +12511,7 @@ new_sub:
nflags |= MDBX_SPLIT_REPLACE;
rc = mdbx_page_split(mc, key, rdata, P_INVALID, nflags);
if (rc == MDBX_SUCCESS && mdbx_audit_enabled())
- rc = mdbx_cursor_check(mc, false);
+ rc = mdbx_cursor_check(mc, 0);
} else {
/* There is room already in this leaf page. */
if (IS_LEAF2(mc->mc_pg[mc->mc_top])) {
@@ -12325,8 +12599,10 @@ new_sub:
}
mdbx_cassert(mc, mc->mc_xcursor->mx_db.md_entries < PTRDIFF_MAX);
ecount = (size_t)mc->mc_xcursor->mx_db.md_entries;
- if (flags & MDBX_APPENDDUP)
- xflags |= MDBX_APPEND;
+#define SHIFT_MDBX_APPENDDUP_TO_MDBX_APPEND 1
+ STATIC_ASSERT((MDBX_APPENDDUP >> SHIFT_MDBX_APPENDDUP_TO_MDBX_APPEND) ==
+ MDBX_APPEND);
+ xflags |= (flags & MDBX_APPENDDUP) >> SHIFT_MDBX_APPENDDUP_TO_MDBX_APPEND;
rc = mdbx_cursor_put(&mc->mc_xcursor->mx_cursor, data, &xdata, xflags);
if (flags & F_SUBDATA) {
void *db = node_data(node);
@@ -12358,7 +12634,7 @@ new_sub:
}
}
if (rc == MDBX_SUCCESS && mdbx_audit_enabled())
- rc = mdbx_cursor_check(mc, false);
+ rc = mdbx_cursor_check(mc, 0);
return rc;
bad_sub:
if (unlikely(rc == MDBX_KEYEXIST))
@@ -12674,14 +12950,16 @@ static int __must_check_result mdbx_node_add_leaf(MDBX_cursor *mc,
memcpy(nodedata, data->iov_base, sizeof(pgno_t));
else if (unlikely(flags & MDBX_RESERVE))
data->iov_base = nodedata;
- else if (likely(nodedata != data->iov_base))
+ else if (likely(nodedata != data->iov_base &&
+ data->iov_len /* to avoid UBSAN traps*/ != 0))
memcpy(nodedata, data->iov_base, data->iov_len);
} else {
poke_pgno(nodedata, largepage->mp_pgno);
nodedata = page_data(largepage);
if (unlikely(flags & MDBX_RESERVE))
data->iov_base = nodedata;
- else if (likely(nodedata != data->iov_base))
+ else if (likely(nodedata != data->iov_base &&
+ data->iov_len /* to avoid UBSAN traps*/ != 0))
memcpy(nodedata, data->iov_base, data->iov_len);
}
return MDBX_SUCCESS;
@@ -12746,6 +13024,13 @@ static void mdbx_node_del(MDBX_cursor *mc, size_t ksize) {
mp->mp_lower -= sizeof(indx_t);
mdbx_cassert(mc, (size_t)UINT16_MAX - mp->mp_upper >= sz);
mp->mp_upper += (indx_t)sz;
+
+#if MDBX_DEBUG > 0
+ if (mdbx_audit_enabled()) {
+ int page_check_err = mdbx_page_check(mc, mp, C_UPDATING);
+ mdbx_cassert(mc, page_check_err == MDBX_SUCCESS);
+ }
+#endif
}
/* Compact the main page after deleting a node on a subpage.
@@ -12819,14 +13104,16 @@ static int mdbx_xcursor_init0(MDBX_cursor *mc) {
mx->mx_cursor.mc_db = &mx->mx_db;
mx->mx_cursor.mc_dbx = &mx->mx_dbx;
mx->mx_cursor.mc_dbi = mc->mc_dbi;
- mx->mx_cursor.mc_dbflag = &mx->mx_dbflag;
+ mx->mx_cursor.mc_dbstate = &mx->mx_dbstate;
mx->mx_cursor.mc_snum = 0;
mx->mx_cursor.mc_top = 0;
- mx->mx_cursor.mc_flags = C_SUB;
+ mx->mx_cursor.mc_flags = C_SUB | (mc->mc_flags & (C_COPYING | C_SKIPORD));
mx->mx_dbx.md_name.iov_len = 0;
mx->mx_dbx.md_name.iov_base = NULL;
mx->mx_dbx.md_cmp = mc->mc_dbx->md_dcmp;
mx->mx_dbx.md_dcmp = NULL;
+ mx->mx_dbx.md_klen_min = INT_MAX;
+ mx->mx_dbx.md_vlen_min = mx->mx_dbx.md_klen_max = mx->mx_dbx.md_vlen_max = 0;
return MDBX_SUCCESS;
}
@@ -12847,13 +13134,11 @@ static int mdbx_xcursor_init1(MDBX_cursor *mc, MDBX_node *node) {
mx->mx_cursor.mc_pg[0] = 0;
mx->mx_cursor.mc_snum = 0;
mx->mx_cursor.mc_top = 0;
- mx->mx_cursor.mc_flags = C_SUB;
+ mx->mx_cursor.mc_flags = C_SUB | (mc->mc_flags & (C_COPYING | C_SKIPORD));
} else {
if (unlikely(node_ds(node) <= PAGEHDRSZ))
return MDBX_CORRUPTED;
MDBX_page *fp = node_data(node);
- mx->mx_db.md_xsize = 0;
- mx->mx_db.md_flags = 0;
mx->mx_db.md_depth = 1;
mx->mx_db.md_branch_pages = 0;
mx->mx_db.md_leaf_pages = 1;
@@ -12862,18 +13147,32 @@ static int mdbx_xcursor_init1(MDBX_cursor *mc, MDBX_node *node) {
mx->mx_db.md_root = fp->mp_pgno;
mx->mx_cursor.mc_snum = 1;
mx->mx_cursor.mc_top = 0;
- mx->mx_cursor.mc_flags = C_INITIALIZED | C_SUB;
+ mx->mx_cursor.mc_flags =
+ C_INITIALIZED | C_SUB | (mc->mc_flags & (C_COPYING | C_SKIPORD));
mx->mx_cursor.mc_pg[0] = fp;
mx->mx_cursor.mc_ki[0] = 0;
- if (mc->mc_db->md_flags & MDBX_DUPFIXED) {
- mx->mx_db.md_xsize = fp->mp_leaf2_ksize;
- if (mc->mc_db->md_flags & MDBX_INTEGERDUP)
- mx->mx_db.md_flags = MDBX_INTEGERKEY;
- }
+ mx->mx_db.md_flags = flags_db2sub(mc->mc_db->md_flags);
+ mx->mx_db.md_xsize =
+ (mc->mc_db->md_flags & MDBX_DUPFIXED) ? fp->mp_leaf2_ksize : 0;
+ }
+
+ if (unlikely(mx->mx_db.md_xsize != mc->mc_db->md_xsize)) {
+ if (unlikely(mc->mc_db->md_xsize != 0))
+ return MDBX_CORRUPTED;
+ if (unlikely((mc->mc_db->md_flags & MDBX_DUPFIXED) == 0))
+ return MDBX_CORRUPTED;
+ if (unlikely(mx->mx_db.md_xsize < mc->mc_dbx->md_vlen_min ||
+ mx->mx_db.md_xsize > mc->mc_dbx->md_vlen_max))
+ return MDBX_CORRUPTED;
+ mc->mc_db->md_xsize = mx->mx_db.md_xsize;
+ mc->mc_dbx->md_vlen_min = mc->mc_dbx->md_vlen_max = mx->mx_db.md_xsize;
}
+ mx->mx_dbx.md_klen_min = mc->mc_dbx->md_vlen_min;
+ mx->mx_dbx.md_klen_max = mc->mc_dbx->md_vlen_max;
+
mdbx_debug("Sub-db -%u root page %" PRIaPGNO, mx->mx_cursor.mc_dbi,
mx->mx_db.md_root);
- mx->mx_dbflag = DB_VALID | DB_USRVALID | DB_DUPDATA;
+ mx->mx_dbstate = DB_VALID | DB_USRVALID | DB_DUPDATA;
return MDBX_SUCCESS;
}
@@ -12895,54 +13194,69 @@ static int mdbx_xcursor_init2(MDBX_cursor *mc, MDBX_xcursor *src_mx,
mx->mx_cursor.mc_top = 0;
mx->mx_cursor.mc_flags |= C_INITIALIZED;
mx->mx_cursor.mc_ki[0] = 0;
- mx->mx_dbflag = DB_VALID | DB_USRVALID | DB_DUPDATA;
- mx->mx_dbx.md_cmp = src_mx->mx_dbx.md_cmp;
- } else if (!(mx->mx_cursor.mc_flags & C_INITIALIZED)) {
- return MDBX_SUCCESS;
+ mx->mx_dbstate = DB_VALID | DB_USRVALID | DB_DUPDATA;
}
+
+ mx->mx_dbx.md_klen_min = src_mx->mx_dbx.md_klen_min;
+ mx->mx_dbx.md_klen_max = src_mx->mx_dbx.md_klen_max;
+ mx->mx_dbx.md_cmp = src_mx->mx_dbx.md_cmp;
mx->mx_db = src_mx->mx_db;
mx->mx_cursor.mc_pg[0] = src_mx->mx_cursor.mc_pg[0];
- mdbx_debug("Sub-db -%u root page %" PRIaPGNO, mx->mx_cursor.mc_dbi,
- mx->mx_db.md_root);
+ if (mx->mx_cursor.mc_flags & C_INITIALIZED) {
+ mdbx_debug("Sub-db -%u root page %" PRIaPGNO, mx->mx_cursor.mc_dbi,
+ mx->mx_db.md_root);
+ }
return MDBX_SUCCESS;
}
-/* Initialize a cursor for a given transaction and database. */
-static int mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, MDBX_dbi dbi) {
- mc->mc_signature = MDBX_MC_SIGNATURE;
- mc->mc_next = NULL;
- mc->mc_backup = NULL;
- mc->mc_dbi = dbi;
- mc->mc_txn = txn;
- mc->mc_db = &txn->mt_dbs[dbi];
- mc->mc_dbx = &txn->mt_dbxs[dbi];
- mc->mc_dbflag = &txn->mt_dbflags[dbi];
- mc->mc_snum = 0;
- mc->mc_top = 0;
- mc->mc_pg[0] = 0;
- mc->mc_flags = 0;
- mc->mc_ki[0] = 0;
- mc->mc_xcursor = NULL;
-
- if (txn->mt_dbs[dbi].md_flags & MDBX_DUPSORT) {
- STATIC_ASSERT(offsetof(MDBX_cursor_couple, outer) == 0);
- MDBX_xcursor *mx = &container_of(mc, MDBX_cursor_couple, outer)->inner;
- mdbx_tassert(txn, mx != NULL);
- mx->mx_cursor.mc_signature = MDBX_MC_SIGNATURE;
- mc->mc_xcursor = mx;
- int rc = mdbx_xcursor_init0(mc);
- if (unlikely(rc != MDBX_SUCCESS))
- return rc;
- }
+static __inline int mdbx_couple_init(MDBX_cursor_couple *couple,
+ const MDBX_dbi dbi, MDBX_txn *const txn,
+ MDBX_db *const db, MDBX_dbx *const dbx,
+ uint8_t *const dbstate) {
+ couple->outer.mc_signature = MDBX_MC_SIGNATURE;
+ couple->outer.mc_next = NULL;
+ couple->outer.mc_backup = NULL;
+ couple->outer.mc_dbi = dbi;
+ couple->outer.mc_txn = txn;
+ couple->outer.mc_db = db;
+ couple->outer.mc_dbx = dbx;
+ couple->outer.mc_dbstate = dbstate;
+ couple->outer.mc_snum = 0;
+ couple->outer.mc_top = 0;
+ couple->outer.mc_pg[0] = 0;
+ couple->outer.mc_flags = 0;
+ couple->outer.mc_ki[0] = 0;
+ couple->outer.mc_xcursor = NULL;
int rc = MDBX_SUCCESS;
- if (unlikely(*mc->mc_dbflag & DB_STALE)) {
- rc = mdbx_page_search(mc, NULL, MDBX_PS_ROOTONLY);
+ if (unlikely(*couple->outer.mc_dbstate & DB_STALE)) {
+ rc = mdbx_page_search(&couple->outer, NULL, MDBX_PS_ROOTONLY);
rc = (rc != MDBX_NOTFOUND) ? rc : MDBX_SUCCESS;
+ } else if (unlikely(couple->outer.mc_dbx->md_klen_max == 0)) {
+ rc = mdbx_setup_dbx(couple->outer.mc_dbx, couple->outer.mc_db,
+ txn->mt_env->me_psize);
+ }
+
+ if (couple->outer.mc_db->md_flags & MDBX_DUPSORT) {
+ couple->inner.mx_cursor.mc_signature = MDBX_MC_SIGNATURE;
+ couple->outer.mc_xcursor = &couple->inner;
+ rc = mdbx_xcursor_init0(&couple->outer);
+ if (unlikely(rc != MDBX_SUCCESS))
+ return rc;
+ couple->inner.mx_dbx.md_klen_min = couple->outer.mc_dbx->md_vlen_min;
+ couple->inner.mx_dbx.md_klen_max = couple->outer.mc_dbx->md_vlen_max;
}
return rc;
}
+/* Initialize a cursor for a given transaction and database. */
+static int mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, MDBX_dbi dbi) {
+ STATIC_ASSERT(offsetof(MDBX_cursor_couple, outer) == 0);
+ return mdbx_couple_init(container_of(mc, MDBX_cursor_couple, outer), dbi, txn,
+ &txn->mt_dbs[dbi], &txn->mt_dbxs[dbi],
+ &txn->mt_dbstate[dbi]);
+}
+
int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) {
if (unlikely(!ret))
return MDBX_EINVAL;
@@ -13139,7 +13453,7 @@ static int mdbx_update_key(MDBX_cursor *mc, const MDBX_val *key) {
mdbx_node_del(mc, 0);
int rc = mdbx_page_split(mc, key, NULL, pgno, MDBX_SPLIT_REPLACE);
if (rc == MDBX_SUCCESS && mdbx_audit_enabled())
- rc = mdbx_cursor_check(mc, true);
+ rc = mdbx_cursor_check(mc, C_UPDATING);
return rc;
}
@@ -13163,7 +13477,8 @@ static int mdbx_update_key(MDBX_cursor *mc, const MDBX_val *key) {
/* But even if no shift was needed, update ksize */
node_set_ks(node, key->iov_len);
- memcpy(node_key(node), key->iov_base, key->iov_len);
+ if (likely(key->iov_len /* to avoid UBSAN traps*/ != 0))
+ memcpy(node_key(node), key->iov_base, key->iov_len);
return MDBX_SUCCESS;
}
@@ -13193,7 +13508,7 @@ static int mdbx_node_move(MDBX_cursor *csrc, MDBX_cursor *cdst, int fromleft) {
key4move.iov_base = node_key(srcnode);
if (csrc->mc_ki[csrc->mc_top] == 0) {
- const uint16_t snum = csrc->mc_snum;
+ const unsigned snum = csrc->mc_snum;
mdbx_cassert(csrc, snum > 0);
/* must find the lowest key below src */
rc = mdbx_page_search_lowest(csrc);
@@ -13225,7 +13540,7 @@ static int mdbx_node_move(MDBX_cursor *csrc, MDBX_cursor *cdst, int fromleft) {
}
if (cdst->mc_ki[cdst->mc_top] == 0) {
- const uint16_t snum = cdst->mc_snum;
+ const unsigned snum = cdst->mc_snum;
mdbx_cassert(csrc, snum > 0);
MDBX_cursor mn;
mdbx_cursor_copy(cdst, &mn);
@@ -13532,9 +13847,20 @@ static int mdbx_page_merge(MDBX_cursor *csrc, MDBX_cursor *cdst) {
rc = mdbx_page_search_lowest(&mn);
if (unlikely(rc))
return rc;
- MDBX_node *lowest = page_node(mn.mc_pg[mn.mc_top], 0);
- key.iov_len = node_ks(lowest);
- key.iov_base = node_key(lowest);
+
+ const MDBX_page *mp = mn.mc_pg[mn.mc_top];
+ if (likely(!IS_LEAF2(mp))) {
+ mdbx_cassert(&mn, IS_LEAF(mp));
+ const MDBX_node *lowest = page_node(mp, 0);
+ key.iov_len = node_ks(lowest);
+ key.iov_base = node_key(lowest);
+ } else {
+ mdbx_cassert(&mn, mn.mc_top > csrc->mc_top);
+ key.iov_len = mp->mp_leaf2_ksize;
+ key.iov_base = page_leaf2key(mp, mn.mc_ki[mn.mc_top], key.iov_len);
+ }
+ mdbx_cassert(&mn, key.iov_len >= csrc->mc_dbx->md_klen_min);
+ mdbx_cassert(&mn, key.iov_len <= csrc->mc_dbx->md_klen_max);
const size_t dst_room = page_room(pdst);
const size_t src_used = page_used(cdst->mc_txn->mt_env, psrc);
@@ -13630,7 +13956,7 @@ static int mdbx_page_merge(MDBX_cursor *csrc, MDBX_cursor *cdst) {
mdbx_cassert(cdst, cdst->mc_snum == cdst->mc_top + 1);
MDBX_page *const top_page = cdst->mc_pg[cdst->mc_top];
const indx_t top_indx = cdst->mc_ki[cdst->mc_top];
- const uint16_t save_snum = cdst->mc_snum;
+ const unsigned save_snum = cdst->mc_snum;
const uint16_t save_depth = cdst->mc_db->md_depth;
mdbx_cursor_pop(cdst);
rc = mdbx_rebalance(cdst);
@@ -13948,8 +14274,13 @@ static int mdbx_rebalance(MDBX_cursor *mc) {
}
}
- if (nkeys >= minkeys)
+ if (nkeys >= minkeys) {
+#if MDBX_DEBUG > 0
+ if (mdbx_audit_enabled())
+ return mdbx_cursor_check(mc, C_UPDATING);
+#endif
return MDBX_SUCCESS;
+ }
if (left && (!right || page_room(left) > page_room(right))) {
/* try merge with left */
@@ -13985,8 +14316,10 @@ static int mdbx_rebalance(MDBX_cursor *mc) {
return MDBX_PROBLEM;
}
-static __cold int mdbx_page_check(MDBX_env *env, const MDBX_page *const mp,
- bool maybe_unfinished) {
+static __cold int mdbx_page_check(MDBX_cursor *const mc,
+ const MDBX_page *const mp, unsigned options) {
+ options |= mc->mc_flags & (C_COPYING | C_UPDATING | C_RETIRING | C_SKIPORD);
+ MDBX_env *const env = mc->mc_txn->mt_env;
const unsigned nkeys = page_numkeys(mp);
char *const end_of_page = (char *)mp + env->me_psize;
mdbx_assert(env, mp->mp_pgno >= MIN_PAGENO && mp->mp_pgno <= MAX_PAGENO);
@@ -14001,19 +14334,41 @@ static __cold int mdbx_page_check(MDBX_env *env, const MDBX_page *const mp,
return MDBX_CORRUPTED;
return MDBX_SUCCESS;
}
- if (!(IS_DIRTY(mp) && maybe_unfinished)) {
+ if ((options & C_UPDATING) == 0 || !IS_DIRTY(mp)) {
mdbx_assert(env, nkeys >= 2 || !IS_BRANCH(mp));
if (unlikely(nkeys < 2 && IS_BRANCH(mp)))
return MDBX_CORRUPTED;
}
- for (unsigned i = IS_LEAF(mp) ? 0 : 1; i < nkeys; ++i) {
+ MDBX_val here, prev = {0, 0};
+ for (unsigned i = 0; i < nkeys; ++i) {
if (IS_LEAF2(mp)) {
const size_t ksize = mp->mp_leaf2_ksize;
- const char *const key = page_leaf2key(mp, i, ksize);
+ char *const key = page_leaf2key(mp, i, ksize);
mdbx_assert(env, key + ksize <= end_of_page);
if (unlikely(end_of_page < key + ksize))
return MDBX_CORRUPTED;
+
+ if ((options & C_COPYING) == 0) {
+ if (unlikely(ksize != mc->mc_dbx->md_klen_min)) {
+ mdbx_assert(env, ksize >= mc->mc_dbx->md_klen_min);
+ mdbx_assert(env, ksize <= mc->mc_dbx->md_klen_max);
+ if (unlikely(ksize < mc->mc_dbx->md_klen_min ||
+ ksize > mc->mc_dbx->md_klen_max))
+ return MDBX_CORRUPTED;
+ mc->mc_dbx->md_klen_min = mc->mc_dbx->md_klen_max = ksize;
+ }
+ if ((options & C_SKIPORD) == 0) {
+ here.iov_len = ksize;
+ here.iov_base = key;
+ if (prev.iov_base) {
+ mdbx_assert(env, mc->mc_dbx->md_cmp(&here, &prev) > 0);
+ if (unlikely(mc->mc_dbx->md_cmp(&here, &prev) <= 0))
+ return MDBX_CORRUPTED;
+ }
+ prev = here;
+ }
+ }
} else {
const MDBX_node *const node = page_node(mp, i);
const char *node_end = (char *)node + NODESIZE;
@@ -14026,12 +14381,59 @@ static __cold int mdbx_page_check(MDBX_env *env, const MDBX_page *const mp,
mdbx_assert(env, key + ksize <= end_of_page);
if (unlikely(end_of_page < key + ksize))
return MDBX_CORRUPTED;
+
+ if ((options & C_COPYING) == 0) {
+ mdbx_assert(env, ksize >= mc->mc_dbx->md_klen_min);
+ mdbx_assert(env, ksize <= mc->mc_dbx->md_klen_max);
+ if (unlikely(ksize < mc->mc_dbx->md_klen_min ||
+ ksize > mc->mc_dbx->md_klen_max))
+ return MDBX_CORRUPTED;
+
+ if ((options & C_SKIPORD) == 0) {
+ here.iov_base = key;
+ here.iov_len = ksize;
+ if (prev.iov_base) {
+ mdbx_assert(env, mc->mc_dbx->md_cmp(&here, &prev) > 0);
+ if (unlikely(mc->mc_dbx->md_cmp(&here, &prev) <= 0))
+ return MDBX_CORRUPTED;
+ }
+ prev = here;
+ }
+ }
}
- if (IS_BRANCH(mp))
+ if (IS_BRANCH(mp)) {
+ if ((options & C_RETIRING) == 0) {
+ const pgno_t ref = node_pgno(node);
+ mdbx_assert(env, ref >= MIN_PAGENO);
+ mdbx_assert(env, ref < mc->mc_txn->mt_next_pgno);
+ if (unlikely(ref < MIN_PAGENO || ref >= mc->mc_txn->mt_next_pgno))
+ return MDBX_CORRUPTED;
+ }
continue;
+ }
if (node_flags(node) == F_BIGDATA /* data on large-page */) {
+ const size_t dsize = node_ds(node);
+ if ((options & C_COPYING) == 0) {
+ mdbx_assert(env, dsize > mc->mc_dbx->md_vlen_min);
+ mdbx_assert(env, dsize <= mc->mc_dbx->md_vlen_max);
+ if (unlikely(dsize <= mc->mc_dbx->md_vlen_min ||
+ dsize > mc->mc_dbx->md_vlen_max))
+ return MDBX_CORRUPTED;
+ }
+ if ((options & C_RETIRING) == 0) {
+ MDBX_page *lp;
+ int err = mdbx_page_get(mc, node_largedata_pgno(node), &lp, NULL);
+ if (unlikely(err != MDBX_SUCCESS))
+ return err;
+ mdbx_assert(env, IS_OVERFLOW(lp));
+ mdbx_assert(env, number_of_ovpages(env, dsize) == lp->mp_pages);
+ if (unlikely(!IS_OVERFLOW(lp) ||
+ number_of_ovpages(env, dsize) != lp->mp_pages))
+ return MDBX_CORRUPTED;
+ }
continue;
}
+
const size_t dsize = node_ds(node);
const char *const data = node_data(node);
mdbx_assert(env, data + dsize <= end_of_page);
@@ -14043,6 +14445,13 @@ static __cold int mdbx_page_check(MDBX_env *env, const MDBX_page *const mp,
mdbx_assert(env, false);
return MDBX_CORRUPTED;
case 0 /* usual */:
+ if ((options & C_COPYING) == 0) {
+ mdbx_assert(env, dsize >= mc->mc_dbx->md_vlen_min);
+ mdbx_assert(env, dsize <= mc->mc_dbx->md_vlen_max);
+ if (unlikely(dsize < mc->mc_dbx->md_vlen_min ||
+ dsize > mc->mc_dbx->md_vlen_max))
+ return MDBX_CORRUPTED;
+ }
break;
case F_SUBDATA /* sub-db */:
mdbx_assert(env, dsize >= sizeof(MDBX_db));
@@ -14071,7 +14480,11 @@ static __cold int mdbx_page_check(MDBX_env *env, const MDBX_page *const mp,
return MDBX_CORRUPTED;
}
+ MDBX_val sub_here, sub_prev = {0, 0};
for (int j = 0; j < nsubkeys; j++) {
+ mdbx_assert(env, IS_LEAF(sp));
+ if (unlikely(!IS_LEAF(sp)))
+ return MDBX_CORRUPTED;
if (IS_LEAF2(sp)) {
/* LEAF2 pages have no mp_ptrs[] or node headers */
size_t sub_ksize = sp->mp_leaf2_ksize;
@@ -14079,10 +14492,30 @@ static __cold int mdbx_page_check(MDBX_env *env, const MDBX_page *const mp,
mdbx_assert(env, sub_key + sub_ksize <= end_of_subpage);
if (unlikely(end_of_subpage < sub_key + sub_ksize))
return MDBX_CORRUPTED;
+
+ if ((options & C_COPYING) == 0) {
+ if (unlikely(sub_ksize != mc->mc_dbx->md_vlen_min)) {
+ mdbx_assert(env, sub_ksize >= mc->mc_dbx->md_vlen_min);
+ mdbx_assert(env, sub_ksize <= mc->mc_dbx->md_vlen_max);
+ if (unlikely(sub_ksize < mc->mc_dbx->md_vlen_min ||
+ sub_ksize > mc->mc_dbx->md_vlen_max))
+ return MDBX_CORRUPTED;
+ mc->mc_dbx->md_vlen_min = mc->mc_dbx->md_vlen_max = sub_ksize;
+ }
+ if ((options & C_SKIPORD) == 0) {
+ sub_here.iov_len = sub_ksize;
+ sub_here.iov_base = sub_key;
+ if (sub_prev.iov_base) {
+ mdbx_assert(env,
+ mc->mc_dbx->md_dcmp(&sub_prev, &sub_here) < 0);
+ if (unlikely(mc->mc_dbx->md_dcmp(&sub_prev, &sub_here) >=
+ 0))
+ return MDBX_CORRUPTED;
+ }
+ sub_prev = sub_here;
+ }
+ }
} else {
- mdbx_assert(env, IS_LEAF(sp));
- if (unlikely(!IS_LEAF(sp)))
- return MDBX_CORRUPTED;
const MDBX_node *const sub_node = page_node(sp, j);
const char *sub_node_end = (char *)sub_node + NODESIZE;
mdbx_assert(env, sub_node_end <= end_of_subpage);
@@ -14096,6 +14529,31 @@ static __cold int mdbx_page_check(MDBX_env *env, const MDBX_page *const mp,
char *sub_key = node_key(sub_node);
size_t sub_dsize = node_ds(sub_node);
char *sub_data = node_data(sub_node);
+
+ if ((options & C_COPYING) == 0) {
+ mdbx_assert(env, sub_ksize >= mc->mc_dbx->md_vlen_min);
+ mdbx_assert(env, sub_ksize <= mc->mc_dbx->md_vlen_max);
+ if (unlikely(sub_ksize < mc->mc_dbx->md_vlen_min ||
+ sub_ksize > mc->mc_dbx->md_vlen_max))
+ return MDBX_CORRUPTED;
+
+ if ((options & C_SKIPORD) == 0) {
+ sub_here.iov_len = sub_ksize;
+ sub_here.iov_base = sub_key;
+ if (sub_prev.iov_base) {
+ mdbx_assert(env,
+ mc->mc_dbx->md_dcmp(&sub_prev, &sub_here) < 0);
+ if (unlikely(mc->mc_dbx->md_dcmp(&sub_prev, &sub_here) >=
+ 0))
+ return MDBX_CORRUPTED;
+ }
+ sub_prev = sub_here;
+ }
+ }
+ mdbx_assert(env, sub_dsize == 0);
+ if (unlikely(sub_dsize != 0))
+ return MDBX_CORRUPTED;
+
mdbx_assert(env, sub_key + sub_ksize <= end_of_subpage);
if (unlikely(end_of_subpage < sub_key + sub_ksize))
return MDBX_CORRUPTED;
@@ -14112,7 +14570,7 @@ static __cold int mdbx_page_check(MDBX_env *env, const MDBX_page *const mp,
return MDBX_SUCCESS;
}
-static __cold int mdbx_cursor_check(MDBX_cursor *mc, bool pending) {
+static __cold int mdbx_cursor_check(MDBX_cursor *mc, unsigned options) {
mdbx_tassert(mc->mc_txn, mc->mc_txn->mt_parent ||
mc->mc_txn->tw.dirtyroom +
mc->mc_txn->tw.dirtylist->length ==
@@ -14120,13 +14578,13 @@ static __cold int mdbx_cursor_check(MDBX_cursor *mc, bool pending) {
mdbx_cassert(mc, mc->mc_top == mc->mc_snum - 1);
if (unlikely(mc->mc_top != mc->mc_snum - 1))
return MDBX_CURSOR_FULL;
- mdbx_cassert(mc, pending ? mc->mc_snum <= mc->mc_db->md_depth
- : mc->mc_snum == mc->mc_db->md_depth);
- if (unlikely(pending ? mc->mc_snum > mc->mc_db->md_depth
- : mc->mc_snum != mc->mc_db->md_depth))
+ mdbx_cassert(mc, (options & C_UPDATING) ? mc->mc_snum <= mc->mc_db->md_depth
+ : mc->mc_snum == mc->mc_db->md_depth);
+ if (unlikely((options & C_UPDATING) ? mc->mc_snum > mc->mc_db->md_depth
+ : mc->mc_snum != mc->mc_db->md_depth))
return MDBX_CURSOR_FULL;
- for (int n = 0; n < mc->mc_snum; ++n) {
+ for (int n = 0; n < (int)mc->mc_snum; ++n) {
MDBX_page *mp = mc->mc_pg[n];
const unsigned nkeys = page_numkeys(mp);
const bool expect_branch = (n < mc->mc_db->md_depth - 1) ? true : false;
@@ -14136,7 +14594,7 @@ static __cold int mdbx_cursor_check(MDBX_cursor *mc, bool pending) {
mdbx_cassert(mc, branch == expect_branch);
if (unlikely(branch != expect_branch))
return MDBX_CURSOR_FULL;
- if (!pending) {
+ if ((options & C_UPDATING) == 0) {
mdbx_cassert(mc,
nkeys > mc->mc_ki[n] || (!branch && nkeys == mc->mc_ki[n] &&
(mc->mc_flags & C_EOF) != 0));
@@ -14150,7 +14608,7 @@ static __cold int mdbx_cursor_check(MDBX_cursor *mc, bool pending) {
return MDBX_CURSOR_FULL;
}
- int err = mdbx_page_check(mc->mc_txn->mt_env, mp, pending);
+ int err = mdbx_page_check(mc, mp, options);
if (unlikely(err != MDBX_SUCCESS))
return err;
@@ -14170,7 +14628,7 @@ static __cold int mdbx_cursor_check(MDBX_cursor *mc, bool pending) {
mdbx_cassert(mc, nested_leaf == expect_nested_leaf);
if (unlikely(nested_leaf != expect_nested_leaf))
return MDBX_CURSOR_FULL;
- err = mdbx_page_check(mc->mc_txn->mt_env, np, pending);
+ err = mdbx_page_check(mc, np, options);
if (unlikely(err != MDBX_SUCCESS))
return err;
}
@@ -14312,7 +14770,7 @@ static int mdbx_cursor_del0(MDBX_cursor *mc) {
if (unlikely(rc))
mc->mc_txn->mt_flags |= MDBX_TXN_ERROR;
else if (mdbx_audit_enabled())
- rc = mdbx_cursor_check(mc, false);
+ rc = mdbx_cursor_check(mc, 0);
return rc;
}
@@ -14403,7 +14861,7 @@ static int mdbx_page_split(MDBX_cursor *mc, const MDBX_val *newkey,
unsigned newindx = mc->mc_ki[mc->mc_top];
unsigned nkeys = page_numkeys(mp);
if (mdbx_audit_enabled()) {
- rc = mdbx_cursor_check(mc, true);
+ rc = mdbx_cursor_check(mc, C_UPDATING);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
}
@@ -14607,10 +15065,10 @@ static int mdbx_page_split(MDBX_cursor *mc, const MDBX_val *newkey,
mdbx_debug("separator is %d [%s]", split_indx, DKEY(&sepkey));
if (mdbx_audit_enabled()) {
- rc = mdbx_cursor_check(mc, true);
+ rc = mdbx_cursor_check(mc, C_UPDATING);
if (unlikely(rc != MDBX_SUCCESS))
goto done;
- rc = mdbx_cursor_check(&mn, true);
+ rc = mdbx_cursor_check(&mn, C_UPDATING);
if (unlikely(rc != MDBX_SUCCESS))
goto done;
}
@@ -14627,9 +15085,9 @@ static int mdbx_page_split(MDBX_cursor *mc, const MDBX_val *newkey,
mn, rc = mdbx_page_split(&mn, &sepkey, NULL, rp->mp_pgno, 0));
if (unlikely(rc != MDBX_SUCCESS))
goto done;
- mdbx_cassert(mc, mc->mc_snum - snum == mc->mc_db->md_depth - depth);
+ mdbx_cassert(mc, (int)mc->mc_snum - snum == mc->mc_db->md_depth - depth);
if (mdbx_audit_enabled()) {
- rc = mdbx_cursor_check(mc, true);
+ rc = mdbx_cursor_check(mc, C_UPDATING);
if (unlikely(rc != MDBX_SUCCESS))
goto done;
}
@@ -14919,7 +15377,7 @@ int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data,
typedef struct mdbx_copy {
MDBX_env *mc_env;
MDBX_txn *mc_txn;
- mdbx_condmutex_t mc_condmutex;
+ mdbx_condpair_t mc_condpair;
uint8_t *mc_wbuf[2];
uint8_t *mc_over[2];
size_t mc_wlen[2];
@@ -14939,10 +15397,10 @@ static THREAD_RESULT __cold THREAD_CALL mdbx_env_copythr(void *arg) {
uint8_t *ptr;
int toggle = 0;
- mdbx_condmutex_lock(&my->mc_condmutex);
+ mdbx_condpair_lock(&my->mc_condpair);
while (!my->mc_error) {
while (!my->mc_new && !my->mc_error) {
- int err = mdbx_condmutex_wait(&my->mc_condmutex);
+ int err = mdbx_condpair_wait(&my->mc_condpair, true);
if (err != MDBX_SUCCESS) {
my->mc_error = err;
goto bailout;
@@ -14972,10 +15430,10 @@ static THREAD_RESULT __cold THREAD_CALL mdbx_env_copythr(void *arg) {
toggle ^= 1;
/* Return the empty buffer to provider */
my->mc_new--;
- mdbx_condmutex_signal(&my->mc_condmutex);
+ mdbx_condpair_signal(&my->mc_condpair, false);
}
bailout:
- mdbx_condmutex_unlock(&my->mc_condmutex);
+ mdbx_condpair_unlock(&my->mc_condpair);
return (THREAD_RESULT)0;
}
@@ -14984,15 +15442,15 @@ bailout:
* [in] my control structure.
* [in] adjust (1 to hand off 1 buffer) | (MDBX_EOF when ending). */
static int __cold mdbx_env_cthr_toggle(mdbx_copy *my, int adjust) {
- mdbx_condmutex_lock(&my->mc_condmutex);
+ mdbx_condpair_lock(&my->mc_condpair);
my->mc_new += (short)adjust;
- mdbx_condmutex_signal(&my->mc_condmutex);
+ mdbx_condpair_signal(&my->mc_condpair, true);
while (!my->mc_error && (my->mc_new & 2) /* both buffers in use */) {
- int err = mdbx_condmutex_wait(&my->mc_condmutex);
+ int err = mdbx_condpair_wait(&my->mc_condpair, false);
if (err != MDBX_SUCCESS)
my->mc_error = err;
}
- mdbx_condmutex_unlock(&my->mc_condmutex);
+ mdbx_condpair_unlock(&my->mc_condpair);
my->mc_toggle ^= (adjust & 1);
/* Both threads reset mc_wlen, to be safe from threading errors */
@@ -15005,7 +15463,7 @@ static int __cold mdbx_env_cthr_toggle(mdbx_copy *my, int adjust) {
* [in,out] pg database root.
* [in] flags includes F_DUPDATA if it is a sorted-duplicate sub-DB. */
static int __cold mdbx_env_cwalk(mdbx_copy *my, pgno_t *pg, int flags) {
- MDBX_cursor mc;
+ MDBX_cursor_couple couple;
MDBX_page *mo, *mp, *leaf;
char *buf, *ptr;
int rc, toggle;
@@ -15015,25 +15473,28 @@ static int __cold mdbx_env_cwalk(mdbx_copy *my, pgno_t *pg, int flags) {
if (*pg == P_INVALID)
return MDBX_SUCCESS;
- memset(&mc, 0, sizeof(mc));
- mc.mc_snum = 1;
- mc.mc_txn = my->mc_txn;
+ memset(&couple, 0, sizeof(couple));
+ couple.outer.mc_snum = 1;
+ couple.outer.mc_txn = my->mc_txn;
+ couple.outer.mc_flags = couple.inner.mx_cursor.mc_flags =
+ C_COPYING | C_SKIPORD;
- rc = mdbx_page_get(&mc, *pg, &mc.mc_pg[0], NULL);
+ rc = mdbx_page_get(&couple.outer, *pg, &couple.outer.mc_pg[0], NULL);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
- rc = mdbx_page_search_root(&mc, NULL, MDBX_PS_FIRST);
+ rc = mdbx_page_search_root(&couple.outer, NULL, MDBX_PS_FIRST);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
/* Make cursor pages writable */
- buf = ptr = mdbx_malloc(pgno2bytes(my->mc_env, mc.mc_snum));
+ buf = ptr = mdbx_malloc(pgno2bytes(my->mc_env, couple.outer.mc_snum));
if (buf == NULL)
return MDBX_ENOMEM;
- for (i = 0; i < mc.mc_top; i++) {
- mdbx_page_copy((MDBX_page *)ptr, mc.mc_pg[i], my->mc_env->me_psize);
- mc.mc_pg[i] = (MDBX_page *)ptr;
+ for (i = 0; i < couple.outer.mc_top; i++) {
+ mdbx_page_copy((MDBX_page *)ptr, couple.outer.mc_pg[i],
+ my->mc_env->me_psize);
+ couple.outer.mc_pg[i] = (MDBX_page *)ptr;
ptr += my->mc_env->me_psize;
}
@@ -15041,9 +15502,9 @@ static int __cold mdbx_env_cwalk(mdbx_copy *my, pgno_t *pg, int flags) {
leaf = (MDBX_page *)ptr;
toggle = my->mc_toggle;
- while (mc.mc_snum > 0) {
+ while (couple.outer.mc_snum > 0) {
unsigned n;
- mp = mc.mc_pg[mc.mc_top];
+ mp = couple.outer.mc_pg[couple.outer.mc_top];
n = page_numkeys(mp);
if (IS_LEAF(mp)) {
@@ -15055,7 +15516,7 @@ static int __cold mdbx_env_cwalk(mdbx_copy *my, pgno_t *pg, int flags) {
/* Need writable leaf */
if (mp != leaf) {
- mc.mc_pg[mc.mc_top] = leaf;
+ couple.outer.mc_pg[couple.outer.mc_top] = leaf;
mdbx_page_copy(leaf, mp, my->mc_env->me_psize);
mp = leaf;
node = page_node(mp, i);
@@ -15063,7 +15524,7 @@ static int __cold mdbx_env_cwalk(mdbx_copy *my, pgno_t *pg, int flags) {
const pgno_t pgno = node_largedata_pgno(node);
poke_pgno(node_data(node), my->mc_next_pgno);
- rc = mdbx_page_get(&mc, pgno, &omp, NULL);
+ rc = mdbx_page_get(&couple.outer, pgno, &omp, NULL);
if (unlikely(rc != MDBX_SUCCESS))
goto done;
if (my->mc_wlen[toggle] >= MDBX_WBUF) {
@@ -15075,7 +15536,6 @@ static int __cold mdbx_env_cwalk(mdbx_copy *my, pgno_t *pg, int flags) {
mo = (MDBX_page *)(my->mc_wbuf[toggle] + my->mc_wlen[toggle]);
memcpy(mo, omp, my->mc_env->me_psize);
mo->mp_pgno = my->mc_next_pgno;
- mo->mp_txnid = MIN_TXNID;
my->mc_next_pgno += omp->mp_pages;
my->mc_wlen[toggle] += my->mc_env->me_psize;
if (omp->mp_pages > 1) {
@@ -15094,7 +15554,7 @@ static int __cold mdbx_env_cwalk(mdbx_copy *my, pgno_t *pg, int flags) {
/* Need writable leaf */
if (mp != leaf) {
- mc.mc_pg[mc.mc_top] = leaf;
+ couple.outer.mc_pg[couple.outer.mc_top] = leaf;
mdbx_page_copy(leaf, mp, my->mc_env->me_psize);
mp = leaf;
node = page_node(mp, i);
@@ -15112,23 +15572,26 @@ static int __cold mdbx_env_cwalk(mdbx_copy *my, pgno_t *pg, int flags) {
}
}
} else {
- mc.mc_ki[mc.mc_top]++;
- if (mc.mc_ki[mc.mc_top] < n) {
+ couple.outer.mc_ki[couple.outer.mc_top]++;
+ if (couple.outer.mc_ki[couple.outer.mc_top] < n) {
again:
- rc = mdbx_page_get(&mc, node_pgno(page_node(mp, mc.mc_ki[mc.mc_top])),
- &mp, NULL);
+ rc = mdbx_page_get(
+ &couple.outer,
+ node_pgno(page_node(mp, couple.outer.mc_ki[couple.outer.mc_top])),
+ &mp, NULL);
if (unlikely(rc != MDBX_SUCCESS))
goto done;
- mc.mc_top++;
- mc.mc_snum++;
- mc.mc_ki[mc.mc_top] = 0;
+ couple.outer.mc_top++;
+ couple.outer.mc_snum++;
+ couple.outer.mc_ki[couple.outer.mc_top] = 0;
if (IS_BRANCH(mp)) {
/* Whenever we advance to a sibling branch page,
* we must proceed all the way down to its first leaf. */
- mdbx_page_copy(mc.mc_pg[mc.mc_top], mp, my->mc_env->me_psize);
+ mdbx_page_copy(couple.outer.mc_pg[couple.outer.mc_top], mp,
+ my->mc_env->me_psize);
goto again;
} else
- mc.mc_pg[mc.mc_top] = mp;
+ couple.outer.mc_pg[couple.outer.mc_top] = mp;
continue;
}
}
@@ -15140,14 +15603,14 @@ static int __cold mdbx_env_cwalk(mdbx_copy *my, pgno_t *pg, int flags) {
}
mo = (MDBX_page *)(my->mc_wbuf[toggle] + my->mc_wlen[toggle]);
mdbx_page_copy(mo, mp, my->mc_env->me_psize);
- mo->mp_txnid = MIN_TXNID;
mo->mp_pgno = my->mc_next_pgno++;
my->mc_wlen[toggle] += my->mc_env->me_psize;
- if (mc.mc_top) {
+ if (couple.outer.mc_top) {
/* Update parent if there is one */
- node_set_pgno(page_node(mc.mc_pg[mc.mc_top - 1], mc.mc_ki[mc.mc_top - 1]),
+ node_set_pgno(page_node(couple.outer.mc_pg[couple.outer.mc_top - 1],
+ couple.outer.mc_ki[couple.outer.mc_top - 1]),
mo->mp_pgno);
- mdbx_cursor_pop(&mc);
+ mdbx_cursor_pop(&couple.outer);
} else {
/* Otherwise we're done */
*pg = mo->mp_pgno;
@@ -15161,19 +15624,15 @@ done:
static __cold void compact_fixup_meta(MDBX_env *env, MDBX_meta *meta) {
/* Calculate filesize taking in account shrink/growing thresholds */
- if (meta->mm_geo.next > meta->mm_geo.now) {
- const pgno_t aligned = pgno_align2os_pgno(
- env,
- pgno_add(meta->mm_geo.next,
- meta->mm_geo.grow - meta->mm_geo.next % meta->mm_geo.grow));
- meta->mm_geo.now = aligned;
- } else if (meta->mm_geo.next < meta->mm_geo.now) {
+ if (meta->mm_geo.next != meta->mm_geo.now) {
meta->mm_geo.now = meta->mm_geo.next;
const pgno_t aligner =
meta->mm_geo.grow ? meta->mm_geo.grow : meta->mm_geo.shrink;
- const pgno_t aligned = pgno_align2os_pgno(
- env, meta->mm_geo.next + aligner - meta->mm_geo.next % aligner);
- meta->mm_geo.now = aligned;
+ if (aligner) {
+ const pgno_t aligned = pgno_align2os_pgno(
+ env, meta->mm_geo.next + aligner - meta->mm_geo.next % aligner);
+ meta->mm_geo.now = aligned;
+ }
}
if (meta->mm_geo.now < meta->mm_geo.lower)
@@ -15186,14 +15645,32 @@ static __cold void compact_fixup_meta(MDBX_env *env, MDBX_meta *meta) {
meta->mm_datasync_sign = mdbx_meta_sign(meta);
}
+/* Make resizeable */
+static __cold void make_sizeable(MDBX_meta *meta) {
+ meta->mm_geo.lower = MIN_PAGENO;
+ if (meta->mm_geo.grow == 0) {
+ const size_t step = 1 + (meta->mm_geo.upper - meta->mm_geo.lower) / 42;
+ meta->mm_geo.grow = (step < UINT16_MAX) ? (uint16_t)step : UINT16_MAX;
+ }
+ if (meta->mm_geo.shrink == 0) {
+ const size_t step = meta->mm_geo.grow + meta->mm_geo.grow;
+ meta->mm_geo.shrink = (step < UINT16_MAX) ? (uint16_t)step : UINT16_MAX;
+ }
+}
+
/* Copy environment with compaction. */
static int __cold mdbx_env_compact(MDBX_env *env, MDBX_txn *read_txn,
mdbx_filehandle_t fd, uint8_t *buffer,
- const bool dest_is_pipe) {
+ const bool dest_is_pipe, const int flags) {
const size_t meta_bytes = pgno2bytes(env, NUM_METAS);
uint8_t *const data_buffer =
buffer + ceil_powerof2(meta_bytes, env->me_os_psize);
MDBX_meta *const meta = mdbx_init_metas(env, buffer);
+ mdbx_meta_set_txnid(env, meta, read_txn->mt_txnid);
+
+ if (flags & MDBX_CP_FORCE_RESIZEABLE)
+ make_sizeable(meta);
+
/* copy canary sequenses if present */
if (read_txn->mt_canary.v) {
meta->mm_canary = read_txn->mt_canary;
@@ -15216,13 +15693,13 @@ static int __cold mdbx_env_compact(MDBX_env *env, MDBX_txn *read_txn,
/* Count free pages + GC pages. Subtract from last_pg
* to find the new last_pg, which also becomes the new root. */
pgno_t freecount = 0;
- MDBX_cursor mc;
+ MDBX_cursor_couple couple;
MDBX_val key, data;
- int rc = mdbx_cursor_init(&mc, read_txn, FREE_DBI);
+ int rc = mdbx_cursor_init(&couple.outer, read_txn, FREE_DBI);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
- while ((rc = mdbx_cursor_get(&mc, &key, &data, MDBX_NEXT)) == 0)
+ while ((rc = mdbx_cursor_get(&couple.outer, &key, &data, MDBX_NEXT)) == 0)
freecount += *(pgno_t *)data.iov_base;
if (unlikely(rc != MDBX_NOTFOUND))
return rc;
@@ -15238,7 +15715,7 @@ static int __cold mdbx_env_compact(MDBX_env *env, MDBX_txn *read_txn,
mdbx_copy ctx;
memset(&ctx, 0, sizeof(ctx));
- rc = mdbx_condmutex_init(&ctx.mc_condmutex);
+ rc = mdbx_condpair_init(&ctx.mc_condpair);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
@@ -15261,7 +15738,7 @@ static int __cold mdbx_env_compact(MDBX_env *env, MDBX_txn *read_txn,
rc = mdbx_env_cwalk(&ctx, &root, 0);
mdbx_env_cthr_toggle(&ctx, 1 | MDBX_EOF);
thread_err = mdbx_thread_join(thread);
- mdbx_condmutex_destroy(&ctx.mc_condmutex);
+ mdbx_condpair_destroy(&ctx.mc_condpair);
}
if (unlikely(thread_err != MDBX_SUCCESS))
return thread_err;
@@ -15321,7 +15798,7 @@ static int __cold mdbx_env_compact(MDBX_env *env, MDBX_txn *read_txn,
/* Copy environment as-is. */
static int __cold mdbx_env_copy_asis(MDBX_env *env, MDBX_txn *read_txn,
mdbx_filehandle_t fd, uint8_t *buffer,
- const bool dest_is_pipe) {
+ const bool dest_is_pipe, const int flags) {
/* We must start the actual read txn after blocking writers */
int rc = mdbx_txn_end(read_txn, MDBX_END_RESET_TMP);
if (unlikely(rc != MDBX_SUCCESS))
@@ -15345,9 +15822,12 @@ static int __cold mdbx_env_copy_asis(MDBX_env *env, MDBX_txn *read_txn,
memcpy(buffer, env->me_map, meta_bytes);
MDBX_meta *const headcopy = /* LY: get pointer to the spanshot copy */
(MDBX_meta *)(buffer + ((uint8_t *)mdbx_meta_head(env) - env->me_map));
+ mdbx_txn_unlock(env);
+
+ if (flags & MDBX_CP_FORCE_RESIZEABLE)
+ make_sizeable(headcopy);
/* Update signature to steady */
headcopy->mm_datasync_sign = mdbx_meta_sign(headcopy);
- mdbx_txn_unlock(env);
/* Copy the data */
const size_t whole_size = pgno_align2os_bytes(env, read_txn->mt_end_pgno);
@@ -15463,9 +15943,8 @@ int __cold mdbx_env_copy2fd(MDBX_env *env, mdbx_filehandle_t fd,
if (likely(rc == MDBX_SUCCESS)) {
memset(buffer, 0, pgno2bytes(env, NUM_METAS));
- rc = (flags & MDBX_CP_COMPACT)
- ? mdbx_env_compact(env, read_txn, fd, buffer, dest_is_pipe)
- : mdbx_env_copy_asis(env, read_txn, fd, buffer, dest_is_pipe);
+ rc = ((flags & MDBX_CP_COMPACT) ? mdbx_env_compact : mdbx_env_copy_asis)(
+ env, read_txn, fd, buffer, dest_is_pipe, flags);
}
mdbx_txn_abort(read_txn);
@@ -15540,7 +16019,7 @@ int __cold mdbx_env_set_flags(MDBX_env *env, unsigned flags, int onoff) {
return rc;
if (onoff)
- env->me_flags |= flags;
+ env->me_flags = merge_flags(env->me_flags, flags);
else
env->me_flags &= ~flags;
@@ -15671,6 +16150,56 @@ int __cold mdbx_env_stat_ex(const MDBX_env *env, const MDBX_txn *txn,
}
}
+int __cold mdbx_dbi_dupsort_depthmask(MDBX_txn *txn, MDBX_dbi dbi,
+ uint32_t *mask) {
+ int rc = check_txn(txn, MDBX_TXN_BLOCKED);
+ if (unlikely(rc != MDBX_SUCCESS))
+ return rc;
+
+ if (unlikely(!mask))
+ return MDBX_EINVAL;
+
+ if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_VALID)))
+ return MDBX_EINVAL;
+
+ MDBX_cursor_couple cx;
+ rc = mdbx_cursor_init(&cx.outer, txn, dbi);
+ if (unlikely(rc != MDBX_SUCCESS))
+ return rc;
+ if ((cx.outer.mc_db->md_flags & MDBX_DUPSORT) == 0)
+ return MDBX_RESULT_TRUE;
+
+ MDBX_val key, data;
+ rc = mdbx_cursor_first(&cx.outer, &key, &data);
+ *mask = 0;
+ while (rc == MDBX_SUCCESS) {
+ const MDBX_node *node = page_node(cx.outer.mc_pg[cx.outer.mc_top],
+ cx.outer.mc_ki[cx.outer.mc_top]);
+ const MDBX_db *db = node_data(node);
+ const unsigned flags = node_flags(node);
+ switch (flags) {
+ case F_BIGDATA:
+ case 0:
+ /* single-value entry, deep = 0 */
+ *mask |= 1 << 0;
+ break;
+ case F_DUPDATA:
+ /* single sub-page, deep = 1 */
+ *mask |= 1 << 1;
+ break;
+ case F_DUPDATA | F_SUBDATA:
+ /* sub-tree */
+ *mask |= 1 << unaligned_peek_u16(1, &db->md_depth);
+ break;
+ default:
+ return MDBX_CORRUPTED;
+ }
+ rc = mdbx_cursor_next(&cx.outer, &key, &data, MDBX_NEXT_NODUP);
+ }
+
+ return (rc == MDBX_NOTFOUND) ? MDBX_SUCCESS : rc;
+}
+
int __cold mdbx_env_info(MDBX_env *env, MDBX_envinfo *arg, size_t bytes) {
return mdbx_env_info_ex(env, NULL, arg, bytes);
}
@@ -15807,7 +16336,7 @@ static MDBX_cmp_func *mdbx_default_keycmp(unsigned flags) {
static MDBX_cmp_func *mdbx_default_datacmp(unsigned flags) {
return !(flags & MDBX_DUPSORT)
- ? mdbx_cmp_memn
+ ? mdbx_cmp_lenfast
: ((flags & MDBX_INTEGERDUP)
? mdbx_cmp_int_unaligned
: ((flags & MDBX_REVERSEDUP) ? mdbx_cmp_memnr
@@ -15825,9 +16354,10 @@ static int mdbx_dbi_bind(MDBX_txn *txn, const MDBX_dbi dbi, unsigned user_flags,
* = assume that a properly create request with custom flags;
*/
if ((user_flags ^ txn->mt_dbs[dbi].md_flags) & PERSISTENT_FLAGS) {
- /* flags ara differs, check other conditions */
- if (!user_flags && (!keycmp || keycmp == txn->mt_dbxs[dbi].md_cmp) &&
- (!datacmp || datacmp == txn->mt_dbxs[dbi].md_dcmp)) {
+ /* flags are differs, check other conditions */
+ if ((!user_flags && (!keycmp || keycmp == txn->mt_dbxs[dbi].md_cmp) &&
+ (!datacmp || datacmp == txn->mt_dbxs[dbi].md_dcmp)) ||
+ user_flags == MDBX_ACCEDE) {
/* no comparators were provided and flags are zero,
* seems that is case #1 above */
user_flags = txn->mt_dbs[dbi].md_flags;
@@ -15866,18 +16396,30 @@ static int mdbx_dbi_bind(MDBX_txn *txn, const MDBX_dbi dbi, unsigned user_flags,
int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags,
MDBX_dbi *dbi, MDBX_cmp_func *keycmp,
MDBX_cmp_func *datacmp) {
- if (unlikely(!dbi || (user_flags & ~VALID_FLAGS) != 0))
- return MDBX_EINVAL;
- *dbi = 0;
+ int rc = MDBX_EINVAL;
+ if (unlikely(!dbi))
+ return rc;
- int rc = check_txn(txn, MDBX_TXN_BLOCKED);
- if (unlikely(rc != MDBX_SUCCESS))
+ if (unlikely((user_flags & ~VALID_FLAGS) != 0)) {
+ early_bailout:
+ *dbi = 0;
return rc;
+ }
+
+ rc = check_txn(txn, MDBX_TXN_BLOCKED);
+ if (unlikely(rc != MDBX_SUCCESS))
+ goto early_bailout;
- switch (user_flags &
- (MDBX_INTEGERDUP | MDBX_DUPFIXED | MDBX_DUPSORT | MDBX_REVERSEDUP)) {
+ switch (user_flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED | MDBX_DUPSORT |
+ MDBX_REVERSEDUP | MDBX_ACCEDE)) {
+ case MDBX_ACCEDE:
+ if ((user_flags & MDBX_CREATE) == 0)
+ break;
+ __fallthrough /* fall through */;
default:
- return MDBX_EINVAL;
+ rc = MDBX_EINVAL;
+ goto early_bailout;
+
case MDBX_DUPSORT:
case MDBX_DUPSORT | MDBX_REVERSEDUP:
case MDBX_DUPSORT | MDBX_DUPFIXED:
@@ -15891,8 +16433,9 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags,
/* main table? */
if (!table_name) {
rc = mdbx_dbi_bind(txn, MAIN_DBI, user_flags, keycmp, datacmp);
- if (likely(rc == MDBX_SUCCESS))
- *dbi = MAIN_DBI;
+ if (unlikely(rc != MDBX_SUCCESS))
+ goto early_bailout;
+ *dbi = MAIN_DBI;
return rc;
}
@@ -15914,69 +16457,88 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags,
}
if (len == txn->mt_dbxs[scan].md_name.iov_len &&
!strncmp(table_name, txn->mt_dbxs[scan].md_name.iov_base, len)) {
+ rc = mdbx_dbi_bind(txn, scan, user_flags, keycmp, datacmp);
+ if (unlikely(rc != MDBX_SUCCESS))
+ goto early_bailout;
*dbi = scan;
- return mdbx_dbi_bind(txn, scan, user_flags, keycmp, datacmp);
+ return rc;
}
}
/* Fail, if no free slot and max hit */
MDBX_env *env = txn->mt_env;
- if (unlikely(slot >= env->me_maxdbs))
- return MDBX_DBS_FULL;
+ if (unlikely(slot >= env->me_maxdbs)) {
+ rc = MDBX_DBS_FULL;
+ goto early_bailout;
+ }
/* Cannot mix named table with some main-table flags */
if (unlikely(txn->mt_dbs[MAIN_DBI].md_flags &
- (MDBX_DUPSORT | MDBX_INTEGERKEY)))
- return (user_flags & MDBX_CREATE) ? MDBX_INCOMPATIBLE : MDBX_NOTFOUND;
+ (MDBX_DUPSORT | MDBX_INTEGERKEY))) {
+ rc = (user_flags & MDBX_CREATE) ? MDBX_INCOMPATIBLE : MDBX_NOTFOUND;
+ goto early_bailout;
+ }
/* Find the DB info */
int exact = 0;
MDBX_val key, data;
key.iov_len = len;
key.iov_base = (void *)table_name;
- MDBX_cursor mc;
- rc = mdbx_cursor_init(&mc, txn, MAIN_DBI);
+ MDBX_cursor_couple couple;
+ rc = mdbx_cursor_init(&couple.outer, txn, MAIN_DBI);
if (unlikely(rc != MDBX_SUCCESS))
- return rc;
- rc = mdbx_cursor_set(&mc, &key, &data, MDBX_SET, &exact);
+ goto early_bailout;
+ rc = mdbx_cursor_set(&couple.outer, &key, &data, MDBX_SET, &exact);
if (unlikely(rc != MDBX_SUCCESS)) {
if (rc != MDBX_NOTFOUND || !(user_flags & MDBX_CREATE))
- return rc;
+ goto early_bailout;
} else {
/* make sure this is actually a table */
- MDBX_node *node = page_node(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]);
- if (unlikely((node_flags(node) & (F_DUPDATA | F_SUBDATA)) != F_SUBDATA))
- return MDBX_INCOMPATIBLE;
- if (unlikely(data.iov_len < sizeof(MDBX_db)))
- return MDBX_CORRUPTED;
+ MDBX_node *node = page_node(couple.outer.mc_pg[couple.outer.mc_top],
+ couple.outer.mc_ki[couple.outer.mc_top]);
+ if (unlikely((node_flags(node) & (F_DUPDATA | F_SUBDATA)) != F_SUBDATA)) {
+ rc = MDBX_INCOMPATIBLE;
+ goto early_bailout;
+ }
+ if (unlikely(data.iov_len < sizeof(MDBX_db))) {
+ rc = MDBX_CORRUPTED;
+ goto early_bailout;
+ }
}
- if (rc != MDBX_SUCCESS && unlikely(txn->mt_flags & MDBX_RDONLY))
- return MDBX_EACCESS;
+ if (rc != MDBX_SUCCESS && unlikely(txn->mt_flags & MDBX_RDONLY)) {
+ rc = MDBX_EACCESS;
+ goto early_bailout;
+ }
/* Done here so we cannot fail after creating a new DB */
char *namedup = mdbx_strdup(table_name);
- if (unlikely(!namedup))
- return MDBX_ENOMEM;
+ if (unlikely(!namedup)) {
+ rc = MDBX_ENOMEM;
+ goto early_bailout;
+ }
int err = mdbx_fastmutex_acquire(&env->me_dbi_lock);
if (unlikely(err != MDBX_SUCCESS)) {
+ rc = err;
mdbx_free(namedup);
- return err;
+ goto early_bailout;
}
if (txn->mt_numdbs < env->me_numdbs) {
+ /* Import handles from env */
for (unsigned i = txn->mt_numdbs; i < env->me_numdbs; ++i) {
- txn->mt_dbflags[i] = 0;
+ txn->mt_dbstate[i] = 0;
if (env->me_dbflags[i] & MDBX_VALID) {
txn->mt_dbs[i].md_flags = env->me_dbflags[i] & PERSISTENT_FLAGS;
- txn->mt_dbflags[i] = DB_VALID | DB_USRVALID | DB_STALE;
+ txn->mt_dbstate[i] = DB_VALID | DB_USRVALID | DB_STALE;
mdbx_tassert(txn, txn->mt_dbxs[i].md_cmp != NULL);
}
}
txn->mt_numdbs = env->me_numdbs;
}
+ /* Rescan after mutex acquisition & import handles */
for (slot = scan = txn->mt_numdbs; --scan >= CORE_DBS;) {
if (!txn->mt_dbxs[scan].md_name.iov_len) {
/* Remember this free slot */
@@ -15985,15 +16547,17 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags,
}
if (len == txn->mt_dbxs[scan].md_name.iov_len &&
!strncmp(table_name, txn->mt_dbxs[scan].md_name.iov_base, len)) {
- *dbi = scan;
rc = mdbx_dbi_bind(txn, scan, user_flags, keycmp, datacmp);
- goto bailout;
+ if (unlikely(rc != MDBX_SUCCESS))
+ goto later_bailout;
+ *dbi = scan;
+ goto later_exit;
}
}
if (unlikely(slot >= env->me_maxdbs)) {
rc = MDBX_DBS_FULL;
- goto bailout;
+ goto later_bailout;
}
unsigned dbflag = DB_FRESH | DB_VALID | DB_USRVALID;
@@ -16006,28 +16570,29 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags,
db_dummy.md_flags = user_flags & PERSISTENT_FLAGS;
data.iov_len = sizeof(db_dummy);
data.iov_base = &db_dummy;
- WITH_CURSOR_TRACKING(
- mc,
- rc = mdbx_cursor_put(&mc, &key, &data, F_SUBDATA | MDBX_NOOVERWRITE));
+ WITH_CURSOR_TRACKING(couple.outer,
+ rc = mdbx_cursor_put(&couple.outer, &key, &data,
+ F_SUBDATA | MDBX_NOOVERWRITE));
if (unlikely(rc != MDBX_SUCCESS))
- goto bailout;
+ goto later_bailout;
dbflag |= DB_DIRTY | DB_CREAT;
}
/* Got info, register DBI in this txn */
- txn->mt_dbxs[slot].md_cmp = nullptr;
- txn->mt_dbxs[slot].md_dcmp = nullptr;
+ memset(txn->mt_dbxs + slot, 0, sizeof(MDBX_dbx));
txn->mt_dbs[slot] = *(MDBX_db *)data.iov_base;
env->me_dbflags[slot] = 0;
rc = mdbx_dbi_bind(txn, slot, user_flags, keycmp, datacmp);
if (unlikely(rc != MDBX_SUCCESS)) {
mdbx_tassert(txn, (dbflag & DB_CREAT) == 0);
- bailout:
+ later_bailout:
+ *dbi = 0;
+ later_exit:
mdbx_free(namedup);
} else {
- txn->mt_dbflags[slot] = (uint8_t)dbflag;
+ txn->mt_dbstate[slot] = (uint8_t)dbflag;
txn->mt_dbxs[slot].md_name.iov_base = namedup;
txn->mt_dbxs[slot].md_name.iov_len = len;
txn->mt_numdbs += (slot == txn->mt_numdbs);
@@ -16037,7 +16602,7 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags,
if (env->me_numdbs <= slot)
env->me_numdbs = slot + 1;
} else {
- env->me_dbiseqs[slot] += 1;
+ env->me_dbiseqs[slot]++;
}
txn->mt_dbiseqs[slot] = env->me_dbiseqs[slot];
*dbi = slot;
@@ -16071,7 +16636,7 @@ int __cold mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *dest,
if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED))
return MDBX_BAD_TXN;
- if (unlikely(txn->mt_dbflags[dbi] & DB_STALE)) {
+ if (unlikely(txn->mt_dbstate[dbi] & DB_STALE)) {
rc = mdbx_fetch_sdb(txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
@@ -16093,7 +16658,6 @@ static int mdbx_dbi_close_locked(MDBX_env *env, MDBX_dbi dbi) {
env->me_dbflags[dbi] = 0;
env->me_dbxs[dbi].md_name.iov_len = 0;
mdbx_compiler_barrier();
- env->me_dbiseqs[dbi]++;
env->me_dbxs[dbi].md_name.iov_base = NULL;
mdbx_free(ptr);
return MDBX_SUCCESS;
@@ -16130,7 +16694,7 @@ int mdbx_dbi_flags_ex(MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags,
return MDBX_EINVAL;
*flags = txn->mt_dbs[dbi].md_flags & PERSISTENT_FLAGS;
- *state = txn->mt_dbflags[dbi] & (DB_FRESH | DB_CREAT | DB_DIRTY | DB_STALE);
+ *state = txn->mt_dbstate[dbi] & (DB_FRESH | DB_CREAT | DB_DIRTY | DB_STALE);
return MDBX_SUCCESS;
}
@@ -16272,13 +16836,14 @@ int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, int del) {
if (del && dbi >= CORE_DBS) {
rc = mdbx_del0(txn, MAIN_DBI, &mc->mc_dbx->md_name, NULL, F_SUBDATA);
if (likely(rc == MDBX_SUCCESS)) {
- txn->mt_dbflags[dbi] = DB_STALE;
+ txn->mt_dbstate[dbi] = DB_STALE;
MDBX_env *env = txn->mt_env;
rc = mdbx_fastmutex_acquire(&env->me_dbi_lock);
if (unlikely(rc != MDBX_SUCCESS)) {
txn->mt_flags |= MDBX_TXN_ERROR;
goto bailout;
}
+ env->me_dbiseqs[dbi]++;
mdbx_dbi_close_locked(env, dbi);
mdbx_ensure(env,
mdbx_fastmutex_release(&env->me_dbi_lock) == MDBX_SUCCESS);
@@ -16287,7 +16852,7 @@ int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, int del) {
}
} else {
/* reset the DB record, mark it dirty */
- txn->mt_dbflags[dbi] |= DB_DIRTY;
+ txn->mt_dbstate[dbi] |= DB_DIRTY;
txn->mt_dbs[dbi].md_depth = 0;
txn->mt_dbs[dbi].md_branch_pages = 0;
txn->mt_dbs[dbi].md_leaf_pages = 0;
@@ -16761,21 +17326,23 @@ int mdbx_txn_straggler(const MDBX_txn *txn, int *percent)
typedef struct mdbx_walk_ctx {
void *mw_user;
MDBX_pgvisitor_func *mw_visitor;
- MDBX_cursor mw_cursor;
+ MDBX_txn *mw_txn;
+ MDBX_cursor *mw_cursor;
+ bool mw_dont_check_keys_ordering;
} mdbx_walk_ctx_t;
+static int __cold mdbx_walk_sdb(mdbx_walk_ctx_t *ctx, MDBX_db *const db,
+ const char *name, int deep);
/* Depth-first tree traversal. */
-static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi,
- pgno_t pgno, int deep) {
- if (unlikely(pgno == P_INVALID))
- return MDBX_SUCCESS; /* empty db */
-
+static int __cold mdbx_walk_tree(mdbx_walk_ctx_t *ctx, pgno_t pgno,
+ const char *name, int deep) {
+ assert(pgno != P_INVALID);
MDBX_page *mp;
- int rc = mdbx_page_get(&ctx->mw_cursor, pgno, &mp, NULL);
+ int rc = mdbx_page_get(ctx->mw_cursor, pgno, &mp, NULL);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
- rc = mdbx_page_check(ctx->mw_cursor.mc_txn->mt_env, mp, false);
+ rc = mdbx_page_check(ctx->mw_cursor, mp, 0);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
@@ -16832,10 +17399,10 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi,
const pgno_t large_pgno = node_largedata_pgno(node);
MDBX_page *op;
- rc = mdbx_page_get(&ctx->mw_cursor, large_pgno, &op, NULL);
+ rc = mdbx_page_get(ctx->mw_cursor, large_pgno, &op, NULL);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
- rc = mdbx_page_check(ctx->mw_cursor.mc_txn->mt_env, op, false);
+ rc = mdbx_page_check(ctx->mw_cursor, op, 0);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
@@ -16848,12 +17415,12 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi,
const size_t over_header = PAGEHDRSZ;
const size_t over_payload = node_ds(node);
const size_t over_unused =
- pgno2bytes(ctx->mw_cursor.mc_txn->mt_env, op->mp_pages) -
+ pgno2bytes(ctx->mw_cursor->mc_txn->mt_env, op->mp_pages) -
over_payload - over_header;
rc = ctx->mw_visitor(
- large_pgno, op->mp_pages, ctx->mw_user, deep, dbi,
- pgno2bytes(ctx->mw_cursor.mc_txn->mt_env, op->mp_pages),
+ large_pgno, op->mp_pages, ctx->mw_user, deep, name,
+ pgno2bytes(ctx->mw_cursor->mc_txn->mt_env, op->mp_pages),
MDBX_page_large, 1, over_payload, over_header, over_unused);
} break;
@@ -16909,7 +17476,7 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi,
}
}
- rc = ctx->mw_visitor(pgno, 0, ctx->mw_user, deep + 1, dbi, node_ds(node),
+ rc = ctx->mw_visitor(pgno, 0, ctx->mw_user, deep + 1, name, node_ds(node),
subtype, nsubkeys, subpayload_size, subheader_size,
subunused_size + subalign_bytes);
header_size += subheader_size;
@@ -16926,8 +17493,8 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi,
return rc;
}
- rc = ctx->mw_visitor(mp->mp_pgno, 1, ctx->mw_user, deep, dbi,
- ctx->mw_cursor.mc_txn->mt_env->me_psize, type, nkeys,
+ rc = ctx->mw_visitor(mp->mp_pgno, 1, ctx->mw_user, deep, name,
+ ctx->mw_cursor->mc_txn->mt_env->me_psize, type, nkeys,
payload_size, header_size, unused_size + align_bytes);
if (unlikely(rc != MDBX_SUCCESS))
@@ -16939,7 +17506,7 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi,
MDBX_node *node = page_node(mp, i);
if (type == MDBX_page_branch) {
- rc = mdbx_env_walk(ctx, dbi, node_pgno(node), deep + 1);
+ rc = mdbx_walk_tree(ctx, node_pgno(node), name, deep + 1);
if (unlikely(rc != MDBX_SUCCESS)) {
if (rc != MDBX_RESULT_TRUE)
return rc;
@@ -16959,17 +17526,17 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi,
if (unlikely(namelen == 0 || node_ds(node) != sizeof(MDBX_db)))
return MDBX_CORRUPTED;
- char namebuf_onstask[142];
- char *const name = (namelen < sizeof(namebuf_onstask))
- ? namebuf_onstask
- : mdbx_malloc(namelen + 1);
- if (name) {
- memcpy(name, node_key(node), namelen);
- name[namelen] = 0;
+ char namebuf_onstask[64];
+ char *const sub_name = (namelen < sizeof(namebuf_onstask))
+ ? namebuf_onstask
+ : mdbx_malloc(namelen + 1);
+ if (sub_name) {
+ memcpy(sub_name, node_key(node), namelen);
+ sub_name[namelen] = 0;
memcpy(&db, node_data(node), sizeof(db));
- rc = mdbx_env_walk(ctx, name, db.md_root, deep + 1);
- if (name != namebuf_onstask)
- mdbx_free(name);
+ rc = mdbx_walk_sdb(ctx, &db, sub_name, deep + 1);
+ if (sub_name != namebuf_onstask)
+ mdbx_free(sub_name);
} else {
rc = MDBX_ENOMEM;
}
@@ -16979,8 +17546,19 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi,
if (unlikely(node_ds(node) != sizeof(MDBX_db)))
return MDBX_CORRUPTED;
+ if (unlikely(ctx->mw_cursor->mc_xcursor == NULL))
+ return MDBX_CORRUPTED;
+
memcpy(&db, node_data(node), sizeof(db));
- rc = mdbx_env_walk(ctx, dbi, db.md_root, deep + 1);
+ assert(ctx->mw_cursor->mc_xcursor ==
+ &container_of(ctx->mw_cursor, MDBX_cursor_couple, outer)->inner);
+ ctx->mw_cursor = &ctx->mw_cursor->mc_xcursor->mx_cursor;
+ rc = mdbx_walk_tree(ctx, db.md_root, name, deep + 1);
+ MDBX_xcursor *inner_xcursor =
+ container_of(ctx->mw_cursor, MDBX_xcursor, mx_cursor);
+ MDBX_cursor_couple *couple =
+ container_of(inner_xcursor, MDBX_cursor_couple, inner);
+ ctx->mw_cursor = &couple->outer;
break;
}
@@ -16991,18 +17569,41 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi,
return MDBX_SUCCESS;
}
+static int __cold mdbx_walk_sdb(mdbx_walk_ctx_t *ctx, MDBX_db *const db,
+ const char *name, int deep) {
+ if (unlikely(db->md_root == P_INVALID))
+ return MDBX_SUCCESS; /* empty db */
+
+ MDBX_cursor_couple couple;
+ MDBX_dbx dbx = {.md_klen_min = INT_MAX};
+ uint8_t dbstate = DB_VALID | DB_AUDITED;
+ int rc = mdbx_couple_init(&couple, ~0u, ctx->mw_txn, db, &dbx, &dbstate);
+ if (unlikely(rc != MDBX_SUCCESS))
+ return rc;
+
+ if (ctx->mw_dont_check_keys_ordering) {
+ couple.outer.mc_flags |= C_SKIPORD;
+ couple.inner.mx_cursor.mc_flags |= C_SKIPORD;
+ }
+ couple.outer.mc_next = ctx->mw_cursor;
+ ctx->mw_cursor = &couple.outer;
+ rc = mdbx_walk_tree(ctx, db->md_root, name, deep);
+ ctx->mw_cursor = couple.outer.mc_next;
+ return rc;
+}
+
int __cold mdbx_env_pgwalk(MDBX_txn *txn, MDBX_pgvisitor_func *visitor,
- void *user) {
+ void *user, int dont_check_keys_ordering) {
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
mdbx_walk_ctx_t ctx;
memset(&ctx, 0, sizeof(ctx));
- ctx.mw_cursor.mc_snum = 1;
- ctx.mw_cursor.mc_txn = txn;
+ ctx.mw_txn = txn;
ctx.mw_user = user;
ctx.mw_visitor = visitor;
+ ctx.mw_dont_check_keys_ordering = dont_check_keys_ordering != 0;
rc = visitor(0, NUM_METAS, user, 0, MDBX_PGWALK_META,
pgno2bytes(txn->mt_env, NUM_METAS), MDBX_page_meta, NUM_METAS,
@@ -17010,10 +17611,9 @@ int __cold mdbx_env_pgwalk(MDBX_txn *txn, MDBX_pgvisitor_func *visitor,
(txn->mt_env->me_psize - sizeof(MDBX_meta) - PAGEHDRSZ) *
NUM_METAS);
if (!MDBX_IS_ERROR(rc))
- rc = mdbx_env_walk(&ctx, MDBX_PGWALK_GC, txn->mt_dbs[FREE_DBI].md_root, 0);
+ rc = mdbx_walk_sdb(&ctx, &txn->mt_dbs[FREE_DBI], MDBX_PGWALK_GC, 0);
if (!MDBX_IS_ERROR(rc))
- rc =
- mdbx_env_walk(&ctx, MDBX_PGWALK_MAIN, txn->mt_dbs[MAIN_DBI].md_root, 0);
+ rc = mdbx_walk_sdb(&ctx, &txn->mt_dbs[MAIN_DBI], MDBX_PGWALK_MAIN, 0);
if (!MDBX_IS_ERROR(rc))
rc = visitor(P_INVALID, 0, user, INT_MIN, NULL, 0, MDBX_page_void, 0, 0, 0,
0);
@@ -17112,7 +17712,7 @@ int mdbx_cursor_eof(const MDBX_cursor *mc) {
struct diff_result {
ptrdiff_t diff;
- int level;
+ unsigned level;
int root_nkeys;
};
@@ -17203,7 +17803,7 @@ __hot static ptrdiff_t estimate(const MDBX_db *db,
* level-N: branch-page(s) => scale = leaf-factor
* leaf-level: leaf-page(s) => scale = 1
*/
- ptrdiff_t btree_power = db->md_depth - 2 - dr->level;
+ ptrdiff_t btree_power = (ptrdiff_t)db->md_depth - 2 - (ptrdiff_t)dr->level;
if (btree_power < 0)
return dr->diff;
@@ -17339,11 +17939,6 @@ int mdbx_estimate_move(const MDBX_cursor *cursor, MDBX_val *key, MDBX_val *data,
return mdbx_estimate_distance(cursor, &next.outer, distance_items);
}
-static int mdbx_is_samedata(const MDBX_val *a, const MDBX_val *b) {
- return a->iov_len == b->iov_len &&
- memcmp(a->iov_base, b->iov_base, a->iov_len) == 0;
-}
-
int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *begin_key,
MDBX_val *begin_data, MDBX_val *end_key,
MDBX_val *end_data, ptrdiff_t *size_items) {
@@ -17410,7 +18005,8 @@ int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *begin_key,
end_key = begin_key;
}
if (end_key && !begin_data && !end_data &&
- (begin_key == end_key || mdbx_is_samedata(begin_key, end_key))) {
+ (begin_key == end_key ||
+ begin.outer.mc_dbx->md_cmp(begin_key, end_key) == 0)) {
/* LY: single key case */
int exact = 0;
rc = mdbx_cursor_set(&begin.outer, begin_key, NULL, MDBX_SET, &exact);
@@ -17566,7 +18162,7 @@ int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
if (new_data) {
/* обновление конкретного дубликата */
- if (mdbx_is_samedata(old_data, new_data))
+ if (cx.outer.mc_dbx->md_dcmp(old_data, new_data) == 0)
/* если данные совпадают, то ничего делать не надо */
goto bailout;
}
@@ -17589,8 +18185,7 @@ int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
MDBX_page *page = cx.outer.mc_pg[cx.outer.mc_top];
if (txn->mt_dbs[dbi].md_flags & MDBX_DUPSORT) {
if (flags & MDBX_CURRENT) {
- /* для не-уникальных ключей позволяем update/delete только если ключ
- * один */
+ /* disallow update/delete for multi-values */
MDBX_node *node = page_node(page, cx.outer.mc_ki[cx.outer.mc_top]);
if (F_ISSET(node_flags(node), F_DUPDATA)) {
mdbx_tassert(txn, XCURSOR_INITED(&cx.outer) &&
@@ -17601,7 +18196,8 @@ int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
}
}
/* если данные совпадают, то ничего делать не надо */
- if (new_data && mdbx_is_samedata(&present_data, new_data)) {
+ if (new_data &&
+ cx.outer.mc_dbx->md_dcmp(&present_data, new_data) == 0) {
*old_data = *new_data;
goto bailout;
}
@@ -17610,14 +18206,15 @@ int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
* но здесь это в любом случае допустимо, так как мы
* проверили что для ключа есть только одно значение. */
} else if ((flags & MDBX_NODUPDATA) &&
- mdbx_is_samedata(&present_data, new_data)) {
+ cx.outer.mc_dbx->md_dcmp(&present_data, new_data) == 0) {
/* если данные совпадают и установлен MDBX_NODUPDATA */
rc = MDBX_KEYEXIST;
goto bailout;
}
} else {
/* если данные совпадают, то ничего делать не надо */
- if (new_data && mdbx_is_samedata(&present_data, new_data)) {
+ if (new_data &&
+ cx.outer.mc_dbx->md_dcmp(&present_data, new_data) == 0) {
*old_data = *new_data;
goto bailout;
}
@@ -17736,7 +18333,7 @@ int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result,
if (unlikely(TXN_DBI_CHANGED(txn, dbi)))
return MDBX_BAD_DBI;
- if (unlikely(txn->mt_dbflags[dbi] & DB_STALE)) {
+ if (unlikely(txn->mt_dbstate[dbi] & DB_STALE)) {
rc = mdbx_fetch_sdb(txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
@@ -17757,7 +18354,7 @@ int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result,
mdbx_tassert(txn, new > dbs->md_seq);
dbs->md_seq = new;
txn->mt_flags |= MDBX_TXN_DIRTY;
- txn->mt_dbflags[dbi] |= DB_DIRTY;
+ txn->mt_dbstate[dbi] |= DB_DIRTY;
}
return MDBX_SUCCESS;
@@ -17784,6 +18381,7 @@ __cold intptr_t mdbx_limits_dbsize_max(intptr_t pagesize) {
!is_powerof2((size_t)pagesize)))
return -1;
+ STATIC_ASSERT(MAX_MAPSIZE < INTPTR_MAX);
const uint64_t limit = MAX_PAGENO * (uint64_t)pagesize;
return (limit < (intptr_t)MAX_MAPSIZE) ? (intptr_t)limit
: (intptr_t)MAX_MAPSIZE;
@@ -17797,6 +18395,7 @@ __cold intptr_t mdbx_limits_txnsize_max(intptr_t pagesize) {
!is_powerof2((size_t)pagesize)))
return -1;
+ STATIC_ASSERT(MAX_MAPSIZE < INTPTR_MAX);
const uint64_t limit = pagesize * (uint64_t)(MDBX_DPL_TXNFULL - 1);
return (limit < (intptr_t)MAX_MAPSIZE) ? (intptr_t)limit
: (intptr_t)MAX_MAPSIZE;
@@ -18033,7 +18632,7 @@ int mdbx_set_attr(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data,
return MDBX_EINVAL;
if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE))
- return MDBX_VERSION_MISMATCH;
+ return MDBX_EBADSIGN;
if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)))
return MDBX_EINVAL;
@@ -18214,6 +18813,9 @@ __dll_export
#else
#error "FIXME: Unsupported byte order"
#endif /* __BYTE_ORDER__ */
+#if MDBX_HUGE_TRANSACTIONS
+ " MDBX_HUGE_TRANSACTIONS=YES"
+#endif /* MDBX_HUGE_TRANSACTIONS */
" MDBX_TXN_CHECKPID=" MDBX_TXN_CHECKPID_CONFIG
" MDBX_TXN_CHECKOWNER=" MDBX_TXN_CHECKOWNER_CONFIG
" MDBX_64BIT_ATOMIC=" MDBX_64BIT_ATOMIC_CONFIG
diff --git a/libs/libmdbx/src/src/internals.h b/libs/libmdbx/src/src/internals.h
index 7ef818ca35..c751e912af 100644
--- a/libs/libmdbx/src/src/internals.h
+++ b/libs/libmdbx/src/src/internals.h
@@ -207,10 +207,13 @@ typedef uint32_t pgno_t;
#define MAX_PAGENO UINT32_C(0x7FFFffff)
#define MIN_PAGENO NUM_METAS
+#define SAFE64_INVALID_THRESHOLD UINT64_C(0xffffFFFF00000000)
+
/* A transaction ID. */
typedef uint64_t txnid_t;
#define PRIaTXN PRIi64
#define MIN_TXNID UINT64_C(1)
+#define MAX_TXNID (SAFE64_INVALID_THRESHOLD - 1)
#define INVALID_TXNID UINT64_MAX
/* LY: for testing non-atomic 64-bit txnid on 32-bit arches.
* #define MDBX_TXNID_STEP (UINT32_MAX / 3) */
@@ -251,8 +254,6 @@ typedef union mdbx_safe64 {
};
} mdbx_safe64_t;
-#define SAFE64_INVALID_THRESHOLD UINT64_C(0xffffFFFF00000000)
-
/* Information about a single database in the environment. */
typedef struct MDBX_db {
uint16_t md_flags; /* see mdbx_dbi_open */
@@ -646,9 +647,16 @@ typedef MDBX_DP *MDBX_DPL;
#define MDBX_PNL_GRANULATE 1024
#define MDBX_PNL_INITIAL \
(MDBX_PNL_GRANULATE - 2 - MDBX_ASSUME_MALLOC_OVERHEAD / sizeof(pgno_t))
+
+#if MDBX_HUGE_TRANSACTIONS
+#define MDBX_PNL_MAX \
+ ((1u << 26) - 2 - MDBX_ASSUME_MALLOC_OVERHEAD / sizeof(pgno_t))
+#define MDBX_DPL_TXNFULL (MDBX_PNL_MAX / 2)
+#else
#define MDBX_PNL_MAX \
((1u << 24) - 2 - MDBX_ASSUME_MALLOC_OVERHEAD / sizeof(pgno_t))
#define MDBX_DPL_TXNFULL (MDBX_PNL_MAX / 4)
+#endif /* MDBX_HUGE_TRANSACTIONS */
#define MDBX_TXL_GRANULATE 32
#define MDBX_TXL_INITIAL \
@@ -681,9 +689,12 @@ typedef MDBX_DP *MDBX_DPL;
* The information here is mostly static/read-only. There is
* only a single copy of this record in the environment. */
typedef struct MDBX_dbx {
- MDBX_val md_name; /* name of the database */
- MDBX_cmp_func *md_cmp; /* function for comparing keys */
- MDBX_cmp_func *md_dcmp; /* function for comparing data items */
+ MDBX_val md_name; /* name of the database */
+ MDBX_cmp_func *md_cmp; /* function for comparing keys */
+ MDBX_cmp_func *md_dcmp; /* function for comparing data items */
+ size_t md_klen_min, md_klen_max; /* min/max key length for the database */
+ size_t md_vlen_min,
+ md_vlen_max; /* min/max value/data length for the database */
} MDBX_dbx;
/* A database transaction.
@@ -726,6 +737,8 @@ struct MDBX_txn {
MDBX_db *mt_dbs;
/* Array of sequence numbers for each DB handle */
unsigned *mt_dbiseqs;
+ /* In write txns, array of cursors for each DB */
+ MDBX_cursor **mt_cursors;
/* Transaction DB Flags */
#define DB_DIRTY MDBX_TBL_DIRTY /* DB was written in this txn */
@@ -736,10 +749,8 @@ struct MDBX_txn {
#define DB_USRVALID 0x20 /* As DB_VALID, but not set for FREE_DBI */
#define DB_DUPDATA 0x40 /* DB is MDBX_DUPSORT data */
#define DB_AUDITED 0x80 /* Internal flag for accounting during audit */
- /* In write txns, array of cursors for each DB */
- MDBX_cursor **mt_cursors;
/* Array of flags for each DB */
- uint8_t *mt_dbflags;
+ uint8_t *mt_dbstate;
/* Number of DB records in use, or 0 when the txn is finished.
* This number only ever increments until the txn finishes; we
* don't decrement it when individual DB handles are closed. */
@@ -822,18 +833,26 @@ struct MDBX_cursor {
MDBX_db *mc_db;
/* The database auxiliary record for this cursor */
MDBX_dbx *mc_dbx;
- /* The mt_dbflag for this database */
- uint8_t *mc_dbflag;
- uint16_t mc_snum; /* number of pushed pages */
- uint16_t mc_top; /* index of top page, normally mc_snum-1 */
- /* Cursor state flags. */
-#define C_INITIALIZED 0x01 /* cursor has been initialized and is valid */
-#define C_EOF 0x02 /* No more data */
-#define C_SUB 0x04 /* Cursor is a sub-cursor */
-#define C_DEL 0x08 /* last op was a cursor_del */
-#define C_UNTRACK 0x10 /* Un-track cursor when closing */
-#define C_RECLAIMING 0x20 /* GC lookup is prohibited */
-#define C_GCFREEZE 0x40 /* reclaimed_pglist must not be updated */
+ /* The mt_dbstate for this database */
+ uint8_t *mc_dbstate;
+ unsigned mc_snum; /* number of pushed pages */
+ unsigned mc_top; /* index of top page, normally mc_snum-1 */
+
+ /* Cursor state flags. */
+#define C_INITIALIZED 0x01 /* cursor has been initialized and is valid */
+#define C_EOF 0x02 /* No more data */
+#define C_SUB 0x04 /* Cursor is a sub-cursor */
+#define C_DEL 0x08 /* last op was a cursor_del */
+#define C_UNTRACK 0x10 /* Un-track cursor when closing */
+#define C_RECLAIMING 0x20 /* GC lookup is prohibited */
+#define C_GCFREEZE 0x40 /* reclaimed_pglist must not be updated */
+
+ /* Cursor checing flags. */
+#define C_COPYING 0x100 /* skip key-value length check (copying simplify) */
+#define C_UPDATING 0x200 /* update/rebalance pending */
+#define C_RETIRING 0x400 /* refs to child pages may be invalid */
+#define C_SKIPORD 0x800 /* don't check keys ordering */
+
unsigned mc_flags; /* see mdbx_cursor */
MDBX_page *mc_pg[CURSOR_STACK]; /* stack of pushed pages */
indx_t mc_ki[CURSOR_STACK]; /* stack of page indices */
@@ -850,8 +869,8 @@ typedef struct MDBX_xcursor {
MDBX_db mx_db;
/* The auxiliary DB record for this Dup DB */
MDBX_dbx mx_dbx;
- /* The mt_dbflag for this Dup DB */
- uint8_t mx_dbflag;
+ /* The mt_dbstate for this Dup DB */
+ uint8_t mx_dbstate;
} MDBX_xcursor;
typedef struct MDBX_cursor_couple {
@@ -913,7 +932,7 @@ struct MDBX_env {
MDBX_page *me_dpages; /* list of malloc'd blocks for re-use */
/* PNL of pages that became unused in a write txn */
MDBX_PNL me_retired_pages;
- /* MDBX_DP of pages written during a write txn. Length MDBX_DPL_TXNFULL. */
+ /* MDBX_DP of pages written during a write txn. */
MDBX_DPL me_dirtylist;
/* Number of freelist items that can fit in a single overflow page */
unsigned me_maxgc_ov1page;
@@ -1235,11 +1254,6 @@ MDBX_INTERNAL_FUNC void mdbx_rthc_thread_dtor(void *ptr);
* Used in pages of type P_BRANCH and P_LEAF without P_LEAF2.
* We guarantee 2-byte alignment for 'MDBX_node's.
*
- * mn_lo and mn_hi are used for data size on leaf nodes, and for child
- * pgno on branch nodes. On 64 bit platforms, mn_flags is also used
- * for pgno. (Branch nodes have no flags). Lo and hi are in host byte
- * order in case some accesses can be optimized to 32-bit word access.
- *
* Leaf node flags describe node contents. F_BIGDATA says the node's
* data part is the page number of an overflow page with actual data.
* F_DUPDATA and F_SUBDATA can be combined giving duplicate data in
@@ -1247,9 +1261,6 @@ MDBX_INTERNAL_FUNC void mdbx_rthc_thread_dtor(void *ptr);
typedef struct MDBX_node {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
union {
- struct {
- uint16_t mn_lo, mn_hi; /* part of data size or pgno */
- };
uint32_t mn_dsize;
uint32_t mn_pgno32;
};
@@ -1263,9 +1274,6 @@ typedef struct MDBX_node {
union {
uint32_t mn_pgno32;
uint32_t mn_dsize;
- struct {
- uint16_t mn_hi, mn_lo; /* part of data size or pgno */
- };
};
#endif /* __BYTE_ORDER__ */
@@ -1280,11 +1288,11 @@ typedef struct MDBX_node {
} MDBX_node;
#define MDBX_VALID 0x8000 /* DB handle is valid, for me_dbflags */
-#define PERSISTENT_FLAGS (0xffff & ~(MDBX_VALID))
+#define PERSISTENT_FLAGS (0xffff & ~(MDBX_VALID | MDBX_NOSUBDIR))
/* mdbx_dbi_open() flags */
#define VALID_FLAGS \
(MDBX_REVERSEKEY | MDBX_DUPSORT | MDBX_INTEGERKEY | MDBX_DUPFIXED | \
- MDBX_INTEGERDUP | MDBX_REVERSEDUP | MDBX_CREATE)
+ MDBX_INTEGERDUP | MDBX_REVERSEDUP | MDBX_CREATE | MDBX_ACCEDE)
/* max number of pages to commit in one writev() call */
#define MDBX_COMMIT_PAGES 64
diff --git a/libs/libmdbx/src/src/lck-posix.c b/libs/libmdbx/src/src/lck-posix.c
index 6b6127bd4b..9038739f1e 100644
--- a/libs/libmdbx/src/src/lck-posix.c
+++ b/libs/libmdbx/src/src/lck-posix.c
@@ -477,8 +477,10 @@ MDBX_INTERNAL_FUNC int __cold mdbx_lck_destroy(MDBX_env *env,
mdbx_assert(env, rc == 0);
if (rc == 0) {
+ const bool synced = env->me_lck_mmap.lck->mti_unsynced_pages == 0;
mdbx_munmap(&env->me_lck_mmap);
- rc = ftruncate(env->me_lfd, 0) ? errno : 0;
+ if (synced)
+ rc = ftruncate(env->me_lfd, 0) ? errno : 0;
}
mdbx_jitter4testing(false);
diff --git a/libs/libmdbx/src/src/lck-windows.c b/libs/libmdbx/src/src/lck-windows.c
index 19a71cc337..8ec4659a28 100644
--- a/libs/libmdbx/src/src/lck-windows.c
+++ b/libs/libmdbx/src/src/lck-windows.c
@@ -133,10 +133,9 @@ static __inline BOOL funlock(mdbx_filehandle_t fd, uint64_t offset,
#define LCK_MAXLEN (1u + (size_t)(MAXSSIZE_T))
#define LCK_META_OFFSET 0
-#define LCK_META_LEN 0x10000u
+#define LCK_META_LEN (MAX_PAGESIZE * NUM_METAS)
#define LCK_BODY_OFFSET LCK_META_LEN
#define LCK_BODY_LEN (LCK_MAXLEN - LCK_BODY_OFFSET)
-#define LCK_META LCK_META_OFFSET, LCK_META_LEN
#define LCK_BODY LCK_BODY_OFFSET, LCK_BODY_LEN
#define LCK_WHOLE 0, LCK_MAXLEN
@@ -345,13 +344,6 @@ mdbx_resume_threads_after_remap(mdbx_handle_array_t *array) {
* i.e. free/shared/exclusive x free/shared/exclusive == 9.
* Only 6 states of FSM are used, which 2 of ones are transitive.
*
- * The mdbx_lck_seize() moves the locking-FSM from the initial free/unlocked
- * state to the "exclusive write" (and returns MDBX_RESULT_TRUE) if possible,
- * or to the "used" (and returns MDBX_RESULT_FALSE).
- *
- * The mdbx_lck_downgrade() moves the locking-FSM from "exclusive write"
- * state to the "used" (i.e. shared) state.
- *
* States:
* ?-? = free, i.e. unlocked
* S-? = used, i.e. shared lock
@@ -362,6 +354,16 @@ mdbx_resume_threads_after_remap(mdbx_handle_array_t *array) {
* S-E = locked (transitive state)
* E-S
* E-E = exclusive-write, i.e. exclusive due (re)initialization
+ *
+ * The mdbx_lck_seize() moves the locking-FSM from the initial free/unlocked
+ * state to the "exclusive write" (and returns MDBX_RESULT_TRUE) if possible,
+ * or to the "used" (and returns MDBX_RESULT_FALSE).
+ *
+ * The mdbx_lck_downgrade() moves the locking-FSM from "exclusive write"
+ * state to the "used" (i.e. shared) state.
+ *
+ * The mdbx_lck_upgrade() moves the locking-FSM from "used" (i.e. shared)
+ * state to the "exclusive write" state.
*/
static void lck_unlock(MDBX_env *env) {
@@ -397,14 +399,6 @@ static void lck_unlock(MDBX_env *env) {
(void)err;
SetLastError(ERROR_SUCCESS);
- while (funlock(env->me_lazy_fd, LCK_META))
- ;
- err = GetLastError();
- assert(err == ERROR_NOT_LOCKED ||
- (mdbx_RunningUnderWine() && err == ERROR_LOCK_VIOLATION));
- (void)err;
- SetLastError(ERROR_SUCCESS);
-
while (funlock(env->me_lazy_fd, LCK_WHOLE))
;
err = GetLastError();
@@ -415,30 +409,6 @@ static void lck_unlock(MDBX_env *env) {
}
}
-MDBX_INTERNAL_FUNC int mdbx_lck_init(MDBX_env *env,
- MDBX_env *inprocess_neighbor,
- int global_uniqueness_flag) {
- (void)env;
- (void)inprocess_neighbor;
- (void)global_uniqueness_flag;
- return MDBX_SUCCESS;
-}
-
-MDBX_INTERNAL_FUNC int mdbx_lck_destroy(MDBX_env *env,
- MDBX_env *inprocess_neighbor) {
- (void)inprocess_neighbor;
-
- /* LY: should unmap before releasing the locks to avoid race condition and
- * STATUS_USER_MAPPED_FILE/ERROR_USER_MAPPED_FILE */
- if (env->me_map)
- mdbx_munmap(&env->me_dxb_mmap);
- if (env->me_lck)
- mdbx_munmap(&env->me_lck_mmap);
-
- lck_unlock(env);
- return MDBX_SUCCESS;
-}
-
/* Seize state as 'exclusive-write' (E-E and returns MDBX_RESULT_TRUE)
* or as 'used' (S-? and returns MDBX_RESULT_FALSE).
* Oherwise returns an error. */
@@ -535,43 +505,91 @@ MDBX_INTERNAL_FUNC int mdbx_lck_seize(MDBX_env *env) {
}
MDBX_INTERNAL_FUNC int mdbx_lck_downgrade(MDBX_env *env) {
- /* Transite from exclusive state (E-?) to used (S-?) */
+ /* Transite from exclusive-write state (E-E) to used (S-?) */
assert(env->me_lazy_fd != INVALID_HANDLE_VALUE);
assert(env->me_lfd != INVALID_HANDLE_VALUE);
-#if 1
if (env->me_flags & MDBX_EXCLUSIVE)
return MDBX_SUCCESS /* nope since files were must be opened non-shareable */
;
-#else
- /* 1) must be at E-E (exclusive-write) */
- if (env->me_flags & MDBX_EXCLUSIVE) {
- /* transite from E-E to E_? (exclusive-read) */
- if (!funlock(env->me_lfd, LCK_UPPER))
- mdbx_panic("%s(%s) failed: err %u", __func__,
- "E-E(exclusive-write) >> E-?(exclusive-read)", GetLastError());
- return MDBX_SUCCESS /* 2) now at E-? (exclusive-read), done */;
- }
-#endif
-
- /* 3) now at E-E (exclusive-write), transite to ?_E (middle) */
+ /* 1) now at E-E (exclusive-write), transite to ?_E (middle) */
if (!funlock(env->me_lfd, LCK_LOWER))
mdbx_panic("%s(%s) failed: err %u", __func__,
"E-E(exclusive-write) >> ?-E(middle)", GetLastError());
- /* 4) now at ?-E (middle), transite to S-E (locked) */
+ /* 2) now at ?-E (middle), transite to S-E (locked) */
if (!flock(env->me_lfd, LCK_SHARED | LCK_DONTWAIT, LCK_LOWER)) {
- int rc = GetLastError() /* 5) something went wrong, give up */;
+ int rc = GetLastError() /* 3) something went wrong, give up */;
mdbx_error("%s, err %u", "?-E(middle) >> S-E(locked)", rc);
return rc;
}
- /* 6) got S-E (locked), continue transition to S-? (used) */
+ /* 4) got S-E (locked), continue transition to S-? (used) */
if (!funlock(env->me_lfd, LCK_UPPER))
mdbx_panic("%s(%s) failed: err %u", __func__, "S-E(locked) >> S-?(used)",
GetLastError());
- return MDBX_SUCCESS /* 7) now at S-? (used), done */;
+ return MDBX_SUCCESS /* 5) now at S-? (used), done */;
+}
+
+MDBX_INTERNAL_FUNC int mdbx_lck_upgrade(MDBX_env *env) {
+ /* Transite from used state (S-?) to exclusive-write (E-E) */
+ assert(env->me_lfd != INVALID_HANDLE_VALUE);
+
+ if (env->me_flags & MDBX_EXCLUSIVE)
+ return MDBX_SUCCESS /* nope since files were must be opened non-shareable */
+ ;
+
+ int rc;
+ /* 1) now on S-? (used), try S-E (locked) */
+ mdbx_jitter4testing(false);
+ if (!flock(env->me_lfd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_UPPER)) {
+ rc = GetLastError() /* 2) something went wrong, give up */;
+ mdbx_verbose("%s, err %u", "S-?(used) >> S-E(locked)", rc);
+ return rc;
+ }
+
+ /* 3) now on S-E (locked), transite to ?-E (middle) */
+ if (!funlock(env->me_lfd, LCK_LOWER))
+ mdbx_panic("%s(%s) failed: err %u", __func__, "S-E(locked) >> ?-E(middle)",
+ GetLastError());
+
+ /* 4) now on ?-E (middle), try E-E (exclusive-write) */
+ mdbx_jitter4testing(false);
+ if (!flock(env->me_lfd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_LOWER)) {
+ rc = GetLastError() /* 5) something went wrong, give up */;
+ mdbx_verbose("%s, err %u", "?-E(middle) >> E-E(exclusive-write)", rc);
+ return rc;
+ }
+
+ return MDBX_SUCCESS /* 6) now at E-E (exclusive-write), done */;
+}
+
+MDBX_INTERNAL_FUNC int mdbx_lck_init(MDBX_env *env,
+ MDBX_env *inprocess_neighbor,
+ int global_uniqueness_flag) {
+ (void)env;
+ (void)inprocess_neighbor;
+ (void)global_uniqueness_flag;
+ return MDBX_SUCCESS;
+}
+
+MDBX_INTERNAL_FUNC int mdbx_lck_destroy(MDBX_env *env,
+ MDBX_env *inprocess_neighbor) {
+ /* LY: should unmap before releasing the locks to avoid race condition and
+ * STATUS_USER_MAPPED_FILE/ERROR_USER_MAPPED_FILE */
+ if (env->me_map)
+ mdbx_munmap(&env->me_dxb_mmap);
+ if (env->me_lck) {
+ const bool synced = env->me_lck_mmap.lck->mti_unsynced_pages == 0;
+ mdbx_munmap(&env->me_lck_mmap);
+ if (synced && !inprocess_neighbor && env->me_lfd != INVALID_HANDLE_VALUE &&
+ mdbx_lck_upgrade(env) == MDBX_SUCCESS)
+ /* this will fail if LCK is used/mmapped by other process(es) */
+ mdbx_ftruncate(env->me_lfd, 0);
+ }
+ lck_unlock(env);
+ return MDBX_SUCCESS;
}
/*----------------------------------------------------------------------------*/
diff --git a/libs/libmdbx/src/src/man1/mdbx_chk.1 b/libs/libmdbx/src/src/man1/mdbx_chk.1
index d2d03684e2..d6292f3384 100644
--- a/libs/libmdbx/src/src/man1/mdbx_chk.1
+++ b/libs/libmdbx/src/src/man1/mdbx_chk.1
@@ -1,6 +1,6 @@
.\" Copyright 2015-2020 Leonid Yuriev <leo@yuriev.ru>.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
-.TH MDBX_CHK 1 "2020-03-18" "MDBX 0.7.x"
+.TH MDBX_CHK 1 "2020-06-05" "MDBX 0.8.x"
.SH NAME
mdbx_chk \- MDBX checking tool
.SH SYNOPSIS
diff --git a/libs/libmdbx/src/src/man1/mdbx_copy.1 b/libs/libmdbx/src/src/man1/mdbx_copy.1
index a8427bb4df..6c587abdc3 100644
--- a/libs/libmdbx/src/src/man1/mdbx_copy.1
+++ b/libs/libmdbx/src/src/man1/mdbx_copy.1
@@ -2,7 +2,7 @@
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
-.TH MDBX_COPY 1 "2020-03-18" "MDBX 0.7.x"
+.TH MDBX_COPY 1 "2020-06-05" "MDBX 0.8.x"
.SH NAME
mdbx_copy \- MDBX environment copy tool
.SH SYNOPSIS
diff --git a/libs/libmdbx/src/src/man1/mdbx_dump.1 b/libs/libmdbx/src/src/man1/mdbx_dump.1
index 9985127f00..698b7cf414 100644
--- a/libs/libmdbx/src/src/man1/mdbx_dump.1
+++ b/libs/libmdbx/src/src/man1/mdbx_dump.1
@@ -2,7 +2,7 @@
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
-.TH MDBX_DUMP 1 "2020-03-18" "MDBX 0.7.x"
+.TH MDBX_DUMP 1 "2020-06-05" "MDBX 0.8.x"
.SH NAME
mdbx_dump \- MDBX environment export tool
.SH SYNOPSIS
diff --git a/libs/libmdbx/src/src/man1/mdbx_load.1 b/libs/libmdbx/src/src/man1/mdbx_load.1
index b401b38715..bc3d737e17 100644
--- a/libs/libmdbx/src/src/man1/mdbx_load.1
+++ b/libs/libmdbx/src/src/man1/mdbx_load.1
@@ -2,7 +2,7 @@
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
-.TH MDBX_LOAD 1 "2020-03-18" "MDBX 0.7.x"
+.TH MDBX_LOAD 1 "2020-06-05" "MDBX 0.8.x"
.SH NAME
mdbx_load \- MDBX environment import tool
.SH SYNOPSIS
diff --git a/libs/libmdbx/src/src/man1/mdbx_stat.1 b/libs/libmdbx/src/src/man1/mdbx_stat.1
index b7fbeb0833..f510d4e013 100644
--- a/libs/libmdbx/src/src/man1/mdbx_stat.1
+++ b/libs/libmdbx/src/src/man1/mdbx_stat.1
@@ -2,7 +2,7 @@
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
-.TH MDBX_STAT 1 "2020-03-18" "MDBX 0.7.x"
+.TH MDBX_STAT 1 "2020-06-05" "MDBX 0.8.x"
.SH NAME
mdbx_stat \- MDBX environment status tool
.SH SYNOPSIS
diff --git a/libs/libmdbx/src/src/mdbx_chk.c b/libs/libmdbx/src/src/mdbx_chk.c
index 64c1d333bc..657a28a730 100644
--- a/libs/libmdbx/src/src/mdbx_chk.c
+++ b/libs/libmdbx/src/src/mdbx_chk.c
@@ -34,7 +34,7 @@ const flagbit dbflags[] = {{MDBX_DUPSORT, "dupsort"},
{MDBX_DUPFIXED, "dupfixed"},
{MDBX_REVERSEDUP, "reversedup"},
{MDBX_INTEGERDUP, "integerdup"},
- {0, NULL}};
+ {0, nullptr}};
#if defined(_WIN32) || defined(_WIN64)
#include "wingetopt.h"
@@ -122,12 +122,12 @@ static void __printf_args(1, 2) error(const char *msg, ...) {
if (!quiet) {
va_list args;
- fflush(NULL);
+ fflush(nullptr);
va_start(args, msg);
fputs(" ! ", stderr);
vfprintf(stderr, msg, args);
va_end(args);
- fflush(NULL);
+ fflush(nullptr);
}
}
@@ -137,7 +137,7 @@ static int check_user_break(void) {
return MDBX_SUCCESS;
case 1:
print(" - interrupted by signal\n");
- fflush(NULL);
+ fflush(nullptr);
user_break = 2;
}
return MDBX_EINTR;
@@ -148,12 +148,12 @@ static void pagemap_cleanup(void) {
i < ARRAY_LENGTH(walk.dbi); ++i) {
if (walk.dbi[i].name) {
mdbx_free((void *)walk.dbi[i].name);
- walk.dbi[i].name = NULL;
+ walk.dbi[i].name = nullptr;
}
}
mdbx_free(walk.pagemap);
- walk.pagemap = NULL;
+ walk.pagemap = nullptr;
}
static walk_dbi_t *pagemap_lookup_dbi(const char *dbi_name, bool silent) {
@@ -177,11 +177,11 @@ static walk_dbi_t *pagemap_lookup_dbi(const char *dbi_name, bool silent) {
if (verbose > 0 && !silent) {
print(" - found '%s' area\n", dbi_name);
- fflush(NULL);
+ fflush(nullptr);
}
if (dbi == ARRAY_END(walk.dbi))
- return NULL;
+ return nullptr;
dbi->name = mdbx_strdup(dbi_name);
return last = dbi;
@@ -222,14 +222,14 @@ static void __printf_args(4, 5)
}
printf("\n");
if (need_fflush)
- fflush(NULL);
+ fflush(nullptr);
}
}
}
static struct problem *problems_push(void) {
struct problem *p = problems_list;
- problems_list = NULL;
+ problems_list = nullptr;
return p;
}
@@ -249,7 +249,7 @@ static size_t problems_pop(struct problem *list) {
problems_list = p;
}
print("\n");
- fflush(NULL);
+ fflush(nullptr);
}
problems_list = list;
@@ -520,6 +520,13 @@ static int handle_freedb(const uint64_t record_number, const MDBX_val *key,
return check_user_break();
}
+static int equal_or_greater(const MDBX_val *a, const MDBX_val *b) {
+ return (a->iov_len == b->iov_len &&
+ memcmp(a->iov_base, b->iov_base, a->iov_len) == 0)
+ ? 0
+ : 1;
+}
+
static int handle_maindb(const uint64_t record_number, const MDBX_val *key,
const MDBX_val *data) {
char *name;
@@ -566,7 +573,10 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler,
}
if (dbi_handle == ~0u) {
- rc = mdbx_dbi_open(txn, dbi_name, 0, &dbi_handle);
+ rc = mdbx_dbi_open_ex(
+ txn, dbi_name, MDBX_ACCEDE, &dbi_handle,
+ (dbi_name && ignore_wrong_order) ? equal_or_greater : nullptr,
+ (dbi_name && ignore_wrong_order) ? equal_or_greater : nullptr);
if (rc) {
if (!dbi_name ||
rc !=
@@ -582,7 +592,7 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler,
strcmp(only_subdb, dbi_name) != 0) {
if (verbose) {
print("Skip processing '%s'...\n", dbi_name);
- fflush(NULL);
+ fflush(nullptr);
}
skipped_subdb++;
return MDBX_SUCCESS;
@@ -590,7 +600,7 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler,
if (!silent && verbose) {
print("Processing '%s'...\n", dbi_name ? dbi_name : "@MAIN");
- fflush(NULL);
+ fflush(nullptr);
}
rc = mdbx_dbi_flags(txn, dbi_handle, &flags);
@@ -654,13 +664,18 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler,
error("mdbx_cursor_open failed, error %d %s\n", rc, mdbx_strerror(rc));
return rc;
}
+ /* if (ignore_wrong_order) {
+ mc->mc_flags |= C_SKIPORD;
+ if (mc->mc_xcursor)
+ mc->mc_xcursor->mx_cursor.mc_flags |= C_SKIPORD;
+ } */
const size_t maxkeysize = mdbx_env_get_maxkeysize_ex(env, flags);
saved_list = problems_push();
- prev_key.iov_base = NULL;
+ prev_key.iov_base = nullptr;
prev_key.iov_len = 0;
- prev_data.iov_base = NULL;
+ prev_data.iov_base = nullptr;
prev_data.iov_len = 0;
rc = mdbx_cursor_get(mc, &key, &data, MDBX_FIRST);
while (rc == MDBX_SUCCESS) {
@@ -697,26 +712,26 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler,
}
if (!bad_key) {
- int cmp = mdbx_cmp(txn, dbi_handle, &prev_key, &key);
+ int cmp = mdbx_cmp(txn, dbi_handle, &key, &prev_key);
if (cmp == 0) {
++dups;
if ((flags & MDBX_DUPSORT) == 0) {
- problem_add("entry", record_count, "duplicated entries", NULL);
+ problem_add("entry", record_count, "duplicated entries", nullptr);
if (data.iov_len == prev_data.iov_len &&
memcmp(data.iov_base, prev_data.iov_base, data.iov_len) == 0) {
- problem_add("entry", record_count, "complete duplicate", NULL);
+ problem_add("entry", record_count, "complete duplicate", nullptr);
}
} else if (!bad_data) {
- cmp = mdbx_dcmp(txn, dbi_handle, &prev_data, &data);
+ cmp = mdbx_dcmp(txn, dbi_handle, &data, &prev_data);
if (cmp == 0) {
- problem_add("entry", record_count, "complete duplicate", NULL);
- } else if (cmp > 0 && !ignore_wrong_order) {
+ problem_add("entry", record_count, "complete duplicate", nullptr);
+ } else if (cmp < 0 && !ignore_wrong_order) {
problem_add("entry", record_count, "wrong order of multi-values",
- NULL);
+ nullptr);
}
}
- } else if (cmp > 0 && !ignore_wrong_order) {
- problem_add("entry", record_count, "wrong order of entries", NULL);
+ } else if (cmp < 0 && !ignore_wrong_order) {
+ problem_add("entry", record_count, "wrong order of entries", nullptr);
}
}
} else if (verbose) {
@@ -757,7 +772,7 @@ bailout:
" key's bytes, %" PRIu64 " data's "
"bytes, %" PRIu64 " problems\n",
record_count, dups, key_bytes, data_bytes, problems_count);
- fflush(NULL);
+ fflush(nullptr);
}
mdbx_cursor_close(mc);
@@ -765,19 +780,17 @@ bailout:
}
static void usage(char *prog) {
- fprintf(
- stderr,
- "usage: %s [-V] [-v] [-n] [-q] [-c] [-w] [-d] [-i] [-s subdb] dbpath\n"
- " -V\t\tprint version and exit\n"
- " -v\t\tmore verbose, could be used multiple times\n"
- " -n\t\tNOSUBDIR mode for open\n"
- " -q\t\tbe quiet\n"
- " -c\t\tforce cooperative mode (don't try exclusive)\n"
- " -w\t\tlock DB for writing while checking\n"
- " -d\t\tdisable page-by-page traversal of B-tree\n"
- " -i\t\tignore wrong order errors (for custom comparators case)\n"
- " -s subdb\tprocess a specific subdatabase only\n",
- prog);
+ fprintf(stderr,
+ "usage: %s [-V] [-v] [-q] [-c] [-w] [-d] [-i] [-s subdb] dbpath\n"
+ " -V\t\tprint version and exit\n"
+ " -v\t\tmore verbose, could be used multiple times\n"
+ " -q\t\tbe quiet\n"
+ " -c\t\tforce cooperative mode (don't try exclusive)\n"
+ " -w\t\tlock DB for writing while checking\n"
+ " -d\t\tdisable page-by-page traversal of B-tree\n"
+ " -i\t\tignore wrong order errors (for custom comparators case)\n"
+ " -s subdb\tprocess a specific subdatabase only\n",
+ prog);
exit(EXIT_INTERRUPTED);
}
@@ -943,7 +956,7 @@ int main(int argc, char *argv[]) {
if (argc < 2)
usage(prog);
- for (int i; (i = getopt(argc, argv, "Vvqnwcdsi:")) != EOF;) {
+ for (int i; (i = getopt(argc, argv, "Vvqnwcdis:")) != EOF;) {
switch (i) {
case 'V':
printf("mdbx_chk version %d.%d.%d.%d\n"
@@ -1011,7 +1024,7 @@ int main(int argc, char *argv[]) {
mdbx_version.git.describe, mdbx_version.git.datetime,
mdbx_version.git.tree, envname,
(envflags & MDBX_RDONLY) ? "only" : "write");
- fflush(NULL);
+ fflush(nullptr);
rc = mdbx_env_create(&env);
if (rc) {
@@ -1057,7 +1070,7 @@ int main(int argc, char *argv[]) {
locked = true;
}
- rc = mdbx_txn_begin(env, NULL, MDBX_RDONLY, &txn);
+ rc = mdbx_txn_begin(env, nullptr, MDBX_RDONLY, &txn);
if (rc) {
error("mdbx_txn_begin() failed, error %d %s\n", rc, mdbx_strerror(rc));
goto bailout;
@@ -1252,7 +1265,7 @@ int main(int argc, char *argv[]) {
uint64_t empty_pages, lost_bytes;
print("Traversal b-tree by txn#%" PRIaTXN "...\n", txn->mt_txnid);
- fflush(NULL);
+ fflush(nullptr);
walk.pagemap = mdbx_calloc((size_t)backed_pages, sizeof(*walk.pagemap));
if (!walk.pagemap) {
rc = errno ? errno : MDBX_ENOMEM;
@@ -1261,7 +1274,7 @@ int main(int argc, char *argv[]) {
}
saved_list = problems_push();
- rc = mdbx_env_pgwalk(txn, pgvisitor, NULL);
+ rc = mdbx_env_pgwalk(txn, pgvisitor, nullptr, ignore_wrong_order);
traversal_problems = problems_pop(saved_list);
if (rc) {
@@ -1283,8 +1296,8 @@ int main(int argc, char *argv[]) {
if (verbose) {
uint64_t total_page_bytes = walk.pgcount * envstat.ms_psize;
- print(" - pages: total %" PRIu64 ", unused %" PRIu64 "\n", walk.pgcount,
- unused_pages);
+ print(" - pages: walked %" PRIu64 ", left/unused %" PRIu64 "\n",
+ walk.pgcount, unused_pages);
if (verbose > 1) {
for (walk_dbi_t *dbi = walk.dbi; dbi < ARRAY_END(walk.dbi) && dbi->name;
++dbi) {
@@ -1348,12 +1361,12 @@ int main(int argc, char *argv[]) {
}
} else if (verbose) {
print("Skipping b-tree walk...\n");
- fflush(NULL);
+ fflush(nullptr);
}
if (!verbose)
print("Iterating DBIs...\n");
- problems_maindb = process_db(~0u, /* MAIN_DBI */ NULL, NULL, false);
+ problems_maindb = process_db(~0u, /* MAIN_DBI */ nullptr, nullptr, false);
problems_freedb = process_db(FREE_DBI, "@GC", handle_freedb, false);
if (verbose) {
@@ -1403,7 +1416,7 @@ int main(int argc, char *argv[]) {
"monopolistic or read-write mode only)\n");
}
- if (!process_db(MAIN_DBI, NULL, handle_maindb, true)) {
+ if (!process_db(MAIN_DBI, nullptr, handle_maindb, true)) {
if (!userdb_count && verbose)
print(" - does not contain multiple databases\n");
}
@@ -1415,7 +1428,7 @@ int main(int argc, char *argv[]) {
print("Perform sync-to-disk for make steady checkpoint at txn-id #%" PRIi64
"\n",
envinfo.mi_recent_txnid);
- fflush(NULL);
+ fflush(nullptr);
if (locked) {
mdbx_txn_unlock(env);
locked = false;
@@ -1440,7 +1453,7 @@ bailout:
const bool dont_sync = rc != 0 || total_problems;
mdbx_env_close_ex(env, dont_sync);
}
- fflush(NULL);
+ fflush(nullptr);
if (rc) {
if (rc < 0)
return user_break ? EXIT_INTERRUPTED : EXIT_FAILURE_SYS;
diff --git a/libs/libmdbx/src/src/mdbx_copy.c b/libs/libmdbx/src/src/mdbx_copy.c
index 81d177ab99..cde9573a15 100644
--- a/libs/libmdbx/src/src/mdbx_copy.c
+++ b/libs/libmdbx/src/src/mdbx_copy.c
@@ -45,11 +45,10 @@ static void signal_handler(int sig) {
static void usage(const char *prog) {
fprintf(stderr,
- "usage: %s [-V] [-q] [-c] [-n] src_path [dest_path]\n"
+ "usage: %s [-V] [-q] [-c] src_path [dest_path]\n"
" -V\t\tprint version and exit\n"
" -q\t\tbe quiet\n"
" -c\t\tenable compactification (skip unused pages)\n"
- " -n\t\tNOSUBDIR mode for open\n"
" src_path\tsource database\n"
" dest_path\tdestination (stdout if not specified)\n",
prog);
diff --git a/libs/libmdbx/src/src/mdbx_dump.c b/libs/libmdbx/src/src/mdbx_dump.c
index 8bb057b2df..b921a8139b 100644
--- a/libs/libmdbx/src/src/mdbx_dump.c
+++ b/libs/libmdbx/src/src/mdbx_dump.c
@@ -26,7 +26,8 @@
#include <ctype.h>
#define PRINT 1
-static int mode;
+#define GLOBAL 2
+static int mode = GLOBAL;
typedef struct flagbit {
int bit;
@@ -39,7 +40,7 @@ flagbit dbflags[] = {{MDBX_REVERSEKEY, "reversekey"},
{MDBX_DUPFIXED, "dupfixed"},
{MDBX_INTEGERDUP, "integerdup"},
{MDBX_REVERSEDUP, "reversedup"},
- {0, NULL}};
+ {0, nullptr}};
#if defined(_WIN32) || defined(_WIN64)
#include "wingetopt.h"
@@ -92,53 +93,105 @@ static void dumpval(MDBX_val *v) {
putchar(' ');
c = v->iov_base;
end = c + v->iov_len;
- while (c < end) {
+ while (c < end)
dumpbyte(*c++);
- }
putchar('\n');
}
+bool quiet = false, rescue = false;
+const char *prog;
+static void error(const char *func, int rc) {
+ fprintf(stderr, "%s: %s() error %d %s\n", prog, func, rc, mdbx_strerror(rc));
+}
+
/* Dump in BDB-compatible format */
-static int dumpit(MDBX_txn *txn, MDBX_dbi dbi, char *name) {
- MDBX_cursor *mc;
- MDBX_stat ms;
- MDBX_val key, data;
- MDBX_envinfo info;
+static int dump_sdb(MDBX_txn *txn, MDBX_dbi dbi, char *name) {
unsigned int flags;
- int rc, i;
-
- rc = mdbx_dbi_flags(txn, dbi, &flags);
- if (rc)
+ int rc = mdbx_dbi_flags(txn, dbi, &flags);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_dbi_flags", rc);
return rc;
+ }
+ MDBX_stat ms;
rc = mdbx_dbi_stat(txn, dbi, &ms, sizeof(ms));
- if (rc)
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_dbi_stat", rc);
return rc;
+ }
+ MDBX_envinfo info;
rc = mdbx_env_info_ex(mdbx_txn_env(txn), txn, &info, sizeof(info));
- if (rc)
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_env_info_ex", rc);
return rc;
+ }
printf("VERSION=3\n");
+ if (mode & GLOBAL) {
+ mode -= GLOBAL;
+ if (info.mi_geo.upper != info.mi_geo.lower)
+ printf("geometry=l%" PRIu64 ",c%" PRIu64 ",u%" PRIu64 ",s%" PRIu64
+ ",g%" PRIu64 "\n",
+ info.mi_geo.lower, info.mi_geo.current, info.mi_geo.upper,
+ info.mi_geo.shrink, info.mi_geo.grow);
+ printf("mapsize=%" PRIu64 "\n", info.mi_geo.upper);
+ printf("maxreaders=%u\n", info.mi_maxreaders);
+
+ mdbx_canary canary;
+ rc = mdbx_canary_get(txn, &canary);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_canary_get", rc);
+ return rc;
+ }
+ if (canary.v)
+ printf("canary=v%" PRIu64 ",x%" PRIu64 ",y%" PRIu64 ",z%" PRIu64 "\n",
+ canary.v, canary.x, canary.y, canary.z);
+ }
printf("format=%s\n", mode & PRINT ? "print" : "bytevalue");
if (name)
printf("database=%s\n", name);
printf("type=btree\n");
- printf("mapsize=%" PRIu64 "\n", info.mi_geo.upper);
- printf("maxreaders=%u\n", info.mi_maxreaders);
-
- for (i = 0; dbflags[i].bit; i++)
+ printf("db_pagesize=%u\n", ms.ms_psize);
+ /* if (ms.ms_mod_txnid)
+ printf("txnid=%" PRIaTXN "\n", ms.ms_mod_txnid);
+ else if (!name)
+ printf("txnid=%" PRIaTXN "\n", mdbx_txn_id(txn)); */
+
+ printf("duplicates=%d\n", (flags & (MDBX_DUPSORT | MDBX_DUPFIXED |
+ MDBX_INTEGERDUP | MDBX_REVERSEDUP))
+ ? 1
+ : 0);
+ for (int i = 0; dbflags[i].bit; i++)
if (flags & dbflags[i].bit)
printf("%s=1\n", dbflags[i].name);
- printf("db_pagesize=%d\n", ms.ms_psize);
- printf("HEADER=END\n");
+ uint64_t sequence;
+ rc = mdbx_dbi_sequence(txn, dbi, &sequence, 0);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_dbi_sequence", rc);
+ return rc;
+ }
+ if (sequence)
+ printf("sequence=%" PRIu64 "\n", sequence);
+
+ printf("HEADER=END\n"); /*-------------------------------------------------*/
- rc = mdbx_cursor_open(txn, dbi, &mc);
- if (rc)
+ MDBX_cursor *cursor;
+ MDBX_val key, data;
+ rc = mdbx_cursor_open(txn, dbi, &cursor);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_cursor_open", rc);
return rc;
+ }
+ if (MDBX_DEBUG > 0 && rescue) {
+ cursor->mc_flags |= C_SKIPORD;
+ if (cursor->mc_xcursor)
+ cursor->mc_xcursor->mx_cursor.mc_flags |= C_SKIPORD;
+ }
- while ((rc = mdbx_cursor_get(mc, &key, &data, MDBX_NEXT)) == MDBX_SUCCESS) {
+ while ((rc = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) ==
+ MDBX_SUCCESS) {
if (user_break) {
rc = MDBX_EINTR;
break;
@@ -154,13 +207,14 @@ static int dumpit(MDBX_txn *txn, MDBX_dbi dbi, char *name) {
printf("DATA=END\n");
if (rc == MDBX_NOTFOUND)
rc = MDBX_SUCCESS;
-
+ if (unlikely(rc != MDBX_SUCCESS))
+ error("mdbx_cursor_get", rc);
return rc;
}
-static void usage(char *prog) {
+static void usage(void) {
fprintf(stderr,
- "usage: %s [-V] [-q] [-f file] [-l] [-p] [-a|-s subdb] [-r] [-n] "
+ "usage: %s [-V] [-q] [-f file] [-l] [-p] [-a|-s subdb] [-r] "
"dbpath\n"
" -V\t\tprint version and exit\n"
" -q\t\tbe quiet\n"
@@ -170,24 +224,31 @@ static void usage(char *prog) {
" -a\t\tdump main DB and all subDBs,\n"
" \t\tby default dump only the main DB\n"
" -s\t\tdump only the named subDB\n"
- " -r\t\trescure mode (ignore errors to dump corrupted DB)\n"
- " -n\t\tNOSUBDIR mode for open\n",
+ " -r\t\trescure mode (ignore errors to dump corrupted DB)\n",
prog);
exit(EXIT_FAILURE);
}
+static int equal_or_greater(const MDBX_val *a, const MDBX_val *b) {
+ return (a->iov_len == b->iov_len &&
+ memcmp(a->iov_base, b->iov_base, a->iov_len) == 0)
+ ? 0
+ : 1;
+}
+
int main(int argc, char *argv[]) {
int i, rc;
MDBX_env *env;
MDBX_txn *txn;
MDBX_dbi dbi;
- char *prog = argv[0];
+ prog = argv[0];
char *envname;
- char *subname = NULL;
- int alldbs = 0, envflags = 0, list = 0, quiet = 0, rescue = 0;
+ char *subname = nullptr;
+ unsigned envflags = 0;
+ bool alldbs = false, list = false;
if (argc < 2)
- usage(prog);
+ usage();
while ((i = getopt(argc, argv, "af:lnps:Vrq")) != EOF) {
switch (i) {
@@ -206,16 +267,16 @@ int main(int argc, char *argv[]) {
mdbx_build.options);
return EXIT_SUCCESS;
case 'l':
- list = 1;
+ list = true;
/*FALLTHROUGH*/;
__fallthrough;
case 'a':
if (subname)
- usage(prog);
- alldbs++;
+ usage();
+ alldbs = true;
break;
case 'f':
- if (freopen(optarg, "w", stdout) == NULL) {
+ if (freopen(optarg, "w", stdout) == nullptr) {
fprintf(stderr, "%s: %s: reopen: %s\n", prog, optarg,
mdbx_strerror(errno));
exit(EXIT_FAILURE);
@@ -229,22 +290,22 @@ int main(int argc, char *argv[]) {
break;
case 's':
if (alldbs)
- usage(prog);
+ usage();
subname = optarg;
break;
case 'q':
- quiet = 1;
+ quiet = true;
break;
case 'r':
- rescue = 1;
+ rescue = true;
break;
default:
- usage(prog);
+ usage();
}
}
if (optind != argc - 1)
- usage(prog);
+ usage();
#if defined(_WIN32) || defined(_WIN64)
SetConsoleCtrlHandler(ConsoleBreakHandlerRoutine, true);
@@ -264,78 +325,97 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "mdbx_dump %s (%s, T-%s)\nRunning for %s...\n",
mdbx_version.git.describe, mdbx_version.git.datetime,
mdbx_version.git.tree, envname);
- fflush(NULL);
+ fflush(nullptr);
}
rc = mdbx_env_create(&env);
- if (rc) {
- fprintf(stderr, "mdbx_env_create failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_env_create", rc);
return EXIT_FAILURE;
}
if (alldbs || subname) {
- mdbx_env_set_maxdbs(env, 2);
+ rc = mdbx_env_set_maxdbs(env, 2);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_env_set_maxdbs", rc);
+ goto env_close;
+ }
}
rc = mdbx_env_open(
env, envname,
envflags | (rescue ? MDBX_RDONLY | MDBX_EXCLUSIVE : MDBX_RDONLY), 0);
- if (rc) {
- fprintf(stderr, "mdbx_env_open failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_env_open", rc);
goto env_close;
}
- rc = mdbx_txn_begin(env, NULL, MDBX_RDONLY, &txn);
- if (rc) {
- fprintf(stderr, "mdbx_txn_begin failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ rc = mdbx_txn_begin(env, nullptr, MDBX_RDONLY, &txn);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_txn_begin", rc);
goto env_close;
}
- rc = mdbx_dbi_open(txn, subname, 0, &dbi);
- if (rc) {
- fprintf(stderr, "mdbx_open failed, error %d %s\n", rc, mdbx_strerror(rc));
+ rc = mdbx_dbi_open(txn, subname, MDBX_ACCEDE, &dbi);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_dbi_open", rc);
goto txn_abort;
}
if (alldbs) {
- MDBX_cursor *cursor;
- MDBX_val key;
- int count = 0;
+ assert(dbi == MAIN_DBI);
- rc = mdbx_cursor_open(txn, dbi, &cursor);
- if (rc) {
- fprintf(stderr, "mdbx_cursor_open failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ MDBX_cursor *cursor;
+ rc = mdbx_cursor_open(txn, MAIN_DBI, &cursor);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_cursor_open", rc);
goto txn_abort;
}
- while ((rc = mdbx_cursor_get(cursor, &key, NULL, MDBX_NEXT_NODUP)) == 0) {
+ if (MDBX_DEBUG > 0 && rescue) {
+ cursor->mc_flags |= C_SKIPORD;
+ if (cursor->mc_xcursor)
+ cursor->mc_xcursor->mx_cursor.mc_flags |= C_SKIPORD;
+ }
+
+ bool have_raw = false;
+ int count = 0;
+ MDBX_val key;
+ while (MDBX_SUCCESS ==
+ (rc = mdbx_cursor_get(cursor, &key, nullptr, MDBX_NEXT_NODUP))) {
if (user_break) {
rc = MDBX_EINTR;
break;
}
- char *str;
- MDBX_dbi db2;
+
if (memchr(key.iov_base, '\0', key.iov_len))
continue;
- count++;
- str = mdbx_malloc(key.iov_len + 1);
- memcpy(str, key.iov_base, key.iov_len);
- str[key.iov_len] = '\0';
- rc = mdbx_dbi_open(txn, str, 0, &db2);
- if (rc == MDBX_SUCCESS) {
+ subname = mdbx_malloc(key.iov_len + 1);
+ memcpy(subname, key.iov_base, key.iov_len);
+ subname[key.iov_len] = '\0';
+
+ MDBX_dbi sub_dbi;
+ rc = mdbx_dbi_open_ex(txn, subname, MDBX_ACCEDE, &sub_dbi,
+ rescue ? equal_or_greater : nullptr,
+ rescue ? equal_or_greater : nullptr);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ if (rc == MDBX_INCOMPATIBLE) {
+ have_raw = true;
+ continue;
+ }
+ error("mdbx_dbi_open", rc);
+ if (!rescue)
+ break;
+ } else {
+ count++;
if (list) {
- printf("%s\n", str);
- list++;
+ printf("%s\n", subname);
} else {
- rc = dumpit(txn, db2, str);
- if (rc) {
+ rc = dump_sdb(txn, sub_dbi, subname);
+ if (unlikely(rc != MDBX_SUCCESS)) {
if (!rescue)
break;
fprintf(stderr, "%s: %s: ignore %s for `%s` and continue\n", prog,
- envname, mdbx_strerror(rc), str);
+ envname, mdbx_strerror(rc), subname);
/* Here is a hack for rescue mode, don't do that:
* - we should restart transaction in case error due
* database corruption;
@@ -345,39 +425,51 @@ int main(int argc, char *argv[]) {
* mode and transaction is the same, i.e. has the same address
* and so on. */
rc = mdbx_txn_reset(txn);
- if (rc) {
- fprintf(stderr, "mdbx_txn_reset failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_txn_reset", rc);
goto env_close;
}
rc = mdbx_txn_renew(txn);
- if (rc) {
- fprintf(stderr, "mdbx_txn_renew failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_txn_renew", rc);
goto env_close;
}
}
}
- mdbx_dbi_close(env, db2);
+ rc = mdbx_dbi_close(env, sub_dbi);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_dbi_close", rc);
+ break;
+ }
}
- mdbx_free(str);
- if (rc)
- continue;
+ mdbx_free(subname);
}
mdbx_cursor_close(cursor);
- if (!count) {
+ cursor = nullptr;
+
+ if (have_raw && (!count /* || rescue */))
+ rc = dump_sdb(txn, MAIN_DBI, nullptr);
+ else if (!count) {
fprintf(stderr, "%s: %s does not contain multiple databases\n", prog,
envname);
rc = MDBX_NOTFOUND;
- } else if (rc == MDBX_INCOMPATIBLE) {
- /* LY: the record it not a named sub-db. */
- rc = MDBX_SUCCESS;
}
} else {
- rc = dumpit(txn, dbi, subname);
+ rc = dump_sdb(txn, dbi, subname);
+ }
+
+ switch (rc) {
+ case MDBX_NOTFOUND:
+ rc = MDBX_SUCCESS;
+ case MDBX_SUCCESS:
+ break;
+ case MDBX_EINTR:
+ fprintf(stderr, "Interrupted by signal/user\n");
+ break;
+ default:
+ if (unlikely(rc != MDBX_SUCCESS))
+ error("mdbx_cursor_get", rc);
}
- if (rc && rc != MDBX_NOTFOUND)
- fprintf(stderr, "%s: %s: %s\n", prog, envname, mdbx_strerror(rc));
mdbx_dbi_close(env, dbi);
txn_abort:
diff --git a/libs/libmdbx/src/src/mdbx_load.c b/libs/libmdbx/src/src/mdbx_load.c
index eb39cc1174..463f020706 100644
--- a/libs/libmdbx/src/src/mdbx_load.c
+++ b/libs/libmdbx/src/src/mdbx_load.c
@@ -45,178 +45,305 @@ static void signal_handler(int sig) {
#endif /* !WINDOWS */
-#define PRINT 1
-#define NOHDR 2
-static int mode;
-
-static char *subname = NULL;
+static char *prog;
static size_t lineno;
-static int version;
+static void error(const char *func, int rc) {
+ if (lineno)
+ fprintf(stderr, "%s: at input line %" PRIiSIZE ": %s() error %d, %s\n",
+ prog, lineno, func, rc, mdbx_strerror(rc));
+ else
+ fprintf(stderr, "%s: %s() error %d %s\n", prog, func, rc,
+ mdbx_strerror(rc));
+}
-static int dbi_flags;
-static char *prog;
-static bool Eof;
+static char *valstr(char *line, const char *item) {
+ const size_t len = strlen(item);
+ if (strncmp(line, item, len) != 0)
+ return nullptr;
+ if (line[len] != '=') {
+ if (line[len] > ' ')
+ return nullptr;
+ fprintf(stderr, "%s: line %" PRIiSIZE ": unexpected line format for '%s'\n",
+ prog, lineno, item);
+ exit(EXIT_FAILURE);
+ }
+ char *ptr = strchr(line, '\n');
+ if (ptr)
+ *ptr = '\0';
+ return line + len + 1;
+}
+
+static bool valnum(char *line, const char *item, uint64_t *value) {
+ char *str = valstr(line, item);
+ if (!str)
+ return false;
+
+ char *end = nullptr;
+ *value = strtoull(str, &end, 0);
+ if (end && *end) {
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": unexpected number format for '%s'\n", prog,
+ lineno, item);
+ exit(EXIT_FAILURE);
+ }
+ return true;
+}
+static bool valbool(char *line, const char *item, bool *value) {
+ uint64_t u64;
+ if (!valnum(line, item, &u64))
+ return false;
+
+ if (u64 > 1) {
+ fprintf(stderr, "%s: line %" PRIiSIZE ": unexpected value for '%s'\n", prog,
+ lineno, item);
+ exit(EXIT_FAILURE);
+ }
+ *value = u64 != 0;
+ return true;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static char *subname = nullptr;
+static int dbi_flags;
+static txnid_t txnid;
+static uint64_t sequence;
+static mdbx_canary canary;
static MDBX_envinfo envinfo;
+
+#define PRINT 1
+#define NOHDR 2
+#define GLOBAL 4
+static int mode = GLOBAL;
+
static MDBX_val kbuf, dbuf;
static MDBX_val k0buf;
#define STRLENOF(s) (sizeof(s) - 1)
typedef struct flagbit {
- int bit;
+ unsigned bit;
char *name;
- int len;
+ unsigned len;
} flagbit;
#define S(s) s, STRLENOF(s)
-flagbit dbflags[] = {{MDBX_REVERSEKEY, S("reversekey")},
- {MDBX_DUPSORT, S("dupsort")},
- {MDBX_INTEGERKEY, S("integerkey")},
- {MDBX_DUPFIXED, S("dupfixed")},
- {MDBX_INTEGERDUP, S("integerdup")},
- {MDBX_REVERSEDUP, S("reversedup")},
- {0, NULL, 0}};
+flagbit dbflags[] = {
+ {MDBX_REVERSEKEY, S("reversekey")}, {MDBX_DUPSORT, S("duplicates")},
+ {MDBX_DUPSORT, S("dupsort")}, {MDBX_INTEGERKEY, S("integerkey")},
+ {MDBX_DUPFIXED, S("dupfixed")}, {MDBX_INTEGERDUP, S("integerdup")},
+ {MDBX_REVERSEDUP, S("reversedup")}, {0, nullptr, 0}};
+
+static int readhdr(void) {
+ /* reset parameters */
+ if (subname) {
+ free(subname);
+ subname = nullptr;
+ }
+ dbi_flags = 0;
+ txnid = 0;
+ sequence = 0;
-static void readhdr(void) {
- char *ptr;
+ while (true) {
+ errno = 0;
+ if (fgets(dbuf.iov_base, (int)dbuf.iov_len, stdin) == nullptr)
+ return errno ? errno : EOF;
+ if (user_break)
+ return MDBX_EINTR;
- dbi_flags = 0;
- while (fgets(dbuf.iov_base, (int)dbuf.iov_len, stdin) != NULL) {
lineno++;
+ uint64_t u64;
+
+ if (valnum(dbuf.iov_base, "VERSION", &u64)) {
+ if (u64 != 3) {
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": unsupported value %" PRIu64
+ " for %s\n",
+ prog, lineno, u64, "VERSION");
+ exit(EXIT_FAILURE);
+ }
+ continue;
+ }
- if (!strncmp(dbuf.iov_base, "db_pagesize=", STRLENOF("db_pagesize="))) {
- envinfo.mi_dxb_pagesize =
- atoi((char *)dbuf.iov_base + STRLENOF("db_pagesize="));
+ if (valnum(dbuf.iov_base, "db_pagesize", &u64)) {
+ if (!(mode & GLOBAL) && envinfo.mi_dxb_pagesize != u64)
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": ignore value %" PRIu64
+ " for '%s' in non-global context\n",
+ prog, lineno, u64, "db_pagesize");
+ else if (u64 < MDBX_MIN_PAGESIZE || u64 > MDBX_MAX_PAGESIZE)
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": ignore unsupported value %" PRIu64
+ " for %s\n",
+ prog, lineno, u64, "db_pagesize");
+ else
+ envinfo.mi_dxb_pagesize = (uint32_t)u64;
continue;
}
- if (!strncmp(dbuf.iov_base, "duplicates=", STRLENOF("duplicates="))) {
- dbi_flags |= MDBX_DUPSORT;
+ char *str = valstr(dbuf.iov_base, "format");
+ if (str) {
+ if (strcmp(str, "print") == 0) {
+ mode |= PRINT;
+ continue;
+ }
+ if (strcmp(str, "bytevalue") == 0) {
+ mode &= ~PRINT;
+ continue;
+ }
+ fprintf(stderr, "%s: line %" PRIiSIZE ": unsupported value '%s' for %s\n",
+ prog, lineno, str, "format");
+ exit(EXIT_FAILURE);
+ }
+
+ str = valstr(dbuf.iov_base, "database");
+ if (str) {
+ if (*str) {
+ subname = mdbx_strdup(str);
+ if (!subname) {
+ perror("strdup()");
+ exit(EXIT_FAILURE);
+ }
+ }
continue;
}
- if (!strncmp(dbuf.iov_base, "VERSION=", STRLENOF("VERSION="))) {
- version = atoi((char *)dbuf.iov_base + STRLENOF("VERSION="));
- if (version > 3) {
- fprintf(stderr, "%s: line %" PRIiSIZE ": unsupported VERSION %d\n",
- prog, lineno, version);
+ str = valstr(dbuf.iov_base, "type");
+ if (str) {
+ if (strcmp(str, "btree") != 0) {
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": unsupported value '%s' for %s\n", prog,
+ lineno, str, "type");
exit(EXIT_FAILURE);
}
continue;
}
- if (!strncmp(dbuf.iov_base, "HEADER=END", STRLENOF("HEADER=END")))
- return;
+ if (valnum(dbuf.iov_base, "mapaddr", &u64)) {
+ if (u64)
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": ignore unsupported value 0x%" PRIx64
+ " for %s\n",
+ prog, lineno, u64, "mapaddr");
+ continue;
+ }
- if (!strncmp(dbuf.iov_base, "format=", STRLENOF("format="))) {
- if (!strncmp((char *)dbuf.iov_base + STRLENOF("FORMAT="), "print",
- STRLENOF("print")))
- mode |= PRINT;
- else if (strncmp((char *)dbuf.iov_base + STRLENOF("FORMAT="), "bytevalue",
- STRLENOF("bytevalue"))) {
- fprintf(stderr, "%s: line %" PRIiSIZE ": unsupported FORMAT %s\n", prog,
- lineno, (char *)dbuf.iov_base + STRLENOF("FORMAT="));
- exit(EXIT_FAILURE);
- }
+ if (valnum(dbuf.iov_base, "mapsize", &u64)) {
+ if (!(mode & GLOBAL))
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": ignore value %" PRIu64
+ " for '%s' in non-global context\n",
+ prog, lineno, u64, "mapsize");
+ else if (u64 < MIN_MAPSIZE || u64 > MAX_MAPSIZE64)
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": ignore unsupported value 0x%" PRIx64
+ " for %s\n",
+ prog, lineno, u64, "mapsize");
+ else
+ envinfo.mi_mapsize = (size_t)u64;
continue;
}
- if (!strncmp(dbuf.iov_base, "database=", STRLENOF("database="))) {
- ptr = memchr(dbuf.iov_base, '\n', dbuf.iov_len);
- if (ptr)
- *ptr = '\0';
- if (subname)
- mdbx_free(subname);
- subname = mdbx_strdup((char *)dbuf.iov_base + STRLENOF("database="));
+ if (valnum(dbuf.iov_base, "maxreaders", &u64)) {
+ if (!(mode & GLOBAL))
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": ignore value %" PRIu64
+ " for '%s' in non-global context\n",
+ prog, lineno, u64, "maxreaders");
+ else if (u64 < 1 || u64 > MDBX_READERS_LIMIT)
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": ignore unsupported value 0x%" PRIx64
+ " for %s\n",
+ prog, lineno, u64, "maxreaders");
+ else
+ envinfo.mi_maxreaders = (int)u64;
continue;
}
- if (!strncmp(dbuf.iov_base, "type=", STRLENOF("type="))) {
- if (strncmp((char *)dbuf.iov_base + STRLENOF("type="), "btree",
- STRLENOF("btree"))) {
- fprintf(stderr, "%s: line %" PRIiSIZE ": unsupported type %s\n", prog,
- lineno, (char *)dbuf.iov_base + STRLENOF("type="));
- exit(EXIT_FAILURE);
- }
+ if (valnum(dbuf.iov_base, "txnid", &u64)) {
+ if (u64 < MIN_TXNID || u64 > MAX_TXNID)
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": ignore unsupported value 0x%" PRIx64
+ " for %s\n",
+ prog, lineno, u64, "txnid");
+ txnid = u64;
continue;
}
- if (!strncmp(dbuf.iov_base, "mapaddr=", STRLENOF("mapaddr="))) {
- int i;
- ptr = memchr(dbuf.iov_base, '\n', dbuf.iov_len);
- if (ptr)
- *ptr = '\0';
- void *unused;
- i = sscanf((char *)dbuf.iov_base + STRLENOF("mapaddr="), "%p", &unused);
- if (i != 1) {
- fprintf(stderr, "%s: line %" PRIiSIZE ": invalid mapaddr %s\n", prog,
- lineno, (char *)dbuf.iov_base + STRLENOF("mapaddr="));
- exit(EXIT_FAILURE);
- }
+ if (valnum(dbuf.iov_base, "sequence", &u64)) {
+ sequence = u64;
continue;
}
- if (!strncmp(dbuf.iov_base, "mapsize=", STRLENOF("mapsize="))) {
- int i;
- ptr = memchr(dbuf.iov_base, '\n', dbuf.iov_len);
- if (ptr)
- *ptr = '\0';
- i = sscanf((char *)dbuf.iov_base + STRLENOF("mapsize="), "%" PRIu64,
- &envinfo.mi_mapsize);
- if (i != 1) {
- fprintf(stderr, "%s: line %" PRIiSIZE ": invalid mapsize %s\n", prog,
- lineno, (char *)dbuf.iov_base + STRLENOF("mapsize="));
+ str = valstr(dbuf.iov_base, "geometry");
+ if (str) {
+ if (!(mode & GLOBAL))
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": ignore values %s"
+ " for '%s' in non-global context\n",
+ prog, lineno, str, "geometry");
+ else if (sscanf(str,
+ "l%" PRIu64 ",c%" PRIu64 ",u%" PRIu64 ",s%" PRIu64
+ ",g%" PRIu64,
+ &envinfo.mi_geo.lower, &envinfo.mi_geo.current,
+ &envinfo.mi_geo.upper, &envinfo.mi_geo.shrink,
+ &envinfo.mi_geo.grow) != 5) {
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": unexpected line format for '%s'\n",
+ prog, lineno, "geometry");
exit(EXIT_FAILURE);
}
continue;
}
- if (!strncmp(dbuf.iov_base, "maxreaders=", STRLENOF("maxreaders="))) {
- int i;
- ptr = memchr(dbuf.iov_base, '\n', dbuf.iov_len);
- if (ptr)
- *ptr = '\0';
- i = sscanf((char *)dbuf.iov_base + STRLENOF("maxreaders="), "%u",
- &envinfo.mi_maxreaders);
- if (i != 1) {
- fprintf(stderr, "%s: line %" PRIiSIZE ": invalid maxreaders %s\n", prog,
- lineno, (char *)dbuf.iov_base + STRLENOF("maxreaders="));
+ str = valstr(dbuf.iov_base, "canary");
+ if (str) {
+ if (!(mode & GLOBAL))
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": ignore values %s"
+ " for '%s' in non-global context\n",
+ prog, lineno, str, "canary");
+ else if (sscanf(str, "v%" PRIu64 ",x%" PRIu64 ",y%" PRIu64 ",z%" PRIu64,
+ &canary.v, &canary.x, &canary.y, &canary.z) != 4) {
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": unexpected line format for '%s'\n",
+ prog, lineno, "canary");
exit(EXIT_FAILURE);
}
continue;
}
- int i;
- for (i = 0; dbflags[i].bit; i++) {
- if (!strncmp(dbuf.iov_base, dbflags[i].name, dbflags[i].len) &&
- ((char *)dbuf.iov_base)[dbflags[i].len] == '=') {
- if (((char *)dbuf.iov_base)[dbflags[i].len + 1] == '1')
+ for (int i = 0; dbflags[i].bit; i++) {
+ bool value;
+ if (valbool(dbuf.iov_base, dbflags[i].name, &value)) {
+ if (value)
dbi_flags |= dbflags[i].bit;
- break;
+ else
+ dbi_flags &= ~dbflags[i].bit;
+ goto next;
}
}
- if (!dbflags[i].bit) {
- ptr = memchr(dbuf.iov_base, '=', dbuf.iov_len);
- if (!ptr) {
- fprintf(stderr, "%s: line %" PRIiSIZE ": unexpected format\n", prog,
- lineno);
- exit(EXIT_FAILURE);
- } else {
- *ptr = '\0';
- fprintf(stderr,
- "%s: line %" PRIiSIZE ": unrecognized keyword ignored: %s\n",
- prog, lineno, (char *)dbuf.iov_base);
- }
+
+ str = valstr(dbuf.iov_base, "HEADER");
+ if (str) {
+ if (strcmp(str, "END") == 0)
+ return MDBX_SUCCESS;
}
+
+ fprintf(stderr,
+ "%s: line %" PRIiSIZE ": unrecognized keyword ignored: %s\n", prog,
+ lineno, (char *)dbuf.iov_base);
+ next:;
}
- Eof = true;
+ return EOF;
}
-static void badend(void) {
+static int badend(void) {
fprintf(stderr, "%s: line %" PRIiSIZE ": unexpected end of input\n", prog,
lineno);
+ return errno ? errno : MDBX_ENODATA;
}
static int unhex(unsigned char *c2) {
@@ -237,29 +364,26 @@ static int readline(MDBX_val *out, MDBX_val *buf) {
size_t len, l2;
int c;
+ if (user_break)
+ return MDBX_EINTR;
+
+ errno = 0;
if (!(mode & NOHDR)) {
c = fgetc(stdin);
- if (c == EOF) {
- Eof = true;
- return EOF;
- }
+ if (c == EOF)
+ return errno ? errno : EOF;
if (c != ' ') {
lineno++;
- if (fgets(buf->iov_base, (int)buf->iov_len, stdin) == NULL) {
- badend:
- Eof = true;
- badend();
- return EOF;
+ errno = 0;
+ if (fgets(buf->iov_base, (int)buf->iov_len, stdin)) {
+ if (c == 'D' && !strncmp(buf->iov_base, "ATA=END", STRLENOF("ATA=END")))
+ return EOF;
}
- if (c == 'D' && !strncmp(buf->iov_base, "ATA=END", STRLENOF("ATA=END")))
- return EOF;
- goto badend;
+ return badend();
}
}
- if (fgets(buf->iov_base, (int)buf->iov_len, stdin) == NULL) {
- Eof = true;
- return EOF;
- }
+ if (fgets(buf->iov_base, (int)buf->iov_len, stdin) == nullptr)
+ return errno ? errno : EOF;
lineno++;
c1 = buf->iov_base;
@@ -270,18 +394,15 @@ static int readline(MDBX_val *out, MDBX_val *buf) {
while (c1[len - 1] != '\n') {
buf->iov_base = mdbx_realloc(buf->iov_base, buf->iov_len * 2);
if (!buf->iov_base) {
- Eof = true;
fprintf(stderr, "%s: line %" PRIiSIZE ": out of memory, line too long\n",
prog, lineno);
- return EOF;
+ return MDBX_ENOMEM;
}
c1 = buf->iov_base;
c1 += l2;
- if (fgets((char *)c1, (int)buf->iov_len + 1, stdin) == NULL) {
- Eof = true;
- badend();
- return EOF;
- }
+ errno = 0;
+ if (fgets((char *)c1, (int)buf->iov_len + 1, stdin) == nullptr)
+ return errno ? errno : EOF;
buf->iov_len *= 2;
len = strlen((char *)c1);
l2 += len;
@@ -297,11 +418,8 @@ static int readline(MDBX_val *out, MDBX_val *buf) {
if (c2[1] == '\\') {
*c1++ = '\\';
} else {
- if (c2 + 3 > end || !isxdigit(c2[1]) || !isxdigit(c2[2])) {
- Eof = true;
- badend();
- return EOF;
- }
+ if (c2 + 3 > end || !isxdigit(c2[1]) || !isxdigit(c2[2]))
+ return badend();
*c1++ = (char)unhex(++c2);
}
c2 += 2;
@@ -312,17 +430,11 @@ static int readline(MDBX_val *out, MDBX_val *buf) {
}
} else {
/* odd length not allowed */
- if (len & 1) {
- Eof = true;
- badend();
- return EOF;
- }
+ if (len & 1)
+ return badend();
while (c2 < end) {
- if (!isxdigit(*c2) || !isxdigit(c2[1])) {
- Eof = true;
- badend();
- return EOF;
- }
+ if (!isxdigit(*c2) || !isxdigit(c2[1]))
+ return badend();
*c1++ = (char)unhex(c2);
c2 += 2;
}
@@ -330,12 +442,12 @@ static int readline(MDBX_val *out, MDBX_val *buf) {
c2 = out->iov_base = buf->iov_base;
out->iov_len = c1 - c2;
- return 0;
+ return MDBX_SUCCESS;
}
static void usage(void) {
fprintf(stderr,
- "usage: %s [-V] [-q] [-a] [-f file] [-s name] [-N] [-T] [-r] [-n] "
+ "usage: %s [-V] [-q] [-a] [-f file] [-s name] [-N] [-T] [-r] "
"dbpath\n"
" -V\t\tprint version and exit\n"
" -q\t\tbe quiet\n"
@@ -345,29 +457,29 @@ static void usage(void) {
" -s name\tload into named subDB\n"
" -N\t\tuse NOOVERWRITE on puts\n"
" -T\t\tread plaintext\n"
- " -r\t\trescure mode (ignore errors to load corrupted DB dump)\n"
- " -n\t\tNOSUBDIR mode for open\n",
+ " -r\t\trescure mode (ignore errors to load corrupted DB dump)\n",
prog);
exit(EXIT_FAILURE);
}
-static int anyway_greater(const MDBX_val *a, const MDBX_val *b) {
- (void)a;
- (void)b;
- return 1;
+static int equal_or_greater(const MDBX_val *a, const MDBX_val *b) {
+ return (a->iov_len == b->iov_len &&
+ memcmp(a->iov_base, b->iov_base, a->iov_len) == 0)
+ ? 0
+ : 1;
}
int main(int argc, char *argv[]) {
int i, rc;
- MDBX_env *env = NULL;
- MDBX_txn *txn = NULL;
- MDBX_cursor *mc = NULL;
+ MDBX_env *env = nullptr;
+ MDBX_txn *txn = nullptr;
+ MDBX_cursor *mc = nullptr;
MDBX_dbi dbi;
- char *envname = NULL;
+ char *envname = nullptr;
int envflags = MDBX_UTTERLY_NOSYNC, putflags = 0;
- int append = 0;
- int quiet = 0;
- int rescue = 0;
+ bool append = false;
+ bool quiet = false;
+ bool rescue = false;
MDBX_val prevk;
prog = argv[0];
@@ -391,10 +503,10 @@ int main(int argc, char *argv[]) {
mdbx_build.options);
return EXIT_SUCCESS;
case 'a':
- append = 1;
+ append = true;
break;
case 'f':
- if (freopen(optarg, "r", stdin) == NULL) {
+ if (freopen(optarg, "r", stdin) == nullptr) {
fprintf(stderr, "%s: %s: open: %s\n", prog, optarg,
mdbx_strerror(errno));
exit(EXIT_FAILURE);
@@ -413,10 +525,10 @@ int main(int argc, char *argv[]) {
mode |= NOHDR | PRINT;
break;
case 'q':
- quiet = 1;
+ quiet = true;
break;
case 'r':
- rescue = 1;
+ rescue = true;
break;
default:
usage();
@@ -444,56 +556,73 @@ int main(int argc, char *argv[]) {
printf("mdbx_load %s (%s, T-%s)\nRunning for %s...\n",
mdbx_version.git.describe, mdbx_version.git.datetime,
mdbx_version.git.tree, envname);
- fflush(NULL);
+ fflush(nullptr);
dbuf.iov_len = 4096;
dbuf.iov_base = mdbx_malloc(dbuf.iov_len);
/* read first header for mapsize= */
- if (!(mode & NOHDR))
- readhdr();
+ if (!(mode & NOHDR)) {
+ rc = readhdr();
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ if (rc == EOF)
+ rc = MDBX_ENODATA;
+ error("readheader", rc);
+ goto env_close;
+ }
+ }
rc = mdbx_env_create(&env);
- if (rc) {
- fprintf(stderr, "mdbx_env_create failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_env_create", rc);
return EXIT_FAILURE;
}
mdbx_env_set_maxdbs(env, 2);
-
-#ifdef MDBX_FIXEDMAP
- if (info.mi_mapaddr)
- envflags |= MDBX_FIXEDMAP;
-#endif
+ if (envinfo.mi_maxreaders) {
+ rc = mdbx_env_set_maxreaders(env, envinfo.mi_maxreaders);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_env_set_maxreaders", rc);
+ goto env_close;
+ }
+ }
if (envinfo.mi_mapsize) {
- if (envinfo.mi_mapsize > INTPTR_MAX) {
- fprintf(stderr,
- "Database size is too large for current system (mapsize=%" PRIu64
- " is great than system-limit %zi)\n",
- envinfo.mi_mapsize, INTPTR_MAX);
- goto env_close;
+ if (envinfo.mi_geo.current) {
+ rc = mdbx_env_set_geometry(
+ env, (intptr_t)envinfo.mi_geo.lower, (intptr_t)envinfo.mi_geo.current,
+ (intptr_t)envinfo.mi_geo.upper, (intptr_t)envinfo.mi_geo.shrink,
+ (intptr_t)envinfo.mi_geo.grow,
+ envinfo.mi_dxb_pagesize ? (intptr_t)envinfo.mi_dxb_pagesize : -1);
+ } else {
+ if (envinfo.mi_mapsize > MAX_MAPSIZE) {
+ fprintf(
+ stderr,
+ "Database size is too large for current system (mapsize=%" PRIu64
+ " is great than system-limit %zu)\n",
+ envinfo.mi_mapsize, (size_t)MAX_MAPSIZE);
+ goto env_close;
+ }
+ rc = mdbx_env_set_geometry(
+ env, (intptr_t)envinfo.mi_mapsize, (intptr_t)envinfo.mi_mapsize,
+ (intptr_t)envinfo.mi_mapsize, 0, 0,
+ envinfo.mi_dxb_pagesize ? (intptr_t)envinfo.mi_dxb_pagesize : -1);
}
- rc = mdbx_env_set_geometry(env, 0, 0, (intptr_t)envinfo.mi_mapsize, -1, -1,
- -1);
- if (rc) {
- fprintf(stderr, "mdbx_env_set_geometry failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_env_set_geometry", rc);
goto env_close;
}
}
rc = mdbx_env_open(env, envname, envflags, 0664);
- if (rc) {
- fprintf(stderr, "mdbx_env_open failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_env_open", rc);
goto env_close;
}
kbuf.iov_len = mdbx_env_get_maxvalsize_ex(env, MDBX_DUPSORT);
if (kbuf.iov_len >= INTPTR_MAX / 4) {
- fprintf(stderr, "mdbx_env_get_maxkeysize failed, returns %zu\n",
+ fprintf(stderr, "mdbx_env_get_maxkeysize() failed, returns %zu\n",
kbuf.iov_len);
goto env_close;
}
@@ -503,41 +632,83 @@ int main(int argc, char *argv[]) {
k0buf.iov_base = (char *)kbuf.iov_base + kbuf.iov_len;
prevk.iov_base = k0buf.iov_base;
- while (!Eof) {
+ while (rc == MDBX_SUCCESS) {
if (user_break) {
rc = MDBX_EINTR;
break;
}
- rc = mdbx_txn_begin(env, NULL, 0, &txn);
- if (rc) {
- fprintf(stderr, "mdbx_txn_begin failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ rc = mdbx_txn_begin(env, nullptr, 0, &txn);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_txn_begin", rc);
goto env_close;
}
+ if (mode & GLOBAL) {
+ mode -= GLOBAL;
+ if (canary.v | canary.x | canary.y | canary.z) {
+ rc = mdbx_canary_put(txn, &canary);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_canary_put", rc);
+ goto txn_abort;
+ }
+ }
+ }
+
+ const char *const dbi_name = subname ? subname : "@MAIN";
rc = mdbx_dbi_open_ex(txn, subname, dbi_flags | MDBX_CREATE, &dbi,
- append ? anyway_greater : NULL,
- append ? anyway_greater : NULL);
- if (rc) {
- fprintf(stderr, "mdbx_open failed, error %d %s\n", rc, mdbx_strerror(rc));
+ append ? equal_or_greater : nullptr,
+ append ? equal_or_greater : nullptr);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_dbi_open_ex", rc);
goto txn_abort;
}
+ uint64_t present_sequence;
+ rc = mdbx_dbi_sequence(txn, dbi, &present_sequence, 0);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_dbi_sequence", rc);
+ goto txn_abort;
+ }
+ if (present_sequence > sequence) {
+ fprintf(stderr,
+ "present sequence for '%s' value (%" PRIu64
+ ") is greated than loaded (%" PRIu64 ")\n",
+ dbi_name, present_sequence, sequence);
+ rc = MDBX_RESULT_TRUE;
+ goto txn_abort;
+ }
+ if (present_sequence < sequence) {
+ rc = mdbx_dbi_sequence(txn, dbi, nullptr, sequence - present_sequence);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_dbi_sequence", rc);
+ goto txn_abort;
+ }
+ }
+
rc = mdbx_cursor_open(txn, dbi, &mc);
- if (rc) {
- fprintf(stderr, "mdbx_cursor_open failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_cursor_open", rc);
goto txn_abort;
}
+ /* if (append) {
+ mc->mc_flags |= C_SKIPORD;
+ if (mc->mc_xcursor)
+ mc->mc_xcursor->mx_cursor.mc_flags |= C_SKIPORD;
+ } */
int batch = 0;
prevk.iov_len = 0;
- while (1) {
+ while (rc == MDBX_SUCCESS) {
MDBX_val key;
rc = readline(&key, &kbuf);
- if (rc) /* rc == EOF */
+ if (rc != MDBX_SUCCESS) /* rc == EOF */
+ break;
+
+ if (user_break) {
+ rc = MDBX_EINTR;
break;
+ }
MDBX_val data;
rc = readline(&data, &dbuf);
@@ -566,50 +737,81 @@ int main(int argc, char *argv[]) {
mdbx_strerror(rc));
continue;
}
- if (rc) {
- fprintf(stderr, "mdbx_cursor_put failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_cursor_put", rc);
goto txn_abort;
}
batch++;
- if (batch == 100) {
+
+ MDBX_txn_info txn_info;
+ rc = mdbx_txn_info(txn, &txn_info, false);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_txn_info", rc);
+ goto txn_abort;
+ }
+
+ if (batch == 10000 || txn_info.txn_space_dirty > MEGABYTE * 16) {
+ mdbx_cursor_close(mc);
+ mc = nullptr;
rc = mdbx_txn_commit(txn);
- if (rc) {
- fprintf(stderr, "%s: line %" PRIiSIZE ": txn_commit: %s\n", prog,
- lineno, mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_txn_commit", rc);
goto env_close;
}
- rc = mdbx_txn_begin(env, NULL, 0, &txn);
- if (rc) {
- fprintf(stderr, "mdbx_txn_begin failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ batch = 0;
+
+ rc = mdbx_txn_begin(env, nullptr, 0, &txn);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_txn_begin", rc);
goto env_close;
}
rc = mdbx_cursor_open(txn, dbi, &mc);
- if (rc) {
- fprintf(stderr, "mdbx_cursor_open failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_cursor_open", rc);
goto txn_abort;
}
- batch = 0;
+ /* if (append) {
+ mc->mc_flags |= C_SKIPORD;
+ if (mc->mc_xcursor)
+ mc->mc_xcursor->mx_cursor.mc_flags |= C_SKIPORD;
+ } */
}
}
+
+ mdbx_cursor_close(mc);
+ mc = nullptr;
rc = mdbx_txn_commit(txn);
- txn = NULL;
- if (rc) {
- fprintf(stderr, "%s: line %" PRIiSIZE ": txn_commit: %s\n", prog, lineno,
- mdbx_strerror(rc));
+ txn = nullptr;
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_txn_commit", rc);
+ goto env_close;
+ }
+ rc = mdbx_dbi_close(env, dbi);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_dbi_close", rc);
goto env_close;
}
- mdbx_dbi_close(env, dbi);
- subname = NULL;
/* try read next header */
if (!(mode & NOHDR))
- readhdr();
+ rc = readhdr();
+ }
+
+ switch (rc) {
+ case EOF:
+ rc = MDBX_SUCCESS;
+ case MDBX_SUCCESS:
+ break;
+ case MDBX_EINTR:
+ fprintf(stderr, "Interrupted by signal/user\n");
+ break;
+ default:
+ if (unlikely(rc != MDBX_SUCCESS))
+ error("readline", rc);
}
txn_abort:
+ mdbx_cursor_close(mc);
mdbx_txn_abort(txn);
env_close:
mdbx_env_close(env);
diff --git a/libs/libmdbx/src/src/mdbx_stat.c b/libs/libmdbx/src/src/mdbx_stat.c
index d1a805f925..df12717aad 100644
--- a/libs/libmdbx/src/src/mdbx_stat.c
+++ b/libs/libmdbx/src/src/mdbx_stat.c
@@ -43,7 +43,7 @@ static void signal_handler(int sig) {
#endif /* !WINDOWS */
-static void prstat(MDBX_stat *ms) {
+static void print_stat(MDBX_stat *ms) {
printf(" Pagesize: %u\n", ms->ms_psize);
printf(" Tree depth: %u\n", ms->ms_depth);
printf(" Branch pages: %" PRIu64 "\n", ms->ms_branch_pages);
@@ -52,17 +52,16 @@ static void prstat(MDBX_stat *ms) {
printf(" Entries: %" PRIu64 "\n", ms->ms_entries);
}
-static void usage(char *prog) {
+static void usage(const char *prog) {
fprintf(stderr,
- "usage: %s [-V] [-e] [-f[f[f]]] [-r[r]] [-a|-s name] [-n] dbpath\n"
+ "usage: %s [-V] [-e] [-f[f[f]]] [-r[r]] [-a|-s name] dbpath\n"
" -V\t\tprint version and exit\n"
" -e\t\tshow whole DB info\n"
" -f\t\tshow GC info\n"
" -r\t\tshow readers\n"
" -a\t\tprint stat of main DB and all subDBs\n"
" \t\t(default) print stat of only the main DB\n"
- " -s name\tprint stat of only the named subDB\n"
- " -n\t\tNOSUBDIR mode for open\n",
+ " -s name\tprint stat of only the named subDB\n",
prog);
exit(EXIT_FAILURE);
}
@@ -88,6 +87,11 @@ static int reader_list_func(void *ctx, int num, int slot, mdbx_pid_t pid,
return user_break ? MDBX_RESULT_TRUE : MDBX_RESULT_FALSE;
}
+const char *prog;
+static void error(const char *func, int rc) {
+ fprintf(stderr, "%s: %s() error %d %s\n", prog, func, rc, mdbx_strerror(rc));
+}
+
int main(int argc, char *argv[]) {
int o, rc;
MDBX_env *env;
@@ -95,9 +99,9 @@ int main(int argc, char *argv[]) {
MDBX_dbi dbi;
MDBX_stat mst;
MDBX_envinfo mei;
- char *prog = argv[0];
+ prog = argv[0];
char *envname;
- char *subname = NULL;
+ char *subname = nullptr;
int alldbs = 0, envinfo = 0, envflags = 0, freinfo = 0, rdrinfo = 0;
if (argc < 2)
@@ -167,41 +171,51 @@ int main(int argc, char *argv[]) {
printf("mdbx_stat %s (%s, T-%s)\nRunning for %s...\n",
mdbx_version.git.describe, mdbx_version.git.datetime,
mdbx_version.git.tree, envname);
- fflush(NULL);
+ fflush(nullptr);
rc = mdbx_env_create(&env);
- if (rc) {
- fprintf(stderr, "mdbx_env_create failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_env_create", rc);
return EXIT_FAILURE;
}
- if (alldbs || subname)
- mdbx_env_set_maxdbs(env, 4);
+ if (alldbs || subname) {
+ rc = mdbx_env_set_maxdbs(env, 2);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_env_set_maxdbs", rc);
+ goto env_close;
+ }
+ }
rc = mdbx_env_open(env, envname, envflags | MDBX_RDONLY, 0664);
- if (rc) {
- fprintf(stderr, "mdbx_env_open failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_env_open", rc);
goto env_close;
}
- rc = mdbx_txn_begin(env, NULL, MDBX_RDONLY, &txn);
- if (rc) {
- fprintf(stderr, "mdbx_txn_begin failed, error %d %s\n", rc,
- mdbx_strerror(rc));
- goto env_close;
+ rc = mdbx_txn_begin(env, nullptr, MDBX_RDONLY, &txn);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_txn_begin", rc);
+ goto txn_abort;
}
if (envinfo || freinfo) {
- (void)mdbx_env_info_ex(env, txn, &mei, sizeof(mei));
+ rc = mdbx_env_info_ex(env, txn, &mei, sizeof(mei));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_env_info_ex", rc);
+ goto txn_abort;
+ }
} else {
/* LY: zap warnings from gcc */
memset(&mei, 0, sizeof(mei));
}
if (envinfo) {
- (void)mdbx_env_stat_ex(env, txn, &mst, sizeof(mst));
+ rc = mdbx_env_stat_ex(env, txn, &mst, sizeof(mst));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_env_stat_ex", rc);
+ goto txn_abort;
+ }
printf("Environment Info\n");
printf(" Pagesize: %u\n", mst.ms_psize);
if (mei.mi_geo.lower != mei.mi_geo.upper) {
@@ -241,11 +255,19 @@ int main(int argc, char *argv[]) {
if (rdrinfo) {
rc = mdbx_reader_list(env, reader_list_func, nullptr);
+ if (MDBX_IS_ERROR(rc)) {
+ error("mdbx_reader_list", rc);
+ goto txn_abort;
+ }
if (rc == MDBX_RESULT_TRUE)
printf("Reader Table is empty\n");
else if (rc == MDBX_SUCCESS && rdrinfo > 1) {
int dead;
rc = mdbx_reader_check(env, &dead);
+ if (MDBX_IS_ERROR(rc)) {
+ error("mdbx_reader_check", rc);
+ goto txn_abort;
+ }
if (rc == MDBX_RESULT_TRUE) {
printf(" %d stale readers cleared.\n", dead);
rc = mdbx_reader_list(env, reader_list_func, nullptr);
@@ -254,38 +276,31 @@ int main(int argc, char *argv[]) {
} else
printf(" No stale readers.\n");
}
- if (MDBX_IS_ERROR(rc)) {
- fprintf(stderr, "mdbx_txn_begin failed, error %d %s\n", rc,
- mdbx_strerror(rc));
- goto env_close;
- }
if (!(subname || alldbs || freinfo))
- goto env_close;
+ goto txn_abort;
}
if (freinfo) {
- MDBX_cursor *cursor;
- MDBX_val key, data;
- pgno_t pages = 0, *iptr;
- pgno_t reclaimable = 0;
-
printf("Garbage Collection\n");
dbi = 0;
+ MDBX_cursor *cursor;
rc = mdbx_cursor_open(txn, dbi, &cursor);
- if (rc) {
- fprintf(stderr, "mdbx_cursor_open failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_cursor_open", rc);
goto txn_abort;
}
rc = mdbx_dbi_stat(txn, dbi, &mst, sizeof(mst));
- if (rc) {
- fprintf(stderr, "mdbx_dbi_stat failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_dbi_stat", rc);
goto txn_abort;
}
- prstat(&mst);
- while ((rc = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) ==
- MDBX_SUCCESS) {
+ print_stat(&mst);
+
+ pgno_t pages = 0, *iptr;
+ pgno_t reclaimable = 0;
+ MDBX_val key, data;
+ while (MDBX_SUCCESS ==
+ (rc = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT))) {
if (user_break) {
rc = MDBX_EINTR;
break;
@@ -333,6 +348,7 @@ int main(int argc, char *argv[]) {
}
}
mdbx_cursor_close(cursor);
+ cursor = nullptr;
switch (rc) {
case MDBX_SUCCESS:
@@ -342,8 +358,7 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "Interrupted by signal/user\n");
goto txn_abort;
default:
- fprintf(stderr, "mdbx_cursor_get failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ error("mdbx_cursor_get", rc);
goto txn_abort;
}
@@ -381,59 +396,75 @@ int main(int argc, char *argv[]) {
printf(" GC: %" PRIaPGNO " pages\n", pages);
}
- rc = mdbx_dbi_open(txn, subname, 0, &dbi);
- if (rc) {
- fprintf(stderr, "mdbx_open failed, error %d %s\n", rc, mdbx_strerror(rc));
+ rc = mdbx_dbi_open(txn, subname, MDBX_ACCEDE, &dbi);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_dbi_open", rc);
goto txn_abort;
}
-
rc = mdbx_dbi_stat(txn, dbi, &mst, sizeof(mst));
- if (rc) {
- fprintf(stderr, "mdbx_dbi_stat failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_dbi_stat", rc);
goto txn_abort;
}
printf("Status of %s\n", subname ? subname : "Main DB");
- prstat(&mst);
+ print_stat(&mst);
if (alldbs) {
MDBX_cursor *cursor;
- MDBX_val key;
-
rc = mdbx_cursor_open(txn, dbi, &cursor);
- if (rc) {
- fprintf(stderr, "mdbx_cursor_open failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_cursor_open", rc);
goto txn_abort;
}
- while ((rc = mdbx_cursor_get(cursor, &key, NULL, MDBX_NEXT_NODUP)) == 0) {
- char *str;
- MDBX_dbi db2;
+
+ MDBX_val key;
+ while (MDBX_SUCCESS ==
+ (rc = mdbx_cursor_get(cursor, &key, nullptr, MDBX_NEXT_NODUP))) {
+ MDBX_dbi subdbi;
if (memchr(key.iov_base, '\0', key.iov_len))
continue;
- str = mdbx_malloc(key.iov_len + 1);
- memcpy(str, key.iov_base, key.iov_len);
- str[key.iov_len] = '\0';
- rc = mdbx_dbi_open(txn, str, 0, &db2);
+ subname = mdbx_malloc(key.iov_len + 1);
+ memcpy(subname, key.iov_base, key.iov_len);
+ subname[key.iov_len] = '\0';
+ rc = mdbx_dbi_open(txn, subname, MDBX_ACCEDE, &subdbi);
if (rc == MDBX_SUCCESS)
- printf("Status of %s\n", str);
- mdbx_free(str);
- if (rc)
- continue;
- rc = mdbx_dbi_stat(txn, db2, &mst, sizeof(mst));
- if (rc) {
- fprintf(stderr, "mdbx_dbi_stat failed, error %d %s\n", rc,
- mdbx_strerror(rc));
+ printf("Status of %s\n", subname);
+ mdbx_free(subname);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ if (rc == MDBX_INCOMPATIBLE)
+ continue;
+ error("mdbx_dbi_open", rc);
+ goto txn_abort;
+ }
+
+ rc = mdbx_dbi_stat(txn, subdbi, &mst, sizeof(mst));
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_dbi_stat", rc);
+ goto txn_abort;
+ }
+ print_stat(&mst);
+
+ rc = mdbx_dbi_close(env, subdbi);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ error("mdbx_dbi_close", rc);
goto txn_abort;
}
- prstat(&mst);
- mdbx_dbi_close(env, db2);
}
mdbx_cursor_close(cursor);
+ cursor = nullptr;
}
- if (rc == MDBX_NOTFOUND)
- rc = MDBX_SUCCESS;
+ switch (rc) {
+ case MDBX_SUCCESS:
+ case MDBX_NOTFOUND:
+ break;
+ case MDBX_EINTR:
+ fprintf(stderr, "Interrupted by signal/user\n");
+ break;
+ default:
+ if (unlikely(rc != MDBX_SUCCESS))
+ error("mdbx_cursor_get", rc);
+ }
mdbx_dbi_close(env, dbi);
txn_abort:
diff --git a/libs/libmdbx/src/src/options.h b/libs/libmdbx/src/src/options.h
index 715f883bfc..f3805b1eeb 100644
--- a/libs/libmdbx/src/src/options.h
+++ b/libs/libmdbx/src/src/options.h
@@ -13,6 +13,11 @@
*
*/
+/* Support for huge write-transactions */
+#ifndef MDBX_HUGE_TRANSACTIONS
+#define MDBX_HUGE_TRANSACTIONS 0
+#endif /* MDBX_HUGE_TRANSACTIONS */
+
/* using fcntl(F_FULLFSYNC) with 5-10 times slowdown */
#define MDBX_OSX_WANNA_DURABILITY 0
/* using fsync() with chance of data lost on power failure */
diff --git a/libs/libmdbx/src/src/osal.c b/libs/libmdbx/src/src/osal.c
index 94902527b1..e9fff57399 100644
--- a/libs/libmdbx/src/src/osal.c
+++ b/libs/libmdbx/src/src/osal.c
@@ -362,106 +362,98 @@ char *mdbx_strdup(const char *str) {
/*----------------------------------------------------------------------------*/
-MDBX_INTERNAL_FUNC int mdbx_condmutex_init(mdbx_condmutex_t *condmutex) {
+MDBX_INTERNAL_FUNC int mdbx_condpair_init(mdbx_condpair_t *condpair) {
+ int rc;
+ memset(condpair, 0, sizeof(mdbx_condpair_t));
#if defined(_WIN32) || defined(_WIN64)
- int rc = MDBX_SUCCESS;
- condmutex->event = NULL;
- condmutex->mutex = CreateMutexW(NULL, FALSE, NULL);
- if (!condmutex->mutex)
- return GetLastError();
-
- condmutex->event = CreateEventW(NULL, TRUE, FALSE, NULL);
- if (!condmutex->event) {
+ if ((condpair->mutex = CreateMutexW(NULL, FALSE, NULL)) == NULL) {
rc = GetLastError();
- (void)CloseHandle(condmutex->mutex);
- condmutex->mutex = NULL;
+ goto bailout_mutex;
}
- return rc;
-#else
- memset(condmutex, 0, sizeof(mdbx_condmutex_t));
- int rc = pthread_mutex_init(&condmutex->mutex, NULL);
- if (rc == 0) {
- rc = pthread_cond_init(&condmutex->cond, NULL);
- if (rc != 0)
- (void)pthread_mutex_destroy(&condmutex->mutex);
+ if ((condpair->event[0] = CreateEventW(NULL, FALSE, FALSE, NULL)) == NULL) {
+ rc = GetLastError();
+ goto bailout_event;
}
- return rc;
-#endif
-}
+ if ((condpair->event[1] = CreateEventW(NULL, FALSE, FALSE, NULL)) != NULL)
+ return MDBX_SUCCESS;
-static bool is_allzeros(const void *ptr, size_t bytes) {
- const uint8_t *u8 = ptr;
- for (size_t i = 0; i < bytes; ++i)
- if (u8[i] != 0)
- return false;
- return true;
+ rc = GetLastError();
+ (void)CloseHandle(condpair->event[0]);
+bailout_event:
+ (void)CloseHandle(condpair->mutex);
+#else
+ rc = pthread_mutex_init(&condpair->mutex, NULL);
+ if (unlikely(rc != 0))
+ goto bailout_mutex;
+ rc = pthread_cond_init(&condpair->cond[0], NULL);
+ if (unlikely(rc != 0))
+ goto bailout_cond;
+ rc = pthread_cond_init(&condpair->cond[1], NULL);
+ if (likely(rc == 0))
+ return MDBX_SUCCESS;
+
+ (void)pthread_cond_destroy(&condpair->cond[0]);
+bailout_cond:
+ (void)pthread_mutex_destroy(&condpair->mutex);
+#endif
+bailout_mutex:
+ memset(condpair, 0, sizeof(mdbx_condpair_t));
+ return rc;
}
-MDBX_INTERNAL_FUNC int mdbx_condmutex_destroy(mdbx_condmutex_t *condmutex) {
- int rc = MDBX_EINVAL;
+MDBX_INTERNAL_FUNC int mdbx_condpair_destroy(mdbx_condpair_t *condpair) {
#if defined(_WIN32) || defined(_WIN64)
- if (condmutex->event) {
- rc = CloseHandle(condmutex->event) ? MDBX_SUCCESS : GetLastError();
- if (rc == MDBX_SUCCESS)
- condmutex->event = NULL;
- }
- if (condmutex->mutex) {
- rc = CloseHandle(condmutex->mutex) ? MDBX_SUCCESS : GetLastError();
- if (rc == MDBX_SUCCESS)
- condmutex->mutex = NULL;
- }
+ int rc = CloseHandle(condpair->mutex) ? MDBX_SUCCESS : GetLastError();
+ rc = CloseHandle(condpair->event[0]) ? rc : GetLastError();
+ rc = CloseHandle(condpair->event[1]) ? rc : GetLastError();
#else
- if (!is_allzeros(&condmutex->cond, sizeof(condmutex->cond))) {
- rc = pthread_cond_destroy(&condmutex->cond);
- if (rc == 0)
- memset(&condmutex->cond, 0, sizeof(condmutex->cond));
- }
- if (!is_allzeros(&condmutex->mutex, sizeof(condmutex->mutex))) {
- rc = pthread_mutex_destroy(&condmutex->mutex);
- if (rc == 0)
- memset(&condmutex->mutex, 0, sizeof(condmutex->mutex));
- }
+ int err, rc = pthread_mutex_destroy(&condpair->mutex);
+ rc = (err = pthread_cond_destroy(&condpair->cond[0])) ? err : rc;
+ rc = (err = pthread_cond_destroy(&condpair->cond[1])) ? err : rc;
#endif
+ memset(condpair, 0, sizeof(mdbx_condpair_t));
return rc;
}
-MDBX_INTERNAL_FUNC int mdbx_condmutex_lock(mdbx_condmutex_t *condmutex) {
+MDBX_INTERNAL_FUNC int mdbx_condpair_lock(mdbx_condpair_t *condpair) {
#if defined(_WIN32) || defined(_WIN64)
- DWORD code = WaitForSingleObject(condmutex->mutex, INFINITE);
+ DWORD code = WaitForSingleObject(condpair->mutex, INFINITE);
return waitstatus2errcode(code);
#else
- return pthread_mutex_lock(&condmutex->mutex);
+ return pthread_mutex_lock(&condpair->mutex);
#endif
}
-MDBX_INTERNAL_FUNC int mdbx_condmutex_unlock(mdbx_condmutex_t *condmutex) {
+MDBX_INTERNAL_FUNC int mdbx_condpair_unlock(mdbx_condpair_t *condpair) {
#if defined(_WIN32) || defined(_WIN64)
- return ReleaseMutex(condmutex->mutex) ? MDBX_SUCCESS : GetLastError();
+ return ReleaseMutex(condpair->mutex) ? MDBX_SUCCESS : GetLastError();
#else
- return pthread_mutex_unlock(&condmutex->mutex);
+ return pthread_mutex_unlock(&condpair->mutex);
#endif
}
-MDBX_INTERNAL_FUNC int mdbx_condmutex_signal(mdbx_condmutex_t *condmutex) {
+MDBX_INTERNAL_FUNC int mdbx_condpair_signal(mdbx_condpair_t *condpair,
+ bool part) {
#if defined(_WIN32) || defined(_WIN64)
- return SetEvent(condmutex->event) ? MDBX_SUCCESS : GetLastError();
+ return SetEvent(condpair->event[part]) ? MDBX_SUCCESS : GetLastError();
#else
- return pthread_cond_signal(&condmutex->cond);
+ return pthread_cond_signal(&condpair->cond[part]);
#endif
}
-MDBX_INTERNAL_FUNC int mdbx_condmutex_wait(mdbx_condmutex_t *condmutex) {
+MDBX_INTERNAL_FUNC int mdbx_condpair_wait(mdbx_condpair_t *condpair,
+ bool part) {
#if defined(_WIN32) || defined(_WIN64)
- DWORD code =
- SignalObjectAndWait(condmutex->mutex, condmutex->event, INFINITE, FALSE);
+ DWORD code = SignalObjectAndWait(condpair->mutex, condpair->event[part],
+ INFINITE, FALSE);
if (code == WAIT_OBJECT_0) {
- code = WaitForSingleObject(condmutex->mutex, INFINITE);
+ code = WaitForSingleObject(condpair->mutex, INFINITE);
if (code == WAIT_OBJECT_0)
- return ResetEvent(condmutex->event) ? MDBX_SUCCESS : GetLastError();
+ return MDBX_SUCCESS;
}
return waitstatus2errcode(code);
#else
- return pthread_cond_wait(&condmutex->cond, &condmutex->mutex);
+ return pthread_cond_wait(&condpair->cond[part], &condpair->mutex);
#endif
}
diff --git a/libs/libmdbx/src/src/osal.h b/libs/libmdbx/src/src/osal.h
index d9012ce768..a6188bb382 100644
--- a/libs/libmdbx/src/src/osal.h
+++ b/libs/libmdbx/src/src/osal.h
@@ -35,7 +35,7 @@
#define _CRT_SECURE_NO_WARNINGS
#endif
#if !defined(_NO_CRT_STDIO_INLINE) && MDBX_BUILD_SHARED_LIBRARY && \
- !defined(MDBX_TOOLS)
+ !defined(MDBX_TOOLS) && MDBX_AVOID_CRT
#define _NO_CRT_STDIO_INLINE
#endif
#elif !defined(_POSIX_C_SOURCE)
@@ -164,8 +164,8 @@ typedef unsigned mdbx_thread_key_t;
#define THREAD_RESULT DWORD
typedef struct {
HANDLE mutex;
- HANDLE event;
-} mdbx_condmutex_t;
+ HANDLE event[2];
+} mdbx_condpair_t;
typedef CRITICAL_SECTION mdbx_fastmutex_t;
#if MDBX_AVOID_CRT
@@ -226,8 +226,8 @@ typedef pthread_key_t mdbx_thread_key_t;
#define THREAD_RESULT void *
typedef struct {
pthread_mutex_t mutex;
- pthread_cond_t cond;
-} mdbx_condmutex_t;
+ pthread_cond_t cond[2];
+} mdbx_condpair_t;
typedef pthread_mutex_t mdbx_fastmutex_t;
#define mdbx_malloc malloc
#define mdbx_calloc calloc
@@ -546,12 +546,13 @@ MDBX_INTERNAL_FUNC int mdbx_memalign_alloc(size_t alignment, size_t bytes,
MDBX_INTERNAL_FUNC void mdbx_memalign_free(void *ptr);
#endif
-MDBX_INTERNAL_FUNC int mdbx_condmutex_init(mdbx_condmutex_t *condmutex);
-MDBX_INTERNAL_FUNC int mdbx_condmutex_lock(mdbx_condmutex_t *condmutex);
-MDBX_INTERNAL_FUNC int mdbx_condmutex_unlock(mdbx_condmutex_t *condmutex);
-MDBX_INTERNAL_FUNC int mdbx_condmutex_signal(mdbx_condmutex_t *condmutex);
-MDBX_INTERNAL_FUNC int mdbx_condmutex_wait(mdbx_condmutex_t *condmutex);
-MDBX_INTERNAL_FUNC int mdbx_condmutex_destroy(mdbx_condmutex_t *condmutex);
+MDBX_INTERNAL_FUNC int mdbx_condpair_init(mdbx_condpair_t *condpair);
+MDBX_INTERNAL_FUNC int mdbx_condpair_lock(mdbx_condpair_t *condpair);
+MDBX_INTERNAL_FUNC int mdbx_condpair_unlock(mdbx_condpair_t *condpair);
+MDBX_INTERNAL_FUNC int mdbx_condpair_signal(mdbx_condpair_t *condpair,
+ bool part);
+MDBX_INTERNAL_FUNC int mdbx_condpair_wait(mdbx_condpair_t *condpair, bool part);
+MDBX_INTERNAL_FUNC int mdbx_condpair_destroy(mdbx_condpair_t *condpair);
MDBX_INTERNAL_FUNC int mdbx_fastmutex_init(mdbx_fastmutex_t *fastmutex);
MDBX_INTERNAL_FUNC int mdbx_fastmutex_acquire(mdbx_fastmutex_t *fastmutex);
diff --git a/libs/libmdbx/src/test/config.cc b/libs/libmdbx/src/test/config.cc
index f7f960c579..5e1979718a 100644
--- a/libs/libmdbx/src/test/config.cc
+++ b/libs/libmdbx/src/test/config.cc
@@ -395,6 +395,8 @@ void dump(const char *title) {
i->params.keygen.split,
i->params.keygen.width - i->params.keygen.split);
log_verbose("keygen.seed: %u\n", i->params.keygen.seed);
+ log_verbose("keygen.zerofill: %s\n",
+ i->params.keygen.zero_fill ? "Yes" : "No");
log_verbose("key: minlen %u, maxlen %u\n", i->params.keylen_min,
i->params.keylen_max);
log_verbose("data: minlen %u, maxlen %u\n", i->params.datalen_min,
@@ -450,18 +452,14 @@ using namespace config;
actor_config::actor_config(actor_testcase testcase, const actor_params &params,
unsigned space_id, unsigned wait4id)
- : params(params) {
- this->space_id = space_id;
- this->actor_id = 1 + (unsigned)global::actors.size();
- this->testcase = testcase;
- this->wait4id = wait4id;
- signal_nops = 0;
-}
+ : actor_config_pod(1 + unsigned(global::actors.size()), testcase, space_id,
+ wait4id),
+ params(params) {}
const std::string actor_config::serialize(const char *prefix) const {
simple_checksum checksum;
-
std::string result;
+
if (prefix)
result.append(prefix);
@@ -473,13 +471,13 @@ const std::string actor_config::serialize(const char *prefix) const {
result.append(params.pathname_log);
result.push_back('|');
- static_assert(std::is_pod<actor_params_pod>::value,
+ static_assert(std::is_trivially_copyable<actor_params_pod>::value,
"actor_params_pod should by POD");
result.append(data2hex(static_cast<const actor_params_pod *>(&params),
sizeof(actor_params_pod), checksum));
result.push_back('|');
- static_assert(std::is_pod<actor_config_pod>::value,
+ static_assert(std::is_trivially_copyable<actor_config_pod>::value,
"actor_config_pod should by POD");
result.append(data2hex(static_cast<const actor_config_pod *>(this),
sizeof(actor_config_pod), checksum));
@@ -525,7 +523,7 @@ bool actor_config::deserialize(const char *str, actor_config &config) {
TRACE("<< actor_config::deserialize: slash-3\n");
return false;
}
- static_assert(std::is_pod<actor_params_pod>::value,
+ static_assert(std::is_trivially_copyable<actor_params_pod>::value,
"actor_params_pod should by POD");
if (!hex2data(str, slash, static_cast<actor_params_pod *>(&config.params),
sizeof(actor_params_pod), checksum)) {
@@ -540,7 +538,7 @@ bool actor_config::deserialize(const char *str, actor_config &config) {
TRACE("<< actor_config::deserialize: slash-4\n");
return false;
}
- static_assert(std::is_pod<actor_config_pod>::value,
+ static_assert(std::is_trivially_copyable<actor_config_pod>::value,
"actor_config_pod should by POD");
if (!hex2data(str, slash, static_cast<actor_config_pod *>(&config),
sizeof(actor_config_pod), checksum)) {
diff --git a/libs/libmdbx/src/test/config.h b/libs/libmdbx/src/test/config.h
index 7efd09dd54..2ab4742a2e 100644
--- a/libs/libmdbx/src/test/config.h
+++ b/libs/libmdbx/src/test/config.h
@@ -124,7 +124,8 @@ inline bool parse_option_intptr(int argc, char *const argv[], int &narg,
#pragma pack(push, 1)
struct keygen_params_pod {
- /* Параметры генератора пар key-value.
+ /* Параметры генератора пар key-value. Также может быть полезным описание
+ * алгоритма генерации в keygen.h
*
* Ключи и значения генерируются по задаваемым параметрам на основе "плоской"
* исходной координаты. При этом, в общем случае, в процессе тестов исходная
@@ -141,20 +142,20 @@ struct keygen_params_pod {
* - libmdbx поддерживает два существенно различающихся вида таблиц,
* "уникальные" (без дубликатов и без multi-value), и так называемые
* "с дубликатами" (c multi-value).
- * - Для таблиц "без дубликатов" только размер связанных к ключами значений
+ * - Для таблиц "без дубликатов" только размер связанных с ключами значений
* (данных) оказывает влияния на работу движка, непосредственно содержимое
* данных не анализируется движком и не оказывает влияния на его работу.
* - Для таблиц "с дубликатами", при наличии более одного значения для
* некоторого ключа, формируется дочернее btree-поддерево. Это дерево
- * формируется в отдельном "кусте" страниц и обслуживается независимо
- * от окружения родительского ключа.
+ * формируется во вложенной странице или отдельном "кусте" страниц,
+ * и обслуживается независимо от окружения родительского ключа.
* - Таким образом, паттерн генерации значений имеет смысл только для
* таблиц "с дубликатами" и только в контексте одного значения ключа.
- * Иначе говоря, нет смысла в со-координации генерации паттернов для
- * ключей и значений. Более того, генерацию значений всегда необходимо
- * рассматривать в контексте связки с одним значением ключа.
- * - Тем не менее, во всех случаях достаточно важным является равномерная
- * всех возможных сочетаний длин ключей и данных.
+ * Иначе говоря, не имеет смысла взаимная координация при генерации
+ * значений для разных ключей. Поэтому генерацию значений следует
+ * рассматривать только в контексте связки с одним значением ключа.
+ * - Тем не менее, во всех случаях достаточно важным является равновероятное
+ * распределение всех возможных сочетаний длин ключей и данных.
*
* width:
* Большинство тестов предполагают создание или итерирование некоторого
@@ -166,7 +167,7 @@ struct keygen_params_pod {
* степени двойки. Это ограничение можно снять, но ценой увеличения
* вычислительной сложности, включая потерю простоты и прозрачности.
*
- * С другой стороны, не-битовый width может быть полезен:
+ * С другой стороны, не-n-битовый width может быть полезен:
* - Позволит генерировать ключи/значения в точно задаваемом диапазоне.
* Например, перебрать в псевдо-случайном порядке 10001 значение.
* - Позволит поровну разделять заданное пространство (диапазон)
@@ -203,7 +204,7 @@ struct keygen_params_pod {
* rotate и offset:
* Для проверки слияния и разделения страниц внутри движка требуются
* генерация ключей/значений в виде не-смежных последовательностей, как-бы
- * в виде "пунктира", который постепенно заполняет весь заданных диапазон.
+ * в виде "пунктира", который постепенно заполняет весь заданный диапазон.
*
* Параметры позволяют генерировать такой "пунктир". Соответственно rotate
* задает циклический сдвиг вправо, а offset задает смещение, точнее говоря
@@ -224,55 +225,62 @@ struct keygen_params_pod {
* номера будет отрезано для генерации значения.
*/
- uint8_t width;
- uint8_t mesh;
- uint8_t rotate;
- uint8_t split;
- uint32_t seed;
- uint64_t offset;
- keygen_case keycase;
+ uint8_t width{0};
+ uint8_t mesh{0};
+ uint8_t rotate{0};
+ uint8_t split{0};
+ uint32_t seed{0};
+ uint64_t offset{0};
+ keygen_case keycase{kc_random};
+ bool zero_fill{false};
};
struct actor_params_pod {
- unsigned mode_flags;
- unsigned table_flags;
- intptr_t size_lower;
- intptr_t size_now;
- intptr_t size_upper;
- int shrink_threshold;
- int growth_step;
- int pagesize;
-
- unsigned test_duration;
- unsigned test_nops;
- unsigned nrepeat;
- unsigned nthreads;
-
- unsigned keylen_min, keylen_max;
- unsigned datalen_min, datalen_max;
-
- unsigned batch_read;
- unsigned batch_write;
-
- unsigned delaystart;
- unsigned waitfor_nops;
- unsigned inject_writefaultn;
-
- unsigned max_readers;
- unsigned max_tables;
+ unsigned mode_flags{0};
+ unsigned table_flags{0};
+ intptr_t size_lower{0};
+ intptr_t size_now{0};
+ intptr_t size_upper{0};
+ int shrink_threshold{0};
+ int growth_step{0};
+ int pagesize{0};
+
+ unsigned test_duration{0};
+ unsigned test_nops{0};
+ unsigned nrepeat{0};
+ unsigned nthreads{0};
+
+ unsigned keylen_min{0}, keylen_max{0};
+ unsigned datalen_min{0}, datalen_max{0};
+
+ unsigned batch_read{0};
+ unsigned batch_write{0};
+
+ unsigned delaystart{0};
+ unsigned waitfor_nops{0};
+ unsigned inject_writefaultn{0};
+
+ unsigned max_readers{0};
+ unsigned max_tables{0};
keygen_params_pod keygen;
- uint8_t loglevel;
- bool drop_table;
- bool ignore_dbfull;
- bool speculum;
+ uint8_t loglevel{0};
+ bool drop_table{0};
+ bool ignore_dbfull{0};
+ bool speculum{0};
};
struct actor_config_pod {
- unsigned actor_id, space_id;
- actor_testcase testcase;
- unsigned wait4id;
- unsigned signal_nops;
+ unsigned actor_id{0}, space_id{0};
+ actor_testcase testcase{ac_none};
+ unsigned wait4id{0};
+ unsigned signal_nops{0};
+
+ actor_config_pod() = default;
+ actor_config_pod(unsigned actor_id, actor_testcase testcase,
+ unsigned space_id, unsigned wait4id)
+ : actor_id(actor_id), space_id(space_id), testcase(testcase),
+ wait4id(wait4id) {}
};
#pragma pack(pop)
@@ -286,8 +294,9 @@ void dump(const char *title = "config-dump: ");
struct actor_params : public config::actor_params_pod {
std::string pathname_log;
std::string pathname_db;
- void set_defaults(const std::string &tmpdir);
+ actor_params() = default;
+ void set_defaults(const std::string &tmpdir);
unsigned mdbx_keylen_min() const;
unsigned mdbx_keylen_max() const;
unsigned mdbx_datalen_min() const;
@@ -299,10 +308,11 @@ struct actor_config : public config::actor_config_pod {
bool wanna_event4signalling() const { return true /* TODO ? */; }
+ actor_config() = default;
actor_config(actor_testcase testcase, const actor_params &params,
unsigned space_id, unsigned wait4id);
- actor_config(const char *str) {
+ actor_config(const char *str) : actor_config() {
if (!deserialize(str, *this))
failure("Invalid internal parameter '%s'\n", str);
}
diff --git a/libs/libmdbx/src/test/dump-load.sh b/libs/libmdbx/src/test/dump-load.sh
new file mode 100644
index 0000000000..55fa5c7f33
--- /dev/null
+++ b/libs/libmdbx/src/test/dump-load.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+
+echo "------------------------------------------------------------------------------"
+
+if [ -z "$1" ]; then
+ echo "No mdbx-db pathname given";
+ exit 2
+elif [ ! -e "$1" ]; then
+ echo "The mdbx-db '$1' don't exists";
+ exit 2
+else
+ echo ">>>>>>>>>> $1"
+ RECO="$1.recovered"
+ rm -f dump1.txt dump2.txt "$RECO"
+ if ./mdbx_chk "$1"; then
+ echo ">>>>>>>>>> SOURCE VALID"
+ (./mdbx_dump -a "$1" > dump1.txt && \
+ ./mdbx_load -nf dump1.txt "$RECO" && \
+ ./mdbx_chk "$RECO" && \
+ echo ">>>>>>>>>> DUMP/LOAD/CHK OK") || (echo ">>>>>>>>>> DUMP/LOAD/CHK FAILED"; exit 1)
+ REMOVE_RECO=1
+ elif ./mdbx_chk -i "$1"; then
+ echo ">>>>>>>>>> SOURCE HAS WRONG-ORDER, TRY RECOVERY"
+ (./mdbx_dump -a "$1" > dump1.txt && \
+ ./mdbx_load -anf dump1.txt "$RECO" && \
+ ./mdbx_chk -i "$RECO" && \
+ echo ">>>>>>>>>> DUMP/LOAD/CHK OK") || (echo ">>>>>>>>>> DUMP/LOAD/CHK FAILED"; exit 1)
+ REMOVE_RECO=0
+ else
+ echo ">>>>>>>>>> SOURCE CORRUPTED, TRY RECOVERY"
+ (./mdbx_dump -ar "$1" > dump1.txt && \
+ ./mdbx_load -ranf dump1.txt "$RECO" && \
+ ./mdbx_chk -i "$RECO" && \
+ echo ">>>>>>>>>> DUMP/LOAD/CHK OK") || (echo ">>>>>>>>>> DUMP/LOAD/CHK FAILED"; exit 1)
+ REMOVE_RECO=0
+ fi
+ ./mdbx_dump -a "$RECO" > dump2.txt && diff -u dump1.txt dump2.txt && \
+ rm -f dump1.txt dump2.txt && [ $REMOVE_RECO -ne 0 ] && rm -f "$RECO"
+ exit 0
+fi
diff --git a/libs/libmdbx/src/test/hill.cc b/libs/libmdbx/src/test/hill.cc
index 37f748c9f7..efc43abe2a 100644
--- a/libs/libmdbx/src/test/hill.cc
+++ b/libs/libmdbx/src/test/hill.cc
@@ -232,6 +232,47 @@ bool testcase_hill::run() {
}
}
+ if (txn_guard) {
+ MDBX_stat stat;
+ err = mdbx_dbi_stat(txn_guard.get(), dbi, &stat, sizeof(stat));
+ if (unlikely(err != MDBX_SUCCESS))
+ failure_perror("mdbx_dbi_stat()", err);
+
+ uint32_t nested_deepmask;
+ err = mdbx_dbi_dupsort_depthmask(txn_guard.get(), dbi, &nested_deepmask);
+ if (unlikely(err != MDBX_SUCCESS && err != MDBX_RESULT_TRUE))
+ failure_perror("mdbx_dbi_stat_nested_deepmask()", err);
+
+ if (err != MDBX_SUCCESS) {
+ log_notice("hill: reached %d tree depth", stat.ms_depth);
+ } else {
+ std::string str;
+ int prev = -2, i = 0;
+ do {
+ while (!(nested_deepmask & 1))
+ ++i, nested_deepmask >>= 1;
+ if (prev + 1 == i) {
+ if (str.back() != '-')
+ str.push_back('-');
+ prev = i;
+ continue;
+ }
+ if (!str.empty()) {
+ if (str.back() == '-')
+ str.append(std::to_string(prev));
+ str.push_back(',');
+ }
+ str.append(std::to_string(i));
+ prev = i;
+ } while (++i, nested_deepmask >>= 1);
+ if (str.back() == '-')
+ str.append(std::to_string(prev));
+
+ log_notice("hill: reached %d tree depth & %s sub-tree depth(s)",
+ stat.ms_depth, str.c_str());
+ }
+ }
+
while (serial_count > 1) {
if (unlikely(!keyvalue_maker.increment(serial_count, -2)))
failure("downhill: unexpected key-space underflow");
diff --git a/libs/libmdbx/src/test/keygen.cc b/libs/libmdbx/src/test/keygen.cc
index 374537dd6e..05070afe02 100644
--- a/libs/libmdbx/src/test/keygen.cc
+++ b/libs/libmdbx/src/test/keygen.cc
@@ -78,9 +78,11 @@ void __hot maker::pair(serial_t serial, const buffer &key, buffer &value,
assert(mapping.mesh <= mapping.width);
assert(mapping.rotate <= mapping.width);
assert(mapping.offset <= mask(mapping.width));
- assert(!(key_essentials.flags &
- ~(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT)));
- assert(!(value_essentials.flags & ~(MDBX_INTEGERDUP | MDBX_REVERSEDUP)));
+ assert(
+ !(key_essentials.flags & ~(essentials::prng_fill_flag | MDBX_INTEGERKEY |
+ MDBX_REVERSEKEY | MDBX_DUPSORT)));
+ assert(!(value_essentials.flags &
+ ~(essentials::prng_fill_flag | MDBX_INTEGERDUP | MDBX_REVERSEDUP)));
log_trace("keygen-pair: serial %" PRIu64 ", data-age %" PRIu64, serial,
value_age);
@@ -213,6 +215,11 @@ void maker::setup(const config::actor_params_pod &actor, unsigned actor_id,
(uint32_t)actor.datalen_max,
(uint32_t)mdbx_limits_valsize_max(actor.pagesize, key_essentials.flags));
+ if (!actor.keygen.zero_fill) {
+ key_essentials.flags |= essentials::prng_fill_flag;
+ value_essentials.flags |= essentials::prng_fill_flag;
+ }
+
(void)thread_number;
mapping = actor.keygen;
salt = (actor.keygen.seed + actor_id) * UINT64_C(14653293970879851569);
@@ -298,6 +305,10 @@ void __hot maker::mk_begin(const serial_t serial, const essentials &params,
void __hot maker::mk_continue(const serial_t serial, const essentials &params,
result &out) {
+ static_assert((essentials::prng_fill_flag &
+ (MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERKEY |
+ MDBX_INTEGERDUP | MDBX_REVERSEKEY | MDBX_REVERSEDUP)) == 0,
+ "WTF?");
out.value.iov_base = out.bytes;
if (params.flags & (MDBX_INTEGERKEY | MDBX_INTEGERDUP)) {
assert(params.maxlen == params.minlen);
@@ -308,7 +319,11 @@ void __hot maker::mk_continue(const serial_t serial, const essentials &params,
out.u32 = (uint32_t)serial;
} else if (params.flags & (MDBX_REVERSEKEY | MDBX_REVERSEDUP)) {
if (out.value.iov_len > 8) {
- memset(out.bytes, '\0', out.value.iov_len - 8);
+ if (params.flags & essentials::prng_fill_flag) {
+ uint64_t state = serial ^ UINT64_C(0x41803711c9b75f19);
+ prng_fill(state, out.bytes, out.value.iov_len - 8);
+ } else
+ memset(out.bytes, '\0', out.value.iov_len - 8);
unaligned::store(out.bytes + out.value.iov_len - 8, htobe64(serial));
} else {
out.u64 = htobe64(serial);
@@ -317,8 +332,13 @@ void __hot maker::mk_continue(const serial_t serial, const essentials &params,
}
} else {
out.u64 = htole64(serial);
- if (out.value.iov_len > 8)
- memset(out.bytes + 8, '\0', out.value.iov_len - 8);
+ if (out.value.iov_len > 8) {
+ if (params.flags & essentials::prng_fill_flag) {
+ uint64_t state = serial ^ UINT64_C(0x923ab47b7ee6f6e4);
+ prng_fill(state, out.bytes + 8, out.value.iov_len - 8);
+ } else
+ memset(out.bytes + 8, '\0', out.value.iov_len - 8);
+ }
}
assert(out.value.iov_len >= params.minlen);
diff --git a/libs/libmdbx/src/test/keygen.h b/libs/libmdbx/src/test/keygen.h
index b5674f1ca6..c36cc1a2e7 100644
--- a/libs/libmdbx/src/test/keygen.h
+++ b/libs/libmdbx/src/test/keygen.h
@@ -23,9 +23,8 @@ namespace keygen {
/* Под "генерацией ключей" здесь понимается генерация обоих значений для
* пар key-value, т.е. не только ключей, но и ассоциированных с ними данных.
- */
-
-/* Генерацию ключей нельзя отнести к простым задачам, так как требования
+ *
+ * Генерацию ключей нельзя отнести к простым задачам, так как требования
* примерно следующие:
* - генерация разного количества уникальных ключей различной длины
* в задаваемом диапазоне;
@@ -67,7 +66,8 @@ namespace keygen {
* 1) смещение (сложение) по модулю;
* 2) циклический сдвиг;
* 3) добавление абсолютного смещения (базы);
- */
+ *
+ * Также см. описание параметров генератора ключей и значений в config.h */
typedef uint64_t serial_t;
@@ -103,13 +103,14 @@ buffer alloc(size_t limit);
class maker {
config::keygen_params_pod mapping;
- serial_t base;
- serial_t salt;
+ serial_t base{0};
+ serial_t salt{0};
struct essentials {
- uint16_t minlen;
- uint16_t flags;
- uint32_t maxlen;
+ uint16_t minlen{0};
+ enum { prng_fill_flag = 1 };
+ uint16_t flags{0};
+ uint32_t maxlen{0};
} key_essentials, value_essentials;
static void mk_begin(const serial_t serial, const essentials &params,
@@ -122,8 +123,6 @@ class maker {
}
public:
- maker() { memset(this, 0, sizeof(*this)); }
-
void pair(serial_t serial, const buffer &key, buffer &value,
serial_t value_age, const bool keylen_changeable);
void setup(const config::actor_params_pod &actor, unsigned actor_id,
diff --git a/libs/libmdbx/src/test/long_stochastic.sh b/libs/libmdbx/src/test/long_stochastic.sh
index 58ec6d5c6e..7141ee62c5 100644
--- a/libs/libmdbx/src/test/long_stochastic.sh
+++ b/libs/libmdbx/src/test/long_stochastic.sh
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
-if ! which make cc c++ tee lz4 >/dev/null; then
- echo "Please install the following prerequisites: make cc c++ tee lz4" >&2
- exit 1
+if ! which make cc c++ tee lz4 banner >/dev/null; then
+ echo "Please install the following prerequisites: make cc c++ tee lz4 banner" >&2
+ exit 1
fi
set -euo pipefail
@@ -19,72 +19,72 @@ UNAME="$(uname -s 2>/dev/null || echo Unknown)"
# 1. clean data from prev runs and examine available RAM
if [[ -v VALGRIND && ! -z "$VALGRIND" ]]; then
- rm -f valgrind-*.log
+ rm -f valgrind-*.log
else
- VALGRIND=time
+ VALGRIND=time
fi
WANNA_MOUNT=0
case ${UNAME} in
- Linux)
- MAKE=make
- if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
- for old_test_dir in $(ls -d /dev/shm/mdbx-test.[0-9]*); do
- rm -rf $old_test_dir
- done
- TESTDB_DIR="/dev/shm/mdbx-test.$$"
- fi
- mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/*
-
- if LC_ALL=C free | grep -q -i available; then
- ram_avail_mb=$(($(LC_ALL=C free | grep -i Mem: | tr -s [:blank:] ' ' | cut -d ' ' -f 7) / 1024))
- else
- ram_avail_mb=$(($(LC_ALL=C free | grep -i Mem: | tr -s [:blank:] ' ' | cut -d ' ' -f 4) / 1024))
- fi
- ;;
-
- FreeBSD)
- MAKE=gmake
- if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
- for old_test_dir in $(ls -d /tmp/mdbx-test.[0-9]*); do
- umount $old_test_dir && rm -r $old_test_dir
- done
- TESTDB_DIR="/tmp/mdbx-test.$$"
- rm -rf $TESTDB_DIR && mkdir -p $TESTDB_DIR
- WANNA_MOUNT=1
- else
- mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/*
- fi
-
- ram_avail_mb=$(($(LC_ALL=C vmstat -s | grep -ie '[0-9] pages free$' | cut -d p -f 1) * ($(LC_ALL=C vmstat -s | grep -ie '[0-9] bytes per page$' | cut -d b -f 1) / 1024) / 1024))
- ;;
-
- Darwin)
- MAKE=make
- if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
- for vol in $(ls -d /Volumes/mdx[0-9]*[0-9]tst); do
- disk=$(mount | grep $vol | cut -d ' ' -f 1)
- echo "umount: volume $vol disk $disk"
- hdiutil unmount $vol -force
- hdiutil detach $disk
- done
- TESTDB_DIR="/Volumes/mdx$$tst"
- WANNA_MOUNT=1
- else
- mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/*
- fi
-
- pagesize=$(($(LC_ALL=C vm_stat | grep -o 'page size of [0-9]\+ bytes' | cut -d' ' -f 4) / 1024))
- freepages=$(LC_ALL=C vm_stat | grep '^Pages free:' | grep -o '[0-9]\+\.$' | cut -d'.' -f 1)
- ram_avail_mb=$((pagesize * freepages / 1024))
- echo "pagesize ${pagesize}K, freepages ${freepages}, ram_avail_mb ${ram_avail_mb}"
-
- ;;
-
- *)
- echo "FIXME: ${UNAME} not supported by this script"
- exit 2
- ;;
+ Linux)
+ MAKE=make
+ if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
+ for old_test_dir in $(ls -d /dev/shm/mdbx-test.[0-9]*); do
+ rm -rf $old_test_dir
+ done
+ TESTDB_DIR="/dev/shm/mdbx-test.$$"
+ fi
+ mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/*
+
+ if LC_ALL=C free | grep -q -i available; then
+ ram_avail_mb=$(($(LC_ALL=C free | grep -i Mem: | tr -s [:blank:] ' ' | cut -d ' ' -f 7) / 1024))
+ else
+ ram_avail_mb=$(($(LC_ALL=C free | grep -i Mem: | tr -s [:blank:] ' ' | cut -d ' ' -f 4) / 1024))
+ fi
+ ;;
+
+ FreeBSD)
+ MAKE=gmake
+ if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
+ for old_test_dir in $(ls -d /tmp/mdbx-test.[0-9]*); do
+ umount $old_test_dir && rm -r $old_test_dir
+ done
+ TESTDB_DIR="/tmp/mdbx-test.$$"
+ rm -rf $TESTDB_DIR && mkdir -p $TESTDB_DIR
+ WANNA_MOUNT=1
+ else
+ mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/*
+ fi
+
+ ram_avail_mb=$(($(LC_ALL=C vmstat -s | grep -ie '[0-9] pages free$' | cut -d p -f 1) * ($(LC_ALL=C vmstat -s | grep -ie '[0-9] bytes per page$' | cut -d b -f 1) / 1024) / 1024))
+ ;;
+
+ Darwin)
+ MAKE=make
+ if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
+ for vol in $(ls -d /Volumes/mdx[0-9]*[0-9]tst); do
+ disk=$(mount | grep $vol | cut -d ' ' -f 1)
+ echo "umount: volume $vol disk $disk"
+ hdiutil unmount $vol -force
+ hdiutil detach $disk
+ done
+ TESTDB_DIR="/Volumes/mdx$$tst"
+ WANNA_MOUNT=1
+ else
+ mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/*
+ fi
+
+ pagesize=$(($(LC_ALL=C vm_stat | grep -o 'page size of [0-9]\+ bytes' | cut -d' ' -f 4) / 1024))
+ freepages=$(LC_ALL=C vm_stat | grep '^Pages free:' | grep -o '[0-9]\+\.$' | cut -d'.' -f 1)
+ ram_avail_mb=$((pagesize * freepages / 1024))
+ echo "pagesize ${pagesize}K, freepages ${freepages}, ram_avail_mb ${ram_avail_mb}"
+
+ ;;
+
+ *)
+ echo "FIXME: ${UNAME} not supported by this script"
+ exit 2
+ ;;
esac
###############################################################################
@@ -93,8 +93,8 @@ esac
echo "=== ${ram_avail_mb}M RAM available"
ram_reserve4logs_mb=1234
if [ $ram_avail_mb -lt $ram_reserve4logs_mb ]; then
- echo "=== At least ${ram_reserve4logs_mb}Mb RAM required"
- exit 3
+ echo "=== At least ${ram_reserve4logs_mb}Mb RAM required"
+ exit 3
fi
#
@@ -116,36 +116,36 @@ fi
# system immediately, as well some space is required for logs.
#
db_size_mb=$(((ram_avail_mb - ram_reserve4logs_mb) / 4))
-if [ $db_size_mb -gt 3072 ]; then
- db_size_mb=3072
+if [ $db_size_mb -gt 17408 ]; then
+ db_size_mb=17408
fi
echo "=== use ${db_size_mb}M for DB"
###############################################################################
# 3. Create test-directory in ramfs/tmpfs, i.e. create/format/mount if required
case ${UNAME} in
- Linux)
- ;;
-
- FreeBSD)
- if [[ WANNA_MOUNT ]]; then
- mount -t tmpfs tmpfs $TESTDB_DIR
- fi
- ;;
-
- Darwin)
- if [[ WANNA_MOUNT ]]; then
- ramdisk_size_mb=$((42 + db_size_mb * 2 + ram_reserve4logs_mb))
- number_of_sectors=$((ramdisk_size_mb * 2048))
- ramdev=$(hdiutil attach -nomount ram://${number_of_sectors})
- diskutil erasevolume ExFAT "mdx$$tst" ${ramdev}
- fi
- ;;
-
- *)
- echo "FIXME: ${UNAME} not supported by this script"
- exit 2
- ;;
+ Linux)
+ ;;
+
+ FreeBSD)
+ if [[ WANNA_MOUNT ]]; then
+ mount -t tmpfs tmpfs $TESTDB_DIR
+ fi
+ ;;
+
+ Darwin)
+ if [[ WANNA_MOUNT ]]; then
+ ramdisk_size_mb=$((42 + db_size_mb * 2 + ram_reserve4logs_mb))
+ number_of_sectors=$((ramdisk_size_mb * 2048))
+ ramdev=$(hdiutil attach -nomount ram://${number_of_sectors})
+ diskutil erasevolume ExFAT "mdx$$tst" ${ramdev}
+ fi
+ ;;
+
+ *)
+ echo "FIXME: ${UNAME} not supported by this script"
+ exit 2
+ ;;
esac
###############################################################################
@@ -157,64 +157,115 @@ rm -f ${TESTDB_DIR}/*
###############################################################################
# 5. run stochastic iterations
-function rep9 { printf "%*s" $1 '' | tr ' ' '9'; }
function join { local IFS="$1"; shift; echo "$*"; }
function bit2option { local -n arr=$1; (( ($2&(1<<$3)) != 0 )) && echo -n '+' || echo -n '-'; echo "${arr[$3]}"; }
options=(writemap coalesce lifo notls)
function bits2list {
- local -n arr=$1
- local i
- local list=()
- for ((i=0; i<${#arr[@]}; ++i)) do
- list[$i]=$(bit2option $1 $2 $i)
- done
- join , "${list[@]}"
+ local -n arr=$1
+ local i
+ local list=()
+ for ((i=0; i<${#arr[@]}; ++i)) do
+ list[$i]=$(bit2option $1 $2 $i)
+ done
+ join , "${list[@]}"
}
function probe {
- echo "=============================================== $(date)"
- echo "${caption}: $*"
- rm -f ${TESTDB_DIR}/* \
- && ${VALGRIND} ./mdbx_test --ignore-dbfull --repeat=42 --pathname=${TESTDB_DIR}/long.db "$@" | lz4 > ${TESTDB_DIR}/long.log.lz4 \
- && ${VALGRIND} ./mdbx_chk -nvvv ${TESTDB_DIR}/long.db | tee ${TESTDB_DIR}/long-chk.log \
- && ([ ! -e ${TESTDB_DIR}/long.db-copy ] || ${VALGRIND} ./mdbx_chk -nvvv ${TESTDB_DIR}/long.db-copy | tee ${TESTDB_DIR}/long-chk-copy.log) \
- || (echo "FAILED"; exit 1)
+ echo "----------------------------------------------- $(date)"
+ echo "${caption}: $*"
+ rm -f ${TESTDB_DIR}/* \
+ && ${VALGRIND} ./mdbx_test ${speculum} --ignore-dbfull --repeat=3 --pathname=${TESTDB_DIR}/long.db --cleanup-after=no "$@" \
+ | tee >(lz4 > ${TESTDB_DIR}/long.log.lz4) | grep -e reach -e achieve \
+ && ${VALGRIND} ./mdbx_chk ${TESTDB_DIR}/long.db | tee ${TESTDB_DIR}/long-chk.log \
+ && ([ ! -e ${TESTDB_DIR}/long.db-copy ] || ${VALGRIND} ./mdbx_chk ${TESTDB_DIR}/long.db-copy | tee ${TESTDB_DIR}/long-chk-copy.log) \
+ || (echo "FAILED"; exit 1)
}
#------------------------------------------------------------------------------
count=0
-for nops in $(seq 2 6); do
- for ((wbatch=nops-1; wbatch > 0; --wbatch)); do
- loops=$(((111 >> nops) / nops + 3))
- for ((rep=0; rep++ < loops; )); do
- for ((bits=2**${#options[@]}; --bits >= 0; )); do
- seed=$(($(date +%s) + RANDOM))
- caption="Probe #$((++count)) int-key,w/o-dups, repeat ${rep} of ${loops}" probe \
- --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,-data.dups --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
- --nops=$( rep9 $nops ) --batch.write=$( rep9 $wbatch ) --mode=$(bits2list options $bits) \
- --keygen.seed=${seed} basic
- caption="Probe #$((++count)) int-key,with-dups, repeat ${rep} of ${loops}" probe \
- --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.dups --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
- --nops=$( rep9 $nops ) --batch.write=$( rep9 $wbatch ) --mode=$(bits2list options $bits) \
- --keygen.seed=${seed} basic
- caption="Probe #$((++count)) int-key,int-data, repeat ${rep} of ${loops}" probe \
- --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
- --nops=$( rep9 $nops ) --batch.write=$( rep9 $wbatch ) --mode=$(bits2list options $bits) \
- --keygen.seed=${seed} basic
- caption="Probe #$((++count)) w/o-dups, repeat ${rep} of ${loops}" probe \
- --pagesize=min --size-upper=${db_size_mb}M --table=-data.dups --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
- --nops=$( rep9 $nops ) --batch.write=$( rep9 $wbatch ) --mode=$(bits2list options $bits) \
- --keygen.seed=${seed} basic
- caption="Probe #$((++count)) with-dups, repeat ${rep} of ${loops}" probe \
- --pagesize=min --size-upper=${db_size_mb}M --table=+data.dups --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
- --nops=$( rep9 $nops ) --batch.write=$( rep9 $wbatch ) --mode=$(bits2list options $bits) \
- --keygen.seed=${seed} basic
- done
- done
- done
-done
+cases='?'
+for nops in 10 100 1000 10000 100000 1000000 10000000 100000000 1000000000; do
+ echo "======================================================================="
+ wbatch=$((nops / 10 + 1))
+ speculum=$([ $nops -le 1000 ] && echo '--speculum' || true)
+ while true; do
+ echo "======================================================================="
+ banner "$nops / $wbatch"
+ subcase=0
+ for ((bits=2**${#options[@]}; --bits >= 0; )); do
+ seed=$(($(date +%s) + RANDOM))
+
+ split=30
+ caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
+ --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
+ --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \
+ --keygen.seed=${seed} basic
+ caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
+ --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
+ --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \
+ --keygen.seed=${seed} basic
+ caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
+ --pagesize=min --size-upper=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
+ --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \
+ --keygen.seed=${seed} basic
+
+ split=24
+ caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
+ --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
+ --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \
+ --keygen.seed=${seed} basic
+ caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
+ --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
+ --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \
+ --keygen.seed=${seed} basic
+ caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
+ --pagesize=min --size-upper=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
+ --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \
+ --keygen.seed=${seed} basic
+
+ split=16
+ caption="Probe #$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
+ --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
+ --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \
+ --keygen.seed=${seed} basic
+ caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
+ --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
+ --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \
+ --keygen.seed=${seed} basic
+ caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
+ --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
+ --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \
+ --keygen.seed=${seed} basic
+ caption="Probe #$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
+ --pagesize=min --size-upper=${db_size_mb}M --table=-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
+ --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \
+ --keygen.seed=${seed} basic
+ caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
+ --pagesize=min --size-upper=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
+ --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \
+ --keygen.seed=${seed} basic
+
+ split=4
+ caption="Probe #$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
+ --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
+ --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \
+ --keygen.seed=${seed} basic
+ caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
+ --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
+ --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \
+ --keygen.seed=${seed} basic
+ caption="Probe #$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
+ --pagesize=min --size-upper=${db_size_mb}M --table=-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
+ --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \
+ --keygen.seed=${seed} basic
+ done # options
+ cases="${subcase}"
+ wbatch=$(((wbatch > 9) ? wbatch / 10 : 1))
+ if [ $wbatch -eq 1 -o $((nops / wbatch)) -gt 1000 ]; then break; fi
+ done # batch (write-ops per txn)
+done # n-ops
echo "=== ALL DONE ====================== $(date)"
diff --git a/libs/libmdbx/src/test/main.cc b/libs/libmdbx/src/test/main.cc
index 656d7c52f9..c6b15ea29b 100644
--- a/libs/libmdbx/src/test/main.cc
+++ b/libs/libmdbx/src/test/main.cc
@@ -81,6 +81,7 @@ void __noreturn usage(void) {
" --keygen.width=N TBD (see the source code)\n"
" --keygen.mesh=N TBD (see the source code)\n"
" --keygen.seed=N TBD (see the source code)\n"
+ " --keygen.zerofill=yes|NO TBD (see the source code)\n"
" --keygen.split=N TBD (see the source code)\n"
" --keygen.rotate=N TBD (see the source code)\n"
" --keygen.offset=N TBD (see the source code)\n"
@@ -136,6 +137,7 @@ void actor_params::set_defaults(const std::string &tmpdir) {
pagesize = -1;
keygen.seed = 1;
+ keygen.zero_fill = false;
keygen.keycase = kc_random;
keygen.width = (table_flags & MDBX_DUPSORT) ? 32 : 64;
keygen.mesh = keygen.width;
@@ -209,7 +211,11 @@ std::string thunk_param(const actor_config &config) {
void cleanup() {
log_trace(">> cleanup");
- /* TODO: remove each database */
+ for (const auto &db_path : global::databases) {
+ int err = osal_removefile(db_path);
+ if (err != MDBX_SUCCESS && err != MDBX_ENOFILE)
+ failure_perror(db_path.c_str(), err);
+ }
log_trace("<< cleanup");
}
@@ -317,19 +323,22 @@ int main(int argc, char *const argv[]) {
continue;
if (config::parse_option(argc, argv, narg, "keygen.width",
- params.keygen.width, 1, 64))
+ params.keygen.width, 8, 64))
continue;
if (config::parse_option(argc, argv, narg, "keygen.mesh",
- params.keygen.mesh, 1, 64))
+ params.keygen.mesh, 0, 64))
continue;
if (config::parse_option(argc, argv, narg, "keygen.seed",
params.keygen.seed, config::no_scale))
continue;
+ if (config::parse_option(argc, argv, narg, "keygen.zerofill",
+ params.keygen.zero_fill))
+ continue;
if (config::parse_option(argc, argv, narg, "keygen.split",
- params.keygen.split, 1, 64))
+ params.keygen.split, 0, 63))
continue;
if (config::parse_option(argc, argv, narg, "keygen.rotate",
- params.keygen.rotate, 1, 64))
+ params.keygen.rotate, 0, 63))
continue;
if (config::parse_option(argc, argv, narg, "keygen.offset",
params.keygen.offset, config::binary))
@@ -587,7 +596,7 @@ int main(int argc, char *const argv[]) {
}
log_notice("RESULT: %s\n", failed ? "Failed" : "Successful");
- if (global::config::cleanup_before) {
+ if (global::config::cleanup_after) {
if (failed)
log_verbose("skip cleanup");
else
diff --git a/libs/libmdbx/src/test/nested.cc b/libs/libmdbx/src/test/nested.cc
index beebd88071..85df6fa62b 100644
--- a/libs/libmdbx/src/test/nested.cc
+++ b/libs/libmdbx/src/test/nested.cc
@@ -15,6 +15,25 @@
#include "test.h"
#include <cmath>
+/* LY: тест "эмуляцией time-to-live" с вложенными транзакциями:
+ * - организуется "скользящее окно", которое каждую транзакцию сдвигается
+ * вперед вдоль числовой оси.
+ * - по переднему краю "скользящего окна" записи добавляются в таблицу,
+ * а по заднему удаляются.
+ * - количество добавляемых/удаляемых записей псевдослучайно зависит
+ * от номера транзакции, но с экспоненциальным распределением.
+ * - размер "скользящего окна" также псевдослучайно зависит от номера
+ * транзакции с "отрицательным" экспоненциальным распределением
+ * MAX_WIDTH - exp(rnd(N)), при уменьшении окна сдвигается задний
+ * край и удаляются записи позади него.
+ * - групповое добавление данных в начало окна и групповое удаление в конце,
+ * преимущественно выполняются во вложенных транзакциях.
+ * - меньшая часть запускаемых вложенных транзакций отменяется, с последующим
+ * продолжением итераций с состояния предыдущиего коммита.
+ *
+ * Таким образом имитируется поведение таблицы с TTL: записи стохастически
+ * добавляются и удаляются, и изредка происходят массивные удаления. */
+
bool testcase_nested::setup() {
if (!inherited::setup())
return false;
@@ -56,18 +75,6 @@ bool testcase_nested::teardown() {
return inherited::teardown() && ok;
}
-static unsigned edge2window(uint64_t edge, unsigned window_max) {
- const double rnd = u64_to_double1(bleach64(edge));
- const unsigned window = window_max - std::lrint(std::pow(window_max, rnd));
- return window;
-}
-
-static unsigned edge2count(uint64_t edge, unsigned count_max) {
- const double rnd = u64_to_double1(prng64_map1_white(edge));
- const unsigned count = std::lrint(std::pow(count_max, rnd));
- return count;
-}
-
void testcase_nested::push_txn() {
MDBX_txn *txn;
unsigned flags =
@@ -75,11 +82,7 @@ void testcase_nested::push_txn() {
int err = mdbx_txn_begin(db_guard.get(), txn_guard.get(), flags, &txn);
if (unlikely(err != MDBX_SUCCESS))
failure_perror("mdbx_txn_begin(nested)", err);
-#if __cplusplus >= 201703L
- stack.emplace(txn, serial, fifo, speculum);
-#else
- stack.push(std::make_tuple(scoped_txn_guard(txn), serial, fifo, speculum));
-#endif
+ stack.emplace(scoped_txn_guard(txn), serial, fifo, speculum);
std::swap(txn_guard, std::get<0>(stack.top()));
log_verbose("begin level#%zu txn #%" PRIu64 ", flags 0x%x, serial %" PRIu64,
stack.size(), mdbx_txn_id(txn), flags, serial);
@@ -150,7 +153,8 @@ bool testcase_nested::stochastic_breakable_restart_with_nested(
}
bool testcase_nested::trim_tail(unsigned window_width) {
- if (window_width) {
+ if (window_width || flipcoin()) {
+ clear_stepbystep_passed += window_width == 0;
while (fifo.size() > window_width) {
uint64_t tail_serial = fifo.back().first;
const unsigned tail_count = fifo.back().second;
@@ -171,6 +175,7 @@ bool testcase_nested::trim_tail(unsigned window_width) {
if (unlikely(!keyvalue_maker.increment(tail_serial, 1)))
failure("nested: unexpected key-space overflow on the tail");
}
+ report(tail_count);
}
} else if (!fifo.empty()) {
log_verbose("nested: purge state %" PRIu64 " - %" PRIu64 ", fifo-items %zu",
@@ -178,7 +183,8 @@ bool testcase_nested::trim_tail(unsigned window_width) {
fifo.size());
db_table_clear(dbi, txn_guard.get());
fifo.clear();
- speculum.clear();
+ clear_wholetable_passed += 1;
+ report(1);
}
return true;
}
@@ -198,6 +204,7 @@ retry:
log_notice("nested: head-insert skip due '%s'", mdbx_strerror(err));
head_count = n;
stochastic_breakable_restart_with_nested(true);
+ dbfull_passed += 1;
goto retry;
}
failure_perror("mdbx_put(head)", err);
@@ -205,7 +212,10 @@ retry:
if (unlikely(!keyvalue_maker.increment(serial, 1))) {
log_notice("nested: unexpected key-space overflow");
- return false;
+ keyspace_overflow = true;
+ head_count = n;
+ stochastic_breakable_restart_with_nested(true);
+ goto retry;
}
}
@@ -213,48 +223,19 @@ retry:
}
bool testcase_nested::run() {
- /* LY: тест "эмуляцией time-to-live" с вложенными транзакциями:
- * - организуется "скользящее окно", которое каждую транзакцию сдвигается
- * вперед вдоль числовой оси.
- * - по переднему краю "скользящего окна" записи добавляются в таблицу,
- * а по заднему удаляются.
- * - количество добавляемых/удаляемых записей псевдослучайно зависит
- * от номера транзакции, но с экспоненциальным распределением.
- * - размер "скользящего окна" также псевдослучайно зависит от номера
- * транзакции с "отрицательным" экспоненциальным распределением
- * MAX_WIDTH - exp(rnd(N)), при уменьшении окна сдвигается задний
- * край и удаляются записи позади него.
- * - групповое добавление данных в начало окна и групповое уделение в конце,
- * в половине случаев выполняются во вложенных транзакциях.
- * - половина запускаемых вложенных транзакций отменяется, последуюим
- * повтором групповой операции.
- *
- * Таким образом имитируется поведение таблицы с TTL: записи стохастически
- * добавляются и удаляются, но изредка происходят массивные удаления. */
-
- /* LY: для параметризации используем подходящие параметры, которые не имеют
- * здесь смысла в первоначальном значении. */
- const unsigned window_max_lower = 333;
- const unsigned count_max_lower = 333;
-
- const unsigned window_max = (config.params.batch_read > window_max_lower)
- ? config.params.batch_read
- : window_max_lower;
- const unsigned count_max = (config.params.batch_write > count_max_lower)
- ? config.params.batch_write
- : count_max_lower;
- log_verbose("nested: using `batch_read` value %u for window_max", window_max);
- log_verbose("nested: using `batch_write` value %u for count_max", count_max);
-
uint64_t seed =
prng64_map2_white(config.params.keygen.seed) + config.actor_id;
- while (should_continue()) {
+ clear_wholetable_passed = 0;
+ clear_stepbystep_passed = 0;
+ dbfull_passed = 0;
+ unsigned loops = 0;
+ while (true) {
const uint64_t salt = prng64_white(seed) /* mdbx_txn_id(txn_guard.get()) */;
const unsigned window_width =
- flipcoin_x4() ? 0 : edge2window(salt, window_max);
- const unsigned head_count = edge2count(salt, count_max);
- log_debug("nested: step #%zu (serial %" PRIu64
+ (!should_continue() || flipcoin_x4()) ? 0 : edge2window(salt);
+ const unsigned head_count = edge2count(salt);
+ log_debug("nested: step #%" PRIu64 " (serial %" PRIu64
", window %u, count %u) salt %" PRIu64,
nops_completed, serial, window_width, head_count, salt);
@@ -269,16 +250,33 @@ bool testcase_nested::run() {
return false;
}
- if (!grow_head(head_count))
- return false;
- if (!stochastic_breakable_restart_with_nested())
- log_notice("nested: skip commit/restart after head-grow");
- if (!speculum_verify()) {
- log_notice("nested: bailout after head-grow");
- return false;
+ if (!keyspace_overflow && (should_continue() || !clear_wholetable_passed ||
+ !clear_stepbystep_passed)) {
+ unsigned underutilization_x256 =
+ txn_underutilization_x256(txn_guard.get());
+ if (dbfull_passed > underutilization_x256) {
+ log_notice("nested: skip head-grow to avoid one more dbfull (was %u, "
+ "underutilization %.2f%%)",
+ dbfull_passed, underutilization_x256 / 2.560);
+ continue;
+ }
+ if (!grow_head(head_count))
+ return false;
+ if (!stochastic_breakable_restart_with_nested())
+ log_notice("nested: skip commit/restart after head-grow");
+ if (!speculum_verify()) {
+ log_notice("nested: bailout after head-grow");
+ return false;
+ }
+ loops += 1;
+ } else if (fifo.empty()) {
+ log_notice("nested: done %u whole loops, %" PRIu64 " ops, %" PRIu64
+ " items",
+ loops, nops_completed, serial);
+ break;
+ } else {
+ log_notice("nested: done, wait for empty, skip head-grow");
}
-
- report(1);
}
while (!stack.empty())
diff --git a/libs/libmdbx/src/test/osal-unix.cc b/libs/libmdbx/src/test/osal-unix.cc
index dc0774063a..757c8583c0 100644
--- a/libs/libmdbx/src/test/osal-unix.cc
+++ b/libs/libmdbx/src/test/osal-unix.cc
@@ -517,9 +517,9 @@ std::string osal_tempdir(void) {
tempdir = getenv("TEMPDIR");
if (!tempdir)
tempdir = getenv("TEMP");
- if (tempdir) {
+ if (tempdir && *tempdir) {
std::string dir(tempdir);
- if (!dir.empty() && dir.at(dir.length() - 1) != '/')
+ if (dir.back() != '/')
dir.append("/");
return dir;
}
diff --git a/libs/libmdbx/src/test/test.cc b/libs/libmdbx/src/test/test.cc
index 9da2cf2fff..beeba7abab 100644
--- a/libs/libmdbx/src/test/test.cc
+++ b/libs/libmdbx/src/test/test.cc
@@ -172,28 +172,29 @@ void testcase::txn_begin(bool readonly, unsigned flags) {
if (unlikely(rc != MDBX_SUCCESS))
failure_perror("mdbx_txn_begin()", rc);
txn_guard.reset(txn);
+ need_speculum_assign = config.params.speculum && !readonly;
log_trace("<< txn_begin(%s, 0x%04X)", readonly ? "read-only" : "read-write",
flags);
}
int testcase::breakable_commit() {
- int rc = MDBX_SUCCESS;
log_trace(">> txn_commit");
assert(txn_guard);
MDBX_txn *txn = txn_guard.release();
txn_inject_writefault(txn);
- int err = mdbx_txn_commit(txn);
- if (unlikely(err != MDBX_SUCCESS)) {
- if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
- rc = err;
- err = mdbx_txn_abort(txn);
- if (unlikely(err != MDBX_SUCCESS && err != MDBX_THREAD_MISMATCH &&
- err != MDBX_BAD_TXN))
- failure_perror("mdbx_txn_abort()", err);
- } else
- failure_perror("mdbx_txn_commit()", err);
+ int rc = mdbx_txn_commit(txn);
+ if (unlikely(rc != MDBX_SUCCESS) &&
+ (rc != MDBX_MAP_FULL || !config.params.ignore_dbfull))
+ failure_perror("mdbx_txn_commit()", rc);
+
+ if (need_speculum_assign) {
+ need_speculum_assign = false;
+ if (unlikely(rc != MDBX_SUCCESS))
+ speculum = speculum_commited;
+ else
+ speculum_commited = speculum;
}
log_trace("<< txn_commit: %s", rc ? "failed" : "Ok");
@@ -221,14 +222,17 @@ void testcase::txn_end(bool abort) {
MDBX_txn *txn = txn_guard.release();
if (abort) {
int err = mdbx_txn_abort(txn);
- if (unlikely(err != MDBX_SUCCESS && err != MDBX_THREAD_MISMATCH &&
- err != MDBX_BAD_TXN))
+ if (unlikely(err != MDBX_SUCCESS))
failure_perror("mdbx_txn_abort()", err);
+ if (need_speculum_assign)
+ speculum = speculum_commited;
} else {
txn_inject_writefault(txn);
int err = mdbx_txn_commit(txn);
if (unlikely(err != MDBX_SUCCESS))
failure_perror("mdbx_txn_commit()", err);
+ if (need_speculum_assign)
+ speculum_commited = speculum;
}
log_trace("<< txn_end(%s)", abort ? "abort" : "commit");
@@ -336,18 +340,18 @@ void testcase::report(size_t nops_done) {
return;
nops_completed += nops_done;
- log_debug("== complete +%" PRIuPTR " iteration, total %" PRIuPTR " done",
+ log_debug("== complete +%" PRIuPTR " iteration, total %" PRIu64 " done",
nops_done, nops_completed);
kick_progress(true);
if (config.signal_nops && !signalled &&
config.signal_nops <= nops_completed) {
- log_trace(">> signal(n-ops %" PRIuPTR ")", nops_completed);
+ log_trace(">> signal(n-ops %" PRIu64 ")", nops_completed);
if (!global::singlemode)
osal_broadcast(config.actor_id);
signalled = true;
- log_trace("<< signal(n-ops %" PRIuPTR ")", nops_completed);
+ log_trace("<< signal(n-ops %" PRIu64 ")", nops_completed);
}
}
@@ -491,6 +495,7 @@ void testcase::db_table_drop(MDBX_dbi handle) {
int rc = mdbx_drop(txn_guard.get(), handle, true);
if (unlikely(rc != MDBX_SUCCESS))
failure_perror("mdbx_drop(delete=true)", rc);
+ speculum.clear();
log_trace("<< testcase::db_table_drop");
} else {
log_trace("<< testcase::db_table_drop: not needed");
@@ -502,6 +507,7 @@ void testcase::db_table_clear(MDBX_dbi handle, MDBX_txn *txn) {
int rc = mdbx_drop(txn ? txn : txn_guard.get(), handle, false);
if (unlikely(rc != MDBX_SUCCESS))
failure_perror("mdbx_drop(delete=false)", rc);
+ speculum.clear();
log_trace("<< testcase::db_table_clear");
}
diff --git a/libs/libmdbx/src/test/test.h b/libs/libmdbx/src/test/test.h
index 8837cb598b..8ecf5c46fe 100644
--- a/libs/libmdbx/src/test/test.h
+++ b/libs/libmdbx/src/test/test.h
@@ -104,7 +104,9 @@ protected:
using data_view = std::string;
#endif
static inline data_view S(const MDBX_val &v) {
- return data_view(static_cast<const char *>(v.iov_base), v.iov_len);
+ return (v.iov_base && v.iov_len)
+ ? data_view(static_cast<const char *>(v.iov_base), v.iov_len)
+ : data_view();
}
static inline data_view S(const keygen::buffer &b) { return S(b->value); }
@@ -131,18 +133,22 @@ protected:
return cmp < 0;
}
};
+
+ // for simplify the set<pair<key,value>>
+ // is used instead of multimap<key,value>
using SET = std::set<Item, ItemCompare>;
const actor_config &config;
const mdbx_pid_t pid;
- MDBX_dbi dbi;
+ MDBX_dbi dbi{0};
scoped_db_guard db_guard;
scoped_txn_guard txn_guard;
scoped_cursor_guard cursor_guard;
- bool signalled;
+ bool signalled{false};
+ bool need_speculum_assign{false};
- size_t nops_completed;
+ uint64_t nops_completed{0};
chrono::time start_timestamp;
keygen::buffer key;
keygen::buffer data;
@@ -152,7 +158,7 @@ protected:
mdbx_canary canary;
} last;
- SET speculum;
+ SET speculum{ItemCompare(this)}, speculum_commited{ItemCompare(this)};
bool speculum_verify();
int insert(const keygen::buffer &akey, const keygen::buffer &adata,
unsigned flags);
@@ -211,8 +217,7 @@ protected:
public:
testcase(const actor_config &config, const mdbx_pid_t pid)
- : config(config), pid(pid), signalled(false), nops_completed(0),
- speculum(ItemCompare(this)) {
+ : config(config), pid(pid) {
start_timestamp.reset();
memset(&last, 0, sizeof(last));
}
@@ -223,20 +228,10 @@ public:
virtual ~testcase() {}
};
-class testcase_ttl : public testcase {
-public:
- testcase_ttl(const actor_config &config, const mdbx_pid_t pid)
- : testcase(config, pid) {}
- bool run() override;
-};
-
class testcase_hill : public testcase {
- using inherited = testcase;
- SET speculum_commited;
-
public:
testcase_hill(const actor_config &config, const mdbx_pid_t pid)
- : testcase(config, pid), speculum_commited(ItemCompare(this)) {}
+ : testcase(config, pid) {}
bool run() override;
};
@@ -286,11 +281,33 @@ public:
bool run() override;
};
-class testcase_nested : public testcase {
+class testcase_ttl : public testcase {
using inherited = testcase;
+
+protected:
+ struct {
+ unsigned max_window_size{0};
+ unsigned max_step_size{0};
+ } sliding;
+ unsigned edge2window(uint64_t edge);
+ unsigned edge2count(uint64_t edge);
+
+public:
+ testcase_ttl(const actor_config &config, const mdbx_pid_t pid)
+ : inherited(config, pid) {}
+ bool setup() override;
+ bool run() override;
+};
+
+class testcase_nested : public testcase_ttl {
+ using inherited = testcase_ttl;
using FIFO = std::deque<std::pair<uint64_t, unsigned>>;
- uint64_t serial;
+ uint64_t serial{0};
+ unsigned clear_wholetable_passed{0};
+ unsigned clear_stepbystep_passed{0};
+ unsigned dbfull_passed{0};
+ bool keyspace_overflow{false};
FIFO fifo;
std::stack<std::tuple<scoped_txn_guard, uint64_t, FIFO, SET>> stack;
@@ -306,7 +323,7 @@ class testcase_nested : public testcase {
public:
testcase_nested(const actor_config &config, const mdbx_pid_t pid)
- : testcase(config, pid) {}
+ : inherited(config, pid) {}
bool setup() override;
bool run() override;
bool teardown() override;
diff --git a/libs/libmdbx/src/test/ttl.cc b/libs/libmdbx/src/test/ttl.cc
index 92e99b82f6..e3927d9cd4 100644
--- a/libs/libmdbx/src/test/ttl.cc
+++ b/libs/libmdbx/src/test/ttl.cc
@@ -16,18 +16,99 @@
#include <cmath>
#include <deque>
-static unsigned edge2window(uint64_t edge, unsigned window_max) {
- const double rnd = u64_to_double1(bleach64(edge));
- const unsigned window = window_max - std::lrint(std::pow(window_max, rnd));
- return window;
-}
+/* LY: тест "эмуляцией time-to-live":
+ * - организуется "скользящее окно", которое двигается вперед вдоль
+ * числовой оси каждую транзакцию.
+ * - по переднему краю "скользящего окна" записи добавляются в таблицу,
+ * а по заднему удаляются.
+ * - количество добавляемых/удаляемых записей псевдослучайно зависит
+ * от номера транзакции, но с экспоненциальным распределением.
+ * - размер "скользящего окна" также псевдослучайно зависит от номера
+ * транзакции с "отрицательным" экспоненциальным распределением
+ * MAX_WIDTH - exp(rnd(N)), при уменьшении окна сдвигается задний
+ * край и удаляются записи позади него.
+ *
+ * Таким образом имитируется поведение таблицы с TTL: записи стохастически
+ * добавляются и удаляются, но изредка происходит массивное удаление.
+ */
-static unsigned edge2count(uint64_t edge, unsigned count_max) {
+unsigned testcase_ttl::edge2count(uint64_t edge) {
const double rnd = u64_to_double1(prng64_map1_white(edge));
- const unsigned count = std::lrint(std::pow(count_max, rnd));
+ const unsigned count = std::lrint(std::pow(sliding.max_step_size, rnd));
+ // average value: (X - 1) / ln(X), where X = sliding.max_step_size
return count;
}
+unsigned testcase_ttl::edge2window(uint64_t edge) {
+ const double rnd = u64_to_double1(bleach64(edge));
+ const unsigned size = sliding.max_window_size -
+ std::lrint(std::pow(sliding.max_window_size, rnd));
+ // average value: Y - (Y - 1) / ln(Y), where Y = sliding.max_window_size
+ return size;
+}
+
+static inline double estimate(const double x, const double y) {
+ /* среднее кол-во операций N = X' * Y', где X' и Y' средние значения
+ * размера окна и кол-ва добавляемых за один шаг записей:
+ * X' = (X - 1) / ln(X), где X = sliding.max_step_size
+ * Y' = Y - (Y - 1) / ln(Y), где Y = sliding.max_window_size */
+ return (x - 1) / std::log(x) * (y - (y - 1) / std::log(y));
+}
+
+bool testcase_ttl::setup() {
+ const unsigned window_top_lower =
+ 7 /* нижний предел для верхней границы диапазона, в котором будет
+ стохастически колебаться размер окна */
+ ;
+ const unsigned count_top_lower =
+ 7 /* нижний предел для верхней границы диапазона, в котором будет
+ стохастически колебаться кол-во записей добавляемых на одном шаге */
+ ;
+
+ /* для параметризации используем подходящие параметры,
+ * которые не имеют здесь смысла в первоначальном значении. */
+ const double ratio =
+ double(config.params.batch_read ? config.params.batch_read : 1) /
+ double(config.params.batch_write ? config.params.batch_write : 1);
+
+ /* проще найти двоичным поиском (вариация метода Ньютона) */
+ double hi = config.params.test_nops, lo = 1;
+ double x = std::sqrt(hi + lo) / ratio;
+ while (hi > lo) {
+ const double n = estimate(x, x * ratio);
+ if (n > config.params.test_nops)
+ hi = x - 1;
+ else
+ lo = x + 1;
+ x = (hi + lo) / 2;
+ }
+
+ sliding.max_step_size = std::lrint(x);
+ if (sliding.max_step_size < count_top_lower)
+ sliding.max_step_size = count_top_lower;
+ sliding.max_window_size = std::lrint(x * ratio);
+ if (sliding.max_window_size < window_top_lower)
+ sliding.max_window_size = window_top_lower;
+
+ while (estimate(sliding.max_step_size, sliding.max_window_size) >
+ config.params.test_nops * 2.0) {
+ if (ratio * sliding.max_step_size > sliding.max_window_size) {
+ if (sliding.max_step_size < count_top_lower)
+ break;
+ sliding.max_step_size = sliding.max_step_size * 7 / 8;
+ } else {
+ if (sliding.max_window_size < window_top_lower)
+ break;
+ sliding.max_window_size = sliding.max_window_size * 7 / 8;
+ }
+ }
+
+ log_verbose("come up window_max %u from `batch_read`",
+ sliding.max_window_size);
+ log_verbose("come up step_max %u from `batch_write`", sliding.max_step_size);
+ return inherited::setup();
+}
+
bool testcase_ttl::run() {
int err = db_open__begin__table_create_open_clean(dbi);
if (unlikely(err != MDBX_SUCCESS)) {
@@ -35,36 +116,6 @@ bool testcase_ttl::run() {
return false;
}
- /* LY: тест "эмуляцией time-to-live":
- * - организуется "скользящее окно", которое двигается вперед вдоль
- * числовой оси каждую транзакцию.
- * - по переднему краю "скользящего окна" записи добавляются в таблицу,
- * а по заднему удаляются.
- * - количество добавляемых/удаляемых записей псевдослучайно зависит
- * от номера транзакции, но с экспоненциальным распределением.
- * - размер "скользящего окна" также псевдослучайно зависит от номера
- * транзакции с "отрицательным" экспоненциальным распределением
- * MAX_WIDTH - exp(rnd(N)), при уменьшении окна сдвигается задний
- * край и удаляются записи позади него.
- *
- * Таким образом имитируется поведение таблицы с TTL: записи стохастически
- * добавляются и удаляются, но изредка происходят массивные удаления.
- */
-
- /* LY: для параметризации используем подходящие параметры, которые не имеют
- * здесь смысла в первоначальном значении. */
- const unsigned window_max_lower = 333;
- const unsigned count_max_lower = 333;
-
- const unsigned window_max = (config.params.batch_read > window_max_lower)
- ? config.params.batch_read
- : window_max_lower;
- const unsigned count_max = (config.params.batch_write > count_max_lower)
- ? config.params.batch_write
- : count_max_lower;
- log_verbose("ttl: using `batch_read` value %u for window_max", window_max);
- log_verbose("ttl: using `batch_write` value %u for count_max", count_max);
-
uint64_t seed =
prng64_map2_white(config.params.keygen.seed) + config.actor_id;
keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */);
@@ -77,17 +128,23 @@ bool testcase_ttl::run() {
std::deque<std::pair<uint64_t, unsigned>> fifo;
uint64_t serial = 0;
bool rc = false;
- while (should_continue()) {
+ unsigned clear_wholetable_passed = 0;
+ unsigned clear_stepbystep_passed = 0;
+ unsigned dbfull_passed = 0;
+ unsigned loops = 0;
+ bool keyspace_overflow = false;
+ while (true) {
const uint64_t salt = prng64_white(seed) /* mdbx_txn_id(txn_guard.get()) */;
const unsigned window_width =
- flipcoin_x4() ? 0 : edge2window(salt, window_max);
- unsigned head_count = edge2count(salt, count_max);
- log_debug("ttl: step #%zu (serial %" PRIu64
+ (!should_continue() || flipcoin_x4()) ? 0 : edge2window(salt);
+ unsigned head_count = edge2count(salt);
+ log_debug("ttl: step #%" PRIu64 " (serial %" PRIu64
", window %u, count %u) salt %" PRIu64,
nops_completed, serial, window_width, head_count, salt);
- if (window_width) {
+ if (window_width || flipcoin()) {
+ clear_stepbystep_passed += window_width == 0;
while (fifo.size() > window_width) {
uint64_t tail_serial = fifo.back().first;
const unsigned tail_count = fifo.back().second;
@@ -97,7 +154,7 @@ bool testcase_ttl::run() {
for (unsigned n = 0; n < tail_count; ++n) {
log_trace("ttl: remove-tail %" PRIu64, tail_serial);
generate_pair(tail_serial);
- err = mdbx_del(txn_guard.get(), dbi, &key->value, &data->value);
+ err = remove(key, data);
if (unlikely(err != MDBX_SUCCESS)) {
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
log_notice("ttl: tail-bailout due '%s'", mdbx_strerror(err));
@@ -108,11 +165,14 @@ bool testcase_ttl::run() {
if (unlikely(!keyvalue_maker.increment(tail_serial, 1)))
failure("ttl: unexpected key-space overflow on the tail");
}
+ report(tail_count);
}
} else {
log_trace("ttl: purge state");
db_table_clear(dbi);
fifo.clear();
+ clear_wholetable_passed += 1;
+ report(1);
}
err = breakable_restart();
@@ -120,38 +180,68 @@ bool testcase_ttl::run() {
log_notice("ttl: bailout at commit due '%s'", mdbx_strerror(err));
break;
}
- fifo.push_front(std::make_pair(serial, head_count));
- retry:
- for (unsigned n = 0; n < head_count; ++n) {
- log_trace("ttl: insert-head %" PRIu64, serial);
- generate_pair(serial);
- err = mdbx_put(txn_guard.get(), dbi, &key->value, &data->value,
- insert_flags);
- if (unlikely(err != MDBX_SUCCESS)) {
- if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
- log_notice("ttl: head-insert skip due '%s'", mdbx_strerror(err));
+ if (!speculum_verify()) {
+ log_notice("ttl: bailout after tail-trim");
+ return false;
+ }
+
+ if (!keyspace_overflow && (should_continue() || !clear_wholetable_passed ||
+ !clear_stepbystep_passed)) {
+ unsigned underutilization_x256 =
+ txn_underutilization_x256(txn_guard.get());
+ if (dbfull_passed > underutilization_x256) {
+ log_notice("ttl: skip head-grow to avoid one more dbfull (was %u, "
+ "underutilization %.2f%%)",
+ dbfull_passed, underutilization_x256 / 2.560);
+ continue;
+ }
+ fifo.push_front(std::make_pair(serial, head_count));
+ retry:
+ for (unsigned n = 0; n < head_count; ++n) {
+ log_trace("ttl: insert-head %" PRIu64, serial);
+ generate_pair(serial);
+ err = insert(key, data, insert_flags);
+ if (unlikely(err != MDBX_SUCCESS)) {
+ if ((err == MDBX_TXN_FULL || err == MDBX_MAP_FULL) &&
+ config.params.ignore_dbfull) {
+ log_notice("ttl: head-insert skip due '%s'", mdbx_strerror(err));
+ txn_restart(true, false);
+ serial = fifo.front().first;
+ fifo.front().second = head_count = n;
+ dbfull_passed += 1;
+ goto retry;
+ }
+ failure_perror("mdbx_put(head)", err);
+ }
+
+ if (unlikely(!keyvalue_maker.increment(serial, 1))) {
+ log_notice("ttl: unexpected key-space overflow");
+ keyspace_overflow = true;
txn_restart(true, false);
serial = fifo.front().first;
fifo.front().second = head_count = n;
goto retry;
}
- failure_perror("mdbx_put(head)", err);
}
-
- if (unlikely(!keyvalue_maker.increment(serial, 1))) {
- log_notice("ttl: unexpected key-space overflow");
- goto bailout;
+ err = breakable_restart();
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("ttl: head-commit skip due '%s'", mdbx_strerror(err));
+ serial = fifo.front().first;
+ fifo.pop_front();
}
+ if (!speculum_verify()) {
+ log_notice("ttl: bailout after head-grow");
+ return false;
+ }
+ loops += 1;
+ } else if (fifo.empty()) {
+ log_notice("ttl: done %u whole loops, %" PRIu64 " ops, %" PRIu64 " items",
+ loops, nops_completed, serial);
+ rc = true;
+ break;
+ } else {
+ log_notice("ttl: done, wait for empty, skip head-grow");
}
- err = breakable_restart();
- if (unlikely(err != MDBX_SUCCESS)) {
- log_notice("ttl: head-commit skip due '%s'", mdbx_strerror(err));
- serial = fifo.front().first;
- fifo.pop_front();
- }
-
- report(1);
- rc = true;
}
bailout:
diff --git a/libs/libmdbx/src/test/utils.cc b/libs/libmdbx/src/test/utils.cc
index 596e66e3f5..051671ff87 100644
--- a/libs/libmdbx/src/test/utils.cc
+++ b/libs/libmdbx/src/test/utils.cc
@@ -265,22 +265,24 @@ uint32_t prng32(uint64_t &state) {
}
void prng_fill(uint64_t &state, void *ptr, size_t bytes) {
+ uint32_t u32 = prng32(state);
+
while (bytes >= 4) {
- *((uint32_t *)ptr) = prng32(state);
+ memcpy(ptr, &u32, 4);
ptr = (uint32_t *)ptr + 1;
bytes -= 4;
+ u32 = prng32(state);
}
switch (bytes & 3) {
- case 3: {
- uint32_t u32 = prng32(state);
+ case 3:
memcpy(ptr, &u32, 3);
- } break;
+ break;
case 2:
- *((uint16_t *)ptr) = (uint16_t)prng32(state);
+ memcpy(ptr, &u32, 2);
break;
case 1:
- *((uint8_t *)ptr) = (uint8_t)prng32(state);
+ memcpy(ptr, &u32, 1);
break;
case 0:
break;
diff --git a/libs/libmdbx/src/test/utils.h b/libs/libmdbx/src/test/utils.h
index a5061c1444..9e6d4627aa 100644
--- a/libs/libmdbx/src/test/utils.h
+++ b/libs/libmdbx/src/test/utils.h
@@ -243,9 +243,9 @@ static __inline void cpu_relax() {
//-----------------------------------------------------------------------------
struct simple_checksum {
- uint64_t value;
+ uint64_t value{0};
- simple_checksum() : value(0) {}
+ simple_checksum() = default;
void push(const uint32_t &data) {
value += data * UINT64_C(9386433910765580089) + 1;
diff --git a/libs/libmdbx/src/test/valgrind_suppress.txt b/libs/libmdbx/src/test/valgrind_suppress.txt
index 98309ceb4f..e1e152051d 100644
--- a/libs/libmdbx/src/test/valgrind_suppress.txt
+++ b/libs/libmdbx/src/test/valgrind_suppress.txt
@@ -12,8 +12,12 @@
msync(start)
fun:msync
...
- fun:mdbx_env_sync_ex
+ fun:mdbx_env_sync_internal
}
+
+# modern Valgrind don't support the `vector[...]` pattern
+# for((i=0;i<64;++i)); do echo -e "{\n pwrite-page-flush-$i\n Memcheck:Param\n pwritev(vector[$i])\n fun:pwritev\n ...\n fun:mdbx_page_flush\n}"; done >> valgrind_suppress.txt
+
{
pwrite-page-flush
Memcheck:Param
@@ -22,3 +26,515 @@
...
fun:mdbx_page_flush
}
+{
+ pwrite-page-flush-0
+ Memcheck:Param
+ pwritev(vector[0])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-1
+ Memcheck:Param
+ pwritev(vector[1])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-2
+ Memcheck:Param
+ pwritev(vector[2])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-3
+ Memcheck:Param
+ pwritev(vector[3])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-4
+ Memcheck:Param
+ pwritev(vector[4])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-5
+ Memcheck:Param
+ pwritev(vector[5])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-6
+ Memcheck:Param
+ pwritev(vector[6])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-7
+ Memcheck:Param
+ pwritev(vector[7])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-8
+ Memcheck:Param
+ pwritev(vector[8])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-9
+ Memcheck:Param
+ pwritev(vector[9])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-10
+ Memcheck:Param
+ pwritev(vector[10])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-11
+ Memcheck:Param
+ pwritev(vector[11])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-12
+ Memcheck:Param
+ pwritev(vector[12])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-13
+ Memcheck:Param
+ pwritev(vector[13])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-14
+ Memcheck:Param
+ pwritev(vector[14])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-15
+ Memcheck:Param
+ pwritev(vector[15])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-16
+ Memcheck:Param
+ pwritev(vector[16])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-17
+ Memcheck:Param
+ pwritev(vector[17])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-18
+ Memcheck:Param
+ pwritev(vector[18])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-19
+ Memcheck:Param
+ pwritev(vector[19])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-20
+ Memcheck:Param
+ pwritev(vector[20])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-21
+ Memcheck:Param
+ pwritev(vector[21])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-22
+ Memcheck:Param
+ pwritev(vector[22])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-23
+ Memcheck:Param
+ pwritev(vector[23])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-24
+ Memcheck:Param
+ pwritev(vector[24])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-25
+ Memcheck:Param
+ pwritev(vector[25])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-26
+ Memcheck:Param
+ pwritev(vector[26])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-27
+ Memcheck:Param
+ pwritev(vector[27])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-28
+ Memcheck:Param
+ pwritev(vector[28])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-29
+ Memcheck:Param
+ pwritev(vector[29])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-30
+ Memcheck:Param
+ pwritev(vector[30])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-31
+ Memcheck:Param
+ pwritev(vector[31])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-32
+ Memcheck:Param
+ pwritev(vector[32])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-33
+ Memcheck:Param
+ pwritev(vector[33])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-34
+ Memcheck:Param
+ pwritev(vector[34])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-35
+ Memcheck:Param
+ pwritev(vector[35])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-36
+ Memcheck:Param
+ pwritev(vector[36])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-37
+ Memcheck:Param
+ pwritev(vector[37])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-38
+ Memcheck:Param
+ pwritev(vector[38])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-39
+ Memcheck:Param
+ pwritev(vector[39])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-40
+ Memcheck:Param
+ pwritev(vector[40])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-41
+ Memcheck:Param
+ pwritev(vector[41])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-42
+ Memcheck:Param
+ pwritev(vector[42])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-43
+ Memcheck:Param
+ pwritev(vector[43])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-44
+ Memcheck:Param
+ pwritev(vector[44])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-45
+ Memcheck:Param
+ pwritev(vector[45])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-46
+ Memcheck:Param
+ pwritev(vector[46])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-47
+ Memcheck:Param
+ pwritev(vector[47])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-48
+ Memcheck:Param
+ pwritev(vector[48])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-49
+ Memcheck:Param
+ pwritev(vector[49])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-50
+ Memcheck:Param
+ pwritev(vector[50])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-51
+ Memcheck:Param
+ pwritev(vector[51])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-52
+ Memcheck:Param
+ pwritev(vector[52])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-53
+ Memcheck:Param
+ pwritev(vector[53])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-54
+ Memcheck:Param
+ pwritev(vector[54])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-55
+ Memcheck:Param
+ pwritev(vector[55])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-56
+ Memcheck:Param
+ pwritev(vector[56])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-57
+ Memcheck:Param
+ pwritev(vector[57])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-58
+ Memcheck:Param
+ pwritev(vector[58])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-59
+ Memcheck:Param
+ pwritev(vector[59])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-60
+ Memcheck:Param
+ pwritev(vector[60])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-61
+ Memcheck:Param
+ pwritev(vector[61])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-62
+ Memcheck:Param
+ pwritev(vector[62])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
+{
+ pwrite-page-flush-63
+ Memcheck:Param
+ pwritev(vector[63])
+ fun:pwritev
+ ...
+ fun:mdbx_page_flush
+}
diff --git a/libs/libmdbx/src/version.c b/libs/libmdbx/src/version.c
index d6b952761e..53bb45848d 100644
--- a/libs/libmdbx/src/version.c
+++ b/libs/libmdbx/src/version.c
@@ -4,7 +4,7 @@
#include "src/internals.h"
#if MDBX_VERSION_MAJOR != 0 || \
- MDBX_VERSION_MINOR != 7
+ MDBX_VERSION_MINOR != 8
#error "API version mismatch! Had `git fetch --tags` done?"
#endif
@@ -24,11 +24,11 @@ __dll_export
#endif
const mdbx_version_info mdbx_version = {
0,
- 7,
- 0,
- 1981,
- {"2020-04-25T11:12:23+03:00", "5c78012e38f306d9601e1f43109c8aecbacb2e14", "ca8fa31c3fe1b4d92278d7a54364f6fb73dbae04",
- "v0.7.0-39-gca8fa31"},
+ 8,
+ 1,
+ 1,
+ {"2020-06-14T13:32:46+03:00", "968e4f98dd38e2a4e5cdf12c8203aa7b7abb149e", "0c5496d4d50ccedf8a182c01a76a8fbc7430d469",
+ "v0.8.1-1-g0c5496d"},
sourcery};
__dll_export