mdbx: merge branch master into stable.

This commit is contained in:
Леонид Юрьев (Leonid Yuriev) 2023-10-23 18:15:26 +03:00
commit 93429d3a23
19 changed files with 654 additions and 130 deletions

View File

@ -512,23 +512,25 @@ else()
mark_as_advanced(MDBX_USE_OFDLOCKS) mark_as_advanced(MDBX_USE_OFDLOCKS)
set(MDBX_AVOID_MSYNC_DEFAULT OFF) set(MDBX_AVOID_MSYNC_DEFAULT OFF)
endif() endif()
option(MDBX_AVOID_MSYNC "Controls dirty pages tracking, spilling and persisting in MDBX_WRITEMAP mode" ${MDBX_AVOID_MSYNC_DEFAULT}) add_mdbx_option(MDBX_AVOID_MSYNC "Controls dirty pages tracking, spilling and persisting in MDBX_WRITEMAP mode" ${MDBX_AVOID_MSYNC_DEFAULT})
add_mdbx_option(MDBX_LOCKING "Locking method (Windows=-1, SysV=5, POSIX=1988, POSIX=2001, POSIX=2008, Futexes=1995)" AUTO) add_mdbx_option(MDBX_LOCKING "Locking method (Windows=-1, SysV=5, POSIX=1988, POSIX=2001, POSIX=2008, Futexes=1995)" AUTO)
mark_as_advanced(MDBX_LOCKING) mark_as_advanced(MDBX_LOCKING)
add_mdbx_option(MDBX_TRUST_RTC "Does a system have battery-backed Real-Time Clock or just a fake" AUTO) add_mdbx_option(MDBX_TRUST_RTC "Does a system have battery-backed Real-Time Clock or just a fake" AUTO)
mark_as_advanced(MDBX_TRUST_RTC) mark_as_advanced(MDBX_TRUST_RTC)
option(MDBX_FORCE_ASSERTIONS "Force enable assertion checking" OFF) add_mdbx_option(MDBX_FORCE_ASSERTIONS "Force enable assertion checking" OFF)
option(MDBX_DISABLE_VALIDATION "Disable some checks to reduce an overhead and detection probability of database corruption to a values closer to the LMDB" OFF) add_mdbx_option(MDBX_DISABLE_VALIDATION "Disable some checks to reduce an overhead and detection probability of database corruption to a values closer to the LMDB" OFF)
option(MDBX_ENABLE_REFUND "Zerocost auto-compactification during write-transactions" ON) mark_as_advanced(MDBX_DISABLE_VALIDATION)
option(MDBX_ENABLE_MADVISE "Using POSIX' madvise() and/or similar hints" ON) add_mdbx_option(MDBX_ENABLE_REFUND "Zerocost auto-compactification during write-transactions" ON)
add_mdbx_option(MDBX_ENABLE_MADVISE "Using POSIX' madvise() and/or similar hints" ON)
if (CMAKE_TARGET_BITNESS GREATER 32) if (CMAKE_TARGET_BITNESS GREATER 32)
set(MDBX_BIGFOOT_DEFAULT ON) set(MDBX_BIGFOOT_DEFAULT ON)
else() else()
set(MDBX_BIGFOOT_DEFAULT OFF) set(MDBX_BIGFOOT_DEFAULT OFF)
endif() endif()
option(MDBX_ENABLE_BIGFOOT "Chunking long list of retired pages during huge transactions commit to avoid use sequences of pages" ${MDBX_BIGFOOT_DEFAULT}) add_mdbx_option(MDBX_ENABLE_BIGFOOT "Chunking long list of retired pages during huge transactions commit to avoid use sequences of pages" ${MDBX_BIGFOOT_DEFAULT})
option(MDBX_ENABLE_PGOP_STAT "Gathering statistics for page operations" ON) add_mdbx_option(MDBX_ENABLE_PGOP_STAT "Gathering statistics for page operations" ON)
option(MDBX_ENABLE_PROFGC "Profiling of GC search and updates" OFF) add_mdbx_option(MDBX_ENABLE_PROFGC "Profiling of GC search and updates" OFF)
mark_as_advanced(MDBX_ENABLE_PROFGC)
if(NOT MDBX_AMALGAMATED_SOURCE) if(NOT MDBX_AMALGAMATED_SOURCE)
if(CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE_UPPERCASE STREQUAL "DEBUG") if(CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE_UPPERCASE STREQUAL "DEBUG")

View File

@ -1,10 +1,87 @@
ChangeLog ChangeLog
--------- =========
English version [by Google](https://gitflic-ru.translate.goog/project/erthink/libmdbx/blob?file=ChangeLog.md&_x_tr_sl=ru&_x_tr_tl=en) English version [by Google](https://gitflic-ru.translate.goog/project/erthink/libmdbx/blob?file=ChangeLog.md&_x_tr_sl=ru&_x_tr_tl=en)
and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md). and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md).
## v0.12.8 (сопровождение и подготовка к релизу)
Поддержка стабильной ветки.
Мелочи:
- Удаление устаревших `mdbx_set_compare()` и `mdbx_set_dupsort()`.
- Корректировка определения `MDBX_LAST_ADDED_ERRCODE`.
- Добавление в C++ API забытого исключения `mdbx::duplicated_lck_file`.
- Обновление патча для старых версий buildroot.
--------------------------------------------------------------------------------
## v0.12.8 "Владимир Уткин" от 2023-10-17
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов,
в день 100-летия со дня рождения выдающегося советского и российского ученого и конструктора [Влади́мира Фёдоровича У́ткина](https://ru.wikipedia.org/wiki/Уткин,_Владимир_Фёдорович).
```
git diff' stat: 24 commits, 18 files changed, 624 insertions(+), 94 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
```
Благодарности:
- [Alain Picard](https://github.com/castortech) за сообщение о проблеме
с обработкой `MDBX_MULTIPLE` и помощь в тестировании.
Исправления и доработки:
- Устранение регресса/ошибки в пути обработки `put(MDBX_MULTIPLE)` при пакетном/оптовом
помещении в БД множественных значений одного ключа (aka multi-value или dupsort).
Проявление проблемы зависит от компилятора и опций оптимизации/кодогенерации, но с большой вероятностью возвращется
ошибка `MDBX_BAD_VALSIZE` (`-30781`), а в отладочных сборках срабатывает проверка `cASSERT(mc, !"Invalid key-size")`.
Сценарии приводящие к другим проявлениям на данный момент не известны.
- Реализована перезапись в `mdbx_put(MDBX_CURRENT)` всех текущих мульти-значений ключа
при отсутствии флага `MDBX_NOOVERWRITE`. Ранее в такой ситуации возвращалась ошибка `MDBX_EMULTIVAL`.
В текущем понимании новое поведение более удобно и не создаёт проблем совместимости с ранее написанным кодом.
- Добавлена возможность использовать `mdbx_cursor_get(MDBX_GET_MULTIPLE)` без предварительной установки
курсора, совмещая операцию пакетного получения данных с позиционированием курсора на передаваемый ключ.
- Микрооптимизация и рефакторинг `cursor_put_nochecklen()` в продолжение исправления
регресса/ошибки в пути обработки `put(MDBX_MULTIPLE)`.
- Уточнение формулировок в описании API, в том числе пояснений о `SIGSEGV`
и недопустимости прямого изменения данных.
Мелочи:
- Исправление несущественных предупреждений при `MDBX_ENABLE_PROFGC=ON`.
- Добавление `slice::as_pod<typename>()` в C++ API.
- Добавление перегрузки `txn::put_multiple()` и контроля POD в C++ API.
- Добавление smoke-теста для `put(MDBX_MULTIPLE)`.
- Добавление дополнительных smoke-тестов в область видимости ctest.
- Устранение жалоб Valgrind на инвариантное чтение неинициализированной памяти
и утечки памяти в одном из тестов.
- Костыль для глушения/игнорирования `EDEADLK` в ряде сценариев при
использовании Valgrind или ASAN. В частности, это устраняет
ложно-негативный результат проверки БД посредством `mdbx_chk -wc`,
т.е. проверку БД в кооперативном (не эксклюзивном) режиме чтения-записи
в сборках с поддержкой Valgrind или включеным ASAN. Для более подробной
информации см. [соответствующий коммит](https://gitflic.ru/project/erthink/libmdbx/commit/1aead6869a7eff1a85e400ab3eeecb4c8b904fe6).
- Доработка `mdbx_dump_val()` используемой для логирования и отладки.
- Устранение предупреждений Valgrind при логировании в отладочных сборках.
- Доработка использования `filesystem` для старых компиляторов.
- Сокращение излишнего вызова `osal_thread_self()`.
- Вывод информации о большинстве mdbx-опций при сборке посредством CMake.
- Добавление определений макросов для Doxygen.
--------------------------------------------------------------------------------
## v0.12.7 "Артек" от 2023-06-16 ## v0.12.7 "Артек" от 2023-06-16
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением
@ -317,7 +394,7 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
- Добавлен явный выбор `tls_model("local-dynamic")` для обхода проблемы - Добавлен явный выбор `tls_model("local-dynamic")` для обхода проблемы
`relocation R_X86_64_TPOFF32 against FOO cannot be used with -shared` `relocation R_X86_64_TPOFF32 against FOO cannot be used with -shared`
из-за ошибки в CLANG приводящей к использованию неверного режима `ls_model`. из-за ошибки в CLANG приводящей к использованию неверного режима `tls_model`.
- Изменение тактики слияния страниц при удалении. - Изменение тактики слияния страниц при удалении.
Теперь слияние выполняется преимущественно с уже измененной/грязной страницей. Теперь слияние выполняется преимущественно с уже измененной/грязной страницей.

View File

@ -2224,14 +2224,24 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator. # recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED = DOXYGEN MDBX_DECLARE_EXCEPTION \
MDBX_NOTHROW_PURE_FUNCTION MDBX_PURE_FUNCTION \ PREDEFINED = DOXYGEN \
MDBX_NOTHROW_CONST_FUNCTION \ MDBX_CXX20_CONCEPT(CONCEPT,NAME)="CONCEPT NAME" \
MDBX_CXX01_CONSTEXPR MDBX_CXX01_CONSTEXPR_VAR \ MDBX_STD_FILESYSTEM_PATH=::mdbx::filesystem::path \
MDBX_CXX11_CONSTEXPR MDBX_CXX11_CONSTEXPR_VAR \ MDBX_U128_TYPE=uint128_t MDBX_I128_TYPE=int128_t \
MDBX_CXX14_CONSTEXPR MDBX_CXX14_CONSTEXPR_VAR \ MDBX_DECLARE_EXCEPTION(NAME)="struct LIBMDBX_API_TYPE NAME : public exception{NAME(const ::mdbx::error &); virtual ~NAME() noexcept; }" \
MDBX_CXX17_CONSTEXPR MDBX_CXX20_CONSTEXPR \ MDBX_PURE_FUNCTION=[[gnu::pure]] \
MDBX_CXX17_NOEXCEPT MDBX_NOTHROW_PURE_FUNCTION="[[gnu::pure, gnu::nothrow]]" \
MDBX_CONST_FUNCTION=[[gnu::const]] \
MDBX_NOTHROW_CONST_FUNCTION="[[gnu::const, gnu::nothrow]]" \
MDBX_CXX01_CONSTEXPR=constexpr MDBX_CXX01_CONSTEXPR_VAR=constexpr \
MDBX_CXX11_CONSTEXPR=constexpr MDBX_CXX11_CONSTEXPR_VAR=constexpr \
MDBX_CXX14_CONSTEXPR=constexpr MDBX_CXX14_CONSTEXPR_VAR=constexpr \
MDBX_CXX17_CONSTEXPR=constexpr MDBX_CXX20_CONSTEXPR=constexpr \
MDBX_CXX17_NOEXCEPT=noexcept MDBX_IF_CONSTEXPR=constexpr \
MDBX_CXX20_LIKELY=[[likely]] MDBX_CXX20_UNLIKELY=[[unlikely]] \
MDBX_MAYBE_UNUSED=[[maybe_unused]] \
MDBX_DEPRECATED=[[deprecated]]
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The # tag can be used to specify a list of macro names that should be expanded. The

88
mdbx.h
View File

@ -1876,7 +1876,8 @@ enum MDBX_error_t {
MDBX_BAD_RSLOT = -30783, MDBX_BAD_RSLOT = -30783,
/** Transaction is not valid for requested operation, /** Transaction is not valid for requested operation,
* e.g. had errored and be must aborted, has a child, or is invalid */ * e.g. had errored and be must aborted, has a child/nested transaction,
* or is invalid */
MDBX_BAD_TXN = -30782, MDBX_BAD_TXN = -30782,
/** Invalid size or alignment of key or data for target database, /** Invalid size or alignment of key or data for target database,
@ -1936,7 +1937,7 @@ enum MDBX_error_t {
MDBX_DUPLICATED_CLK = -30413, MDBX_DUPLICATED_CLK = -30413,
/* The last of MDBX-added error codes */ /* The last of MDBX-added error codes */
MDBX_LAST_ADDED_ERRCODE = MDBX_TXN_OVERLAPPING, MDBX_LAST_ADDED_ERRCODE = MDBX_DUPLICATED_CLK,
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
MDBX_ENODATA = ERROR_HANDLE_EOF, MDBX_ENODATA = ERROR_HANDLE_EOF,
@ -2699,11 +2700,12 @@ MDBX_DEPRECATED LIBMDBX_INLINE_API(int, mdbx_env_info,
* success. The \ref MDBX_RESULT_TRUE means no data pending for flush * success. The \ref MDBX_RESULT_TRUE means no data pending for flush
* to disk, and 0 otherwise. Some possible errors are: * to disk, and 0 otherwise. Some possible errors are:
* *
* \retval MDBX_EACCES the environment is read-only. * \retval MDBX_EACCES The environment is read-only.
* \retval MDBX_BUSY the environment is used by other thread * \retval MDBX_BUSY The environment is used by other thread
* and `nonblock=true`. * and `nonblock=true`.
* \retval MDBX_EINVAL an invalid parameter was specified. * \retval MDBX_EINVAL An invalid parameter was specified.
* \retval MDBX_EIO an error occurred during synchronization. */ * \retval MDBX_EIO An error occurred during the flushing/writing data
* to a storage medium/disk. */
LIBMDBX_API int mdbx_env_sync_ex(MDBX_env *env, bool force, bool nonblock); LIBMDBX_API int mdbx_env_sync_ex(MDBX_env *env, bool force, bool nonblock);
/** \brief The shortcut to calling \ref mdbx_env_sync_ex() with /** \brief The shortcut to calling \ref mdbx_env_sync_ex() with
@ -2846,9 +2848,9 @@ LIBMDBX_INLINE_API(int, mdbx_env_get_syncperiod,
* *
* Only a single thread may call this function. All transactions, databases, * Only a single thread may call this function. All transactions, databases,
* and cursors must already be closed before calling this function. Attempts * and cursors must already be closed before calling this function. Attempts
* to use any such handles after calling this function will cause a `SIGSEGV`. * to use any such handles after calling this function is UB and would cause
* The environment handle will be freed and must not be used again after this * a `SIGSEGV`. The environment handle will be freed and must not be used again
* call. * after this call.
* *
* \param [in] env An environment handle returned by * \param [in] env An environment handle returned by
* \ref mdbx_env_create(). * \ref mdbx_env_create().
@ -2878,7 +2880,8 @@ LIBMDBX_INLINE_API(int, mdbx_env_get_syncperiod,
* is expected, i.e. \ref MDBX_env instance was freed in * is expected, i.e. \ref MDBX_env instance was freed in
* proper manner. * proper manner.
* *
* \retval MDBX_EIO An error occurred during synchronization. */ * \retval MDBX_EIO An error occurred during the flushing/writing data
* to a storage medium/disk. */
LIBMDBX_API int mdbx_env_close_ex(MDBX_env *env, bool dont_sync); LIBMDBX_API int mdbx_env_close_ex(MDBX_env *env, bool dont_sync);
/** \brief The shortcut to calling \ref mdbx_env_close_ex() with /** \brief The shortcut to calling \ref mdbx_env_close_ex() with
@ -3918,7 +3921,8 @@ LIBMDBX_API int mdbx_txn_commit_ex(MDBX_txn *txn, MDBX_commit_latency *latency);
* by current thread. * by current thread.
* \retval MDBX_EINVAL Transaction handle is NULL. * \retval MDBX_EINVAL Transaction handle is NULL.
* \retval MDBX_ENOSPC No more disk space. * \retval MDBX_ENOSPC No more disk space.
* \retval MDBX_EIO A system-level I/O error occurred. * \retval MDBX_EIO An error occurred during the flushing/writing
* data to a storage medium/disk.
* \retval MDBX_ENOMEM Out of memory. */ * \retval MDBX_ENOMEM Out of memory. */
LIBMDBX_INLINE_API(int, mdbx_txn_commit, (MDBX_txn * txn)) { LIBMDBX_INLINE_API(int, mdbx_txn_commit, (MDBX_txn * txn)) {
return mdbx_txn_commit_ex(txn, NULL); return mdbx_txn_commit_ex(txn, NULL);
@ -4084,9 +4088,9 @@ LIBMDBX_API int mdbx_canary_get(const MDBX_txn *txn, MDBX_canary *canary);
* \see mdbx_get_datacmp \see mdbx_dcmp() * \see mdbx_get_datacmp \see mdbx_dcmp()
* *
* \anchor avoid_custom_comparators * \anchor avoid_custom_comparators
* It is recommend not using custom comparison functions, but instead * \deprecated It is recommend not using custom comparison functions, but
* converting the keys to one of the forms that are suitable for built-in * instead converting the keys to one of the forms that are suitable for
* comparators (for instance take look to the \ref value2key). * built-in comparators (for instance take look to the \ref value2key).
* The reasons to not using custom comparators are: * The reasons to not using custom comparators are:
* - The order of records could not be validated without your code. * - The order of records could not be validated without your code.
* So `mdbx_chk` utility will reports "wrong order" errors * So `mdbx_chk` utility will reports "wrong order" errors
@ -4316,7 +4320,7 @@ LIBMDBX_API int mdbx_dbi_dupsort_depthmask(MDBX_txn *txn, MDBX_dbi dbi,
enum MDBX_dbi_state_t { enum MDBX_dbi_state_t {
/** DB was written in this txn */ /** DB was written in this txn */
MDBX_DBI_DIRTY = 0x01, MDBX_DBI_DIRTY = 0x01,
/** Named-DB record is older than txnID */ /** Cached Named-DB record is older than txnID */
MDBX_DBI_STALE = 0x02, MDBX_DBI_STALE = 0x02,
/** Named-DB handle opened in this txn */ /** Named-DB handle opened in this txn */
MDBX_DBI_FRESH = 0x04, MDBX_DBI_FRESH = 0x04,
@ -4398,9 +4402,14 @@ LIBMDBX_API int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, bool del);
* items requires the use of \ref mdbx_cursor_get(). * items requires the use of \ref mdbx_cursor_get().
* *
* \note The memory pointed to by the returned values is owned by the * \note The memory pointed to by the returned values is owned by the
* database. The caller need not dispose of the memory, and may not * database. The caller MUST not dispose of the memory, and MUST not modify it
* modify it in any way. For values returned in a read-only transaction * in any way regardless in a read-only nor read-write transactions!
* any modification attempts will cause a `SIGSEGV`. * For case a database opened without the \ref MDBX_WRITEMAP modification
* attempts likely will cause a `SIGSEGV`. However, when a database opened with
* the \ref MDBX_WRITEMAP or in case values returned inside read-write
* transaction are located on a "dirty" (modified and pending to commit) pages,
* such modification will silently accepted and likely will lead to DB and/or
* data corruption.
* *
* \note Values returned from the database are valid only until a * \note Values returned from the database are valid only until a
* subsequent update operation, or the end of the transaction. * subsequent update operation, or the end of the transaction.
@ -4650,7 +4659,7 @@ LIBMDBX_API int mdbx_replace_ex(MDBX_txn *txn, MDBX_dbi dbi,
LIBMDBX_API int mdbx_del(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, LIBMDBX_API int mdbx_del(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
const MDBX_val *data); const MDBX_val *data);
/** \brief Create a cursor handle but not bind it to transaction nor DBI handle. /** \brief Create a cursor handle but not bind it to transaction nor DBI-handle.
* \ingroup c_cursors * \ingroup c_cursors
* *
* A cursor cannot be used when its database handle is closed. Nor when its * A cursor cannot be used when its database handle is closed. Nor when its
@ -4674,7 +4683,7 @@ LIBMDBX_API int mdbx_del(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
* \returns Created cursor handle or NULL in case out of memory. */ * \returns Created cursor handle or NULL in case out of memory. */
LIBMDBX_API MDBX_cursor *mdbx_cursor_create(void *context); LIBMDBX_API MDBX_cursor *mdbx_cursor_create(void *context);
/** \brief Set application information associated with the \ref MDBX_cursor. /** \brief Set application information associated with the cursor.
* \ingroup c_cursors * \ingroup c_cursors
* \see mdbx_cursor_get_userctx() * \see mdbx_cursor_get_userctx()
* *
@ -4697,11 +4706,11 @@ LIBMDBX_API int mdbx_cursor_set_userctx(MDBX_cursor *cursor, void *ctx);
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API void * MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API void *
mdbx_cursor_get_userctx(const MDBX_cursor *cursor); mdbx_cursor_get_userctx(const MDBX_cursor *cursor);
/** \brief Bind cursor to specified transaction and DBI handle. /** \brief Bind cursor to specified transaction and DBI-handle.
* \ingroup c_cursors * \ingroup c_cursors
* *
* Using of the `mdbx_cursor_bind()` is equivalent to calling * Using of the `mdbx_cursor_bind()` is equivalent to calling
* \ref mdbx_cursor_renew() but with specifying an arbitrary dbi handle. * \ref mdbx_cursor_renew() but with specifying an arbitrary DBI-handle.
* *
* A cursor may be associated with a new transaction, and referencing a new or * A cursor may be associated with a new transaction, and referencing a new or
* the same database handle as it was created with. This may be done whether the * the same database handle as it was created with. This may be done whether the
@ -4715,7 +4724,7 @@ mdbx_cursor_get_userctx(const MDBX_cursor *cursor);
* *
* \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin().
* \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). * \param [in] dbi A database handle returned by \ref mdbx_dbi_open().
* \param [out] cursor A cursor handle returned by \ref mdbx_cursor_create(). * \param [in] cursor A cursor handle returned by \ref mdbx_cursor_create().
* *
* \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: * some possible errors are:
@ -4774,15 +4783,14 @@ LIBMDBX_API int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi,
* or \ref mdbx_cursor_create(). */ * or \ref mdbx_cursor_create(). */
LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor); LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor);
/** \brief Renew a cursor handle. /** \brief Renew a cursor handle for use within the given transaction.
* \ingroup c_cursors * \ingroup c_cursors
* *
* The cursor may be associated with a new transaction, and referencing a new or * A cursor may be associated with a new transaction whether the previous
* the same database handle as it was created with. This may be done whether the * transaction is running or finished.
* previous transaction is live or dead.
* *
* Using of the `mdbx_cursor_renew()` is equivalent to calling * Using of the `mdbx_cursor_renew()` is equivalent to calling
* \ref mdbx_cursor_bind() with the DBI handle that previously * \ref mdbx_cursor_bind() with the DBI-handle that previously
* the cursor was used with. * the cursor was used with.
* *
* \note In contrast to LMDB, the MDBX allow any cursor to be re-used by using * \note In contrast to LMDB, the MDBX allow any cursor to be re-used by using
@ -4796,7 +4804,9 @@ LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor);
* some possible errors are: * some possible errors are:
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned * \retval MDBX_THREAD_MISMATCH Given transaction is not owned
* by current thread. * by current thread.
* \retval MDBX_EINVAL An invalid parameter was specified. */ * \retval MDBX_EINVAL An invalid parameter was specified.
* \retval MDBX_BAD_DBI The cursor was not bound to a DBI-handle
* or such a handle became invalid. */
LIBMDBX_API int mdbx_cursor_renew(MDBX_txn *txn, MDBX_cursor *cursor); LIBMDBX_API int mdbx_cursor_renew(MDBX_txn *txn, MDBX_cursor *cursor);
/** \brief Return the cursor's transaction handle. /** \brief Return the cursor's transaction handle.
@ -4834,6 +4844,16 @@ LIBMDBX_API int mdbx_cursor_copy(const MDBX_cursor *src, MDBX_cursor *dest);
* to which data refers. * to which data refers.
* \see mdbx_get() * \see mdbx_get()
* *
* \note The memory pointed to by the returned values is owned by the
* database. The caller MUST not dispose of the memory, and MUST not modify it
* in any way regardless in a read-only nor read-write transactions!
* For case a database opened without the \ref MDBX_WRITEMAP modification
* attempts likely will cause a `SIGSEGV`. However, when a database opened with
* the \ref MDBX_WRITEMAP or in case values returned inside read-write
* transaction are located on a "dirty" (modified and pending to commit) pages,
* such modification will silently accepted and likely will lead to DB and/or
* data corruption.
*
* \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open(). * \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open().
* \param [in,out] key The key for a retrieved item. * \param [in,out] key The key for a retrieved item.
* \param [in,out] data The data of a retrieved item. * \param [in,out] data The data of a retrieved item.
@ -4860,6 +4880,16 @@ LIBMDBX_API int mdbx_cursor_get(MDBX_cursor *cursor, MDBX_val *key,
* array to which `pairs` refers. * array to which `pairs` refers.
* \see mdbx_cursor_get() * \see mdbx_cursor_get()
* *
* \note The memory pointed to by the returned values is owned by the
* database. The caller MUST not dispose of the memory, and MUST not modify it
* in any way regardless in a read-only nor read-write transactions!
* For case a database opened without the \ref MDBX_WRITEMAP modification
* attempts likely will cause a `SIGSEGV`. However, when a database opened with
* the \ref MDBX_WRITEMAP or in case values returned inside read-write
* transaction are located on a "dirty" (modified and pending to commit) pages,
* such modification will silently accepted and likely will lead to DB and/or
* data corruption.
*
* \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open(). * \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open().
* \param [out] count The number of key and value item returned, on success * \param [out] count The number of key and value item returned, on success
* it always be the even because the key-value * it always be the even because the key-value

View File

@ -80,7 +80,8 @@
#if defined(__cpp_lib_filesystem) && __cpp_lib_filesystem >= 201703L #if defined(__cpp_lib_filesystem) && __cpp_lib_filesystem >= 201703L
#include <filesystem> #include <filesystem>
#elif __has_include(<experimental/filesystem>) #elif defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L && \
__has_include(<experimental/filesystem>)
#include <experimental/filesystem> #include <experimental/filesystem>
#endif #endif
@ -368,6 +369,7 @@ using string = ::std::basic_string<char, ::std::char_traits<char>, ALLOCATOR>;
using filehandle = ::mdbx_filehandle_t; using filehandle = ::mdbx_filehandle_t;
#if defined(DOXYGEN) || \ #if defined(DOXYGEN) || \
(defined(__cpp_lib_filesystem) && __cpp_lib_filesystem >= 201703L && \ (defined(__cpp_lib_filesystem) && __cpp_lib_filesystem >= 201703L && \
defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L && \
(!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || \ (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || \
__MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) && \ __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) && \
(!defined(__IPHONE_OS_VERSION_MIN_REQUIRED) || \ (!defined(__IPHONE_OS_VERSION_MIN_REQUIRED) || \
@ -394,6 +396,16 @@ using path = ::std::wstring;
using path = ::std::string; using path = ::std::string;
#endif /* mdbx::path */ #endif /* mdbx::path */
#if defined(__SIZEOF_INT128__) || \
(defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128)
#ifndef MDBX_U128_TYPE
#define MDBX_U128_TYPE __uint128_t
#endif /* MDBX_U128_TYPE */
#ifndef MDBX_I128_TYPE
#define MDBX_I128_TYPE __int128_t
#endif /* MDBX_I128_TYPE */
#endif /* __SIZEOF_INT128__ || _INTEGRAL_MAX_BITS >= 128 */
#if __cplusplus >= 201103L || defined(DOXYGEN) #if __cplusplus >= 201103L || defined(DOXYGEN)
/// \brief Duration in 1/65536 units of second. /// \brief Duration in 1/65536 units of second.
using duration = ::std::chrono::duration<unsigned, ::std::ratio<1, 65536>>; using duration = ::std::chrono::duration<unsigned, ::std::ratio<1, 65536>>;
@ -546,12 +558,14 @@ MDBX_DECLARE_EXCEPTION(something_busy);
MDBX_DECLARE_EXCEPTION(thread_mismatch); MDBX_DECLARE_EXCEPTION(thread_mismatch);
MDBX_DECLARE_EXCEPTION(transaction_full); MDBX_DECLARE_EXCEPTION(transaction_full);
MDBX_DECLARE_EXCEPTION(transaction_overlapping); MDBX_DECLARE_EXCEPTION(transaction_overlapping);
MDBX_DECLARE_EXCEPTION(duplicated_lck_file);
#undef MDBX_DECLARE_EXCEPTION #undef MDBX_DECLARE_EXCEPTION
[[noreturn]] LIBMDBX_API void throw_too_small_target_buffer(); [[noreturn]] LIBMDBX_API void throw_too_small_target_buffer();
[[noreturn]] LIBMDBX_API void throw_max_length_exceeded(); [[noreturn]] LIBMDBX_API void throw_max_length_exceeded();
[[noreturn]] LIBMDBX_API void throw_out_range(); [[noreturn]] LIBMDBX_API void throw_out_range();
[[noreturn]] LIBMDBX_API void throw_allocators_mismatch(); [[noreturn]] LIBMDBX_API void throw_allocators_mismatch();
[[noreturn]] LIBMDBX_API void throw_bad_value_size();
static MDBX_CXX14_CONSTEXPR size_t check_length(size_t bytes); static MDBX_CXX14_CONSTEXPR size_t check_length(size_t bytes);
static MDBX_CXX14_CONSTEXPR size_t check_length(size_t headroom, static MDBX_CXX14_CONSTEXPR size_t check_length(size_t headroom,
size_t payload); size_t payload);
@ -1029,6 +1043,35 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val {
return slice(size_t(-1)); return slice(size_t(-1));
} }
template <typename POD> MDBX_CXX14_CONSTEXPR POD as_pod() const {
static_assert(::std::is_standard_layout<POD>::value &&
!::std::is_pointer<POD>::value,
"Must be a standard layout type!");
if (MDBX_LIKELY(size() == sizeof(POD)))
MDBX_CXX20_LIKELY {
POD r;
memcpy(&r, data(), sizeof(r));
return r;
}
throw_bad_value_size();
}
#ifdef MDBX_U128_TYPE
MDBX_U128_TYPE as_uint128() const;
#endif /* MDBX_U128_TYPE */
uint64_t as_uint64() const;
uint32_t as_uint32() const;
uint16_t as_uint16() const;
uint8_t as_uint8() const;
#ifdef MDBX_I128_TYPE
MDBX_I128_TYPE as_int128() const;
#endif /* MDBX_I128_TYPE */
int64_t as_int64() const;
int32_t as_int32() const;
int16_t as_int16() const;
int8_t as_int8() const;
protected: protected:
MDBX_CXX11_CONSTEXPR slice(size_t invalid_length) noexcept MDBX_CXX11_CONSTEXPR slice(size_t invalid_length) noexcept
: ::MDBX_val({nullptr, invalid_length}) {} : ::MDBX_val({nullptr, invalid_length}) {}
@ -2292,6 +2335,10 @@ public:
return buffer(::mdbx::slice::wrap(pod), make_reference, allocator); return buffer(::mdbx::slice::wrap(pod), make_reference, allocator);
} }
template <typename POD> MDBX_CXX14_CONSTEXPR POD as_pod() const {
return slice_.as_pod<POD>();
}
/// \brief Reserves storage space. /// \brief Reserves storage space.
void reserve(size_t wanna_headroom, size_t wanna_tailroom) { void reserve(size_t wanna_headroom, size_t wanna_tailroom) {
wanna_headroom = ::std::min(::std::max(headroom(), wanna_headroom), wanna_headroom = ::std::min(::std::max(headroom(), wanna_headroom),
@ -3975,10 +4022,20 @@ public:
size_t values_count, put_mode mode, size_t values_count, put_mode mode,
bool allow_partial = false); bool allow_partial = false);
template <typename VALUE> template <typename VALUE>
size_t put_multiple(map_handle map, const slice &key,
const VALUE *values_array, size_t values_count,
put_mode mode, bool allow_partial = false) {
static_assert(::std::is_standard_layout<VALUE>::value &&
!::std::is_pointer<VALUE>::value &&
!::std::is_array<VALUE>::value,
"Must be a standard layout type!");
return put_multiple(map, key, sizeof(VALUE), values_array, values_count,
mode, allow_partial);
}
template <typename VALUE>
void put_multiple(map_handle map, const slice &key, void put_multiple(map_handle map, const slice &key,
const ::std::vector<VALUE> &vector, put_mode mode) { const ::std::vector<VALUE> &vector, put_mode mode) {
put_multiple(map, key, sizeof(VALUE), vector.data(), vector.size(), mode, put_multiple(map, key, vector.data(), vector.size(), mode);
false);
} }
inline ptrdiff_t estimate(map_handle map, pair from, pair to) const; inline ptrdiff_t estimate(map_handle map, pair from, pair to) const;

View File

@ -1,7 +1,7 @@
From 790cbdc02c2597650964a564e05fbb5af503adc9 Mon Sep 17 00:00:00 2001 From 3efdf07a80f750c23de126ac80e78fb0545a1b63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=9B=D0=B5=D0=BE=D0=BD=D0=B8=D0=B4=20=D0=AE=D1=80=D1=8C?= From: =?UTF-8?q?=D0=9B=D0=B5=D0=BE=D0=BD=D0=B8=D0=B4=20=D0=AE=D1=80=D1=8C?=
=?UTF-8?q?=D0=B5=D0=B2=20=28Leonid=20Yuriev=29?= <leo@yuriev.ru> =?UTF-8?q?=D0=B5=D0=B2=20=28Leonid=20Yuriev=29?= <leo@yuriev.ru>
Date: Wed, 19 Apr 2023 13:34:37 +0300 Date: Mon, 23 Oct 2023 18:07:13 +0300
Subject: [PATCH] package/libmdbx: new package (library/database). Subject: [PATCH] package/libmdbx: new package (library/database).
MIME-Version: 1.0 MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8 Content-Type: text/plain; charset=UTF-8
@ -15,8 +15,9 @@ This patch adds libmdbx:
in terms of reliability, features and performance. in terms of reliability, features and performance.
- https://gitflic.ru/project/erthink/libmdbx - https://gitflic.ru/project/erthink/libmdbx
The v0.12.5 "Dynamo" is stable release of frontward _libmdbx_ branch with new superior features The v0.12.8 "Vladimir Utkin" is stable release of frontward _libmdbx_
on the day of 100 anniversary of USSR' «Dynamo» sports and fitness society. branch with new superior features on the day of 100 anniversary of the birth
of the outstanding Soviet and Russian scientist and engineer Vladimir Fedorovich Utkin.
The complete ChangeLog: https://gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md The complete ChangeLog: https://gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md
@ -111,18 +112,18 @@ index 0000000000..a9a4ac45c5
+ !BR2_TOOLCHAIN_GCC_AT_LEAST_4_4 + !BR2_TOOLCHAIN_GCC_AT_LEAST_4_4
diff --git a/package/libmdbx/libmdbx.hash b/package/libmdbx/libmdbx.hash diff --git a/package/libmdbx/libmdbx.hash b/package/libmdbx/libmdbx.hash
new file mode 100644 new file mode 100644
index 0000000000..7a5b19952e index 0000000000..82cf28d6a9
--- /dev/null --- /dev/null
+++ b/package/libmdbx/libmdbx.hash +++ b/package/libmdbx/libmdbx.hash
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
+# Hashes from: https://libmdbx.dqdkfa.ru/release/SHA256SUMS +# Hashes from: https://libmdbx.dqdkfa.ru/release/SHA256SUMS
+sha256 9c3abaaf9079a9518bb7155734817a2e286fffea46f7cc0825dfbd1cf9174075 libmdbx-amalgamated-0.12.5.tar.xz +sha256 c78c56c53708bbfc519bf53ebf520d1f09d30ee6427a4bedf713316696e671d0 libmdbx-amalgamated-0.12.8.tar.xz
+ +
+# Locally calculated +# Locally calculated
+sha256 310fe25c858a9515fc8c8d7d1f24a67c9496f84a91e0a0e41ea9975b1371e569 LICENSE +sha256 310fe25c858a9515fc8c8d7d1f24a67c9496f84a91e0a0e41ea9975b1371e569 LICENSE
diff --git a/package/libmdbx/libmdbx.mk b/package/libmdbx/libmdbx.mk diff --git a/package/libmdbx/libmdbx.mk b/package/libmdbx/libmdbx.mk
new file mode 100644 new file mode 100644
index 0000000000..9a196eda60 index 0000000000..d198fe5b22
--- /dev/null --- /dev/null
+++ b/package/libmdbx/libmdbx.mk +++ b/package/libmdbx/libmdbx.mk
@@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
@ -132,7 +133,7 @@ index 0000000000..9a196eda60
+# +#
+################################################################################ +################################################################################
+ +
+LIBMDBX_VERSION = 0.12.5 +LIBMDBX_VERSION = 0.12.8
+LIBMDBX_SOURCE = libmdbx-amalgamated-$(LIBMDBX_VERSION).tar.xz +LIBMDBX_SOURCE = libmdbx-amalgamated-$(LIBMDBX_VERSION).tar.xz
+LIBMDBX_SITE = https://libmdbx.dqdkfa.ru/release +LIBMDBX_SITE = https://libmdbx.dqdkfa.ru/release
+LIBMDBX_SUPPORTS_IN_SOURCE_BUILD = NO +LIBMDBX_SUPPORTS_IN_SOURCE_BUILD = NO
@ -169,5 +170,5 @@ index 0000000000..9a196eda60
+ +
+$(eval $(cmake-package)) +$(eval $(cmake-package))
-- --
2.34.1 2.42.0

View File

@ -3634,18 +3634,15 @@ const char *mdbx_dump_val(const MDBX_val *key, char *const buf,
char *const detent = buf + bufsize - 2; char *const detent = buf + bufsize - 2;
char *ptr = buf; char *ptr = buf;
*ptr++ = '<'; *ptr++ = '<';
for (size_t i = 0; i < key->iov_len; i++) { for (size_t i = 0; i < key->iov_len && ptr < detent; i++) {
const ptrdiff_t left = detent - ptr; const char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
assert(left > 0); '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
int len = snprintf(ptr, left, "%02x", data[i]); *ptr++ = hex[data[i] >> 4];
if (len < 0 || len >= left) *ptr++ = hex[data[i] & 15];
break;
ptr += len;
}
if (ptr < detent) {
ptr[0] = '>';
ptr[1] = '\0';
} }
if (ptr < detent)
*ptr++ = '>';
*ptr = '\0';
} }
return buf; return buf;
} }
@ -7244,7 +7241,7 @@ bailout:
#if MDBX_ENABLE_PROFGC #if MDBX_ENABLE_PROFGC
size_t majflt_after; size_t majflt_after;
prof->xtime_cpu += osal_cputime(&majflt_after) - cputime_before; prof->xtime_cpu += osal_cputime(&majflt_after) - cputime_before;
prof->majflt += majflt_after - majflt_before; prof->majflt += (uint32_t)(majflt_after - majflt_before);
#endif /* MDBX_ENABLE_PROFGC */ #endif /* MDBX_ENABLE_PROFGC */
return ret; return ret;
} }
@ -9131,7 +9128,7 @@ static int txn_renew(MDBX_txn *txn, const unsigned flags) {
} }
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__) #if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
txn_valgrind(env, txn); txn_valgrind(env, txn);
#endif #endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */
txn->mt_owner = tid; txn->mt_owner = tid;
return MDBX_SUCCESS; return MDBX_SUCCESS;
} }
@ -9199,7 +9196,7 @@ int mdbx_txn_renew(MDBX_txn *txn) {
rc = txn_renew(txn, MDBX_TXN_RDONLY); rc = txn_renew(txn, MDBX_TXN_RDONLY);
if (rc == MDBX_SUCCESS) { if (rc == MDBX_SUCCESS) {
txn->mt_owner = osal_thread_self(); tASSERT(txn, txn->mt_owner == osal_thread_self());
DEBUG("renew txn %" PRIaTXN "%c %p on env %p, root page %" PRIaPGNO DEBUG("renew txn %" PRIaTXN "%c %p on env %p, root page %" PRIaPGNO
"/%" PRIaPGNO, "/%" PRIaPGNO,
txn->mt_txnid, (txn->mt_flags & MDBX_TXN_RDONLY) ? 'r' : 'w', txn->mt_txnid, (txn->mt_flags & MDBX_TXN_RDONLY) ? 'r' : 'w',
@ -9813,8 +9810,10 @@ static int txn_end(MDBX_txn *txn, const unsigned mode) {
txn->mt_txnid == slot->mr_txnid.weak && txn->mt_txnid == slot->mr_txnid.weak &&
slot->mr_txnid.weak >= env->me_lck->mti_oldest_reader.weak); slot->mr_txnid.weak >= env->me_lck->mti_oldest_reader.weak);
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__) #if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
atomic_add32(&env->me_ignore_EDEADLK, 1);
txn_valgrind(env, nullptr); txn_valgrind(env, nullptr);
#endif atomic_sub32(&env->me_ignore_EDEADLK, 1);
#endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */
atomic_store32(&slot->mr_snapshot_pages_used, 0, mo_Relaxed); atomic_store32(&slot->mr_snapshot_pages_used, 0, mo_Relaxed);
safe64_reset(&slot->mr_txnid, false); safe64_reset(&slot->mr_txnid, false);
atomic_store32(&env->me_lck->mti_readers_refresh_flag, true, atomic_store32(&env->me_lck->mti_readers_refresh_flag, true,
@ -9843,7 +9842,7 @@ static int txn_end(MDBX_txn *txn, const unsigned mode) {
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__) #if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
if (txn == env->me_txn0) if (txn == env->me_txn0)
txn_valgrind(env, nullptr); txn_valgrind(env, nullptr);
#endif #endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */
txn->mt_flags = MDBX_TXN_FINISHED; txn->mt_flags = MDBX_TXN_FINISHED;
txn->mt_owner = 0; txn->mt_owner = 0;
@ -10258,6 +10257,14 @@ static int gcu_prepare_backlog(MDBX_txn *txn, gcu_context_t *ctx) {
} }
static __inline void gcu_clean_reserved(MDBX_env *env, MDBX_val pnl) { static __inline void gcu_clean_reserved(MDBX_env *env, MDBX_val pnl) {
#if MDBX_DEBUG && (defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__))
/* Для предотвращения предупреждения Valgrind из mdbx_dump_val()
* вызванное через макрос DVAL_DEBUG() на выходе
* из cursor_set(MDBX_SET_KEY), которая вызывается ниже внутри update_gc() в
* цикле очистки и цикле заполнения зарезервированных элементов. */
memset(pnl.iov_base, 0xBB, pnl.iov_len);
#endif /* MDBX_DEBUG && (MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__) */
/* PNL is initially empty, zero out at least the length */ /* PNL is initially empty, zero out at least the length */
memset(pnl.iov_base, 0, sizeof(pgno_t)); memset(pnl.iov_base, 0, sizeof(pgno_t));
if ((env->me_flags & (MDBX_WRITEMAP | MDBX_NOMEMINIT)) == 0) if ((env->me_flags & (MDBX_WRITEMAP | MDBX_NOMEMINIT)) == 0)
@ -10573,6 +10580,15 @@ retry:
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
goto bailout; goto bailout;
#if MDBX_DEBUG && (defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__))
/* Для предотвращения предупреждения Valgrind из mdbx_dump_val()
* вызванное через макрос DVAL_DEBUG() на выходе
* из cursor_set(MDBX_SET_KEY), которая вызывается как выше в цикле
* очистки, так и ниже в цикле заполнения зарезервированных элементов.
*/
memset(data.iov_base, 0xBB, data.iov_len);
#endif /* MDBX_DEBUG && (MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__) */
if (retired_pages_before == MDBX_PNL_GETSIZE(txn->tw.retired_pages)) { if (retired_pages_before == MDBX_PNL_GETSIZE(txn->tw.retired_pages)) {
const size_t at = (ctx->lifo == MDBX_PNL_ASCENDING) const size_t at = (ctx->lifo == MDBX_PNL_ASCENDING)
? left - chunk ? left - chunk
@ -10610,6 +10626,16 @@ retry:
rc = cursor_put_nochecklen(&ctx->cursor, &key, &data, MDBX_RESERVE); rc = cursor_put_nochecklen(&ctx->cursor, &key, &data, MDBX_RESERVE);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
goto bailout; goto bailout;
#if MDBX_DEBUG && (defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__))
/* Для предотвращения предупреждения Valgrind из mdbx_dump_val()
* вызванное через макрос DVAL_DEBUG() на выходе
* из cursor_set(MDBX_SET_KEY), которая вызывается как выше в цикле
* очистки, так и ниже в цикле заполнения зарезервированных элементов.
*/
memset(data.iov_base, 0xBB, data.iov_len);
#endif /* MDBX_DEBUG && (MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__) */
/* Retry if tw.retired_pages[] grew during the Put() */ /* Retry if tw.retired_pages[] grew during the Put() */
} while (data.iov_len < MDBX_PNL_SIZEOF(txn->tw.retired_pages)); } while (data.iov_len < MDBX_PNL_SIZEOF(txn->tw.retired_pages));
@ -11094,7 +11120,7 @@ bailout:
MDBX_PNL_SETSIZE(txn->tw.relist, 0); MDBX_PNL_SETSIZE(txn->tw.relist, 0);
#if MDBX_ENABLE_PROFGC #if MDBX_ENABLE_PROFGC
env->me_lck->mti_pgop_stat.gc_prof.wloops += ctx->loop; env->me_lck->mti_pgop_stat.gc_prof.wloops += (uint32_t)ctx->loop;
#endif /* MDBX_ENABLE_PROFGC */ #endif /* MDBX_ENABLE_PROFGC */
TRACE("<<< %zu loops, rc = %d", ctx->loop, rc); TRACE("<<< %zu loops, rc = %d", ctx->loop, rc);
return rc; return rc;
@ -11929,6 +11955,7 @@ int mdbx_txn_commit_ex(MDBX_txn *txn, MDBX_commit_latency *latency) {
(size_t)(commit_txnid - txn->mt_txnid)); (size_t)(commit_txnid - txn->mt_txnid));
} }
#endif #endif
meta.unsafe_sign = MDBX_DATASIGN_NONE;
meta_set_txnid(env, &meta, commit_txnid); meta_set_txnid(env, &meta, commit_txnid);
rc = sync_locked(env, env->me_flags | txn->mt_flags | MDBX_SHRINK_ALLOWED, rc = sync_locked(env, env->me_flags | txn->mt_flags | MDBX_SHRINK_ALLOWED,
@ -15291,7 +15318,7 @@ bailout:
} else { } else {
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__) #if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
txn_valgrind(env, nullptr); txn_valgrind(env, nullptr);
#endif #endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */
} }
osal_free(env_pathname.buffer_for_free); osal_free(env_pathname.buffer_for_free);
return rc; return rc;
@ -16852,11 +16879,13 @@ static __hot int cursor_get(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
} }
break; break;
case MDBX_GET_MULTIPLE: case MDBX_GET_MULTIPLE:
if (unlikely(data == NULL || !(mc->mc_flags & C_INITIALIZED))) if (unlikely(!data))
return MDBX_EINVAL; return MDBX_EINVAL;
if (unlikely(!(mc->mc_db->md_flags & MDBX_DUPFIXED))) if (unlikely((mc->mc_db->md_flags & MDBX_DUPFIXED) == 0))
return MDBX_INCOMPATIBLE; return MDBX_INCOMPATIBLE;
rc = MDBX_SUCCESS; rc = (mc->mc_flags & C_INITIALIZED)
? MDBX_SUCCESS
: cursor_set(mc, key, data, MDBX_SET).err;
if ((mc->mc_xcursor->mx_cursor.mc_flags & (C_INITIALIZED | C_EOF)) != if ((mc->mc_xcursor->mx_cursor.mc_flags & (C_INITIALIZED | C_EOF)) !=
C_INITIALIZED) C_INITIALIZED)
break; break;
@ -17215,9 +17244,6 @@ static __hot int cursor_touch(MDBX_cursor *const mc, const MDBX_val *key,
static __hot int cursor_put_nochecklen(MDBX_cursor *mc, const MDBX_val *key, static __hot int cursor_put_nochecklen(MDBX_cursor *mc, const MDBX_val *key,
MDBX_val *data, unsigned flags) { MDBX_val *data, unsigned flags) {
MDBX_page *sub_root = nullptr;
MDBX_val xdata, *rdata, dkey, olddata;
MDBX_db nested_dupdb;
int err; int err;
DKBUF_DEBUG; DKBUF_DEBUG;
MDBX_env *const env = mc->mc_txn->mt_env; MDBX_env *const env = mc->mc_txn->mt_env;
@ -17225,7 +17251,6 @@ static __hot int cursor_put_nochecklen(MDBX_cursor *mc, const MDBX_val *key,
DDBI(mc), DKEY_DEBUG(key), key->iov_len, DDBI(mc), DKEY_DEBUG(key), key->iov_len,
DVAL_DEBUG((flags & MDBX_RESERVE) ? nullptr : data), data->iov_len); DVAL_DEBUG((flags & MDBX_RESERVE) ? nullptr : data), data->iov_len);
int dupdata_flag = 0;
if ((flags & MDBX_CURRENT) != 0 && (mc->mc_flags & C_SUB) == 0) { if ((flags & MDBX_CURRENT) != 0 && (mc->mc_flags & C_SUB) == 0) {
if (unlikely(flags & (MDBX_APPEND | MDBX_NOOVERWRITE))) if (unlikely(flags & (MDBX_APPEND | MDBX_NOOVERWRITE)))
return MDBX_EINVAL; return MDBX_EINVAL;
@ -17284,10 +17309,11 @@ static __hot int cursor_put_nochecklen(MDBX_cursor *mc, const MDBX_val *key,
rc = MDBX_NO_ROOT; rc = MDBX_NO_ROOT;
} else if ((flags & MDBX_CURRENT) == 0) { } else if ((flags & MDBX_CURRENT) == 0) {
bool exact = false; bool exact = false;
MDBX_val lastkey, olddata;
if ((flags & MDBX_APPEND) && mc->mc_db->md_entries > 0) { if ((flags & MDBX_APPEND) && mc->mc_db->md_entries > 0) {
rc = cursor_last(mc, &dkey, &olddata); rc = cursor_last(mc, &lastkey, &olddata);
if (likely(rc == MDBX_SUCCESS)) { if (likely(rc == MDBX_SUCCESS)) {
const int cmp = mc->mc_dbx->md_cmp(key, &dkey); const int cmp = mc->mc_dbx->md_cmp(key, &lastkey);
if (likely(cmp > 0)) { if (likely(cmp > 0)) {
mc->mc_ki[mc->mc_top]++; /* step forward for appending */ mc->mc_ki[mc->mc_top]++; /* step forward for appending */
rc = MDBX_NOTFOUND; rc = MDBX_NOTFOUND;
@ -17352,7 +17378,7 @@ static __hot int cursor_put_nochecklen(MDBX_cursor *mc, const MDBX_val *key,
} }
mc->mc_flags &= ~C_DEL; mc->mc_flags &= ~C_DEL;
rdata = data; MDBX_val xdata, *rdata = data;
size_t mcount = 0, dcount = 0; size_t mcount = 0, dcount = 0;
if (unlikely(flags & MDBX_MULTIPLE)) { if (unlikely(flags & MDBX_MULTIPLE)) {
dcount = data[1].iov_len; dcount = data[1].iov_len;
@ -17397,11 +17423,15 @@ static __hot int cursor_put_nochecklen(MDBX_cursor *mc, const MDBX_val *key,
mc->mc_flags |= C_INITIALIZED; mc->mc_flags |= C_INITIALIZED;
} }
bool insert_key, insert_data, do_sub = false; MDBX_val dkey, olddata;
insert_key = insert_data = (rc != MDBX_SUCCESS); MDBX_db nested_dupdb;
MDBX_page *sub_root = nullptr;
bool insert_key, insert_data;
uint16_t fp_flags = P_LEAF; uint16_t fp_flags = P_LEAF;
MDBX_page *fp = env->me_pbuf; MDBX_page *fp = env->me_pbuf;
fp->mp_txnid = mc->mc_txn->mt_front; fp->mp_txnid = mc->mc_txn->mt_front;
insert_key = insert_data = (rc != MDBX_SUCCESS);
dkey.iov_base = nullptr;
if (insert_key) { if (insert_key) {
/* The key does not exist */ /* The key does not exist */
DEBUG("inserting key at index %i", mc->mc_ki[mc->mc_top]); DEBUG("inserting key at index %i", mc->mc_ki[mc->mc_top]);
@ -17576,7 +17606,6 @@ static __hot int cursor_put_nochecklen(MDBX_cursor *mc, const MDBX_val *key,
/* Back up original data item */ /* Back up original data item */
memcpy(dkey.iov_base = fp + 1, olddata.iov_base, memcpy(dkey.iov_base = fp + 1, olddata.iov_base,
dkey.iov_len = olddata.iov_len); dkey.iov_len = olddata.iov_len);
dupdata_flag = 1;
/* Make sub-page header for the dup items, with dummy body */ /* Make sub-page header for the dup items, with dummy body */
fp->mp_flags = P_LEAF | P_SUBP; fp->mp_flags = P_LEAF | P_SUBP;
@ -17680,11 +17709,10 @@ static __hot int cursor_put_nochecklen(MDBX_cursor *mc, const MDBX_val *key,
} }
} }
rdata = &xdata;
flags |= F_DUPDATA;
do_sub = true;
if (!insert_key) if (!insert_key)
node_del(mc, 0); node_del(mc, 0);
rdata = &xdata;
flags |= F_DUPDATA;
goto new_sub; goto new_sub;
} }
@ -17769,8 +17797,8 @@ new_sub:;
* storing the user data in the keys field, so there are strict * storing the user data in the keys field, so there are strict
* size limits on dupdata. The actual data fields of the child * size limits on dupdata. The actual data fields of the child
* DB are all zero size. */ * DB are all zero size. */
if (do_sub) { if (flags & F_DUPDATA) {
int xflags; unsigned xflags;
size_t ecount; size_t ecount;
put_sub: put_sub:
xdata.iov_len = 0; xdata.iov_len = 0;
@ -17791,13 +17819,11 @@ new_sub:;
if (sub_root) if (sub_root)
mc->mc_xcursor->mx_cursor.mc_pg[0] = sub_root; mc->mc_xcursor->mx_cursor.mc_pg[0] = sub_root;
/* converted, write the original data first */ /* converted, write the original data first */
if (dupdata_flag) { if (dkey.iov_base) {
rc = cursor_put_nochecklen(&mc->mc_xcursor->mx_cursor, &dkey, &xdata, rc = cursor_put_nochecklen(&mc->mc_xcursor->mx_cursor, &dkey, &xdata,
xflags); xflags);
if (unlikely(rc)) if (unlikely(rc))
goto bad_sub; goto bad_sub;
/* we've done our job */
dkey.iov_len = 0;
} }
if (!(node_flags(node) & F_SUBDATA) || sub_root) { if (!(node_flags(node) & F_SUBDATA) || sub_root) {
/* Adjust other cursors pointing to mp */ /* Adjust other cursors pointing to mp */
@ -17814,7 +17840,7 @@ new_sub:;
continue; continue;
if (m2->mc_pg[i] == mp) { if (m2->mc_pg[i] == mp) {
if (m2->mc_ki[i] == mc->mc_ki[i]) { if (m2->mc_ki[i] == mc->mc_ki[i]) {
err = cursor_xinit2(m2, mx, dupdata_flag); err = cursor_xinit2(m2, mx, dkey.iov_base != nullptr);
if (unlikely(err != MDBX_SUCCESS)) if (unlikely(err != MDBX_SUCCESS))
return err; return err;
} else if (!insert_key && m2->mc_ki[i] < nkeys) { } else if (!insert_key && m2->mc_ki[i] < nkeys) {
@ -17858,6 +17884,7 @@ new_sub:;
if (mcount < dcount) { if (mcount < dcount) {
data[0].iov_base = ptr_disp(data[0].iov_base, data[0].iov_len); data[0].iov_base = ptr_disp(data[0].iov_base, data[0].iov_len);
insert_key = insert_data = false; insert_key = insert_data = false;
dkey.iov_base = nullptr;
goto more; goto more;
} }
} }
@ -21112,6 +21139,10 @@ int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data,
tASSERT(txn, XCURSOR_INITED(&cx.outer) && tASSERT(txn, XCURSOR_INITED(&cx.outer) &&
cx.outer.mc_xcursor->mx_db.md_entries > 1); cx.outer.mc_xcursor->mx_db.md_entries > 1);
rc = MDBX_EMULTIVAL; rc = MDBX_EMULTIVAL;
if ((flags & MDBX_NOOVERWRITE) == 0) {
flags -= MDBX_CURRENT;
rc = cursor_del(&cx.outer, MDBX_ALLDUPS);
}
} }
} }
} }
@ -23035,30 +23066,6 @@ bailout:
return rc; return rc;
} }
int mdbx_set_compare(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cmp_func *cmp) {
int rc = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_ERROR);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
if (unlikely(!check_dbi(txn, dbi, DBI_USRVALID)))
return MDBX_BAD_DBI;
txn->mt_dbxs[dbi].md_cmp = cmp;
return MDBX_SUCCESS;
}
int mdbx_set_dupsort(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cmp_func *cmp) {
int rc = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_ERROR);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
if (unlikely(!check_dbi(txn, dbi, DBI_USRVALID)))
return MDBX_BAD_DBI;
txn->mt_dbxs[dbi].md_dcmp = cmp;
return MDBX_SUCCESS;
}
__cold int mdbx_reader_list(const MDBX_env *env, MDBX_reader_list_func *func, __cold int mdbx_reader_list(const MDBX_env *env, MDBX_reader_list_func *func,
void *ctx) { void *ctx) {
int rc = check_env(env, true); int rc = check_env(env, true);

View File

@ -1482,6 +1482,7 @@ struct MDBX_env {
int me_valgrind_handle; int me_valgrind_handle;
#endif #endif
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__) #if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
MDBX_atomic_uint32_t me_ignore_EDEADLK;
pgno_t me_poison_edge; pgno_t me_poison_edge;
#endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */ #endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */

View File

@ -822,6 +822,11 @@ __cold static int mdbx_ipclock_failed(MDBX_env *env, osal_ipclock_t *ipc,
#error "FIXME" #error "FIXME"
#endif /* MDBX_LOCKING */ #endif /* MDBX_LOCKING */
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
if (rc == EDEADLK && atomic_load32(&env->me_ignore_EDEADLK, mo_Relaxed) > 0)
return rc;
#endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */
ERROR("mutex (un)lock failed, %s", mdbx_strerror(err)); ERROR("mutex (un)lock failed, %s", mdbx_strerror(err));
if (rc != EDEADLK) if (rc != EDEADLK)
env->me_flags |= MDBX_FATAL_ERROR; env->me_flags |= MDBX_FATAL_ERROR;

View File

@ -1,6 +1,6 @@
.\" Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>. .\" Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE. .\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_CHK 1 "2023-06-16" "MDBX 0.12.7" .TH MDBX_CHK 1 "2023-10-17" "MDBX 0.12.8"
.SH NAME .SH NAME
mdbx_chk \- MDBX checking tool mdbx_chk \- MDBX checking tool
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -2,7 +2,7 @@
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>. .\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE. .\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_COPY 1 "2023-06-16" "MDBX 0.12.7" .TH MDBX_COPY 1 "2023-10-17" "MDBX 0.12.8"
.SH NAME .SH NAME
mdbx_copy \- MDBX environment copy tool mdbx_copy \- MDBX environment copy tool
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -1,7 +1,7 @@
.\" Copyright 2021-2023 Leonid Yuriev <leo@yuriev.ru>. .\" Copyright 2021-2023 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2014-2021 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2014-2021 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE. .\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_DROP 1 "2023-06-16" "MDBX 0.12.7" .TH MDBX_DROP 1 "2023-10-17" "MDBX 0.12.8"
.SH NAME .SH NAME
mdbx_drop \- MDBX database delete tool mdbx_drop \- MDBX database delete tool
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -2,7 +2,7 @@
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>. .\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE. .\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_DUMP 1 "2023-06-16" "MDBX 0.12.7" .TH MDBX_DUMP 1 "2023-10-17" "MDBX 0.12.8"
.SH NAME .SH NAME
mdbx_dump \- MDBX environment export tool mdbx_dump \- MDBX environment export tool
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -2,7 +2,7 @@
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>. .\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE. .\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_LOAD 1 "2023-06-16" "MDBX 0.12.7" .TH MDBX_LOAD 1 "2023-10-17" "MDBX 0.12.8"
.SH NAME .SH NAME
mdbx_load \- MDBX environment import tool mdbx_load \- MDBX environment import tool
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -2,7 +2,7 @@
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>. .\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE. .\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_STAT 1 "2023-06-16" "MDBX 0.12.7" .TH MDBX_STAT 1 "2023-10-17" "MDBX 0.12.8"
.SH NAME .SH NAME
mdbx_stat \- MDBX environment status tool mdbx_stat \- MDBX environment status tool
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -233,6 +233,10 @@ namespace mdbx {
"into an incompatible memory allocation scheme."); "into an incompatible memory allocation scheme.");
} }
[[noreturn]] __cold void throw_bad_value_size() {
throw bad_value_size(MDBX_BAD_VALSIZE);
}
__cold exception::exception(const ::mdbx::error &error) noexcept __cold exception::exception(const ::mdbx::error &error) noexcept
: base(error.what()), error_(error) {} : base(error.what()), error_(error) {}
@ -281,7 +285,7 @@ DEFINE_EXCEPTION(something_busy)
DEFINE_EXCEPTION(thread_mismatch) DEFINE_EXCEPTION(thread_mismatch)
DEFINE_EXCEPTION(transaction_full) DEFINE_EXCEPTION(transaction_full)
DEFINE_EXCEPTION(transaction_overlapping) DEFINE_EXCEPTION(transaction_overlapping)
DEFINE_EXCEPTION(duplicated_lck_file)
#undef DEFINE_EXCEPTION #undef DEFINE_EXCEPTION
__cold const char *error::what() const noexcept { __cold const char *error::what() const noexcept {
@ -367,6 +371,7 @@ __cold void error::throw_exception() const {
CASE_EXCEPTION(thread_mismatch, MDBX_THREAD_MISMATCH); CASE_EXCEPTION(thread_mismatch, MDBX_THREAD_MISMATCH);
CASE_EXCEPTION(transaction_full, MDBX_TXN_FULL); CASE_EXCEPTION(transaction_full, MDBX_TXN_FULL);
CASE_EXCEPTION(transaction_overlapping, MDBX_TXN_OVERLAPPING); CASE_EXCEPTION(transaction_overlapping, MDBX_TXN_OVERLAPPING);
CASE_EXCEPTION(duplicated_lck_file, MDBX_DUPLICATED_CLK);
#undef CASE_EXCEPTION #undef CASE_EXCEPTION
default: default:
if (is_mdbx_error()) if (is_mdbx_error())
@ -483,6 +488,109 @@ bool slice::is_printable(bool disable_utf8) const noexcept {
return true; return true;
} }
#ifdef MDBX_U128_TYPE
MDBX_U128_TYPE slice::as_uint128() const {
static_assert(sizeof(MDBX_U128_TYPE) == 16, "WTF?");
if (size() == 16) {
MDBX_U128_TYPE r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_uint64();
}
#endif /* MDBX_U128_TYPE */
uint64_t slice::as_uint64() const {
static_assert(sizeof(uint64_t) == 8, "WTF?");
if (size() == 8) {
uint64_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_uint32();
}
uint32_t slice::as_uint32() const {
static_assert(sizeof(uint32_t) == 4, "WTF?");
if (size() == 4) {
uint32_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_uint16();
}
uint16_t slice::as_uint16() const {
static_assert(sizeof(uint16_t) == 2, "WTF?");
if (size() == 2) {
uint16_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_uint8();
}
uint8_t slice::as_uint8() const {
static_assert(sizeof(uint8_t) == 1, "WTF?");
if (size() == 1)
return *static_cast<const uint8_t *>(data());
else if (size() == 0)
return 0;
else
MDBX_CXX20_UNLIKELY throw_bad_value_size();
}
#ifdef MDBX_I128_TYPE
MDBX_I128_TYPE slice::as_int128() const {
static_assert(sizeof(MDBX_I128_TYPE) == 16, "WTF?");
if (size() == 16) {
MDBX_I128_TYPE r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_int64();
}
#endif /* MDBX_I128_TYPE */
int64_t slice::as_int64() const {
static_assert(sizeof(int64_t) == 8, "WTF?");
if (size() == 8) {
uint64_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_int32();
}
int32_t slice::as_int32() const {
static_assert(sizeof(int32_t) == 4, "WTF?");
if (size() == 4) {
int32_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_int16();
}
int16_t slice::as_int16() const {
static_assert(sizeof(int16_t) == 2, "WTF?");
if (size() == 2) {
int16_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_int8();
}
int8_t slice::as_int8() const {
if (size() == 1)
return *static_cast<const int8_t *>(data());
else if (size() == 0)
return 0;
else
MDBX_CXX20_UNLIKELY throw_bad_value_size();
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
char *to_hex::write_bytes(char *__restrict const dest, size_t dest_size) const { char *to_hex::write_bytes(char *__restrict const dest, size_t dest_size) const {

View File

@ -80,6 +80,13 @@ if(UNIX AND NOT SUBPROJECT)
set_target_properties(test_extra_maindb_ordinal PROPERTIES set_target_properties(test_extra_maindb_ordinal PROPERTIES
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON) CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
endif() endif()
add_executable(test_extra_dupfixed_multiple extra/dupfixed_multiple.c++)
target_include_directories(test_extra_dupfixed_multiple PRIVATE "${PROJECT_SOURCE_DIR}")
target_link_libraries(test_extra_dupfixed_multiple ${TOOL_MDBX_LIB})
if(MDBX_CXX_STANDARD)
set_target_properties(test_extra_dupfixed_multiple PROPERTIES
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
endif()
endif() endif()
endif() endif()
@ -159,4 +166,12 @@ else()
REQUIRED_FILES uniq_nested.db-copy) REQUIRED_FILES uniq_nested.db-copy)
endif() endif()
if(UNIX AND NOT SUBPROJECT)
add_test(NAME extra_upsert_alldups COMMAND test_extra_upsert_alldups)
if(MDBX_BUILD_CXX)
add_test(NAME extra_maindb_ordinal COMMAND test_extra_maindb_ordinal)
add_test(NAME extra_dupfixed_multiple COMMAND test_extra_dupfixed_multiple)
endif()
endif()
endif() endif()

View File

@ -0,0 +1,210 @@
#include "mdbx.h++"
#include <array>
#include <iostream>
#include <unistd.h>
int main(int argc, const char *argv[]) {
(void)argc;
(void)argv;
unlink("." MDBX_DATANAME);
unlink("." MDBX_LOCKNAME);
mdbx::env_managed env(".", mdbx::env_managed::create_parameters(),
mdbx::env::operate_parameters());
using buffer =
mdbx::buffer<mdbx::default_allocator, mdbx::default_capacity_policy>;
auto txn = env.start_write();
auto map = txn.create_map(nullptr, mdbx::key_mode::ordinal,
mdbx::value_mode::multi_ordinal);
txn.insert(map, buffer::key_from_u64(21), buffer::key_from_u64(18));
txn.insert(map, buffer::key_from_u64(7), buffer::key_from_u64(19));
txn.insert(map, buffer::key_from_u64(22), buffer::key_from_u64(17));
txn.insert(map, buffer::key_from_u64(26), buffer::key_from_u64(13));
txn.insert(map, buffer::key_from_u64(24), buffer::key_from_u64(15));
txn.insert(map, buffer::key_from_u64(23), buffer::key_from_u64(16));
txn.insert(map, buffer::key_from_u64(25), buffer::key_from_u64(14));
txn.insert(map, buffer::key_from_u64(27), buffer::key_from_u64(12));
txn.commit();
txn = env.start_read();
auto cursor = txn.open_cursor(map);
if (cursor.to_first().value.as_uint64() != 19 ||
cursor.to_next().value.as_uint64() != 18 ||
cursor.to_next().value.as_uint64() != 17 ||
cursor.to_next().value.as_uint64() != 16 ||
cursor.to_next().value.as_uint64() != 15 ||
cursor.to_next().value.as_uint64() != 14 ||
cursor.to_next().value.as_uint64() != 13 ||
cursor.to_next().value.as_uint64() != 12 || cursor.to_next(false).done ||
!cursor.eof()) {
std::cerr << "Fail\n";
return EXIT_FAILURE;
}
txn.abort();
const uint64_t array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 42, 17, 99, 0, 33, 333};
txn = env.start_write();
txn.put_multiple(map, buffer::key_from_u64(13), array + 3, 4, mdbx::upsert);
txn.put_multiple(map, buffer::key_from_u64(10), array + 0, 1, mdbx::upsert);
txn.put_multiple(map, buffer::key_from_u64(12), array + 2, 3, mdbx::upsert);
txn.put_multiple(map, buffer::key_from_u64(15), array + 5, 6, mdbx::upsert);
txn.put_multiple(map, buffer::key_from_u64(14), array + 4, 5, mdbx::upsert);
txn.put_multiple(map, buffer::key_from_u64(11), array + 1, 2, mdbx::upsert);
txn.put_multiple(map, buffer::key_from_u64(16), array + 6, 7, mdbx::upsert);
txn.commit();
txn = env.start_read();
cursor = txn.open_cursor(map);
if (/* key = 7 */ cursor.to_first().value.as_uint64() != 19 ||
/* key = 10: 1 элемент */
cursor.to_next().value.as_uint64() != 1 ||
/* key = 11: 2 элемента, пропуск 1 */
cursor.to_next().value.as_uint64() != 2 ||
cursor.to_next().value.as_uint64() != 3 ||
/* key = 12: 3 элемента, пропуск 2 */
cursor.to_next().value.as_uint64() != 3 ||
cursor.to_next().value.as_uint64() != 4 ||
cursor.to_next().value.as_uint64() != 5 ||
/* key = 13: 4 элемента, пропуск 3 */
cursor.to_next().value.as_uint64() != 4 ||
cursor.to_next().value.as_uint64() != 5 ||
cursor.to_next().value.as_uint64() != 6 ||
cursor.to_next().value.as_uint64() != 7 ||
/* key = 14: 5 элементов, пропуск 4 */
cursor.to_next().value.as_uint64() != 5 ||
cursor.to_next().value.as_uint64() != 6 ||
cursor.to_next().value.as_uint64() != 7 ||
cursor.to_next().value.as_uint64() != 8 ||
cursor.to_next().value.as_uint64() != 9 ||
/* key = 15: 6 элементов, пропуск 5 */
cursor.to_next().value.as_uint64() != 6 ||
cursor.to_next().value.as_uint64() != 7 ||
cursor.to_next().value.as_uint64() != 8 ||
cursor.to_next().value.as_uint64() != 9 ||
cursor.to_next().value.as_uint64() != 17 ||
cursor.to_next().value.as_uint64() != 42 ||
/* key = 16: 7 элементов, пропуск 6 */
cursor.to_next().value.as_uint64() != 0 ||
cursor.to_next().value.as_uint64() != 7 ||
cursor.to_next().value.as_uint64() != 8 ||
cursor.to_next().value.as_uint64() != 9 ||
cursor.to_next().value.as_uint64() != 17 ||
cursor.to_next().value.as_uint64() != 42 ||
cursor.to_next().value.as_uint64() != 99 ||
/* key = 21 */ cursor.to_next().value.as_uint64() != 18 ||
/* key = 22 */ cursor.to_next().value.as_uint64() != 17 ||
/* key = 23 */ cursor.to_next().value.as_uint64() != 16 ||
/* key = 24 */ cursor.to_next().value.as_uint64() != 15 ||
/* key = 25 */ cursor.to_next().value.as_uint64() != 14 ||
/* key = 26 */ cursor.to_next().value.as_uint64() != 13 ||
/* key = 27 */ cursor.to_next().value.as_uint64() != 12 ||
cursor.to_next(false).done || !cursor.eof()) {
std::cerr << "Fail\n";
return EXIT_FAILURE;
}
txn.abort();
txn = env.start_write();
txn.put_multiple(map, buffer::key_from_u64(7), array + 3, 4, mdbx::update);
txn.upsert(map, buffer::key_from_u64(10), buffer::key_from_u64(14));
txn.put_multiple(map, buffer::key_from_u64(11), array + 4, 5, mdbx::upsert);
txn.put_multiple(map, buffer::key_from_u64(12), array + 0, 1, mdbx::update);
txn.update(map, buffer::key_from_u64(13), buffer::key_from_u64(18));
txn.put_multiple(map, buffer::key_from_u64(14), array + 2, 3, mdbx::update);
txn.update(map, buffer::key_from_u64(15), buffer::key_from_u64(13));
txn.put_multiple(map, buffer::key_from_u64(16), array + 6, 9, mdbx::update);
txn.update(map, buffer::key_from_u64(21), buffer::key_from_u64(17));
txn.update(map, buffer::key_from_u64(22), buffer::key_from_u64(15));
txn.put_multiple(map, buffer::key_from_u64(23), array + 1, 2, mdbx::update);
txn.update(map, buffer::key_from_u64(24), buffer::key_from_u64(16));
txn.put_multiple(map, buffer::key_from_u64(25), array + 5, 6, mdbx::update);
txn.upsert(map, buffer::key_from_u64(26), buffer::key_from_u64(12));
txn.put_multiple(map, buffer::key_from_u64(27), array + 12, 3, mdbx::update);
txn.commit();
txn = env.start_read();
cursor = txn.open_cursor(map);
if (/* key = 7 */
cursor.to_first().value.as_uint64() != 4 ||
cursor.to_next().value.as_uint64() != 5 ||
cursor.to_next().value.as_uint64() != 6 ||
cursor.to_next().value.as_uint64() != 7 ||
/* key = 10: 1 элемент */
cursor.to_next().value.as_uint64() != 1 ||
/* +1 upsert */
cursor.to_next().value.as_uint64() != 14 ||
/* key = 11: 2 элемента, пропуск 1 */
cursor.to_next().value.as_uint64() != 2 ||
cursor.to_next().value.as_uint64() != 3 ||
/* +5 элементов, пропуск 4 */
cursor.to_next().value.as_uint64() != 5 ||
cursor.to_next().value.as_uint64() != 6 ||
cursor.to_next().value.as_uint64() != 7 ||
cursor.to_next().value.as_uint64() != 8 ||
cursor.to_next().value.as_uint64() != 9 ||
/* key = 12: 1 элемент */
cursor.to_next().value.as_uint64() != 1 ||
/* key = 13 */ cursor.to_next().value.as_uint64() != 18 ||
/* key = 14: 3 элемента, пропуск 2 */
cursor.to_next().value.as_uint64() != 3 ||
cursor.to_next().value.as_uint64() != 4 ||
cursor.to_next().value.as_uint64() != 5 ||
/* key = 15 */ cursor.to_next().value.as_uint64() != 13 ||
/* key = 16: 9 элементов, пропуск 6 */
cursor.to_next().value.as_uint64() != 0 ||
cursor.to_next().value.as_uint64() != 7 ||
cursor.to_next().value.as_uint64() != 8 ||
cursor.to_next().value.as_uint64() != 9 ||
cursor.to_next().value.as_uint64() != 17 ||
cursor.to_next().value.as_uint64() != 33 ||
cursor.to_next().value.as_uint64() != 42 ||
cursor.to_next().value.as_uint64() != 99 ||
cursor.to_next().value.as_uint64() != 333 ||
/* key = 21 */ cursor.to_next().value.as_uint64() != 17 ||
/* key = 22 */ cursor.to_next().value.as_uint64() != 15 ||
/* key = 23: 2 элемента, пропуск 1 */
cursor.to_next().value.as_uint64() != 2 ||
cursor.to_next().value.as_uint64() != 3 ||
/* key = 24 */ cursor.to_next().value.as_uint64() != 16 ||
/* key = 25: 6 элемента, пропуск 5 */
cursor.to_next().value.as_uint64() != 6 ||
cursor.to_next().value.as_uint64() != 7 ||
cursor.to_next().value.as_uint64() != 8 ||
cursor.to_next().value.as_uint64() != 9 ||
cursor.to_next().value.as_uint64() != 17 ||
cursor.to_next().value.as_uint64() != 42 ||
/* key = 26, 1+1 upsert */
cursor.to_next().value.as_uint64() != 12 ||
cursor.to_next().value.as_uint64() != 13 ||
/* key = 27: 3 элемента, пропуск 12 */
cursor.to_next().value.as_uint64() != 0 ||
cursor.to_next().value.as_uint64() != 33 ||
cursor.to_next().value.as_uint64() != 333 ||
cursor.to_next(false).done || !cursor.eof()) {
std::cerr << "Fail\n";
return EXIT_FAILURE;
}
std::cout << "OK\n";
return EXIT_SUCCESS;
}

View File

@ -182,6 +182,7 @@ int main(int argc, const char *argv[]) {
errmsg = "failed to mdbx_txn_commit: %s\n"; errmsg = "failed to mdbx_txn_commit: %s\n";
goto Fail; goto Fail;
} }
mdbx_cursor_close(cur);
if ((rc = mdbx_env_close(env))) { if ((rc = mdbx_env_close(env))) {
errmsg = "failed to mdbx_env_close: %s\n"; errmsg = "failed to mdbx_env_close: %s\n";
goto Fail; goto Fail;