Compare commits

...

140 Commits

Author SHA1 Message Date
Леонид Юрьев (Leonid Yuriev)
566b0f93c7 mdbx: release v0.13.7 "Дружба" (Friendship).
The supporting release of a stable branch with bug fixes and bug fixes,
on [International Friendship Day](https://www.un.org/ru/observances/friendship-day).

For the list of improvements and changes, please see [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 22 files changed, 682 insertions(+), 291 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2025-07-30 11:44:04 +03:00
Леонид Юрьев (Leonid Yuriev)
df3a18d0cf mdbx: update ChangeLog (v0.3.17 release candidate). 2025-07-26 15:40:22 +03:00
Леонид Юрьев (Leonid Yuriev)
675b462601 mdbx-tests: random order of a parameterized tests inside the stochastic script (backport). 2025-07-26 15:40:04 +03:00
Леонид Юрьев (Leonid Yuriev)
7c990542e3 mdbx: *** using english for commit titles at the request of community ***.
At the request of several non-Russian-speaking developers, it was
decided to return to using English in the commit' brief-headers at least.
2025-07-26 14:57:43 +03:00
Леонид Юрьев (Leonid Yuriev)
9093435ce6 mdbx: ссылки на зеркала в документации (backport). 2025-07-26 14:54:26 +03:00
Леонид Юрьев (Leonid Yuriev)
7ef7a1f983 mdbx-tests: актуализация секции cross-qemu в GNUmakefile (backport). 2025-07-25 13:55:41 +03:00
Леонид Юрьев (Leonid Yuriev)
ac275d246b mdbx-tests: использование SysV-семафоров при cross-тестах посредством qemu (backport). 2025-07-25 13:55:41 +03:00
Леонид Юрьев (Leonid Yuriev)
d2f4697df3 mdbx-tests: явное NUMA-распределение в battery/tmux-тесте (backport). 2025-07-25 13:55:41 +03:00
Леонид Юрьев (Leonid Yuriev)
b107842c3d mdbx-tests: поддержка опции --numa # для привязки стохастического теста к NUMA-узлу (backport). 2025-07-25 13:55:41 +03:00
Леонид Юрьев (Leonid Yuriev)
cb428e613f mdbx: дополнение ChangeLog. 2025-07-20 20:46:33 +03:00
Леонид Юрьев (Leonid Yuriev)
5c69cb322a mdbx: переработка ошибок файловых блокировок в API копирования с устранением проблемы на OSX (backport).
На POSIX-платформах внутри API копирования используются файловый
блокировки `fcntl(F_SETLK)` и `flock()`, так как только совместное
использование обеспечивает блокировку на всех платформах и файловых
системах, включая NFS и SMB.

Однако, в зависимости от платформы, версии ядра ОС, типа файловой
системы, а в случае NFS/SMB также от удаленной стороны, используемые
системные файловые блокировки могут не работать или конфликтовать между
собой (в частности на OSX).

Поэтому в этом коммите реализуется более гибкий подход. Если кратко,
то допускается отказ одной из блокировок при успехе другой:

 - При успехе fcntl(F_SETLK) допускается EAGAIN/EWOULDBLOCK и EREMOTEIO от flock(),
   если целевой файл на не-локальной файловой системе, а также на не-Linux платформах,
   где одновременная блокировка может быть не разрешена fcntl(F_SETLK) и flock().

 - При успехе flock() допускается ENOTSUP и REMOTEIO от fcntl(F_SETLK),
   если целевой файл на не-локальной файловой системе.
2025-07-20 17:05:31 +03:00
Леонид Юрьев (Leonid Yuriev)
6318ca701a mdbx: добавление ignore_enosys_and_einval() и её использование для отказа от OFD-блокировок (backport). 2025-07-20 17:05:31 +03:00
Леонид Юрьев (Leonid Yuriev)
9c1f3aa4b5 mdbx: разделение ignore_enosys() и ignore_enosys_and_eagain() (backport-required). 2025-07-20 17:05:31 +03:00
Леонид Юрьев (Leonid Yuriev)
759d0442d4 mdbx-tests: дополнение extra/dbi (backport). 2025-07-19 21:56:11 +03:00
Леонид Юрьев (Leonid Yuriev)
6cb9305b31 mdbx: устранение возможности неверного возврата MDBX_DBS_FULL при открытии DBI-дескрипторов (backport).
В lockfree-пути открытия DBI-дескрипторов, при просмотре уже открытых
таблиц, пропускались элементы отличающиеся не только по имени, но также
и при несовпадении запрашиваемых флагов и актуальных флагов уже открытой
таблицы.

Если при этом уже было достигнуто (ранее заданное) максимальное
количество открытых DBI-дескрипторов, то возвращалась ошибка
`MDBX_DBS_FULL`, в том числе в ситуациях когда результат должен быть
другим.

Спасибо [Артёму Воротникову](https://github.com/vorot93) за сообщение о проблеме!
2025-07-19 21:56:05 +03:00
Леонид Юрьев (Leonid Yuriev)
68f9dc18be mdbx-tests: дополнение extra/txn сценарием провоцирующим гонку за атрибуты MainDB-DBI (backport). 2025-07-19 21:56:05 +03:00
Леонид Юрьев (Leonid Yuriev)
f5aa804a2d mdbx: устранение неожиданной ошибки MDBX_BAD_DBI при гонках внутри процесса (backport).
Запуск читающих и пишущих транзакций взаимно не блокируется. Однако,
внутри одного процесса, DBI-хендлы и атрибуты таблиц используются
совместно всеми транзакциями (в рамках экземпляра среды работы с БД).
Поэтому после изменения атрибутов таблиц, в том числе при первоначальном
чтении актуальных атрибутов MainDB, может возникать состояние гонок при
одновременном старте нескольких транзакций.

Этим коммитом исправляются недочеты в обработке ситуации таких гонок,
из-за чего могла возвращается неожиданная (с точки зрения пользователя)
ошибка `MDBX_BAD_DBI`.

Формально ошибка присутствовала начиная с коммита `e6af7d7c53428ca2892bcbf7eec1c2acee06fd44` от 2023-11-05.
Однако, до этого (исторически, как было унаследовано от LMDB)
отсутствовал какой-либо контроль смены атрибутов MainDB во время старта
и/или работы транзакций. Поэтому вместо возврата каких-либо ошибок
подобные состояние гонок и/или связанные с изменением атрибутов MainDB
оставались необработанными/незамеченными, либо проявлялись как редкие
неуловимые сбои пользовательских приложений.

Спасибо [Артёму Воротникову](https://github.com/vorot93) за сообщение о проблеме!
2025-07-19 21:55:52 +03:00
Леонид Юрьев (Leonid Yuriev)
7468ce6ada mdbx: исправление resurrect-after-fork при использовании SysV-семафоров (backport).
Ошибка/недоработка была с первой реализации resurrect-after-fork в
ноябре 2023, но оставалась не замеченной из-за отсутствия
CI-тестирования на платформе OSX/Mac (где нет поддержки разделяемых
мьютексов).
2025-07-16 23:18:28 +03:00
Леонид Юрьев (Leonid Yuriev)
decf34fd2b mdbx-make: изменение кол-ва внутренних итераций прогона тестов для уменьшения затрат на CI (backport). 2025-07-16 23:18:28 +03:00
Леонид Юрьев (Leonid Yuriev)
f1b933c541 mdbx: отключение MSVC предупреждений C5286 и C5287 (backport). 2025-07-16 23:18:28 +03:00
Леонид Юрьев (Leonid Yuriev)
230fbd64f5 mdbx: удаление/исправление лишних assert-проверок (backport, squashed). 2025-07-16 23:17:38 +03:00
Леонид Юрьев (Leonid Yuriev)
e8bfffc9f6 mdbx: исправление опечатки MDBX_ENOMEM (backport).
Вместо `MDBX_ENOMEM` был использован идентификатор `ENOMEM`,
что могло ломать сборку на не-POSIX/Windows платформах,
в зависимости от конфигурации и/или версии SDK.
2025-06-01 11:32:37 +03:00
Леонид Юрьев (Leonid Yuriev)
6abaa67eb8 mdbx: дополнение ChangeLog. 2025-05-21 14:22:49 +03:00
Леонид Юрьев (Leonid Yuriev)
266937e72d mdbx: перемещение и доработка проверки _FILE_OFFSET_BITS для Android (backport). 2025-05-21 14:20:09 +03:00
Леонид Юрьев (Leonid Yuriev)
5c44dd201c mdbx: обновление патча для старых версий buildroot. 2025-04-22 14:43:37 +03:00
Леонид Юрьев (Leonid Yuriev)
f4384800b5 mdbx: обновление ChangeLog. 2025-04-22 13:10:30 +03:00
Леонид Юрьев (Leonid Yuriev)
a971c76aff v0.13.6 "Бузина".
Поддерживающий выпуск стабильной ветки с исправлением обнаруженных ошибок и устранением недочётов,
в память о погибшем украинском историке и писателе [Алесе Бузине](https://ru.ruwiki.ru/wiki/Бузина,_Олесь_Алексеевич).

За перечнем доработок и изменений обращайтесь к [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 15 files changed, 194 insertions(+), 36 deletions(-).
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2025-04-22 11:53:23 +03:00
Леонид Юрьев (Leonid Yuriev)
b6f918aa1c mdbx: исправление опечатки в логировании (backport). 2025-04-22 11:17:39 +03:00
Леонид Юрьев (Leonid Yuriev)
ab2f661c97 mdbx: дополнение ChangeLog. 2025-04-20 00:01:59 +03:00
Леонид Юрьев (Leonid Yuriev)
5548ef20f6 mdbx: переупорядочивание атрибутов для совместимости с GCC-15 в режиме C23 (backport). 2025-04-19 23:44:07 +03:00
Леонид Юрьев (Leonid Yuriev)
679c1eb939 mdbx-tests: обнуление pid на входе в osal_actor_poll() (backport). 2025-04-19 23:43:43 +03:00
Леонид Юрьев (Leonid Yuriev)
76a588f91b mdbx: исправление возврата MDBX_BAD_TXN вместо MDBX_EINVAL из mdbx_cursor_unbind() в особых случаях (backport). 2025-04-19 23:42:59 +03:00
Леонид Юрьев (Leonid Yuriev)
6b5515908b mdbx: предотвращение возврата неожиданной ошибки MDBX_BUSY из mdbx_txn_lock(dont_wait=false) (backport). 2025-04-19 23:42:34 +03:00
Леонид Юрьев (Leonid Yuriev)
214fa153e2 mdbx: дополнение ChangeLog. 2025-04-10 16:54:13 +03:00
Леонид Юрьев (Leonid Yuriev)
819551ce13 mdbx-tests: расширение и доработка сценария extra/cursor-closing (backport). 2025-04-10 16:37:42 +03:00
Леонид Юрьев (Leonid Yuriev)
a22c0c5c48 mdbx: подсказка для Coverity для подавления ложно-положительных предупреждений (backport). 2025-04-10 16:35:53 +03:00
Леонид Юрьев (Leonid Yuriev)
9540cabf5f mdbx: возврат MDBX_EINVAL из mdbx_cursor_bind() при невозможности отвязки курсора от его текущей транзакции (backport). 2025-04-10 16:34:57 +03:00
Леонид Юрьев (Leonid Yuriev)
0e3b093eb5 mdbx: исправление неверной assert-проверки и микрооптимизация (backport).
В пути фиксации вложенных транзакций, условие в assert-проверке не было
корректным для случая, когда таблица уже существовала и её дескриптор
был открыт, использовался в завершаемой вложенной транзакции, но не
использовался в родительской.

Это исправление недочета также передаёт, уже загруженное из БД, кешируемое
состояние таблицы в родительскую транзакцию.
2025-04-10 16:34:19 +03:00
Леонид Юрьев (Leonid Yuriev)
5d38add405 mdbx: исправление ошибок merge/rebase (backport). 2025-04-10 16:33:10 +03:00
Леонид Юрьев (Leonid Yuriev)
b55a41f604 mdbx: дополнение ChangeLog. 2025-04-09 22:18:13 +03:00
Леонид Юрьев (Leonid Yuriev)
29bed7cf5d mdbx: игнорирование EAGAIN от flock() в случае копирования на NFS. 2025-04-09 22:18:07 +03:00
Леонид Юрьев (Leonid Yuriev)
8d0eceee9f mdbx: отключение использования copy_file_range() для ядер linux 5.3-5.18 включительно. 2025-04-07 05:28:16 +03:00
Леонид Юрьев (Leonid Yuriev)
56a6377622 mdbx: понижение уровня логирования для "skip update meta" (backport).
Спасибо [Илье Михееву](https://github.com/JkLondon) за сообщение о недочете.
2025-03-28 15:14:54 +03:00
Леонид Юрьев (Leonid Yuriev)
19dc93fc76 mdbx: дополнение ChangeLog. 2025-03-23 17:46:28 +03:00
Леонид Юрьев (Leonid Yuriev)
5f1d8dcb3e mdbx: уточнение типа адреса для донатов (backport). 2025-03-22 23:33:13 +03:00
Леонид Юрьев (Leonid Yuriev)
3d2b221256 mdbx++: вброс std::invalid_argument с явным сообщением "MDBX_EINVAL" (backport). 2025-03-22 23:32:57 +03:00
Леонид Юрьев (Leonid Yuriev)
ca1808d57f mdbx-test: расширение extra/cursor-closing (backport). 2025-03-22 23:32:57 +03:00
Леонид Юрьев (Leonid Yuriev)
aa98d6a88e mdbx: обновление NOTICE. 2025-03-22 23:31:47 +03:00
Леонид Юрьев (Leonid Yuriev)
b9b14f0061 mdbx: устранение регресса при использовании курсоров для DBI=0 в читающих транзакциях (hotfix).
В результате рефакторинга и ряда оптимизаций для завершения/гашения
курсоров в читающих и пишущих транзакций стал использоваться общий код.
Причем за основу, был взят соответствующий фрагмент относящийся к
пишущим транзакциям, в которых пользователю не позволяется
использоваться курсоры для DBI=0 и поэтому эта итераций пропускалась.

В результате, при завершении читающих транзакциях, курсоры связанные с
DBI=0 не завершались должным образом, а при их повторном использовании
или явном закрытии после завершения читающей транзакции происходило
обращение к уже освобожденной памяти. Если же такие курсоры
отсоединялись или закрывались до завершения читающей транзакции, то
ошибка не имела шансов на проявление.

Спасибо Илье Михееву (https://github.com/JkLondon) и команде Erigon (https://erigon.tech) за сообщения о проблеме.
2025-03-22 20:01:52 +03:00
Леонид Юрьев (Leonid Yuriev)
e3324cef91 mdbx: выпуск 0.13.5 "Труба".
Поддерживающий выпуск стабильной ветки с исправлением обнаруженных ошибок и устранением недочётов.

За перечнем доработок и изменений обращайтесь к [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 49 files changed, 2106 insertions(+), 1135 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2025-03-21 21:14:00 +03:00
Леонид Юрьев (Leonid Yuriev)
bb664152b8 mdbx: дополнение ChangeLog (подготовка к выпуску). 2025-03-20 23:53:22 +03:00
Леонид Юрьев (Leonid Yuriev)
5d9fb63fb8 mdbx: усиление контроля сигнатур курсоров (backport). 2025-03-20 18:00:44 +03:00
Леонид Юрьев (Leonid Yuriev)
1e0a1014a4 mdbx-tests: корректировка обработки прерывания теста посредством SIGTERM/SIGINT (backport). 2025-03-20 14:23:19 +03:00
Леонид Юрьев (Leonid Yuriev)
35349cf538 mdbx: добавление опции сборки MDBX_ENABLE_NON_READONLY_EXPORT и логирование соответствующих ситуаций (backport).
Закрывает [запрос](https://gitflic.ru/project/erthink/libmdbx/issue/16).
2025-03-20 13:55:07 +03:00
Леонид Юрьев (Leonid Yuriev)
8157d07b00 mdbx: использование при наличии EREMOTEIO вместо ENOTBLK в качестве MDBX_EREMOTE (backport). 2025-03-20 13:53:47 +03:00
Леонид Юрьев (Leonid Yuriev)
3c3628c798 mdbx-tests: кратное сокращение итераций тестов в зависимости от конфигурации Valgrind/Debug/CI (backport). 2025-03-20 01:58:02 +03:00
Леонид Юрьев (Leonid Yuriev)
3a0dbee58c mdbx-tests: устранение невыравненного доступа в extra/close-dbi для UBSAN (backport). 2025-03-20 01:57:38 +03:00
Леонид Юрьев (Leonid Yuriev)
e11d419d20 mdbx-tests: перехват и логирование исключений в extra-C++ тестах (backport). 2025-03-20 01:56:55 +03:00
Леонид Юрьев (Leonid Yuriev)
59343d9106 mdbx++: minor reflow Doxygen comments (backport). 2025-03-20 01:56:07 +03:00
Леонид Юрьев (Leonid Yuriev)
2127d3b7d7 mdbx-tests: расширение extra/dupfix-multiple (backport). 2025-03-20 01:55:41 +03:00
Леонид Юрьев (Leonid Yuriev)
065aef35ea mdbx++: добавление mdbx::cursor::put_multiple_samelength() (backport). 2025-03-20 01:55:10 +03:00
Леонид Юрьев (Leonid Yuriev)
9653c8f45b mdbx: рефакторинг проверок с выносом в cursor_check_multiple() (backport). 2025-03-20 01:52:21 +03:00
Леонид Юрьев (Leonid Yuriev)
7ed769e9c6 mdbx: поддержка MDBX_MULTIPLE с нулевым размером данных (backport). 2025-03-20 01:51:37 +03:00
Леонид Юрьев (Leonid Yuriev)
52a19fecca mdbx++: явное определение external-инстанцирования mdbx::buffer<> c API-атрибутами (backport). 2025-03-20 01:50:27 +03:00
Леонид Юрьев (Leonid Yuriev)
3282adf8bd mdbx: исправление регресса в пути обработки MDBX_MULTIPLE (backport).
Пакетная вставка значений посредством операции `MDBX_MULTIPLE` могла
приводить к падениям и повреждению структуры БД. Ошибка оставалось не
замеченной из-за специфических условий проявления, которые не
реализовались в тестах.

Проблема присутствовала во всех выпусках начиная с v0.13.1, но
соответствующая ошибка не связана с конкретным коммита в истории, а
является следствием нескольких доработок (шагов рефакторинга), которые
суммарно привели к регрессу.

Технически ошибка обусловлена не-обнулением переменной, которая не
обнулялась в некотором пути выполнения и исходно не требовала обнуления,
но такое обнуление потребовалось после ряда этапов оптимизации кода и
рефакторинга.

Основным условием проявления является пакетная вставка multi-значений в
dupsort-таблицу с фиксированным размером значений, при котором набор
значений соответствующий обновляемом ключу, перестаёт помещаться на
вложенной странице и преобразуется/выносится во вложенное дерево
страниц. Если такой вынос/преобразование происходило до исчерпания
переданного набора значений, то при следующей итерации повторно
производились действия соответствующие выносу данных в отдельное дерево
страниц. Что могла приводить как к разыменованию неверных указателей
(повреждению содержимого памяти) и/или к повреждению содержимого страниц
образующих структуру БД.

Исправление свелось к добавлению одной строчки кода, но также были
расширены тесты для покрытия соответствующих сценариев.
2025-03-20 01:49:42 +03:00
Леонид Юрьев (Leonid Yuriev)
529f2c2380 mdbx-tests: уменьшение кол-ва итераций в extra/crunched-delete для 32-битных сборок во избежания MDBX_MAP_FULL (backport). 2025-03-20 01:49:17 +03:00
Леонид Юрьев (Leonid Yuriev)
1dfe1e872e mdbx++: добавление mdbx::cursor::seek_multiple_samelength() (backport). 2025-03-20 01:48:58 +03:00
Леонид Юрьев (Leonid Yuriev)
767ba21977 mdbx: костыли для CLANG < 20 при использовании [[атрибутов]] C23 (backport). 2025-03-20 01:48:22 +03:00
Леонид Юрьев (Leonid Yuriev)
0a9c9840da mdbx-tests: существенное расширение extra/cursor-closing (backport). 2025-03-20 01:47:56 +03:00
Леонид Юрьев (Leonid Yuriev)
9c177de034 mdbx-tests: дополнение extra/txn (backport). 2025-03-20 01:47:24 +03:00
Леонид Юрьев (Leonid Yuriev)
5f37ea60d2 mdbx++: проверка __cpp_concepts >= 202002 для использования концептов C++ (backport). 2025-03-20 01:46:43 +03:00
Леонид Юрьев (Leonid Yuriev)
c457804fad mdbx: исправление затенения курсоров во вложенных транзакциях (backport). 2025-03-20 01:46:13 +03:00
Леонид Юрьев (Leonid Yuriev)
6c036add8b mdbx: переработка проверки курсоров на входе API-функций с добавлением cursor_check() (backport). 2025-03-20 01:44:20 +03:00
Леонид Юрьев (Leonid Yuriev)
5fd319bbc2 mdbx: переработка mdbx_txn_release_all_cursors_ex() (backport). 2025-03-20 01:42:50 +03:00
Леонид Юрьев (Leonid Yuriev)
682233ba28 mdbx++: переформатирование (временно) неиспользуемого кода (backport). 2025-03-20 01:41:58 +03:00
Леонид Юрьев (Leonid Yuriev)
c5936eb5da mdbx++: удаление исключения при запросе транзакции у отсоединённого курсора (backport). 2025-03-20 01:41:32 +03:00
Леонид Юрьев (Leonid Yuriev)
d8890bc169 mdbx++: добавление inplace_storage_size_rounding в capacity_policy для буферов (backport). 2025-03-20 01:41:05 +03:00
Леонид Юрьев (Leonid Yuriev)
73d52c1963 mdbx++: добавление mdbx::cursor_managed::withdraw_handle() (backport). 2025-03-20 01:40:41 +03:00
Леонид Юрьев (Leonid Yuriev)
58729a2fbd mdbx: корректировка описания MDBX_MVCC_RETARDED и соответствующего сообщения об ошибке (backport). 2025-03-20 01:40:11 +03:00
Леонид Юрьев (Leonid Yuriev)
5dfe3433a8 mdbx: устранение гонки в tbl_setup(MDBX_DUPFIXED | MDBX_INTEGERDUP) при работе в разных потоках (backport).
Проблема была в том, что в случаях фиксированного размера значений
clc.lmin/clc.lmax устанавливались в env->kvs[], а затем корректировались
по актуальному размеру данных в БД. Поэтому при конкурентном вызове из
разных потоков, один поток мог выполнять инициализацию, а второй
прочитать временные/промежуточные значения lmin/lmax.

В результате, при конкурентном старте транзакций в разных потоках при
использовании только-что открытого dbi-хендла, проверка допустимости
длины значения могла заканчиваться ложной ошибкой MDBX_BAD_VALSIZE.
2025-03-20 01:24:34 +03:00
Леонид Юрьев (Leonid Yuriev)
1720762080 mdbx: переименование cursor_validate() (backport). 2025-03-20 01:24:30 +03:00
Леонид Юрьев (Leonid Yuriev)
91570a084f mdbx: добавление MDBX_SEEK_AND_GET_MULTIPLE в API операций курсора (backport). 2025-03-20 01:17:47 +03:00
Леонид Юрьев (Leonid Yuriev)
0fff8d0704 mdbx-doc: doxygen-описания для doubtless-positioning констант (backport). 2025-03-20 01:17:11 +03:00
Леонид Юрьев (Leonid Yuriev)
484b488f92 mdbx-tests: добавление поддержки опции MDBX_VALIDATION и использование в стохастическом тесте (backport). 2025-03-20 01:16:48 +03:00
Леонид Юрьев (Leonid Yuriev)
2fbdaccf60 mdbx-tests: поддержка значений on/off для опций командной строки (backport). 2025-03-20 01:15:53 +03:00
Леонид Юрьев (Leonid Yuriev)
753b2270fd mdbx: добавление mdbx_cursor_close2() в API (backport). 2025-03-20 01:14:34 +03:00
Леонид Юрьев (Leonid Yuriev)
33ceba0a5a mdbx: добавление cursor_reset() и cursor_drown() (backport). 2025-03-20 01:13:44 +03:00
Леонид Юрьев (Leonid Yuriev)
2476fba287 mdbx: рефакторинг cursor_eot() для упрощения txn_done_cursors() (backport). 2025-03-20 01:12:59 +03:00
Леонид Юрьев (Leonid Yuriev)
2b6a768750 mdbx: косметический рефакторинг cursor_shadow() (backport). 2025-03-20 01:12:15 +03:00
Леонид Юрьев (Leonid Yuriev)
b6dcdcf2dc mdbx: запрещение unbind/close курсоров для вложенных транзакций (backport). 2025-03-20 01:10:11 +03:00
Леонид Юрьев (Leonid Yuriev)
175e4a2e1b mdbx: проверка владельца потока владеющего транзакцией только при MDBX_TXN_CHECKOWNER=ON (backport). 2025-03-20 01:06:56 +03:00
Леонид Юрьев (Leonid Yuriev)
f9d7eb5525 mdbx-doc: актуализация раздела MacOS в README (backport). 2025-03-20 01:01:05 +03:00
Леонид Юрьев (Leonid Yuriev)
69895e2b55 mdbx-make: поиск gnu-sed на Darwin/MacOS (backport). 2025-03-20 00:57:17 +03:00
Леонид Юрьев (Leonid Yuriev)
15bd9cfc89 mdbx: удаление const у транзакции в cursor_bind() и cursor_renew() (backport). 2025-03-20 00:52:16 +03:00
Леонид Юрьев (Leonid Yuriev)
d8f9f3ba58 mdbx: проверяем выравнивание размера БД на юнит выделения памяти, а не на размер страницы (backport).
Теоретически до этого коммита могла быть некоторая неувязка:
 - при открытии БД с размером страницы 4K на Windows (где размер секции кратен 64K) в режиме read-only,
 - после того как БД использовалась на POSIX (где размер отображения кратен размеру системной страницы).

Ранее ошибка могла возвращаться со стороны системы (например INVALID_PARAMETER) и по ней крайне сложно было понять в чем дело.
Теперь же будет логирование ошибки и возврат MDBX_WANNA_RECOVERY.
2025-03-20 00:46:45 +03:00
Леонид Юрьев (Leonid Yuriev)
4150f411dc mdbx: переработка проверка размера файла БД при открытии (backport).
Переработка 05cdf9d202. У предыдущего
варианта был недостаток, при необходимости выдачи предупреждения
и открытии БД с изменением геометрии, предупреждение не выдавалось,
что может затруднять анализ/разбор проблемных ситуаций.
2025-03-20 00:45:57 +03:00
Леонид Юрьев (Leonid Yuriev)
32ca9691c3 mdbx-doc: добавление ссылки на привязку к Zig (backport). 2025-03-20 00:44:54 +03:00
Леонид Юрьев (Leonid Yuriev)
4f59864ef5 mdbx-cmake: используем -flto=auto для GCC >= 11.4 (backport).
При сборке посредством GCC >= 11.4 больше не возникает предупреждений:
  lto-wrapper: warning: using serial compilation of # LTRANS jobs
  lto-wrapper: note: see the ‘-flto’ option documentation for more information

Однако, использование auto-режима не является оптимальным решением, так
как при параллельной сборке посредством make или ninja, каждая уже
запущенная ветвь компиляции породит потоки ещё для каждого ядра ЦПУ.

Таким образом реальная нагрузка может расти квадратично, т.е. чем больше
у вас ядер -- тем хуже и при 96 ядрах может работать 9216 потоков сборки.

Тем не менее, использование `job-server` в CMake пока не возможно, а при
сборке libmdbx не так много работы чтобы чтобы обрушить систему нагрузкой.
2025-03-20 00:41:13 +03:00
Леонид Юрьев (Leonid Yuriev)
f82b760b6e mdbx-cmake: избегаем двойной работы compiler.cmake без необходимости (backport). 2025-03-20 00:40:12 +03:00
Леонид Юрьев (Leonid Yuriev)
d6b359756c mdbx-cmake: расслабление условий для использования LTO с CLANG на Linux (backport). 2025-03-20 00:40:02 +03:00
Леонид Юрьев (Leonid Yuriev)
4d454d6e80 mdbx-cmake: расширение поиска LLVMgold.so в относительных lib-директориях (backport). 2025-03-20 00:35:47 +03:00
Леонид Юрьев (Leonid Yuriev)
44467d0883 mdbx-make: добавление цели ninja-assertions и её использование при make check (backport). 2025-03-20 00:30:01 +03:00
Леонид Юрьев (Leonid Yuriev)
49e6bd9296 mdbx++: использование mdbx_txn_release_all_cursors_ex() (backport). 2025-03-20 00:29:20 +03:00
Леонид Юрьев (Leonid Yuriev)
805d84480d mdbx: исправление опечатки в ChangeLog. 2025-03-05 01:44:21 +03:00
Леонид Юрьев (Leonid Yuriev)
7504a8f8f2 mdbx: обновление ChangeLog. 2025-03-04 14:47:13 +03:00
Леонид Юрьев (Leonid Yuriev)
94a2abaf31 mdbx: добавление в API mdbx_txn_release_all_cursors_ex() и изменение семантики результата mdbx_txn_release_all_cursors() (backport).
По недосмотру в выпусках остался предварительный/черновой вариант
функции mdbx_txn_release_all_cursors(), который смешивает в возвращаемом
значении информацию об ошибке/успехе и количество обработанных курсоров.
За-за чего невозможно отличить одно от другого, например ошибку EPERM на
Linux от одного успешно закрытого курсора.

Теперь mdbx_txn_release_all_cursors() возвращает только код ошибки,
а для получения кол-ва закрытых курсоров в API добавлена функция mdbx_txn_release_all_cursors_ex().
2025-03-04 14:45:13 +03:00
Леонид Юрьев (Leonid Yuriev)
0604accecf mdbx: проверка владельца потока владеющего транзакцией только при MDBX_TXN_CHECKOWNER=ON (backport). 2025-03-04 10:44:42 +03:00
Леонид Юрьев (Leonid Yuriev)
bc2f1c59cb mdbx: обновление ChangeLog. 2025-03-04 01:14:03 +03:00
Леонид Юрьев (Leonid Yuriev)
40f655e2da mdbx: корректировка log_error() для устранение ложных ошибок при работе mdbx_chk с высоким уровнем логирования (backport).
Некая проблема была в том, что при высоком уровне логирования в логгер
также отправлялись неизбежные MDBX_NOTFOND при достижении конца
интегрируемых данных. В свою очередь, chk-логика формирования отчета
подсчитывала эти сообщения как ошибки при проверке БД...
2025-03-03 01:49:32 +03:00
Леонид Юрьев (Leonid Yuriev)
5e714ed946 mdbx: переделка env_owned_wrtxn() и мест её вызова (backport).
Цель в том чтобы избавить от коллизии блокировки возникающей внутри
dxb_sanitize_tail() при использовании Valgrind/ASAN, а также упросить
код.
2025-03-03 01:49:25 +03:00
Леонид Юрьев (Leonid Yuriev)
d313008d82 mdbx: дополнительные проверки сигнатур курсоров при итерации связанных списков (backport). 2025-03-02 16:38:27 +03:00
Леонид Юрьев (Leonid Yuriev)
9277daa185 mdbx: более полная очистка курсоров при закрытии/отключении (backport). 2025-03-02 16:37:49 +03:00
Леонид Юрьев (Leonid Yuriev)
1792bdc763 mdbx-tests: расширение extra/dbi (backport). 2025-03-02 16:03:20 +03:00
Леонид Юрьев (Leonid Yuriev)
90635e7248 mdbx: исправление наследования dbi-хендла открытого в дочерней транзакции без изменения данных. 2025-03-02 16:03:20 +03:00
Леонид Юрьев (Leonid Yuriev)
1ec13c63ab mdbx: устранение сбоя аудита таблиц при инвалидации dbi-хендла вследствие отмены вложенной транзакции (backport). 2025-03-02 16:03:20 +03:00
Леонид Юрьев (Leonid Yuriev)
c712147eeb mdbx: исправление оплошности в спецификации формата при логировании имен таблиц (backport). 2025-03-02 16:03:20 +03:00
Леонид Юрьев (Leonid Yuriev)
23600241e1 mdbx: уменьшение в 16 раз предлагаемого размера БД для устранения проблем Valgrind/ASAN (backport). 2025-03-02 16:03:20 +03:00
Леонид Юрьев (Leonid Yuriev)
22c6763d57 mdbx-tests: удаление тестовой БД перед началом теста в extra/dupfix_addodd (backport). 2025-03-02 16:03:20 +03:00
Леонид Юрьев (Leonid Yuriev)
c585fcd613 mdbx-tests: расширение extra/open (backport). 2025-03-02 16:03:20 +03:00
Леонид Юрьев (Leonid Yuriev)
0ef0f49e2e mdbx: устранение излишнего предупреждения при смене размера БД во время открытия (backport).
Изменение геометрии (увеличение размера) больших БД может быть не
возможно после их открытия вследствие системных ограничений (отсутствия
свободного адресного пространства).

Поэтому API предусматривает возможность запросить изменение
геометрии/размера БД перед её открытием. В этом сценарии ранее могло
выдаваться лишнее/ненужное предупреждение о несоответствии файла БД
новому размеру. Теперь этот недостаток исправлен.

Спасибо Илье Михееву (Erigon) за сообщение об этом недочете.
2025-02-19 23:38:15 +03:00
Леонид Юрьев (Leonid Yuriev)
80de77b1ee mdbx-doc: опечатки в README (backport). 2025-02-16 22:15:32 +03:00
Леонид Юрьев (Leonid Yuriev)
822213f75d mdbx: информация о статусе Github (backport). 2025-02-15 15:46:44 +03:00
Леонид Юрьев (Leonid Yuriev)
aa2ff20faf mdbx: обновление патча для старых версий buildroot (backport). 2025-02-14 21:37:47 +03:00
Леонид Юрьев (Leonid Yuriev)
fcdd2e2db3 mdbx: обновление ChangeLog. 2025-02-14 15:23:28 +03:00
Леонид Юрьев (Leonid Yuriev)
75122b311d mdbx: выпуск 0.13.4 "Sigma Boy".
Поддерживающий выпуск стабильной ветки с исправлением обнаруженных ошибок и устранением недочётов.

За перечнем доработок и изменений обращайтесь к [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 139 files changed, 391 insertions(+), 208 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2025-02-14 12:56:12 +03:00
Леонид Юрьев (Leonid Yuriev)
79572b4850 mdbx: корректировка излишне строгого условия в assert-проверке внутри recalculate_subpage_thresholds() (backport). 2025-02-11 14:03:13 +03:00
Леонид Юрьев (Leonid Yuriev)
24f2b9b099 mdbx-conan: исправление опечатки в имени переменной version_json_pathname в verbose-выводе.
Спасибо Виктору Логунову (https://t.me/vl_username) за сообщение о проблеме.
2025-02-03 18:46:53 +03:00
Леонид Юрьев (Leonid Yuriev)
b46d2def80 mdbx: дополнение ChangeLog. 2025-01-27 12:12:38 +03:00
Леонид Юрьев (Leonid Yuriev)
21630ea115 mdbx: устранение регресса вероятности SIGSEGV при вытеснении/spilling страниц (backport).
Ошибка внесена коммитом `a6f7d74a32a3cbcc310916a624a31302dbebfa07` от
2024-03-07 и присутствует в выпусках v0.13.1, v0.13.2, v0.13.3. Проблема
оставалась незамеченной из-за специфических условий и низкой вероятности
проявления.

Суть ошибки:

- функция cursor_touch() подготавливает стек страниц курсора к внесению
  изменений, при этом все страницы в стеке (от корневой до листовой
  в текущей позиции курсора) должны стать доступными для модификации.

- микрооптимизация добавленная коммитом пропускала обход стека, если
  корневая страница уже доступна для модификации, но это
  допустимо/корректно только при отсутствии в стеке вытесненных/spilled
  страниц.

- если же складывалась ситуация когда в стека была вытесненная
  некорневая страница, то она так и оставалась недоступной для записи и
  при попытке её изменения возникал SIGSEGV.
2025-01-27 11:18:50 +03:00
Леонид Юрьев (Leonid Yuriev)
6d346d8630 mdbx-cmake: поддержка MacOS universal binaries (backport).
Thank Alain Picard (Castor Technologies) so much for this patch and supporting the Java bindings!
2025-01-27 11:18:50 +03:00
Леонид Юрьев (Leonid Yuriev)
b59937adb8 mdbx-doc: исправление опечатки в упоминании mdbx_env_resurrect_after_fork() (backport). 2025-01-26 17:38:37 +03:00
Леонид Юрьев (Leonid Yuriev)
11e1346f9d mdbx: исправление опечатки в cursor_touch() (backport).
При переделке курсоров было пропущено отрицание в условии, при оценке
кол-ва страниц, которые могут потребоваться для выполнения операции.

В текущем понимании ошибка не приводила к каким-либо проблемам, ибо
оценка делает по верхней границе с существенным запасом, а в худшем
случае это могло приводить к прерыванию транзакции из-за достижения
ограничения на кол-во грязных страниц.
2025-01-26 17:05:40 +03:00
Леонид Юрьев (Leonid Yuriev)
a59c5f9316 mdbx: упрощение gcu_loose() (backport). 2025-01-26 10:16:02 +03:00
Леонид Юрьев (Leonid Yuriev)
27a2166be7 mdbx-doc: исправление орфографии/опечатки в ChangeLog (backport). 2025-01-21 15:41:21 +03:00
Леонид Юрьев (Leonid Yuriev)
c615e4d0a6 mdbx-doc: доработка/актуализация раздела "Restrictions & Caveats" (backport). 2025-01-19 02:30:35 +03:00
Леонид Юрьев (Leonid Yuriev)
03685aba5a mdbx-doc: разделение актуальных и устаревших/неподдерживаемых привязок в README (backport). 2025-01-18 19:03:14 +03:00
Леонид Юрьев (Leonid Yuriev)
4a0a32a54b mdbx-doc: добавление в README ссылки на архив сообщений телеграмм-группы 2020-2024 годов (backport). 2025-01-18 19:03:11 +03:00
Леонид Юрьев (Leonid Yuriev)
36abcc57f0 mdbx: обновление года в © (backport). 2025-01-18 10:58:31 +03:00
Леонид Юрьев (Leonid Yuriev)
88d782e5eb mdbx: обновление патча для старых версий buildroot (backport). 2025-01-14 13:08:18 +03:00
Леонид Юрьев (Leonid Yuriev)
d3daa23c63 mdbx: обновление ChangeLog (подготовка выпуска 0.13.4). 2025-01-13 13:13:26 +03:00
150 changed files with 3240 additions and 1650 deletions

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2020-2024 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> ###############################################
# Copyright (c) 2020-2025 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> ###############################################
# SPDX-License-Identifier: Apache-2.0
#
# Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`. Всё будет хорошо!
@@ -213,6 +213,20 @@ if(DEFINED PROJECT_NAME AND NOT MDBX_FORCE_BUILD_AS_MAIN_PROJECT)
else()
set(SUBPROJECT OFF)
set(NOT_SUBPROJECT ON)
# Setup Apple stuff which should be set prior to the first project() or enable_language()
if(APPLE)
# Enable universal binaries for macOS (target arm64 and x86_64)
if(NOT DEFINED CMAKE_OSX_ARCHITECTURES)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
endif()
# Set the minimum macOS deployment target if not already defined
if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET)
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.0")
endif()
endif()
project(libmdbx C)
if(NOT MDBX_AMALGAMATED_SOURCE AND NOT DEFINED BUILD_TESTING)
set(BUILD_TESTING ON)

View File

@@ -1,4 +1,4 @@
Copyright (c) 2015-2024 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
Copyright (c) 2015-2025 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -4,6 +4,306 @@ ChangeLog
English version [by liar Google](https://libmdbx-dqdkfa-ru.translate.goog/md__change_log.html?_x_tr_sl=ru&_x_tr_tl=en)
and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/libmdbx.dqdkfa.ru/md__change_log.html).
The source code is availale on [Gitflic](https://gitflic.ru/project/erthink/libmdbx) and mirrors on [abf.io](https://abf.io/erthink/libmdbx), [hub.mos.ru](https://hub.mos.ru/leo/libmdbx) and [Github](https://github.com/erthink/libmdbx).
Please use the `stable` branch or the latest release for production environment through stagging, but the `master` branch for development a derivative projects.
Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
Всё будет хорошо!
## v0.13.7 "Дружба" (Friendship) от 2025-07-30.
Поддерживающий выпуск стабильной ветки с исправлением обнаруженных ошибок и устранением недочётов,
в [международный день дружбы](https://www.un.org/ru/observances/friendship-day).
Благодарности:
- [Erigon](https://erigon.tech/) за спонсорство.
- [Артёму Воротникову](https://github.com/vorot93) за сообщение об ошибках и тестировании [призязок для Rust](https://github.com/vorot93/libmdbx-rs).
Исправления:
- Устранена критическая ошибка в функционале `mdbx_env_resurrect_after_fork()` при использовании SysV-семафоров.
Проявлялась ошибка только после порождения дочернего процесса посредством `fork()` на фоне выполняющейся пишущей транзакции, что
приводило к неверной работе семафоров и далее к самым различным ошибкам, вплоть до повреждения БД. Проблема существовала начиная с появления
`mdbx_env_resurrect_after_fork()` и затрагивала OSX, а также POSIX-платформы при сборке с опцией `MDBX_LOCKING=5`.
- Устранена проблема в API копирования БД на отличных от Linux системах POSIX,
а также в некоторых случаях при расположении целевого файла на не-локальной файловой системе.
Проблема проявлялась в основном на OSX, возвратом ошибки `EWOULDBLOCK`/`EAGAIN` (35),
что обусловлено недочетом/конфликтом блокировок `fcntl(F_SETLK)` и `flock()` в ядре ОС.
Переработана обработка ошибок захвата файловых блокировок в API копирования на системах POSIX.
- Устранена ошибка приводившая к неожиданному возврату `MDBX_BAD_DBI` при одновременном старте нескольких транзакций внутри одного процесса после открытия БД.
- Устранена ошибка приводившая к неожиданному возврату `MDBX_DBS_FULL` при повторном открытии уже открытых таблиц и уже достигнутом лимите открытых DBI-дескрипторов.
- Исправлена ошибка сборки для платформы Android при явном определении `_FILE_OFFSET_BITS`.
- Исправлена ошибка использования `ENOMEM` вместо `MDBX_ENOMEM`.
Что могло ломать сборку на не-POSIX/Windows платформах, в зависимости от конфигурации и/или версии SDK.
- Поправлено либо удалено несколько неверных assert-проверок, из-за которых происходили падения отладочных сборок в специфических ситуациях.
Главным образом, в коде функций `txn_end()`, `txn_lock()` и `txn_unlock()` как на Windows, так и на POSIX.
- Устранены несущественные предупреждения MSVC. Отключены предупреждения `C5286` и `C5287`.
Прочие доработки:
- Доработана логика отказа от использования OFD-блокировок на POSIX-платформах.
Теперь кроме `EINVAL` учитываются дополнительные коды ошибок (`ENOSYS`, `ENOIMPL`, `ENOTSUP`, `ENOSUPP`, `EOPNOTSUPP`),
что позволит работать собранной библиотеке в некоторых случаях,
когда актуальное ядро/контейнер/эмулятор не поддерживает требуемых системных вызовов.
- В тестовый фреймворк добавлена поддержка опции --numa # для привязки стохастического теста к NUMA-узлу,
а в battery/tmux-скрипте добавлено явное распределение по NUMA-узлам, что существенно увеличило КПД
при тестировании на NUMA-машинах.
- В стохастическом скрипте реализован случайный порядок запуска отдельных тестов.
--------------------------------------------------------------------------------
## v0.13.6 "Бузина" от 2025-04-22.
Поддерживающий выпуск стабильной ветки с исправлением обнаруженных ошибок и устранением недочётов,
в память о погибшем украинском историке и писателе [Алесе Бузине](https://ru.ruwiki.ru/wiki/Бузина,_Олесь_Алексеевич).
Благодарности:
- [Erigon](https://erigon.tech/) за спонсорство.
- [Илье Михееву](https://t.me/IlyaMkhv) и команде [Erigon](https://github.com/erigontech) за сообщения о проблеме и тестирование.
- [Алексею Костюку (aka Keller)](https://t.me/keller18306) за сообщения о проблеме копирования на NFS.
Исправления:
- Устранён регресс при использовании курсоров для DBI=0 (aka GC/FreeDB) в читающих транзакциях.
После рефакторинга и ряда оптимизаций для завершения/гашения
курсоров в читающих и пишущих транзакций, стал использоваться общий код.
Причем за основу, был взят соответствующий фрагмент относящийся к
пишущим транзакциям, в которых пользователю не позволяется
использоваться курсоры для DBI=0 и поэтому эта итераций пропускалась.
В результате, при завершении читающих транзакциях, курсоры связанные с
DBI=0 не завершались должным образом, а при их повторном использовании
или явном закрытии после завершения читающей транзакции происходило
обращение к уже освобожденной памяти. Если же такие курсоры
отсоединялись или закрывались до завершения читающей транзакции, то
ошибка не имела шансов на проявление.
- Устранён регресс в виде ошибки `EAGAIN` при копировании БД на NFS и CIFS/SMB.
При доработках/развитии API в функции копирования был добавлен захват
файловой блокировки посредством как `fcntl()`, так и `flock()`. Однако,
в зависимости от версии локального ядра, версии удалённого сервера NFS и
опций монтирования, это могло приводить к возврату POSIX-ошибки `EAGAIN`
(`11` на большинстве платформ, включая Linux).
- Устранена ошибка merge/rebase внутри `mdbx_txn_release_all_cursors_ex()`,
что могло приводить к последующим неожиданным ошибкам `MDBX_EBADSIGN` и утечкам памяти.
Для проверки сценария дополнен соответствующий тест.
- Исправлена assert-проверка в пути завершения вложенных транзакций.
Для проверки сценария дополнен соответствующий тест.
- Устранена возможность возврата неожиданной ошибки `MDBX_BUSY` из `mdbx_txn_lock(dont_wait=false)`.
- Для совместимости с GCC 15.x в режиме C23 изменен порядок указания атрибутов функций.
Изменение поведения:
- При невозможности отвязки курсора от его текущей транзакции функция `mdbx_cursor_bind()`
теперь возвращает `MDBX_EINVAL` вместо `MDBX_BAD_TXN`.
Прочие доработки:
- Во избежание потенциальных проблем отключено использование `copy_file_range()` на ядрах Linux 5.3 - 5.18.
- Вброс `std::invalid_argument` теперь производится явным сообщением `MDBX_EINVAL`.
- Уточнен тип адреса для пожертвований.
Ethereum/ERC-20 позволяет перечислять не только ETH, но и другие валюты/токены, в том числе USDC.
- Дополнен тест курсоров extra/cursor-closing.
- В `NOTICE` обновлена информация о Github.
--------------------------------------------------------------------------------
## v0.13.5 "Труба" от 2025-03-21
Поддерживающий выпуск стабильной ветки с исправлением обнаруженных ошибок и устранением недочётов.
Благодарности:
- [Erigon](https://erigon.tech/) за спонсорство.
- [Илье Михееву](https://t.me/IlyaMkhv) за сообщения о недочетах и тестирование.
- [Alex Sharov](https://github.com/AskAlexSharov) за сообщение об ошибках и тестирование.
- [maxc0d3r](https://gitflic.ru/user/maxc0d3r) for bug reporting and testing.
- [Alain Picard](https://github.com/castortech) for support [Java bindings](https://github.com/castortech/mdbxjni) and MacOS universal binaries patch for CMake build scenario,
also for bug reporting (put-`MDBX_MULTIPLE` regression). Big thank for assistance with debugging and testing.
Новое:
- Добавлена опция сборки `MDBX_ENABLE_NON_READONLY_EXPORT` позволяющая использовать в режиме чтения-записи БД расположенных в файловых системах экспортированных через NFS.
По-умолчанию опция выключена и при открытии в неэксклюзивном режиме чтения-записи БД расположенных файловых системах доступных извне по NFS будет возвращаться ошибка `MDBX_EREMOTE`.
Включение опции позволяет открывать БД в описанных выше ситуациях, но риск чтения неверных данных на удалённой стороне ложится на пользователя.
- Поддержка MacOS universal binaries при сборке посредством CMake.
- Для закрытия или отсоединения всех курсоров с получением их количества в API добавлена функция `mdbx_txn_release_all_cursors_ex()`.
- Добавлена операция `MDBX_SEEK_AND_GET_MULTIPLE` в API курсора, позволяющая за одну операцию выполнить позиционирование
курсора на конкретное значение и начать чтение multi-значений в пакетном режиме.
- Добавлены методы `mdbx::cursor::put_multiple_samelength()`, `mdbx::cursor::seek_multiple_samelength()`, `mdbx::cursor_managed::withdraw_handle()`.
- В политику управления выделением для `mdbx::buffer<ALLOCATOR, CAPACITY_POLICY>` добавлен параметр `inplace_storage_size_rounding`.
Одновременно с этим переработан внутренний union-тип `mdbx::buffer<ALLOCATOR, CAPACITY_POLICY>::silo::bin` для возможности увеличения без пенальти встроенного в экземпляр буфера места под данные.
- В API добавлена функция `mdbx_cursor_close2()` возвращающая код ошибки.
Исправления:
- Устранение лишнего/ненужного предупреждения в сценарии изменения размера БД посредством вызова `mdbx_env_set_geometry()` до её открытия.
API предусматривает возможность запросить изменение геометрии/размера БД перед её открытием, чтобы избежать как лишних накладных расходов,
так и потенциальных ошибок из-за нехватки адресного пространства. В этом сценарии ранее могло выдаваться лишнее/ненужное предупреждение
о несоответствии файла БД новому размеру. Теперь этот недостаток исправлен.
- Восстановлена доступность дескрипторов таблиц, открытых в дочерней транзакции, после её фиксации, в случае отсутствия изменений в данных.
Проблема не была замечена ранее из-за специфического сценария проявления.
Ошибка присутствует в версиях 0.13.x и последующих, начиная с коммита `e6af7d7c53428ca2892bcbf7eec1c2acee06fd44` от 2023-11-05.
- Устранён сбой аудита таблиц при инвалидации дескрипторов таблиц вследствие отмены вложенной транзакции.
Проблема не была замечена ранее из-за специфического сценария проявления.
Ошибка присутствует в версиях 0.13.x и последующих, начиная с коммита `e6af7d7c53428ca2892bcbf7eec1c2acee06fd44` от 2023-11-05.
- Устранена причина потенциальных сбоев и/и деградации производительности в сценарии закрытия курсора до завершения вложенной транзакции,
с последующим изменением данных той-же таблицы в текущей вложенной транзакции, либо её дочерних транзакциях.
Проблема обнаружена при ручном анализе кода, сценарии воспроизведения/проявления проблемы пока не известны.
Ошибка присутствует в версиях 0.13.x и последующих, начиная с коммита `3de3d425a128a3c6f7866503f5f93b80c09dbe41` от 2024-05-19.
- Устранена причина ложных ошибок при работе `mdbx_chk` с высоким уровнем логирования.
Проблема возникала из-за неверной трактовки `MDBX_NOTFOUND` при штатном окончании итерируемых данных.
- Устранена причина попыток рекурсивного захвата мьютекса при работе `mdbx_chk -w` в сборах с поддержкой Valring/ASAN и под управлением этих инструментов.
- Устранены проверки потока владеющего транзакцией при сборке с опцией `MDBX_TXN_CHECKOWNER=OFF`.
- Устранена вероятность ситуации гонки в `tbl_setup(MDBX_DUPFIXED | MDBX_INTEGERDUP)` при работе в разных потоках.
В реальных сценариях вероятность проявления проблемы была близка к нулю.
Для подробностей смотрите комментарий коммита `3e91500fac475947f5b58268d5edd3c9cc4f77f6`.
- Устранён регресс затенения курсоров во вложенных транзакциях.
При реализации отложенной/ленивой инициализации dbi-дескрипторов также было реализовано отложенное затенение курсоров (создание копии состояния для отката при прерывании транзакции),
что существенно уменьшало накладные расходы при старте и завершении вложенных транзакций в сценариях с большим количеством курсоров.
Однако, была допущена логическая ошибка, вследствие которой отложенная инициализация и затенение выполнялись при использовании dbi-дескрипторов, но не курсора открытого в родительской транзакции.
В результате, родительские курсоры во вложенных транзакциях могли не затеняться, что приводило к неконсистентному состоянию в случае
прерывания/откате вложенной транзакции и в соответствующей таблицы были изменения в рамках прерванной вложенной транзакции.
Проблема не реализовывалась в тестовых сценариях и не была замечена при эксплуатации, но была обнаружена при расширении тестов.
Ошибка присутствует в версиях 0.13.x и последующих, начиная с коммита `e6af7d7c53428ca2892bcbf7eec1c2acee06fd44` от 2023-11-05.
- Устранён регресс в пути обработки операции `MDBX_MULTIPLE`.
Пакетная вставка значений посредством `MDBX_MULTIPLE` могла приводить к падениям и повреждению структуры БД. Ошибка оставалось не
замеченной из-за специфических условий проявления, которые не реализовались в тестах.
Проблема присутствовала во всех выпусках начиная с v0.13.1, но соответствующая ошибка не связана с конкретным коммита в истории, а
является следствием нескольких доработок (шагов рефакторинга), которые суммарно привели к регрессу.
Технически ошибка обусловлена не-обнулением переменной, чего не происходило в некотором пути выполнения, так как исходно не требовалось.
Однако, такое обнуление потребовалось после ряда этапов оптимизации и рефакторинга смежных участков кода.
Для подробностей смотрите комментарий коммита `23a417fe19614481c6546845995d6dc845baf797`.
- Скорректировано описание ошибки `MDBX_MVCC_RETARDED` и текста соответствующего сообщения.
- В C++ API добавлена упущенная проверка `__cpp_concepts >= 202002` для использования концептов C++.
Изменение поведения:
- Функция `mdbx_txn_release_all_cursors()` возвращает только код ошибки, не смешивая его с количеством обработанных/закрытых курсоров.
Для аналогичных действий с получением количества закрытых курсоров в API добавлена функция `mdbx_txn_release_all_cursors_ex()`.
- Использование системного кода ошибки `EREMOTEIO` ("Remote I/O error") вместо `ENOTBLK` ("Block device required") в качестве `MDBX_EREMOTE` для индикации ошибочной ситуации открытия БД расположенной на сетевом носителе.
- Для основных вариантов использования шаблона `mdbx::buffer<>` теперь явно инстанцируются внутри библиотеки,
одновременно соответствующие специализации шаблона помечены как `external` для предотвращения повторного инстанцирования в пользовательском коде.
- Запрещена отвязка/открепление курсоров во вложенных транзакциях, т.е. вызовы `mdbx_cursor_unbind()` и
`mdbx_txn_release_all_cursors(unbind=true)` для курсоров открытых в одной из родительских транзакций.
Причина в том, что в случае отмены вложенной транзакции возникает неконструктивная неопределенность
— следует ли восстанавливать состояние курсоров. Если не восстанавливать, то получается что вложенная транзакция может
поломать родительскую, сделав её продолжение невозможным. Если восстанавливать, то также следует «воскрешать» закрытые
курсоры, что неизбежно приведет к путанице, утечкам памяти и использованию после освобождения.
- В C++ API отменён вброс исключения при запросе транзакции у отсоединённого курсора посредством вызова `mdbx::cursor::txn()`.
Прочие доработки:
- Доработка использования LTO в CMake-сценариях: использование `-flto=auto` для GCC >= 11.4,
расслабление условий для включения LTO для CLANG на Linux, расширение поиска `LLVMgold.so` в относительных lib-директориях.
- Добавлены дополнительные проверки сигнатур курсоров при итерации связанных списков.
- Кратное сокращение итераций тестов в зависимости от конфигурации Valgrind/Debug/CI.
- Устранены предупреждения UBASN о невыравненном доступе в тесте extra/close-dbi.
- Добавлен перехват и логирование исключений в extra-тестах на C++.
- Расширены тесты extra/dupfix-multiple, extra/cursor-closing и extra/txn.
- В утилиту тестирования добавлена поддержка режима/опции `MDBX_VALIDATION` и поддержка значений `on`/`off` для опций командной строки.
- Добавлены doxygen-описания для doubtless-positioning констант.
- Переработана проверка курсоров на входе в API-функций с добавлением `cursor_check()`, `cursor_reset()` и `cursor_drown()`.
- Отключено использование C23 `[[атрибутов]]` для версий CLANG меньше 20.
--------------------------------------------------------------------------------
## v0.13.4 "Sigma Boy" от 2025-02-14
Поддерживающий выпуск стабильной ветки с исправлением обнаруженных ошибок и устранением недочётов.
Благодарности:
- [Erigon](https://docs.erigon.tech/) за спонсорство.
- [Алексею Костюку (aka Keller)](https://t.me/keller18306) за сообщения об ошибках и недочетах.
- [Alain Picard](https://github.com/castortech) for support [Java bindings](https://github.com/castortech/mdbxjni) and MacOS universal binaries patch for CMake build scenario.
- [Alex Sharov](https://github.com/AskAlexSharov) за сообщение об ошибках и тестирование.
- [Виктору Логунову](https://t.me/vl_username) за сообщение об опечатки в имени переменной в Conan-рецепте.
Новое:
- Поддержка MacOS universal binaries при сборке посредством CMake.
Исправления:
- Устранён регресс допускающий SIGSEGV в операциях обновления после вытеснения/spilling страниц в больших транзакциях.
Ошибка присутствует в выпусках v0.13.1, v0.13.2, v0.13.3 и оставалась незамеченной из-за специфических условий и низкой вероятности проявления.
Более подробная информация в описании коммита `21630ea115690a5cb39cfa921f9d199271a08102`.
- Исправлена опечатка в документации в упоминании `mdbx_env_resurrect_after_fork()`.
- Исправлена опечатка в условном операторе внутри `cursor_touch()`.
При переделке курсоров было пропущено отрицание в условии, при оценке количества страниц, которые могут потребоваться для выполнения операции.
В текущем понимании ошибка не приводила к каким-либо проблемам, ибо оценка делает по верхней границе с существенным запасом, а в худшем
случае это могло приводить к прерыванию транзакции из-за достижения ограничения на кол-во грязных страниц.
- Корректировка излишне строгого условия в assert-проверке внутри `recalculate_subpage_thresholds()`.
Ошибка могла проявляться только в отладочных сборках при выставлении определенной комбинации предельных значений опций `MDBX_opt_subpage_limit`,
`MDBX_opt_subpage_room_threshold`, `MDBX_opt_subpage_reserve_prereq`, `MDBX_opt_subpage_reserve_limit`.
- Исправление опечатки в Conan-рецепте в коде протокольно-отладочного вывода в имени переменной `version_json_pathname`.
--------------------------------------------------------------------------------
## v0.13.3 "Королёв" от 2025-01-12
@@ -2449,7 +2749,7 @@ Deprecated functions and flags:
- Rework `MADV_DONTNEED` threshold.
- Fix `mdbx_chk` utility for don't checking some numbers if walking on the B-tree was disabled.
- Use page's mp_txnid for basic integrity checking.
- Add `MDBX_FORCE_ASSERTIONS` built-time option.
- Add `MDBX_FORCE_ASSERTIONS` build-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'.

View File

@@ -164,6 +164,10 @@ else
$(info $(TIP) Use `make V=1` for verbose.)
endif
ifeq ($(UNAME),Darwin)
$(info $(TIP) Use `brew install gnu-sed gnu-tar` and add ones to the beginning of the PATH.)
endif
all: show-options $(LIBRARIES) $(MDBX_TOOLS)
help:
@@ -295,6 +299,10 @@ lib-shared libmdbx.$(SO_SUFFIX): mdbx-dylib.o $(call select_by,MDBX_BUILD_CXX,md
@echo ' LD $@'
$(QUIET)$(call select_by,MDBX_BUILD_CXX,$(CXX) $(CXXFLAGS),$(CC) $(CFLAGS)) $^ -pthread -shared $(LDFLAGS) $(call select_by,MDBX_BUILD_CXX,$(LIB_STDCXXFS)) $(LIBS) -o $@
ninja-assertions: CMAKE_OPT += -DMDBX_FORCE_ASSERTIONS=ON
ninja-assertions: cmake-build
ninja-debug: CMAKE_OPT += -DCMAKE_BUILD_TYPE=Debug
ninja-debug: cmake-build
ninja: cmake-build
cmake-build:
@echo " RUN: cmake -G Ninja && cmake --build"
@@ -376,8 +384,9 @@ endef
define uname2titer
case "$(UNAME)" in
CYGWIN*|MINGW*|MSYS*|Windows*) echo 2;;
Darwin*|Mach*) echo 2;;
*) echo 12;;
*) if [ -z "${CI}" ]; then echo 7; else echo 3; fi;;
esac
endef
@@ -392,6 +401,9 @@ TEST_ITER := $(shell $(uname2titer))
TEST_SRC := test/osal-$(TEST_OSAL).c++ $(filter-out $(wildcard test/osal-*.c++),$(wildcard test/*.c++)) $(call select_by,MDBX_BUILD_CXX,,src/mdbx.c++)
TEST_INC := $(wildcard test/*.h++)
TEST_OBJ := $(patsubst %.c++,%.o,$(TEST_SRC))
ifndef SED
SED := $(shell which gnu-sed 2>&- || echo sed)
endif
TAR ?= $(shell which gnu-tar 2>&- || echo tar)
ZIP ?= $(shell which zip || echo "echo 'Please install zip'")
CLANG_FORMAT ?= $(shell (which clang-format-19 || which clang-format) 2>/dev/null)
@@ -408,11 +420,11 @@ MAN_SRCDIR := src/man1/
ALLOY_DEPS := $(shell git ls-files src/ | grep -e /tools -e /man -v)
MDBX_GIT_DIR := $(shell if [ -d .git ]; then echo .git; elif [ -s .git -a -f .git ]; then grep '^gitdir: ' .git | cut -d ':' -f 2; else echo git_directory_is_absent; fi)
MDBX_GIT_LASTVTAG := $(shell git describe --tags --dirty=-DIRTY --abbrev=0 '--match=v[0-9]*' 2>&- || echo 'Please fetch tags and/or install non-obsolete git version')
MDBX_GIT_3DOT := $(shell set -o pipefail; echo "$(MDBX_GIT_LASTVTAG)" | sed -n 's|^v*\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\(.*\)|\1|p' || echo 'Please fetch tags and/or use non-obsolete git version')
MDBX_GIT_3DOT := $(shell set -o pipefail; echo "$(MDBX_GIT_LASTVTAG)" | $(SED) -n 's|^v*\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\(.*\)|\1|p' || echo 'Please fetch tags and/or use non-obsolete git version')
MDBX_GIT_TWEAK := $(shell set -o pipefail; git rev-list $(shell git describe --tags --abbrev=0 '--match=v[0-9]*')..HEAD --count 2>&- || echo 'Please fetch tags and/or use non-obsolete git version')
MDBX_GIT_TIMESTAMP := $(shell git show --no-patch --format=%cI HEAD 2>&- || echo 'Please install latest get version')
MDBX_GIT_DESCRIBE := $(shell git describe --tags --long --dirty '--match=v[0-9]*' 2>&- || echo 'Please fetch tags and/or install non-obsolete git version')
MDBX_GIT_PRERELEASE := $(shell echo "$(MDBX_GIT_LASTVTAG)" | sed -n 's|^v*\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\(.*\)-\([-.0-1a-zA-Z]\+\)|\3|p')
MDBX_GIT_PRERELEASE := $(shell echo "$(MDBX_GIT_LASTVTAG)" | $(SED) -n 's|^v*\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\(.*\)-\([-.0-1a-zA-Z]\+\)|\3|p')
MDBX_VERSION_PURE = $(MDBX_GIT_3DOT)$(if $(filter-out 0,$(MDBX_GIT_TWEAK)),.$(MDBX_GIT_TWEAK),)$(if $(MDBX_GIT_PRERELEASE),-$(MDBX_GIT_PRERELEASE),)
MDBX_VERSION_IDENT = $(shell set -o pipefail; echo -n '$(MDBX_GIT_DESCRIBE)' | tr -c -s '[a-zA-Z0-9.]' _)
MDBX_VERSION_NODOT = $(subst .,_,$(MDBX_VERSION_IDENT))
@@ -424,7 +436,7 @@ MDBX_SMOKE_EXTRA ?=
check: DESTDIR = $(shell pwd)/@check-install
check: CMAKE_OPT = -Werror=dev
check: smoke-assertion ninja dist install test ctest
check: smoke-assertion ninja-assertions dist install test ctest
smoke-assertion: MDBX_BUILD_OPTIONS:=$(strip $(MDBX_BUILD_OPTIONS) -DMDBX_FORCE_ASSERTIONS=1 -UNDEBUG -DMDBX_DEBUG=0)
smoke-assertion: smoke
@@ -552,7 +564,7 @@ $(MDBX_GIT_DIR)/HEAD $(MDBX_GIT_DIR)/index $(MDBX_GIT_DIR)/refs/tags:
src/version.c: src/version.c.in $(lastword $(MAKEFILE_LIST)) $(MDBX_GIT_DIR)/HEAD $(MDBX_GIT_DIR)/index $(MDBX_GIT_DIR)/refs/tags LICENSE NOTICE
@echo ' MAKE $@'
$(QUIET)sed \
$(QUIET)$(SED) \
-e "s|@MDBX_GIT_TIMESTAMP@|$(MDBX_GIT_TIMESTAMP)|" \
-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')|" \
@@ -586,7 +598,7 @@ mdbx-static.o: src/config.h src/version.c src/alloy.c $(ALLOY_DEPS) $(lastword $
docs/Doxyfile: docs/Doxyfile.in src/version.c $(lastword $(MAKEFILE_LIST))
@echo ' MAKE $@'
$(QUIET)sed \
$(QUIET)$(SED) \
-e "s|@MDBX_GIT_TIMESTAMP@|$(MDBX_GIT_TIMESTAMP)|" \
-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')|" \
@@ -602,7 +614,7 @@ docs/Doxyfile: docs/Doxyfile.in src/version.c $(lastword $(MAKEFILE_LIST))
define md-extract-section
docs/__$(1).md: $(2) $(lastword $(MAKEFILE_LIST))
@echo ' EXTRACT $1'
$(QUIET)sed -n '/<!-- section-begin $(1) -->/,/<!-- section-end -->/p' $(2) >$$@ && test -s $$@
$(QUIET)$(SED) -n '/<!-- section-begin $(1) -->/,/<!-- section-end -->/p' $(2) >$$@ && test -s $$@
endef
$(foreach section,overview mithril characteristics improvements history usage performance bindings,$(eval $(call md-extract-section,$(section),README.md)))
@@ -617,16 +629,16 @@ docs/overall.md: docs/__overview.md docs/_toc.md docs/__mithril.md docs/__histor
docs/intro.md: docs/_preface.md docs/__characteristics.md docs/__improvements.md docs/_restrictions.md docs/__performance.md
@echo ' MAKE $@'
$(QUIET)cat $^ | sed 's/^Performance comparison$$/Performance comparison {#performance}/;s/^Improvements beyond LMDB$$/Improvements beyond LMDB {#improvements}/' >$@
$(QUIET)cat $^ | $(SED) 's/^Performance comparison$$/Performance comparison {#performance}/;s/^Improvements beyond LMDB$$/Improvements beyond LMDB {#improvements}/' >$@
docs/usage.md: docs/__usage.md docs/_starting.md docs/__bindings.md
@echo ' MAKE $@'
$(QUIET)echo -e "\\page usage Usage\n\\section getting Building & Embedding" | cat - $^ | sed 's/^Bindings$$/Bindings {#bindings}/' >$@
$(QUIET)echo -e "\\page usage Usage\n\\section getting Building & Embedding" | cat - $^ | $(SED) 's/^Bindings$$/Bindings {#bindings}/' >$@
doxygen: docs/Doxyfile docs/overall.md docs/intro.md docs/usage.md mdbx.h mdbx.h++ src/options.h ChangeLog.md COPYRIGHT LICENSE NOTICE $(lastword $(MAKEFILE_LIST))
@echo ' RUNNING doxygen...'
$(QUIET)rm -rf docs/html && \
cat mdbx.h | tr '\n' '\r' | sed -e 's/LIBMDBX_INLINE_API\s*(\s*\([^,]\+\),\s*\([^,]\+\),\s*(\s*\([^)]\+\)\s*)\s*)\s*{/inline \1 \2(\3) {/g' | tr '\r' '\n' >docs/mdbx.h && \
cat mdbx.h | tr '\n' '\r' | $(SED) -e 's/LIBMDBX_INLINE_API\s*(\s*\([^,]\+\),\s*\([^,]\+\),\s*(\s*\([^)]\+\)\s*)\s*)\s*{/inline \1 \2(\3) {/g' | tr '\r' '\n' >docs/mdbx.h && \
cp mdbx.h++ src/options.h ChangeLog.md docs/ && (cd docs && doxygen Doxyfile $(HUSH)) && cp COPYRIGHT LICENSE NOTICE docs/html/
mdbx++-dylib.o: src/config.h src/mdbx.c++ mdbx.h mdbx.h++ $(lastword $(MAKEFILE_LIST))
@@ -650,7 +662,7 @@ release-assets: libmdbx-amalgamated-$(MDBX_GIT_3DOT).zpaq \
libmdbx-amalgamated-$(MDBX_GIT_3DOT).tar.gz \
libmdbx-amalgamated-$(subst .,_,$(MDBX_GIT_3DOT)).zip
$(QUIET)([ \
"$$(set -o pipefail; git describe | sed -n '/^v[0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}$$/p' || echo fail-left)" \
"$$(set -o pipefail; git describe | $(SED) -n '/^v[0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}$$/p' || echo fail-left)" \
== \
"$$(git describe --tags --dirty=-dirty || echo fail-right)" ] \
|| (echo 'ERROR: Is not a valid release because not in the clean state with a suitable annotated tag!!!' >&2 && false)) \
@@ -660,7 +672,7 @@ release-assets: libmdbx-amalgamated-$(MDBX_GIT_3DOT).zpaq \
@echo -n ' VERIFY amalgamated sources...'
$(QUIET)rm -rf $@ $(DIST_DIR)/@tmp-essentials.inc $(DIST_DIR)/@tmp-internals.inc \
&& if grep -R "define xMDBX_ALLOY" dist | grep -q MDBX_BUILD_SOURCERY; then echo "sed output is WRONG!" >&2; exit 2; fi \
&& rm -rf @dist-check && cp -r -p $(DIST_DIR) @dist-check && ($(MAKE) -j IOARENA=false CXXSTD=$(CXXSTD) -C @dist-check all ninja >@dist-check.log 2>@dist-check.err || (cat @dist-check.err && exit 1)) \
&& rm -rf @dist-check && cp -r -p $(DIST_DIR) @dist-check && ($(MAKE) -j IOARENA=false CXXSTD=$(CXXSTD) -C @dist-check all ninja-assertions >@dist-check.log 2>@dist-check.err || (cat @dist-check.err && exit 1)) \
&& touch $@ || (echo " FAILED! See @dist-check.log and @dist-check.err" >&2; exit 2) && echo " Ok"
%.tar.gz: @dist-checked.tag
@@ -687,7 +699,7 @@ $(DIST_DIR)/@tmp-essentials.inc: src/version.c $(ALLOY_DEPS) $(lastword $(MAKEFI
@echo ' ALLOYING...'
$(QUIET)mkdir -p dist \
&& (grep -v '#include ' src/alloy.c && echo '#define MDBX_BUILD_SOURCERY $(MDBX_BUILD_SOURCERY)' \
&& sed \
&& $(SED) \
-e 's|#include "../mdbx.h"|@INCLUDE "mdbx.h"|' \
-e '/#include "preface.h"/r src/preface.h' \
-e '/#include "osal.h"/r src/osal.h' \
@@ -699,14 +711,14 @@ $(DIST_DIR)/@tmp-essentials.inc: src/version.c $(ALLOY_DEPS) $(lastword $(MAKEFI
-e '/#include "utils.h"/r src/utils.h' \
-e '/#include "pnl.h"/r src/pnl.h' \
src/essentials.h \
| sed \
| $(SED) \
-e '/#pragma once/d' -e '/#include "/d' \
-e '/ clang-format o/d' -e '/ \*INDENT-O/d' \
| grep -v '^/// ') >$@
$(DIST_DIR)/@tmp-internals.inc: $(DIST_DIR)/@tmp-essentials.inc src/version.c $(ALLOY_DEPS) $(lastword $(MAKEFILE_LIST))
$(QUIET)(cat $(DIST_DIR)/@tmp-essentials.inc \
&& sed \
&& $(SED) \
-e '/#include "essentials.h"/d' \
-e '/#include "atomics-ops.h"/r src/atomics-ops.h' \
-e '/#include "proto.h"/r src/proto.h' \
@@ -728,22 +740,22 @@ $(DIST_DIR)/@tmp-internals.inc: $(DIST_DIR)/@tmp-essentials.inc src/version.c $(
-e '/#include "walk.h"/r src/walk.h' \
-e '/#include "windows-import.h"/r src/windows-import.h' \
src/internals.h \
| sed \
| $(SED) \
-e '/#pragma once/d' -e '/#include "/d' \
-e '/ clang-format o/d' -e '/ \*INDENT-O/d' \
| grep -v '^/// ') >$@
$(DIST_DIR)/mdbx.c: $(DIST_DIR)/@tmp-internals.inc $(lastword $(MAKEFILE_LIST))
@echo ' MAKE $@'
$(QUIET)(cat $(DIST_DIR)/@tmp-internals.inc $(shell git ls-files src/*.c | grep -v alloy) src/version.c | sed \
$(QUIET)(cat $(DIST_DIR)/@tmp-internals.inc $(shell git ls-files src/*.c | grep -v alloy) src/version.c | $(SED) \
-e '/#include "debug_begin.h"/r src/debug_begin.h' \
-e '/#include "debug_end.h"/r src/debug_end.h' \
) | sed -e '/#include "/d;/#pragma once/d' -e 's|@INCLUDE|#include|' \
) | $(SED) -e '/#include "/d;/#pragma once/d' -e 's|@INCLUDE|#include|' \
-e '/ clang-format o/d;/ \*INDENT-O/d' -e '3i /* clang-format off */' | cat -s >$@
$(DIST_DIR)/mdbx.c++: $(DIST_DIR)/@tmp-essentials.inc src/mdbx.c++ $(lastword $(MAKEFILE_LIST))
@echo ' MAKE $@'
$(QUIET)cat $(DIST_DIR)/@tmp-essentials.inc src/mdbx.c++ | sed \
$(QUIET)cat $(DIST_DIR)/@tmp-essentials.inc src/mdbx.c++ | $(SED) \
-e '/#define xMDBX_ALLOY/d' \
-e '/#include "/d;/#pragma once/d' \
-e 's|@INCLUDE|#include|;s|"mdbx.h"|"mdbx.h++"|' \
@@ -753,12 +765,12 @@ define dist-tool-rule
$(DIST_DIR)/mdbx_$(1).c: src/tools/$(1).c src/tools/wingetopt.h src/tools/wingetopt.c \
$(DIST_DIR)/@tmp-internals.inc $(lastword $(MAKEFILE_LIST))
@echo ' MAKE $$@'
$(QUIET)mkdir -p dist && sed \
$(QUIET)mkdir -p dist && $(SED) \
-e '/#include "essentials.h"/r $(DIST_DIR)/@tmp-essentials.inc' \
-e '/#include "wingetopt.h"/r src/tools/wingetopt.c' \
-e '/ clang-format o/d' -e '/ \*INDENT-O/d' \
src/tools/$(1).c \
| sed -e '/#include "/d;/#pragma once/d;/#define xMDBX_ALLOY/d' -e 's|@INCLUDE|#include|' \
| $(SED) -e '/#include "/d;/#pragma once/d;/#define xMDBX_ALLOY/d' -e 's|@INCLUDE|#include|' \
-e '/ clang-format o/d;/ \*INDENT-O/d' -e '9i /* clang-format off */' | cat -s >$$@
endef
@@ -767,7 +779,7 @@ $(foreach file,$(TOOLS),$(eval $(call dist-tool-rule,$(file))))
define dist-extra-rule
$(DIST_DIR)/$(1): $(1) src/version.c $(lastword $(MAKEFILE_LIST))
@echo ' REFINE $$@'
$(QUIET)mkdir -p $$(dir $$@) && sed -e '/^#> dist-cutoff-begin/,/^#< dist-cutoff-end/d' $$< | cat -s >$$@
$(QUIET)mkdir -p $$(dir $$@) && $(SED) -e '/^#> dist-cutoff-begin/,/^#< dist-cutoff-end/d' $$< | cat -s >$$@
endef
$(foreach file,mdbx.h mdbx.h++ $(filter-out man1/% VERSION.json .clang-format-ignore %.in ntdll.def,$(DIST_EXTRA)),$(eval $(call dist-extra-rule,$(file))))
@@ -798,17 +810,21 @@ endif
# Cross-compilation simple test
CROSS_LIST = \
mips64-linux-gnuabi64-gcc mips-linux-gnu-gcc \
hppa-linux-gnu-gcc s390x-linux-gnu-gcc \
powerpc64-linux-gnu-gcc powerpc-linux-gnu-gcc \
arm-linux-gnueabihf-gcc aarch64-linux-gnu-gcc
aarch64-linux-gnu-gcc \
arm-linux-gnueabihf-gcc \
hppa-linux-gnu-gcc \
mips64-linux-gnuabi64-gcc \
mips-linux-gnu-gcc \
powerpc64-linux-gnu-gcc\
riscv64-linux-gnu-gcc \
s390x-linux-gnu-gcc \
sh4-linux-gnu-gcc
## On Ubuntu Focal (22.04) with QEMU 6.2 (1:6.2+dfsg-2ubuntu6.6) & GCC 11.3 (11.3.0-1ubuntu1~22.04)
# sh4-linux-gnu-gcc - coredump (qemu mmap-troubles)
# sparc64-linux-gnu-gcc - coredump (qemu mmap-troubles, previously: qemu fails fcntl for F_SETLK/F_GETLK)
# alpha-linux-gnu-gcc - coredump (qemu mmap-troubles)
# risc64-linux-gnu-gcc - coredump (qemu qemu fails fcntl for F_SETLK/F_GETLK)
CROSS_LIST_NOQEMU = sh4-linux-gnu-gcc sparc64-linux-gnu-gcc alpha-linux-gnu-gcc riscv64-linux-gnu-gcc
## On Ubuntu Noble (24.04.2) with QEMU 8.2 (8.2.2+ds-0ubuntu1.7) & GCC 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04)
# sparc64-linux-gnu-gcc - fails mmap/BAD_ADDRESS (previously: qemu-coredump sice mmap-troubles, qemu fails fcntl for F_SETLK/F_GETLK)
# alpha-linux-gnu-gcc - qemu-coredump (qemu mmap-troubles)
# powerpc-linux-gnu-gcc - fails mmap/BAD_ADDRESS (previously: qemu-coredump sice mmap-troubles, qemu fails fcntl for F_SETLK/F_GETLK)
CROSS_LIST_NOQEMU = sparc64-linux-gnu-gcc alpha-linux-gnu-gcc powerpc-linux-gnu-gcc
cross-gcc:
@echo ' Re-building by cross-compiler for: $(CROSS_LIST_NOQEMU) $(CROSS_LIST)'
@@ -816,7 +832,7 @@ cross-gcc:
@echo "FOR INSTANCE: sudo apt install \$$(apt list 'g++-*' | grep 'g++-[a-z0-9]\+-linux-gnu/' | cut -f 1 -d / | sort -u)"
$(QUIET)for CC in $(CROSS_LIST_NOQEMU) $(CROSS_LIST); do \
echo "===================== $$CC"; \
$(MAKE) IOARENA=false CXXSTD= clean && CC=$$CC CXX=$$(echo $$CC | sed 's/-gcc/-g++/') EXE_LDFLAGS=-static $(MAKE) IOARENA=false all || exit $$?; \
$(MAKE) IOARENA=false CXXSTD= clean && CC=$$CC CXX=$$(echo $$CC | $(SED) 's/-gcc/-g++/') EXE_LDFLAGS=-static $(MAKE) IOARENA=false all || exit $$?; \
done
# Unfortunately qemu don't provide robust support for futexes.
@@ -830,7 +846,7 @@ cross-qemu:
$(QUIET)for CC in $(CROSS_LIST); do \
echo "===================== $$CC + qemu"; \
$(MAKE) IOARENA=false CXXSTD= clean && \
CC=$$CC CXX=$$(echo $$CC | sed 's/-gcc/-g++/') EXE_LDFLAGS=-static MDBX_BUILD_OPTIONS="-DMDBX_SAFE4QEMU $(MDBX_BUILD_OPTIONS)" \
CC=$$CC CXX=$$(echo $$CC | $(SED) 's/-gcc/-g++/') EXE_LDFLAGS=-static MDBX_BUILD_OPTIONS="-DMDBX_LOCKING=5 -DMDBX_SAFE4QEMU $(MDBX_BUILD_OPTIONS)" \
$(MAKE) IOARENA=false smoke-singleprocess test-singleprocess || exit $$?; \
done
@@ -897,13 +913,13 @@ bench-$(1)_$(2).txt: $(3) $(IOARENA) $(lastword $(MAKEFILE_LIST))
$(QUIET)(export LD_LIBRARY_PATH="./:$$$${LD_LIBRARY_PATH}"; \
ldd $(IOARENA) | grep -i $(1) && \
$(IOARENA) -D $(1) -B batch -m $(BENCH_CRUD_MODE) -n $(2) \
| tee $$@ | grep throughput | sed 's/throughput/batch×N/' && \
| tee $$@ | grep throughput | $(SED) 's/throughput/batch×N/' && \
$(IOARENA) -D $(1) -B crud -m $(BENCH_CRUD_MODE) -n $(2) \
| tee -a $$@ | grep throughput | sed 's/throughput/ crud/' && \
| tee -a $$@ | grep throughput | $(SED) 's/throughput/ crud/' && \
$(IOARENA) -D $(1) -B iterate,get,iterate,get,iterate -m $(BENCH_CRUD_MODE) -r 4 -n $(2) \
| tee -a $$@ | grep throughput | sed '0,/throughput/{s/throughput/iterate/};s/throughput/ get/' && \
| tee -a $$@ | grep throughput | $(SED) '0,/throughput/{s/throughput/iterate/};s/throughput/ get/' && \
$(IOARENA) -D $(1) -B delete -m $(BENCH_CRUD_MODE) -n $(2) \
| tee -a $$@ | grep throughput | sed 's/throughput/ delete/' && \
| tee -a $$@ | grep throughput | $(SED) 's/throughput/ delete/' && \
true) || mv -f $$@ $$@.error
endef

32
NOTICE
View File

@@ -8,16 +8,32 @@ documentation, C++ API description and links to the original git repo
with the source code. Questions, feedback and suggestions are welcome
to the Telegram' group https://t.me/libmdbx.
Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
Donations are welcome to the Ethereum/ERC-20 `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
Всё будет хорошо!
Copyright 2015-2024 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
Copyright 2015-2025 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
SPDX-License-Identifier: Apache-2.0
For notes about the license change, credits and acknowledgments,
please refer to the COPYRIGHT file within original libmdbx source code
repository https://gitflic.ru/project/erthink/libmdbx
please refer to the COPYRIGHT file within libmdbx source.
On 2022-04-15 the Github administration, without any warning nor
explanation, deleted _libmdbx_ along with a lot of other projects,
simultaneously blocking access for many developers.
For the same reason ~~Github~~ is blacklisted forever.
---
On 2022-04-15, without any warnings or following explanations, the
Github administration deleted _libmdbx_, my account and all other
projects (status 404). A few months later, without any involvement or
notification from/to me, the projects were restored/opened in the "public
read-only archive" status from some kind of incomplete backup. I regard
these actions of Github as malicious sabotage, and I consider the Github
service itself to have lost trust forever.
As a result of what has happened, I will never, under any circumstances,
post the primary sources (aka origins) of my projects on Github, or rely
in any way on the Github infrastructure.
Nevertheless, realizing that it is more convenient for users of
_libmdbx_ and other my projects to access ones on Github, I do not want
to restrict their freedom or create inconvenience, and therefore I place
mirrors (aka mirrors) of such repositories on Github since 2025. At the
same time, I would like to emphasize once again that these are only
mirrors that can be frozen, blocked or deleted at any time, as was the
case in 2022.

View File

@@ -1,13 +1,15 @@
<!-- Required extensions: pymdownx.betterem, pymdownx.tilde, pymdownx.emoji, pymdownx.tasklist, pymdownx.superfences -->
> Please refer to the online [documentation](https://libmdbx.dqdkfa.ru)
> Please refer to the online [official libmdbx documentation site](https://libmdbx.dqdkfa.ru)
> with [`C` API description](https://libmdbx.dqdkfa.ru/group__c__api.html)
> and pay attention to the [`C++` API](https://gitflic.ru/project/erthink/libmdbx/blob?file=mdbx.h%2B%2B#line-num-1).
> Questions, feedback and suggestions are welcome to the [Telegram' group](https://t.me/libmdbx).
> Questions, feedback and suggestions are welcome to the [Telegram' group](https://t.me/libmdbx) (archive [1](https://libmdbx.dqdkfa.ru/tg-archive/messages1.html),
> [2](https://libmdbx.dqdkfa.ru/tg-archive/messages2.html), [3](https://libmdbx.dqdkfa.ru/tg-archive/messages3.html), [4](https://libmdbx.dqdkfa.ru/tg-archive/messages4.html),
> [5](https://libmdbx.dqdkfa.ru/tg-archive/messages5.html), [6](https://libmdbx.dqdkfa.ru/tg-archive/messages6.html), [7](https://libmdbx.dqdkfa.ru/tg-archive/messages7.html)).
> See the [ChangeLog](https://gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md) for `NEWS` and latest updates.
> Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
> Donations are welcome to the Ethereum/ERC-20 `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
> Всё будет хорошо!
@@ -63,7 +65,51 @@ Historically, _libmdbx_ is a deeply revised and extended descendant of the amazi
[Lightning Memory-Mapped Database](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database).
_libmdbx_ inherits all benefits from _LMDB_, but resolves some issues and adds [a set of improvements](#improvements-beyond-lmdb).
### MithrilDB and Future
## Github
### на Русском (мой родной язык)
Весной 2022, без каких-либо предупреждений или пояснений, администрация
Github удалила мой аккаунт и все проекты. Через несколько месяцев, без
какого-либо моего участия или уведомления, проекты были
восстановлены/открыты в статусе "public read-only archive" из какой-то
неполноценной резервной копии. Эти действия Github я расцениваю как
злонамеренный саботаж, а сам сервис Github считаю навсегда утратившим
какое-либо доверие.
Вследствие произошедшего, никогда и ни при каких условиях, я не буду
размещать на Github первоисточники (aka origins) моих проектов, либо
как-либо полагаться на инфраструктуру Github.
Тем не менее, понимая что пользователям моих проектов удобнее получать к
ним доступ именно на Github, я не хочу ограничивать их свободу или
создавать неудобство, и поэтому размещаю на Github зеркала (aka mirrors)
репозиториев моих проектов. При этом ещё раз акцентирую внимание, что
это только зеркала, которые могут быть заморожены, заблокированы или
удалены в любой момент, как это уже было в 2022.
### in English
In the spring of 2022, without any warnings or explanations, the Github
administration deleted my account and all projects. A few months later,
without any involvement or notification from me, the projects were
restored/opened in the "public read-only archive" status from some kind
of incomplete backup. I regard these actions of Github as malicious
sabotage, and I consider the Github service itself to have lost any
trust forever.
As a result of what has happened, I will never, under any circumstances,
post the primary sources (aka origins) of my projects on Github, or rely
in any way on the Github infrastructure.
Nevertheless, realizing that it is more convenient for users of my
projects to access them on Github, I do not want to restrict their
freedom or create inconvenience, and therefore I place mirrors of my
project repositories on Github. At the same time, I would like to
emphasize once again that these are only mirrors that can be frozen,
blocked or deleted at any time, as was the case in 2022.
## MithrilDB and Future
<!-- section-begin mithril -->
@@ -233,7 +279,7 @@ which is also (mostly) applicable to _libmdbx_ with minor clarification:
- a database could shared by multiple processes, i.e. no multi-process issues;
- no issues with moving a cursor(s) after the deletion;
- _libmdbx_ provides zero-overhead database compactification, so a database file could be shrinked/truncated in particular cases;
- excluding dist I/O time _libmdbx_ could be -3 times faster than BoltDB and up to 10-100K times faster than both BoltDB and LMDB in particular extreme cases;
- excluding disk I/O time _libmdbx_ could be 3 times faster than BoltDB and up to 10-100K times faster than both BoltDB and LMDB in particular extreme cases;
- _libmdbx_ provides more features compared to BoltDB and/or LMDB.
<!-- section-end -->
@@ -605,16 +651,19 @@ error when opening the database in a _WSL1_ environment.
### MacOS
Current [native build tools](https://en.wikipedia.org/wiki/Xcode) for
MacOS include GNU Make, CLANG and an outdated version of Bash.
Therefore, to build the library, it is enough to run `make all` in the
However, the build script uses GNU-kind of `sed` and `tar`.
So the easiest way to install all prerequirements is to use [Homebrew](https://brew.sh/),
just by `brew install bash make cmake ninja gnu-sed gnu-tar --with-default-names`.
Next, to build the library, it is enough to run `make all` in the
directory with source code, and run `make check` to execute the base
tests. If something goes wrong, it is recommended to install
[Homebrew](https://brew.sh/) and try again.
To run the [long stochastic test scenario](test/stochastic.sh), you
will need to install the current (not outdated) version of
[Bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)). To do this, I
recommend that you install [Homebrew](https://brew.sh/) and then execute
`brew install bash`.
[Bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)).
Just install it as noted above.
### Android
I recommend using CMake to build _libmdbx_ for Android.
@@ -639,19 +688,25 @@ Bindings
| Runtime | Repo | Author |
| ------- | ------ | ------ |
| Rust | [libmdbx-rs](https://github.com/vorot93/libmdbx-rs) | [Artem Vorotnikov](https://github.com/vorot93) |
| Python | [PyPi/libmdbx](https://pypi.org/project/libmdbx/) | [Lazymio](https://github.com/wtdcode) |
| Java | [mdbxjni](https://github.com/castortech/mdbxjni) | [Castor Technologies](https://castortech.com/) |
| Go | [mdbx-go](https://github.com/torquem-ch/mdbx-go) | [Alex Sharov](https://github.com/AskAlexSharov) |
| Ruby | [ruby-mdbx](https://rubygems.org/gems/mdbx/) | [Mahlon E. Smith](https://github.com/mahlonsmith) |
| Zig | [mdbx-zig](https://github.com/theseyan/lmdbx-zig) | [Sayan J. Das](https://github.com/theseyan) |
##### Obsolete/Outdated/Unsupported:
| Runtime | Repo | Author |
| ------- | ------ | ------ |
| .NET | [mdbx.NET](https://github.com/wangjia184/mdbx.NET) | [Jerry Wang](https://github.com/wangjia184) |
| Scala | [mdbx4s](https://github.com/david-bouyssie/mdbx4s) | [David Bouyssié](https://github.com/david-bouyssie) |
| Rust | [mdbx](https://crates.io/crates/mdbx) | [gcxfd](https://github.com/gcxfd) |
| Haskell | [libmdbx-hs](https://hackage.haskell.org/package/libmdbx) | [Francisco Vallarino](https://github.com/fjvallarino) |
| Lua | [lua-libmdbx](https://github.com/mah0x211/lua-libmdbx) | [Masatoshi Fukunaga](https://github.com/mah0x211) |
| NodeJS, [Deno](https://deno.land/) | [lmdbx-js](https://github.com/kriszyp/lmdbx-js) | [Kris Zyp](https://github.com/kriszyp/)
| NodeJS | [node-mdbx](https://www.npmjs.com/package/node-mdbx/) | [Сергей Федотов](mailto:sergey.fedotov@corp.mail.ru) |
| Ruby | [ruby-mdbx](https://rubygems.org/gems/mdbx/) | [Mahlon E. Smith](https://github.com/mahlonsmith) |
| Go | [mdbx-go](https://github.com/torquem-ch/mdbx-go) | [Alex Sharov](https://github.com/AskAlexSharov) |
| [Nim](https://en.wikipedia.org/wiki/Nim_(programming_language)) | [NimDBX](https://github.com/snej/nimdbx) | [Jens Alfke](https://github.com/snej)
| Lua | [lua-libmdbx](https://github.com/mah0x211/lua-libmdbx) | [Masatoshi Fukunaga](https://github.com/mah0x211) |
| Rust | [libmdbx-rs](https://github.com/vorot93/libmdbx-rs) | [Artem Vorotnikov](https://github.com/vorot93) |
| Rust | [mdbx](https://crates.io/crates/mdbx) | [gcxfd](https://github.com/gcxfd) |
| Java | [mdbxjni](https://github.com/castortech/mdbxjni) | [Castor Technologies](https://castortech.com/) |
| Python | [PyPi/libmdbx](https://pypi.org/project/libmdbx/) | [Lazymio](https://github.com/wtdcode) |
| .NET (obsolete) | [mdbx.NET](https://github.com/wangjia184/mdbx.NET) | [Jerry Wang](https://github.com/wangjia184) |
| Nim | [NimDBX](https://github.com/snej/nimdbx) | [Jens Alfke](https://github.com/snej)
<!-- section-end -->

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2010-2024 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> ###############################################
# Copyright (c) 2010-2025 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> ###############################################
# SPDX-License-Identifier: Apache-2.0
if(CMAKE_VERSION VERSION_LESS 3.8.2)
@@ -502,7 +502,11 @@ if(CMAKE_COMPILER_IS_GNU${CMAKE_PRIMARY_LANG}
AND CMAKE_GCC_RANLIB
AND gcc_lto_wrapper)
message(STATUS "Found GCC's LTO toolset: ${gcc_lto_wrapper}, ${CMAKE_GCC_AR}, ${CMAKE_GCC_RANLIB}")
set(GCC_LTO_CFLAGS "-flto -fno-fat-lto-objects -fuse-linker-plugin")
if(CMAKE_${CMAKE_PRIMARY_LANG}_COMPILER_VERSION VERSION_LESS 11.4)
set(GCC_LTO_CFLAGS "-flto -fno-fat-lto-objects -fuse-linker-plugin")
else()
set(GCC_LTO_CFLAGS "-flto=auto -fno-fat-lto-objects -fuse-linker-plugin")
endif()
set(GCC_LTO_AVAILABLE TRUE)
message(STATUS "Link-Time Optimization by GCC is available")
else()
@@ -541,13 +545,21 @@ if(CMAKE_COMPILER_IS_CLANG)
if(regexp_valid)
string(REGEX REPLACE "(^|\n.*)(.*programs: =)([^\n]+)((\n.*)|$)" "\\3" list ${clang_search_dirs})
string(REPLACE ":" ";" list "${list}")
set(libs_extra_subdirs "lib;../lib;lib64;../lib64;lib32;../lib32")
foreach(dir IN LISTS list)
get_filename_component(dir "${dir}" REALPATH)
if(dir MATCHES ".*llvm.*" OR dir MATCHES ".*clang.*")
list(APPEND clang_bindirs "${dir}")
set(list_suffix "")
else()
list(APPEND clang_bindirs_x "${dir}")
set(list_suffix "_x")
endif()
list(APPEND clang_bindirs${list_suffix} "${dir}")
foreach(subdir IN LISTS libs_extra_subdirs)
get_filename_component(subdir "${dir}/${subdir}" REALPATH)
if(EXISTS "${subdir}")
list(APPEND clang_libdirs${list_suffix} "${subdir}")
endif()
endforeach()
endforeach()
list(APPEND clang_bindirs "${clang_bindirs_x}")
list(REMOVE_DUPLICATES clang_bindirs)
@@ -559,10 +571,11 @@ if(CMAKE_COMPILER_IS_CLANG)
foreach(dir IN LISTS list)
get_filename_component(dir "${dir}" REALPATH)
if(dir MATCHES ".*llvm.*" OR dir MATCHES ".*clang.*")
list(APPEND clang_libdirs "${dir}")
set(list_suffix "")
else()
list(APPEND clang_libdirs_x "${dir}")
set(list_suffix "_x")
endif()
list(APPEND clang_libdirs${list_suffix} "${dir}")
endforeach()
list(APPEND clang_libdirs "${clang_libdirs_x}")
list(REMOVE_DUPLICATES clang_libdirs)
@@ -656,7 +669,7 @@ if(CMAKE_COMPILER_IS_CLANG)
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 CMAKE_CLANG_LD
OR APPLE))
if(ANDROID AND CMAKE_${CMAKE_PRIMARY_LANG}_COMPILER_VERSION VERSION_LESS 12)
set(CLANG_LTO_AVAILABLE FALSE)

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2012-2024 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> ###############################################
# Copyright (c) 2012-2025 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> ###############################################
# SPDX-License-Identifier: Apache-2.0
if(CMAKE_VERSION VERSION_LESS 3.8.2)

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2012-2024 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> ###############################################
# Copyright (c) 2012-2025 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> ###############################################
# SPDX-License-Identifier: Apache-2.0
if(CMAKE_VERSION VERSION_LESS 3.8.2)

View File

@@ -228,7 +228,7 @@ class libmdbx(ConanFile):
if os.path.exists(version_json_pathname):
self.version = json.load(
open(version_json_pathname, encoding='utf-8'))['semver']
version_from = "'" + version_jsonpath_name + "'"
version_from = "'" + version_json_pathname + "'"
else:
self.version = self.fetch_versioninfo_from_git()['semver']
version_from = 'Git'

View File

@@ -42,18 +42,33 @@ long-lived read transactions, and using the \ref MDBX_LIFORECLAIM mode
which addresses subsequent performance degradation. The "next" version
of libmdbx (aka \ref MithrilDB) will completely solve this.
- Avoid suspending a process with active transactions. These would then be
"long-lived" as above.
Nonetheless, situations that encourage lengthy read transactions while
intensively updating data should be avoided. For example, you should
avoid suspending/blocking processes/threads performing read
transactions, including during debugging, and use transaction parking if
necessary.
- Avoid aborting a process with an active read-only transaction in scenarios
with high rate of write transactions. The transaction becomes "long-lived"
as above until a check for stale readers is performed or the LCK-file is
reset, since the process may not remove it from the lockfile. This does
not apply to write transactions if the system clears stale writers, see
above.
You should also beware of aborting processes that perform reading
transactions. Despite the fact that libmdbx automatically checks and
cleans readers, as an a process aborting (especially with core dump) can
take a long time, and checking readers cannot be performed too often due
to performance degradation.
This issue will be addressed in MithrlDB and one of libmdbx releases,
presumably in 2025. To do this, nonlinear GC recycling will be
implemented, without stopping garbage recycling on the old MVCC snapshot
used by a long read transaction.
After the planned implementation, any long-term reading transaction will
still keep the used MVCC-snapshot (all the database pages forming it)
from being recycled, but it will allow all unused MVCC snapshots to be
recycled, both before and after the readable one. This will eliminate
one of the main architectural flaws inherited from LMDB and caused the
growth of a database in proportion to a volume of data changes made
concurrently with a long-running read transaction.
## Large data items and huge transactions
## Large data items
MDBX allows you to store values up to 1 gigabyte in size, but this is
not the main functionality for a key-value storage, but an additional
@@ -71,6 +86,18 @@ and in the absence of a sufficient sequence of free pages, increase the
DB file. Thus, for long values, MDBX provides maximum read performance
at the expense of write performance.
Some aspects related to GC have been refined and improved in 2022 within
the first releases of the 0.12.x series. In particular the search for
free consecutive/adjacent pages through GC has been significantly
speeded, including acceleration using NOEN/SSE2/AVX2/AVX512
instructions.
This issue will be addressed in MithrlDB and refined within one of
0.15.x libmdbx releases, presumably at end of 2025.
### Huge transactions
A similar situation can be with huge transactions, in which a lot of
database pages are retired. The retired pages should be put into GC as a
list of page numbers for future reuse. But in huge transactions, such a
@@ -80,20 +107,15 @@ you delete large amounts of information from the database in a single
transaction, MDBX may need to increase the database file to save the
list of pages to be retired.
Both of these issues will be addressed in MithrilDB.
This issue was fixed in 2022 within the first releases of the 0.12.x
series by `Big Foot` feature, which now is enabled by default.
See \ref MDBX_ENABLE_BIGFOOT build-time option.
#### Since v0.12.1 and later
Some aspects related to GC have been refined and improved, in particular:
- The search for free consecutive/adjacent pages through GC has been
significantly speeded, including acceleration using
NOEN/SSE2/AVX2/AVX512 instructions.
- The `Big Foot` feature which significantly reduces GC overhead for
processing large lists of retired pages from huge transactions. Now
libmdbx avoid creating large chunks of PNLs (page number lists) which
required a long sequences of free pages, aka large/overflow pages. Thus
avoiding searching, allocating and storing such sequences inside GC.
The `Big Foot` feature which significantly reduces GC overhead for
processing large lists of retired pages from huge transactions. Now
libmdbx avoid creating large chunks of PNLs (page number lists) which
required a long sequences of free pages, aka large/overflow pages. Thus
avoiding searching, allocating and storing such sequences inside GC.
## Space reservation
@@ -135,9 +157,11 @@ On the other hand, MDBX allow calling \ref mdbx_env_close() in such cases to
release resources, but no more and in general this is a wrong way.
#### Since v0.13.1 and later
Начиная с версии 0.13.1 в API доступна функция \ref mdbx_env_resurrect_after_fork(),
которая позволяет пере-использовать в дочерних процессах уже открытую среду БД,
но строго без наследования транзакций от родительского процесса.
Starting from the v0.13.1 release, the \ref mdbx_env_resurrect_after_fork()
is available, which allows you to reuse an already open database
environment in child processes, but strictly without inheriting any
transactions from a parent process.
## Read-only mode
@@ -149,9 +173,6 @@ to without-LCK mode on appropriate errors (`EROFS`, `EACCESS`, `EPERM`)
if the read-only mode was requested by the \ref MDBX_RDONLY flag which is
described below.
The "next" version of libmdbx (\ref MithrilDB) will solve this issue for the "many
readers without writer" case.
## Troubleshooting the LCK-file
1. A broken LCK-file can cause sync issues, including appearance of
@@ -166,8 +187,6 @@ readers without writer" case.
multiple processes in a lock-free manner and any locking is
unwise due to a large overhead.
The "next" version of libmdbx (\ref MithrilDB) will solve this issue.
\note Workaround: Just make all programs using the database close it;
the LCK-file is always reset on first open.

View File

@@ -1,4 +1,4 @@
The source code is availale on [Gitflic](https://gitflic.ru/project/erthink/libmdbx).
The source code is availale on [Gitflic](https://gitflic.ru/project/erthink/libmdbx) and mirrors on [abf.io](https://abf.io/erthink/libmdbx), [hub.mos.ru](https://hub.mos.ru/leo/libmdbx) and [Github](https://github.com/erthink/libmdbx).
Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
Всё будет хорошо!

View File

@@ -4,7 +4,7 @@
*/
/*
* Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015-2025 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2017 Ilya Shipitsin <chipitsine@gmail.com>.
* Copyright 2012-2015 Howard Chu, Symas Corp.
* All rights reserved.

View File

@@ -4,7 +4,7 @@
*/
/*
* Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015-2025 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2012-2015 Howard Chu, Symas Corp.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.

143
mdbx.h
View File

@@ -31,7 +31,7 @@ developers. For the same reason ~~Github~~ is blacklisted forever.
\copyright SPDX-License-Identifier: Apache-2.0
\note Please refer to the COPYRIGHT file for explanations license change,
credits and acknowledgments.
\author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
\author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
*******************************************************************************/
@@ -204,7 +204,7 @@ typedef mode_t mdbx_mode_t;
#ifndef __has_cpp_attribute
#define __has_cpp_attribute(x) 0
#define __has_cpp_attribute_qualified(x) 0
#elif defined(_MSC_VER)
#elif defined(_MSC_VER) || (__clang__ && __clang__ < 14)
/* MSVC don't support `namespace::attr` syntax */
#define __has_cpp_attribute_qualified(x) 0
#else
@@ -318,7 +318,7 @@ typedef mode_t mdbx_mode_t;
#ifndef MDBX_DEPRECATED
#ifdef __deprecated
#define MDBX_DEPRECATED __deprecated
#elif defined(DOXYGEN) || ((!defined(__GNUC__) || defined(__clang__) || __GNUC__ > 5) && \
#elif defined(DOXYGEN) || ((!defined(__GNUC__) || (defined(__clang__) && __clang__ > 19) || __GNUC__ > 5) && \
((defined(__cplusplus) && __cplusplus >= 201403L && __has_cpp_attribute(deprecated) && \
__has_cpp_attribute(deprecated) >= 201309L) || \
(!defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202304L)))
@@ -504,7 +504,7 @@ typedef mode_t mdbx_mode_t;
#if defined(DOXYGEN) || \
(defined(__cplusplus) && __cplusplus >= 201603L && __has_cpp_attribute(maybe_unused) && \
__has_cpp_attribute(maybe_unused) >= 201603L) || \
__has_cpp_attribute(maybe_unused) >= 201603L && (!defined(__clang__) || __clang__ > 19)) || \
(!defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ > 202005L)
#define MDBX_MAYBE_UNUSED [[maybe_unused]]
#elif defined(__GNUC__) || __has_attribute(__unused__)
@@ -1718,7 +1718,7 @@ typedef enum MDBX_cursor_op {
/** \ref MDBX_DUPFIXED -only: Return up to a page of duplicate data items
* from current cursor position. Move cursor to prepare
* for \ref MDBX_NEXT_MULTIPLE. */
* for \ref MDBX_NEXT_MULTIPLE. \see MDBX_SEEK_AND_GET_MULTIPLE */
MDBX_GET_MULTIPLE,
/** Position at last key/data item */
@@ -1734,8 +1734,8 @@ typedef enum MDBX_cursor_op {
MDBX_NEXT_DUP,
/** \ref MDBX_DUPFIXED -only: Return up to a page of duplicate data items
* from next cursor position. Move cursor to prepare
* for `MDBX_NEXT_MULTIPLE`. */
* from next cursor position. Move cursor to prepare for `MDBX_NEXT_MULTIPLE`.
* \see MDBX_SEEK_AND_GET_MULTIPLE \see MDBX_GET_MULTIPLE */
MDBX_NEXT_MULTIPLE,
/** Position at first data item of next key */
@@ -1760,7 +1760,8 @@ typedef enum MDBX_cursor_op {
MDBX_SET_RANGE,
/** \ref MDBX_DUPFIXED -only: Position at previous page and return up to
* a page of duplicate data items. */
* a page of duplicate data items.
* \see MDBX_SEEK_AND_GET_MULTIPLE \see MDBX_GET_MULTIPLE */
MDBX_PREV_MULTIPLE,
/** Positions cursor at first key-value pair greater than or equal to
@@ -1791,26 +1792,33 @@ typedef enum MDBX_cursor_op {
* \ref MDBX_NOTFOUND otherwise. */
MDBX_SET_UPPERBOUND,
/* Doubtless cursor positioning at a specified key. */
/** Doubtless cursor positioning at a specified key. */
MDBX_TO_KEY_LESSER_THAN,
MDBX_TO_KEY_LESSER_OR_EQUAL,
MDBX_TO_KEY_EQUAL,
MDBX_TO_KEY_GREATER_OR_EQUAL,
MDBX_TO_KEY_GREATER_THAN,
MDBX_TO_KEY_LESSER_OR_EQUAL /** \copydoc MDBX_TO_KEY_LESSER_THAN */,
MDBX_TO_KEY_EQUAL /** \copydoc MDBX_TO_KEY_LESSER_THAN */,
MDBX_TO_KEY_GREATER_OR_EQUAL /** \copydoc MDBX_TO_KEY_LESSER_THAN */,
MDBX_TO_KEY_GREATER_THAN /** \copydoc MDBX_TO_KEY_LESSER_THAN */,
/* Doubtless cursor positioning at a specified key-value pair
/** Doubtless cursor positioning at a specified key-value pair
* for dupsort/multi-value hives. */
MDBX_TO_EXACT_KEY_VALUE_LESSER_THAN,
MDBX_TO_EXACT_KEY_VALUE_LESSER_OR_EQUAL,
MDBX_TO_EXACT_KEY_VALUE_EQUAL,
MDBX_TO_EXACT_KEY_VALUE_GREATER_OR_EQUAL,
MDBX_TO_EXACT_KEY_VALUE_GREATER_THAN,
MDBX_TO_EXACT_KEY_VALUE_LESSER_OR_EQUAL /** \copydoc MDBX_TO_EXACT_KEY_VALUE_LESSER_THAN */,
MDBX_TO_EXACT_KEY_VALUE_EQUAL /** \copydoc MDBX_TO_EXACT_KEY_VALUE_LESSER_THAN */,
MDBX_TO_EXACT_KEY_VALUE_GREATER_OR_EQUAL /** \copydoc MDBX_TO_EXACT_KEY_VALUE_LESSER_THAN */,
MDBX_TO_EXACT_KEY_VALUE_GREATER_THAN /** \copydoc MDBX_TO_EXACT_KEY_VALUE_LESSER_THAN */,
/** Doubtless cursor positioning at a specified key-value pair
* for dupsort/multi-value hives. */
MDBX_TO_PAIR_LESSER_THAN,
MDBX_TO_PAIR_LESSER_OR_EQUAL,
MDBX_TO_PAIR_EQUAL,
MDBX_TO_PAIR_GREATER_OR_EQUAL,
MDBX_TO_PAIR_GREATER_THAN
MDBX_TO_PAIR_LESSER_OR_EQUAL /** \copydoc MDBX_TO_PAIR_LESSER_THAN */,
MDBX_TO_PAIR_EQUAL /** \copydoc MDBX_TO_PAIR_LESSER_THAN */,
MDBX_TO_PAIR_GREATER_OR_EQUAL /** \copydoc MDBX_TO_PAIR_LESSER_THAN */,
MDBX_TO_PAIR_GREATER_THAN /** \copydoc MDBX_TO_PAIR_LESSER_THAN */,
/** \ref MDBX_DUPFIXED -only: Seek to given key and return up to a page of
* duplicate data items from current cursor position. Move cursor to prepare
* for \ref MDBX_NEXT_MULTIPLE. \see MDBX_GET_MULTIPLE */
MDBX_SEEK_AND_GET_MULTIPLE
} MDBX_cursor_op;
/** \brief Errors and return codes
@@ -1964,8 +1972,7 @@ typedef enum MDBX_error {
* recycling old MVCC snapshots. */
MDBX_OUSTED = -30411,
/** MVCC snapshot used by read transaction is outdated and could not be
* copied since corresponding meta-pages was overwritten. */
/** MVCC snapshot used by parked transaction was bygone. */
MDBX_MVCC_RETARDED = -30410,
/* The last of MDBX-added error codes */
@@ -1999,7 +2006,12 @@ typedef enum MDBX_error {
MDBX_EPERM = EPERM,
MDBX_EINTR = EINTR,
MDBX_ENOFILE = ENOENT,
#if defined(EREMOTEIO) || defined(DOXYGEN)
/** Cannot use the database on a network file system or when exporting it via NFS. */
MDBX_EREMOTE = EREMOTEIO,
#else
MDBX_EREMOTE = ENOTBLK,
#endif /* EREMOTEIO */
MDBX_EDEADLK = EDEADLK
#endif /* !Windows */
} MDBX_error_t;
@@ -5116,6 +5128,10 @@ MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API void *mdbx_cursor_get_userctx(const MDBX_
* the same table handle as it was created with. This may be done whether the
* previous transaction is live or dead.
*
* If the transaction is nested, then the cursor should not be used in its parent transaction.
* Otherwise it is no way to restore state if this nested transaction will be aborted,
* nor impossible to define the expected behavior.
*
* \note In contrast to LMDB, the MDBX required that any opened cursors can be
* reused and must be freed explicitly, regardless ones was opened in a
* read-only or write transaction. The REASON for this is eliminates ambiguity
@@ -5131,7 +5147,7 @@ MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API void *mdbx_cursor_get_userctx(const MDBX_
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
* by current thread.
* \retval MDBX_EINVAL An invalid parameter was specified. */
LIBMDBX_API int mdbx_cursor_bind(const MDBX_txn *txn, MDBX_cursor *cursor, MDBX_dbi dbi);
LIBMDBX_API int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *cursor, MDBX_dbi dbi);
/** \brief Unbind cursor from a transaction.
* \ingroup c_cursors
@@ -5140,6 +5156,10 @@ LIBMDBX_API int mdbx_cursor_bind(const MDBX_txn *txn, MDBX_cursor *cursor, MDBX_
* the original DBI-handle internally. Thus it could be renewed with any running
* transaction or closed.
*
* If the transaction is nested, then the cursor should not be used in its parent transaction.
* Otherwise it is no way to restore state if this nested transaction will be aborted,
* nor impossible to define the expected behavior.
*
* \see mdbx_cursor_renew()
* \see mdbx_cursor_bind()
* \see mdbx_cursor_close()
@@ -5200,14 +5220,19 @@ LIBMDBX_API int mdbx_cursor_reset(MDBX_cursor *cursor);
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
* by current thread.
* \retval MDBX_EINVAL An invalid parameter was specified. */
LIBMDBX_API int mdbx_cursor_open(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **cursor);
LIBMDBX_API int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **cursor);
/** \brief Close a cursor handle.
/** \brief Closes a cursor handle without returning error code.
* \ingroup c_cursors
*
* The cursor handle will be freed and must not be used again after this call,
* but its transaction may still be live.
*
* This function returns `void` but panic in case of error. Use \ref mdbx_cursor_close2()
* if you need to receive an error code instead of an app crash.
*
* \see mdbx_cursor_close2
*
* \note In contrast to LMDB, the MDBX required that any opened cursors can be
* reused and must be freed explicitly, regardless ones was opened in a
* read-only or write transaction. The REASON for this is eliminates ambiguity
@@ -5218,11 +5243,59 @@ LIBMDBX_API int mdbx_cursor_open(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor
* or \ref mdbx_cursor_create(). */
LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor);
/** \brief Unbind or closes all cursors of a given transaction.
/** \brief Closes a cursor handle with returning error code.
* \ingroup c_cursors
*
* Unbinds either closes all cursors associated (opened or renewed) with
* a given transaction in a bulk with minimal overhead.
* The cursor handle will be freed and must not be used again after this call,
* but its transaction may still be live.
*
* \see mdbx_cursor_close
*
* \note In contrast to LMDB, the MDBX required that any opened cursors can be
* reused and must be freed explicitly, regardless ones was opened in a
* read-only or write transaction. The REASON for this is eliminates ambiguity
* which helps to avoid errors such as: use-after-free, double-free, i.e.
* memory corruption and segfaults.
*
* \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open()
* or \ref mdbx_cursor_create().
* \returns A non-zero error value on failure and 0 on success,
* some possible errors are:
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
* by current thread.
* \retval MDBX_EINVAL An invalid parameter was specified. */
LIBMDBX_API int mdbx_cursor_close2(MDBX_cursor *cursor);
/** \brief Unbind or closes all cursors of a given transaction and of all
* its parent transactions if ones are.
* \ingroup c_cursors
*
* Unbinds either closes all cursors associated (opened, renewed or binded) with
* the given transaction in a bulk with minimal overhead.
*
* \see mdbx_cursor_unbind()
* \see mdbx_cursor_close()
*
* \param [in] txn A transaction handle returned by \ref mdbx_txn_begin().
* \param [in] unbind If non-zero, unbinds cursors and leaves ones reusable.
* Otherwise close and dispose cursors.
* \param [in,out] count An optional pointer to return the number of cursors
* processed by the requested operation.
*
* \returns A non-zero error value on failure and 0 on success,
* some possible errors are:
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
* by current thread.
* \retval MDBX_BAD_TXN Given transaction is invalid or has
* a child/nested transaction transaction. */
LIBMDBX_API int mdbx_txn_release_all_cursors_ex(const MDBX_txn *txn, bool unbind, size_t *count);
/** \brief Unbind or closes all cursors of a given transaction and of all
* its parent transactions if ones are.
* \ingroup c_cursors
*
* Unbinds either closes all cursors associated (opened, renewed or binded) with
* the given transaction in a bulk with minimal overhead.
*
* \see mdbx_cursor_unbind()
* \see mdbx_cursor_close()
@@ -5231,13 +5304,15 @@ LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor);
* \param [in] unbind If non-zero, unbinds cursors and leaves ones reusable.
* Otherwise close and dispose cursors.
*
* \returns A negative error value on failure or the number of closed cursors
* on success, some possible errors are:
* \returns A non-zero error value on failure and 0 on success,
* some possible errors are:
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
* by current thread.
* \retval MDBX_BAD_TXN Given transaction is invalid or has
* a child/nested transaction transaction. */
LIBMDBX_API int mdbx_txn_release_all_cursors(const MDBX_txn *txn, bool unbind);
LIBMDBX_INLINE_API(int, mdbx_txn_release_all_cursors, (const MDBX_txn *txn, bool unbind)) {
return mdbx_txn_release_all_cursors_ex(txn, unbind, NULL);
}
/** \brief Renew a cursor handle for use within the given transaction.
* \ingroup c_cursors
@@ -5263,7 +5338,7 @@ LIBMDBX_API int mdbx_txn_release_all_cursors(const MDBX_txn *txn, bool unbind);
* \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(const 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.
* \ingroup c_cursors

421
mdbx.h++
View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2020-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2020-2025
///
/// Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
/// Всё будет хорошо!
@@ -107,6 +107,16 @@
#include <span>
#endif
#if !defined(_MSC_VER) || defined(__clang__)
/* adequate compilers */
#define MDBX_EXTERN_API_TEMPLATE(API_ATTRIBUTES, API_TYPENAME) extern template class API_ATTRIBUTES API_TYPENAME
#define MDBX_INSTALL_API_TEMPLATE(API_ATTRIBUTES, API_TYPENAME) template class API_TYPENAME
#else
/* stupid microsoft showing off */
#define MDBX_EXTERN_API_TEMPLATE(API_ATTRIBUTES, API_TYPENAME) extern template class API_TYPENAME
#define MDBX_INSTALL_API_TEMPLATE(API_ATTRIBUTES, API_TYPENAME) template class API_ATTRIBUTES API_TYPENAME
#endif
#if __cplusplus >= 201103L
#include <chrono>
#include <ratio>
@@ -148,8 +158,7 @@
#endif
#endif /* Byte Order */
/** Workaround for old compilers without properly support for `C++17 constexpr`.
*/
/** Workaround for old compilers without properly support for `C++17 constexpr` */
#if defined(DOXYGEN)
#define MDBX_CXX17_CONSTEXPR constexpr
#elif defined(__cpp_constexpr) && __cpp_constexpr >= 201603L && \
@@ -160,8 +169,7 @@
#define MDBX_CXX17_CONSTEXPR inline
#endif /* MDBX_CXX17_CONSTEXPR */
/** Workaround for old compilers without properly support for C++20 `constexpr`.
*/
/** Workaround for old compilers without properly support for C++20 `constexpr`. */
#if defined(DOXYGEN)
#define MDBX_CXX20_CONSTEXPR constexpr
#elif defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L && \
@@ -185,8 +193,7 @@
#define MDBX_CXX20_CONSTEXPR_ENUM inline
#endif /* CONSTEXPR_ENUM_FLAGS_OPERATIONS */
/** Workaround for old compilers without support assertion inside `constexpr`
* functions. */
/** Workaround for old compilers without support assertion inside `constexpr` functions. */
#if defined(CONSTEXPR_ASSERT)
#define MDBX_CONSTEXPR_ASSERT(expr) CONSTEXPR_ASSERT(expr)
#elif defined NDEBUG
@@ -211,8 +218,7 @@
#endif
#endif /* MDBX_UNLIKELY */
/** Workaround for old compilers without properly support for C++20 `if
* constexpr`. */
/** Workaround for old compilers without properly support for C++20 `if constexpr`. */
#if defined(DOXYGEN)
#define MDBX_IF_CONSTEXPR constexpr
#elif defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
@@ -243,7 +249,7 @@
#endif /* MDBX_CXX20_UNLIKELY */
#ifndef MDBX_HAVE_CXX20_CONCEPTS
#if defined(__cpp_lib_concepts) && __cpp_lib_concepts >= 202002L
#if defined(__cpp_concepts) && __cpp_concepts >= 202002L && defined(__cpp_lib_concepts) && __cpp_lib_concepts >= 202002L
#include <concepts>
#define MDBX_HAVE_CXX20_CONCEPTS 1
#elif defined(DOXYGEN)
@@ -388,8 +394,7 @@ namespace filesystem = ::std::experimental::filesystem;
namespace filesystem = ::std::filesystem;
/// \brief Defined if `mdbx::filesystem::path` is available.
/// \details If defined, it is always `mdbx::filesystem::path`,
/// which in turn can be refs to `std::filesystem::path`
/// or `std::experimental::filesystem::path`.
/// which in turn can be refs to `std::filesystem::path` or `std::experimental::filesystem::path`.
/// Nonetheless `MDBX_STD_FILESYSTEM_PATH` not defined if the `::mdbx::path`
/// is fallbacked to c `std::string` or `std::wstring`.
#define MDBX_STD_FILESYSTEM_PATH ::mdbx::filesystem::path
@@ -489,9 +494,7 @@ public:
static inline void success_or_panic(int error_code, const char *context_where, const char *func_who) noexcept;
};
/// \brief Base class for all libmdbx's exceptions that are corresponds
/// to libmdbx errors.
///
/// \brief Base class for all libmdbx's exceptions that are corresponds to libmdbx errors.
/// \see MDBX_error_t
class LIBMDBX_API_TYPE exception : public ::std::runtime_error {
using base = ::std::runtime_error;
@@ -507,8 +510,7 @@ public:
const ::mdbx::error error() const noexcept { return error_; }
};
/// \brief Fatal exception that lead termination anyway
/// in dangerous unrecoverable cases.
/// \brief Fatal exception that lead termination anyway in dangerous unrecoverable cases.
class LIBMDBX_API_TYPE fatal : public exception {
using base = exception;
@@ -644,8 +646,7 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val {
/// \brief Create an empty slice.
MDBX_CXX11_CONSTEXPR slice() noexcept;
/// \brief Create a slice that refers to [0,bytes-1] of memory bytes pointed
/// by ptr.
/// \brief Create a slice that refers to [0,bytes-1] of memory bytes pointed by ptr.
MDBX_CXX14_CONSTEXPR slice(const void *ptr, size_t bytes);
/// \brief Create a slice that refers to [begin,end] of memory bytes.
@@ -1000,8 +1001,7 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val {
/// \brief Checks the slice is not refers to null address or has zero length.
MDBX_CXX11_CONSTEXPR bool is_valid() const noexcept { return !(iov_base == nullptr && iov_len != 0); }
/// \brief Build an invalid slice which non-zero length and refers to null
/// address.
/// \brief Build an invalid slice which non-zero length and refers to null address.
MDBX_CXX14_CONSTEXPR static slice invalid() noexcept { return slice(size_t(-1)); }
template <typename POD> MDBX_CXX14_CONSTEXPR POD as_pod() const {
@@ -1162,7 +1162,12 @@ template <typename T, typename A> struct swap_alloc<T, A, true> {
} // namespace allocation_aware_details
struct default_capacity_policy {
enum : size_t { extra_inplace_storage = 0, pettiness_threshold = 64, max_reserve = 65536 };
enum : size_t {
extra_inplace_storage = 0,
inplace_storage_size_rounding = 16,
pettiness_threshold = 64,
max_reserve = 65536
};
static MDBX_CXX11_CONSTEXPR size_t round(const size_t value) {
static_assert((pettiness_threshold & (pettiness_threshold - 1)) == 0, "pettiness_threshold must be a power of 2");
@@ -1224,8 +1229,7 @@ struct LIBMDBX_API to_hex {
char *write_bytes(char *dest, size_t dest_size) const;
/// \brief Output hexadecimal dump of passed slice to the std::ostream.
/// \throws std::ios_base::failure corresponding to std::ostream::write()
/// behaviour.
/// \throws std::ios_base::failure corresponding to std::ostream::write() behaviour.
::std::ostream &output(::std::ostream &out) const;
/// \brief Checks whether a passed slice is empty,
@@ -1268,23 +1272,18 @@ struct LIBMDBX_API to_base58 {
return wrap_width ? bytes + bytes / wrap_width : bytes;
}
/// \brief Fills the buffer by [Base58](https://en.wikipedia.org/wiki/Base58)
/// dump of passed slice.
/// \brief Fills the buffer by [Base58](https://en.wikipedia.org/wiki/Base58) dump of passed slice.
/// \throws std::length_error if given buffer is too small.
char *write_bytes(char *dest, size_t dest_size) const;
/// \brief Output [Base58](https://en.wikipedia.org/wiki/Base58)
/// dump of passed slice to the std::ostream.
/// \throws std::ios_base::failure corresponding to std::ostream::write()
/// behaviour.
/// \brief Output [Base58](https://en.wikipedia.org/wiki/Base58) dump of passed slice to the std::ostream.
/// \throws std::ios_base::failure corresponding to std::ostream::write() behaviour.
::std::ostream &output(::std::ostream &out) const;
/// \brief Checks whether a passed slice is empty,
/// and therefore there will be no output bytes.
/// \brief Checks whether a passed slice is empty, and therefore there will be no output bytes.
bool is_empty() const noexcept { return source.empty(); }
/// \brief Checks whether the content of a passed slice is a valid data
/// and could be encoded or unexpectedly not.
/// \brief Checks whether the content of a passed slice is a valid data and could be encoded or unexpectedly not.
bool is_erroneous() const noexcept { return false; }
};
@@ -1326,8 +1325,7 @@ struct LIBMDBX_API to_base64 {
/// \brief Output [Base64](https://en.wikipedia.org/wiki/Base64)
/// dump of passed slice to the std::ostream.
/// \throws std::ios_base::failure corresponding to std::ostream::write()
/// behaviour.
/// \throws std::ios_base::failure corresponding to std::ostream::write() behaviour.
::std::ostream &output(::std::ostream &out) const;
/// \brief Checks whether a passed slice is empty,
@@ -1368,13 +1366,11 @@ struct LIBMDBX_API from_hex {
/// hexadecimal dump from a passed slice to decoded data.
MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept { return source.length() >> 1; }
/// \brief Fills the destination with data decoded from hexadecimal dump
/// from a passed slice.
/// \brief Fills the destination with data decoded from hexadecimal dump from a passed slice.
/// \throws std::length_error if given buffer is too small.
char *write_bytes(char *dest, size_t dest_size) const;
/// \brief Checks whether a passed slice is empty,
/// and therefore there will be no output bytes.
/// \brief Checks whether a passed slice is empty, and therefore there will be no output bytes.
bool is_empty() const noexcept { return source.empty(); }
/// \brief Checks whether the content of a passed slice is a valid hexadecimal
@@ -1407,8 +1403,7 @@ struct LIBMDBX_API from_base58 {
}
/// \brief Returns the number of bytes needed for conversion
/// [Base58](https://en.wikipedia.org/wiki/Base58) dump from a passed slice to
/// decoded data.
/// [Base58](https://en.wikipedia.org/wiki/Base58) dump from a passed slice to decoded data.
MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept {
return source.length() /* могут быть все нули кодируемые один-к-одному */;
}
@@ -1418,13 +1413,11 @@ struct LIBMDBX_API from_base58 {
/// \throws std::length_error if given buffer is too small.
char *write_bytes(char *dest, size_t dest_size) const;
/// \brief Checks whether a passed slice is empty,
/// and therefore there will be no output bytes.
/// \brief Checks whether a passed slice is empty, and therefore there will be no output bytes.
bool is_empty() const noexcept { return source.empty(); }
/// \brief Checks whether the content of a passed slice is a valid
/// [Base58](https://en.wikipedia.org/wiki/Base58) dump, and therefore there
/// could be decoded or not.
/// [Base58](https://en.wikipedia.org/wiki/Base58) dump, and therefore there could be decoded or not.
bool is_erroneous() const noexcept;
};
@@ -1453,8 +1446,7 @@ struct LIBMDBX_API from_base64 {
}
/// \brief Returns the number of bytes needed for conversion
/// [Base64](https://en.wikipedia.org/wiki/Base64) dump from a passed slice to
/// decoded data.
/// [Base64](https://en.wikipedia.org/wiki/Base64) dump from a passed slice to decoded data.
MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept { return (source.length() + 3) / 4 * 3; }
/// \brief Fills the destination with data decoded from
@@ -1486,13 +1478,16 @@ public:
max_length = MDBX_MAXDATASIZE,
max_capacity = (max_length / 3u * 4u + 1023u) & ~size_t(1023),
extra_inplace_storage = reservation_policy::extra_inplace_storage,
inplace_storage_size_rounding =
(alignof(max_align_t) * 2 > size_t(reservation_policy::inplace_storage_size_rounding))
? alignof(max_align_t) * 2
: size_t(reservation_policy::inplace_storage_size_rounding),
pettiness_threshold = reservation_policy::pettiness_threshold
};
private:
friend class txn;
struct silo;
using swap_alloc = allocation_aware_details::swap_alloc<silo, allocator_type>;
using swap_alloc = allocation_aware_details::swap_alloc<struct silo, allocator_type>;
struct silo /* Empty Base Class Optimization */ : public allocator_type {
MDBX_CXX20_CONSTEXPR const allocator_type &get_allocator() const noexcept { return *this; }
MDBX_CXX20_CONSTEXPR allocator_type &get_allocator() noexcept { return *this; }
@@ -1529,41 +1524,51 @@ private:
#endif /* __cpp_lib_to_address */
}
union bin {
struct allocated {
union alignas(max_align_t) bin {
struct stub_allocated_holder /* используется только для вычисления (минимального необходимого) размера,
с учетом выравнивания */
{
allocator_pointer ptr_;
size_t capacity_bytes_;
constexpr allocated(allocator_pointer ptr, size_t bytes) noexcept : ptr_(ptr), capacity_bytes_(bytes) {}
constexpr allocated(const allocated &) noexcept = default;
constexpr allocated(allocated &&) noexcept = default;
MDBX_CXX17_CONSTEXPR allocated &operator=(const allocated &) noexcept = default;
MDBX_CXX17_CONSTEXPR allocated &operator=(allocated &&) noexcept = default;
size_t stub_capacity_bytes_;
};
allocated allocated_;
uint64_t align_hint_;
byte inplace_[(sizeof(allocated) + extra_inplace_storage + 7u) & ~size_t(7)];
static constexpr bool is_suitable_for_inplace(size_t capacity_bytes) noexcept {
static_assert(sizeof(bin) == sizeof(inplace_), "WTF?");
return capacity_bytes < sizeof(bin);
}
enum : byte { lastbyte_inplace_signature = byte(~byte(0)) };
enum : byte { lastbyte_poison = 0, lastbyte_inplace_signature = byte(~byte(lastbyte_poison)) };
enum : size_t {
inplace_signature_limit = size_t(lastbyte_inplace_signature)
<< (sizeof(size_t /* allocated::capacity_bytes_ */) - 1) * CHAR_BIT
<< (sizeof(size_t /* allocated::capacity_bytes_ */) - 1) * CHAR_BIT,
inplace_size_rounding = size_t(inplace_storage_size_rounding) - 1,
inplace_size =
(sizeof(stub_allocated_holder) + extra_inplace_storage + inplace_size_rounding) & ~inplace_size_rounding
};
constexpr byte inplace_lastbyte() const noexcept { return inplace_[sizeof(bin) - 1]; }
MDBX_CXX17_CONSTEXPR byte &inplace_lastbyte() noexcept { return inplace_[sizeof(bin) - 1]; }
struct capacity_holder {
byte pad_[inplace_size - sizeof(allocator_pointer)];
size_t bytes_;
};
struct inplace_flag_holder {
byte buffer_[inplace_size - sizeof(byte)];
byte lastbyte_;
};
allocator_pointer allocated_ptr_;
capacity_holder capacity_;
inplace_flag_holder inplace_;
static constexpr bool is_suitable_for_inplace(size_t capacity_bytes) noexcept {
static_assert((size_t(reservation_policy::inplace_storage_size_rounding) &
(size_t(reservation_policy::inplace_storage_size_rounding) - 1)) == 0,
"CAPACITY_POLICY::inplace_storage_size_rounding must be power of 2");
static_assert(sizeof(bin) == sizeof(inplace_) && sizeof(bin) == sizeof(capacity_), "WTF?");
return capacity_bytes < sizeof(bin);
}
constexpr bool is_inplace() const noexcept {
static_assert(size_t(inplace_signature_limit) > size_t(max_capacity), "WTF?");
static_assert(std::numeric_limits<size_t>::max() - (std::numeric_limits<size_t>::max() >> CHAR_BIT) ==
inplace_signature_limit,
"WTF?");
return inplace_lastbyte() == lastbyte_inplace_signature;
return inplace_.lastbyte_ == lastbyte_inplace_signature;
}
constexpr bool is_allocated() const noexcept { return !is_inplace(); }
@@ -1571,26 +1576,27 @@ private:
if (destroy_ptr) {
MDBX_CONSTEXPR_ASSERT(is_allocated());
/* properly destroy allocator::pointer */
allocated_.~allocated();
allocated_ptr_.~allocator_pointer();
}
if (::std::is_trivial<allocator_pointer>::value)
/* workaround for "uninitialized" warning from some compilers */
memset(&allocated_.ptr_, 0, sizeof(allocated_.ptr_));
inplace_lastbyte() = lastbyte_inplace_signature;
MDBX_CONSTEXPR_ASSERT(is_inplace() && address() == inplace_ && is_suitable_for_inplace(capacity()));
memset(&allocated_ptr_, 0, sizeof(allocated_ptr_));
inplace_.lastbyte_ = lastbyte_inplace_signature;
MDBX_CONSTEXPR_ASSERT(is_inplace() && address() == inplace_.buffer_ && is_suitable_for_inplace(capacity()));
return address();
}
template <bool construct_ptr>
MDBX_CXX17_CONSTEXPR byte *make_allocated(allocator_pointer ptr, size_t capacity_bytes) noexcept {
MDBX_CONSTEXPR_ASSERT(inplace_signature_limit > capacity_bytes);
if (construct_ptr)
if (construct_ptr) {
/* properly construct allocator::pointer */
new (&allocated_) allocated(ptr, capacity_bytes);
else {
new (&allocated_ptr_) allocator_pointer(ptr);
capacity_.bytes_ = capacity_bytes;
} else {
MDBX_CONSTEXPR_ASSERT(is_allocated());
allocated_.ptr_ = ptr;
allocated_.capacity_bytes_ = capacity_bytes;
allocated_ptr_ = ptr;
capacity_.bytes_ = capacity_bytes;
}
MDBX_CONSTEXPR_ASSERT(is_allocated() && address() == to_address(ptr) && capacity() == capacity_bytes);
return address();
@@ -1608,16 +1614,17 @@ private:
MDBX_CXX20_CONSTEXPR ~bin() {
if (is_allocated())
/* properly destroy allocator::pointer */
allocated_.~allocated();
allocated_ptr_.~allocator_pointer();
}
MDBX_CXX20_CONSTEXPR bin(bin &&ditto) noexcept {
if (ditto.is_inplace()) {
// micro-optimization: don't use make_inplace<> here
// since memcpy() will copy the flag.
memcpy(inplace_, ditto.inplace_, sizeof(inplace_));
memcpy(&inplace_, &ditto.inplace_, sizeof(inplace_));
MDBX_CONSTEXPR_ASSERT(is_inplace());
} else {
new (&allocated_) allocated(::std::move(ditto.allocated_));
new (&allocated_ptr_) allocator_pointer(::std::move(ditto.allocated_ptr_));
capacity_.bytes_ = ditto.capacity_.bytes_;
ditto.make_inplace<true>();
MDBX_CONSTEXPR_ASSERT(is_allocated());
}
@@ -1629,13 +1636,13 @@ private:
// since memcpy() will copy the flag.
if (is_allocated())
/* properly destroy allocator::pointer */
allocated_.~allocated();
memcpy(inplace_, ditto.inplace_, sizeof(inplace_));
allocated_ptr_.~allocator_pointer();
memcpy(&inplace_, &ditto.inplace_, sizeof(inplace_));
MDBX_CONSTEXPR_ASSERT(is_inplace());
} else if (is_inplace())
make_allocated<true>(ditto.allocated_.ptr_, ditto.allocated_.capacity_bytes_);
make_allocated<true>(ditto.allocated_ptr_, ditto.capacity_.bytes_);
else
make_allocated<false>(ditto.allocated_.ptr_, ditto.allocated_.capacity_bytes_);
make_allocated<false>(ditto.allocated_ptr_, ditto.capacity_.bytes_);
return *this;
}
@@ -1656,12 +1663,12 @@ private:
}
constexpr const byte *address() const noexcept {
return is_inplace() ? inplace_ : static_cast<const byte *>(to_address(allocated_.ptr_));
return is_inplace() ? inplace_.buffer_ : static_cast<const byte *>(to_address(allocated_ptr_));
}
MDBX_CXX17_CONSTEXPR byte *address() noexcept {
return is_inplace() ? inplace_ : static_cast<byte *>(to_address(allocated_.ptr_));
return is_inplace() ? inplace_.buffer_ : static_cast<byte *>(to_address(allocated_ptr_));
}
constexpr size_t capacity() const noexcept { return is_inplace() ? sizeof(bin) - 1 : allocated_.capacity_bytes_; }
constexpr size_t capacity() const noexcept { return is_inplace() ? sizeof(bin) - 1 : capacity_.bytes_; }
} bin_;
MDBX_CXX20_CONSTEXPR void *init(size_t capacity) {
@@ -1678,7 +1685,7 @@ private:
MDBX_CXX20_CONSTEXPR void release() noexcept {
if (bin_.is_allocated()) {
deallocate_storage(bin_.allocated_.ptr_, bin_.allocated_.capacity_bytes_);
deallocate_storage(bin_.allocated_ptr_, bin_.capacity_.bytes_);
bin_.template make_inplace<true>();
}
}
@@ -1709,7 +1716,7 @@ private:
if (bin::is_suitable_for_inplace(new_capacity)) {
assert(bin_.is_allocated());
const auto old_allocated = ::std::move(bin_.allocated_.ptr_);
const auto old_allocated = ::std::move(bin_.allocated_ptr_);
byte *const new_place = bin_.template make_inplace<true>() + wanna_headroom;
if (MDBX_LIKELY(length))
MDBX_CXX20_LIKELY memcpy(new_place, content, length);
@@ -1727,7 +1734,7 @@ private:
return new_place;
}
const auto old_allocated = ::std::move(bin_.allocated_.ptr_);
const auto old_allocated = ::std::move(bin_.allocated_ptr_);
if (external_content)
deallocate_storage(old_allocated, old_capacity);
const auto pair = allocate_storage(new_capacity);
@@ -1906,8 +1913,7 @@ public:
/// the buffer, rather than stores it.
MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX20_CONSTEXPR bool is_reference() const noexcept { return !is_freestanding(); }
/// \brief Returns the number of bytes that can be held in currently allocated
/// storage.
/// \brief Returns the number of bytes that can be held in currently allocated storage.
MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX20_CONSTEXPR size_t capacity() const noexcept {
return is_freestanding() ? silo_.capacity() : 0;
}
@@ -1931,16 +1937,14 @@ public:
MDBX_CXX11_CONSTEXPR const byte *end_byte_ptr() const noexcept { return slice_.end_byte_ptr(); }
/// \brief Returns casted to pointer to byte an address of data.
/// \pre REQUIRES: The buffer should store data chunk, but not referenced to
/// an external one.
/// \pre REQUIRES: The buffer should store data chunk, but not referenced to an external one.
MDBX_CXX11_CONSTEXPR byte *byte_ptr() noexcept {
MDBX_CONSTEXPR_ASSERT(is_freestanding());
return const_cast<byte *>(slice_.byte_ptr());
}
/// \brief Returns casted to pointer to byte an end of data.
/// \pre REQUIRES: The buffer should store data chunk, but not referenced to
/// an external one.
/// \pre REQUIRES: The buffer should store data chunk, but not referenced to an external one.
MDBX_CXX11_CONSTEXPR byte *end_byte_ptr() noexcept {
MDBX_CONSTEXPR_ASSERT(is_freestanding());
return const_cast<byte *>(slice_.end_byte_ptr());
@@ -1953,16 +1957,14 @@ public:
MDBX_CXX11_CONSTEXPR const char *end_char_ptr() const noexcept { return slice_.end_char_ptr(); }
/// \brief Returns casted to pointer to char an address of data.
/// \pre REQUIRES: The buffer should store data chunk, but not referenced to
/// an external one.
/// \pre REQUIRES: The buffer should store data chunk, but not referenced to an external one.
MDBX_CXX11_CONSTEXPR char *char_ptr() noexcept {
MDBX_CONSTEXPR_ASSERT(is_freestanding());
return const_cast<char *>(slice_.char_ptr());
}
/// \brief Returns casted to pointer to char an end of data.
/// \pre REQUIRES: The buffer should store data chunk, but not referenced to
/// an external one.
/// \pre REQUIRES: The buffer should store data chunk, but not referenced to an external one.
MDBX_CXX11_CONSTEXPR char *end_char_ptr() noexcept {
MDBX_CONSTEXPR_ASSERT(is_freestanding());
return const_cast<char *>(slice_.end_char_ptr());
@@ -1975,16 +1977,14 @@ public:
MDBX_CXX11_CONSTEXPR const void *end() const noexcept { return slice_.end(); }
/// \brief Return a pointer to the beginning of the referenced data.
/// \pre REQUIRES: The buffer should store data chunk, but not referenced to
/// an external one.
/// \pre REQUIRES: The buffer should store data chunk, but not referenced to an external one.
MDBX_CXX11_CONSTEXPR void *data() noexcept {
MDBX_CONSTEXPR_ASSERT(is_freestanding());
return const_cast<void *>(slice_.data());
}
/// \brief Return a pointer to the end of the referenced data.
/// \pre REQUIRES: The buffer should store data chunk, but not referenced to
/// an external one.
/// \pre REQUIRES: The buffer should store data chunk, but not referenced to an external one.
MDBX_CXX11_CONSTEXPR void *end() noexcept {
MDBX_CONSTEXPR_ASSERT(is_freestanding());
return const_cast<void *>(slice_.end());
@@ -2708,8 +2708,13 @@ inline string<ALLOCATOR> make_string(const PRODUCER &producer, const ALLOCATOR &
return result;
}
/// \brief Combines data slice with boolean flag to represent result of certain
/// operations.
MDBX_EXTERN_API_TEMPLATE(LIBMDBX_API_TYPE, buffer<legacy_allocator>);
#if defined(__cpp_lib_memory_resource) && __cpp_lib_memory_resource >= 201603L && _GLIBCXX_USE_CXX11_ABI
MDBX_EXTERN_API_TEMPLATE(LIBMDBX_API_TYPE, buffer<polymorphic_allocator>);
#endif /* __cpp_lib_memory_resource >= 201603L */
/// \brief Combines data slice with boolean flag to represent result of certain operations.
struct value_result {
slice value;
bool done;
@@ -2722,8 +2727,7 @@ struct value_result {
}
};
/// \brief Combines pair of slices for key and value to represent result of
/// certain operations.
/// \brief Combines pair of slices for key and value to represent result of certain operations.
struct pair {
using stl_pair = std::pair<slice, slice>;
slice key, value;
@@ -2833,9 +2837,13 @@ template <typename ALLOCATOR, typename CAPACITY_POLICY> struct buffer_pair_spec
operator pair() const noexcept { return pair(key, value); }
};
/// \brief Combines pair of buffers for key and value to hold an operands for certain operations.
template <typename BUFFER>
using buffer_pair = buffer_pair_spec<typename BUFFER::allocator_type, typename BUFFER::reservation_policy>;
/// \brief Default pair of buffers.
using default_buffer_pair = buffer_pair<default_buffer>;
/// end of cxx_data @}
//------------------------------------------------------------------------------
@@ -2879,8 +2887,7 @@ MDBX_CXX01_CONSTEXPR_ENUM bool is_reverse(key_mode mode) noexcept {
MDBX_CXX01_CONSTEXPR_ENUM bool is_msgpack(key_mode mode) noexcept { return mode == key_mode::msgpack; }
/// \brief Kind of the values and sorted multi-values with corresponding
/// comparison.
/// \brief Kind of the values and sorted multi-values with corresponding comparison.
enum class value_mode {
single = MDBX_DB_DEFAULTS, ///< Usual single value for each key. In terms of
///< keys, they are unique.
@@ -2960,8 +2967,7 @@ MDBX_CXX01_CONSTEXPR_ENUM bool is_reverse(value_mode mode) noexcept {
MDBX_CXX01_CONSTEXPR_ENUM bool is_msgpack(value_mode mode) noexcept { return mode == value_mode::msgpack; }
/// \brief A handle for an individual table (aka key-value space, maps or
/// sub-database) in the environment.
/// \brief A handle for an individual table (aka key-value space, maps or sub-database) in the environment.
/// \see txn::open_map() \see txn::create_map()
/// \see txn::clear_map() \see txn::drop_map()
/// \see txn::get_handle_info() \see txn::get_map_stat()
@@ -3090,12 +3096,10 @@ public:
/// environment).
intptr_t size_upper{default_value};
/// \brief The growth step in bytes, must be greater than zero to allow the
/// database to grow.
/// \brief The growth step in bytes, must be greater than zero to allow the database to grow.
intptr_t growth_step{default_value};
/// \brief The shrink threshold in bytes, must be greater than zero to allow
/// the database to shrink.
/// \brief The shrink threshold in bytes, must be greater than zero to allow the database to shrink.
intptr_t shrink_threshold{default_value};
/// \brief The database page size for new database creation
@@ -3230,47 +3234,34 @@ public:
static inline size_t pagesize_min() noexcept;
/// \brief Returns the maximal database page size in bytes.
static inline size_t pagesize_max() noexcept;
/// \brief Returns the minimal database size in bytes for specified page
/// size.
/// \brief Returns the minimal database size in bytes for specified page size.
static inline size_t dbsize_min(intptr_t pagesize);
/// \brief Returns the maximal database size in bytes for specified page
/// size.
/// \brief Returns the maximal database size in bytes for specified page size.
static inline size_t dbsize_max(intptr_t pagesize);
/// \brief Returns the minimal key size in bytes for specified table
/// flags.
/// \brief Returns the minimal key size in bytes for specified table flags.
static inline size_t key_min(MDBX_db_flags_t flags) noexcept;
/// \brief Returns the minimal key size in bytes for specified keys mode.
static inline size_t key_min(key_mode mode) noexcept;
/// \brief Returns the maximal key size in bytes for specified page size and
/// table flags.
/// \brief Returns the maximal key size in bytes for specified page size and table flags.
static inline size_t key_max(intptr_t pagesize, MDBX_db_flags_t flags);
/// \brief Returns the maximal key size in bytes for specified page size and
/// keys mode.
/// \brief Returns the maximal key size in bytes for specified page size and keys mode.
static inline size_t key_max(intptr_t pagesize, key_mode mode);
/// \brief Returns the maximal key size in bytes for given environment and
/// table flags.
/// \brief Returns the maximal key size in bytes for given environment and table flags.
static inline size_t key_max(const env &, MDBX_db_flags_t flags);
/// \brief Returns the maximal key size in bytes for given environment and
/// keys mode.
/// \brief Returns the maximal key size in bytes for given environment and keys mode.
static inline size_t key_max(const env &, key_mode mode);
/// \brief Returns the minimal values size in bytes for specified table
/// flags.
/// \brief Returns the minimal values size in bytes for specified table flags.
static inline size_t value_min(MDBX_db_flags_t flags) noexcept;
/// \brief Returns the minimal values size in bytes for specified values
/// mode.
/// \brief Returns the minimal values size in bytes for specified values mode.
static inline size_t value_min(value_mode) noexcept;
/// \brief Returns the maximal value size in bytes for specified page size
/// and table flags.
/// \brief Returns the maximal value size in bytes for specified page size and table flags.
static inline size_t value_max(intptr_t pagesize, MDBX_db_flags_t flags);
/// \brief Returns the maximal value size in bytes for specified page size
/// and values mode.
/// \brief Returns the maximal value size in bytes for specified page size and values mode.
static inline size_t value_max(intptr_t pagesize, value_mode);
/// \brief Returns the maximal value size in bytes for given environment and
/// table flags.
/// \brief Returns the maximal value size in bytes for given environment and table flags.
static inline size_t value_max(const env &, MDBX_db_flags_t flags);
/// \brief Returns the maximal value size in bytes for specified page size
/// and values mode.
/// \brief Returns the maximal value size in bytes for specified page size and values mode.
static inline size_t value_max(const env &, value_mode);
/// \brief Returns maximal size of key-value pair to fit in a single page
@@ -3350,13 +3341,11 @@ public:
/// \brief Make sure that the environment is not being used by other
/// processes, or return an error otherwise.
ensure_unused = MDBX_ENV_ENSURE_UNUSED,
/// \brief Wait until other processes closes the environment before
/// deletion.
/// \brief Wait until other processes closes the environment before deletion.
wait_for_unused = MDBX_ENV_WAIT_FOR_UNUSED
};
/// \brief Removes the environment's files in a proper and multiprocess-safe
/// way.
/// \brief Removes the environment's files in a proper and multiprocess-safe way.
#ifdef MDBX_STD_FILESYSTEM_PATH
static bool remove(const MDBX_STD_FILESYSTEM_PATH &pathname, const remove_mode mode = just_remove);
#endif /* MDBX_STD_FILESYSTEM_PATH */
@@ -3382,12 +3371,10 @@ public:
/// \brief Return snapshot information about the MDBX environment.
inline info get_info() const;
/// \brief Return statistics about the MDBX environment accordingly to the
/// specified transaction.
/// \brief Return statistics about the MDBX environment accordingly to the specified transaction.
inline stat get_stat(const txn &) const;
/// \brief Return information about the MDBX environment accordingly to the
/// specified transaction.
/// \brief Return information about the MDBX environment accordingly to the specified transaction.
inline info get_info(const txn &) const;
/// \brief Returns the file descriptor for the DXB file of MDBX environment.
@@ -3399,8 +3386,7 @@ public:
/// Returns environment flags.
inline MDBX_env_flags_t get_flags() const;
/// \brief Returns the maximum number of threads/reader slots for the
/// environment.
/// \brief Returns the maximum number of threads/reader slots for the environment.
/// \see extra_runtime_option::max_readers
inline unsigned max_readers() const;
@@ -3784,8 +3770,7 @@ public:
/// volume of dirty pages) in bytes.
size_t size_max() const { return env().transaction_size_max(); }
/// \brief Returns current write transaction size (i.e.summary volume of dirty
/// pages) in bytes.
/// \brief Returns current write transaction size (i.e.summary volume of dirty pages) in bytes.
size_t size_current() const {
assert(is_readwrite());
return size_t(get_info().txn_space_dirty);
@@ -3944,42 +3929,32 @@ public:
inline map_handle::info get_handle_info(map_handle map) const;
using canary = ::MDBX_canary;
/// \brief Set integers markers (aka "canary") associated with the
/// environment.
/// \brief Set integers markers (aka "canary") associated with the environment.
inline txn &put_canary(const canary &);
/// \brief Returns fours integers markers (aka "canary") associated with the
/// environment.
/// \brief Returns fours integers markers (aka "canary") associated with the environment.
inline canary get_canary() const;
/// Reads sequence generator associated with a key-value map (aka
/// table).
/// Reads sequence generator associated with a key-value map (aka table).
inline uint64_t sequence(map_handle map) const;
/// \brief Reads and increment sequence generator associated with a key-value
/// map (aka table).
/// \brief Reads and increment sequence generator associated with a key-value map (aka table).
inline uint64_t sequence(map_handle map, uint64_t increment);
/// \brief Compare two keys according to a particular key-value map (aka
/// table).
/// \brief Compare two keys according to a particular key-value map (aka table).
inline int compare_keys(map_handle map, const slice &a, const slice &b) const noexcept;
/// \brief Compare two values according to a particular key-value map (aka
/// table).
/// \brief Compare two values according to a particular key-value map (aka table).
inline int compare_values(map_handle map, const slice &a, const slice &b) const noexcept;
/// \brief Compare keys of two pairs according to a particular key-value map
/// (aka table).
/// \brief Compare keys of two pairs according to a particular key-value map (aka table).
inline int compare_keys(map_handle map, const pair &a, const pair &b) const noexcept;
/// \brief Compare values of two pairs according to a particular key-value map
/// (aka table).
/// \brief Compare values of two pairs according to a particular key-value map(aka table).
inline int compare_values(map_handle map, const pair &a, const pair &b) const noexcept;
/// \brief Get value by key from a key-value map (aka table).
inline slice get(map_handle map, const slice &key) const;
/// \brief Get first of multi-value and values count by key from a key-value
/// multimap (aka table).
/// \brief Get first of multi-value and values count by key from a key-value multimap (aka table).
inline slice get(map_handle map, slice key, size_t &values_count) const;
/// \brief Get value by key from a key-value map (aka table).
inline slice get(map_handle map, const slice &key, const slice &value_at_absence) const;
/// \brief Get first of multi-value and values count by key from a key-value
/// multimap (aka table).
/// \brief Get first of multi-value and values count by key from a key-value multimap (aka table).
inline slice get(map_handle map, slice key, size_t &values_count, const slice &value_at_absence) const;
/// \brief Get value for equal or great key from a table.
/// \return Bundle of key-value pair and boolean flag,
@@ -4060,8 +4035,9 @@ public:
return append(map, kv.key, kv.value, multivalue_order_preserved);
}
size_t put_multiple_samelength(map_handle map, const slice &key, const size_t value_length, const void *values_array,
size_t values_count, put_mode mode, bool allow_partial = false);
inline size_t put_multiple_samelength(map_handle map, const slice &key, const size_t value_length,
const void *values_array, size_t values_count, put_mode mode,
bool allow_partial = false);
template <typename VALUE>
size_t put_multiple_samelength(map_handle map, const slice &key, const VALUE *values_array, size_t values_count,
put_mode mode, bool allow_partial = false) {
@@ -4155,9 +4131,9 @@ public:
class LIBMDBX_API_TYPE cursor {
protected:
MDBX_cursor *handle_{nullptr};
MDBX_CXX11_CONSTEXPR cursor(MDBX_cursor *ptr) noexcept;
public:
MDBX_CXX11_CONSTEXPR cursor(MDBX_cursor *ptr) noexcept;
MDBX_CXX11_CONSTEXPR cursor() noexcept = default;
cursor(const cursor &) noexcept = default;
inline cursor &operator=(cursor &&other) noexcept;
@@ -4244,9 +4220,14 @@ public:
batch_samelength = MDBX_GET_MULTIPLE,
batch_samelength_next = MDBX_NEXT_MULTIPLE,
batch_samelength_previous = MDBX_PREV_MULTIPLE
batch_samelength_previous = MDBX_PREV_MULTIPLE,
seek_and_batch_samelength = MDBX_SEEK_AND_GET_MULTIPLE
};
// TODO: добавить легковесный proxy-класс для замещения параметра throw_notfound более сложным набором опций,
// в том числе с explicit-конструктором из bool, чтобы защититься от неявной конвертации ключей поиска
// и других параметров в bool-throw_notfound.
struct move_result : public pair_result {
inline move_result(const cursor &cursor, bool throw_notfound);
move_result(cursor &cursor, move_operation operation, bool throw_notfound)
@@ -4444,8 +4425,8 @@ public:
inline move_result lower_bound_multivalue(const slice &key, const slice &value, bool throw_notfound = false);
inline move_result upper_bound_multivalue(const slice &key, const slice &value, bool throw_notfound = false);
inline move_result get_multiple_samelength(const slice &key, bool throw_notfound = true) {
return move(batch_samelength, key, throw_notfound);
inline move_result seek_multiple_samelength(const slice &key, bool throw_notfound = true) {
return move(seek_and_batch_samelength, key, throw_notfound);
}
inline move_result get_multiple_samelength(bool throw_notfound = false) {
@@ -4472,13 +4453,11 @@ public:
//----------------------------------------------------------------------------
/// \brief Renew/bind a cursor with a new transaction and previously used
/// key-value map handle.
inline void renew(const ::mdbx::txn &txn);
/// \brief Renew/bind a cursor with a new transaction and previously used key-value map handle.
inline void renew(::mdbx::txn &txn);
/// \brief Bind/renew a cursor with a new transaction and specified key-value
/// map handle.
inline void bind(const ::mdbx::txn &txn, ::mdbx::map_handle map_handle);
/// \brief Bind/renew a cursor with a new transaction and specified key-value map handle.
inline void bind(::mdbx::txn &txn, ::mdbx::map_handle map_handle);
/// \brief Unbind cursor from a transaction.
inline void unbind();
@@ -4510,18 +4489,31 @@ public:
value_result try_insert(const pair &kv) { return try_insert(kv.key, kv.value); }
void upsert(const pair &kv) { return upsert(kv.key, kv.value); }
/// \brief Removes single key-value pair or all multi-values at the current
/// cursor position.
/// \brief Removes single key-value pair or all multi-values at the current cursor position.
inline bool erase(bool whole_multivalue = false);
/// \brief Seeks and removes first value or whole multi-value of the given
/// key.
/// \brief Seeks and removes first value or whole multi-value of the given key.
/// \return `True` if the key is found and a value(s) is removed.
inline bool erase(const slice &key, bool whole_multivalue = true);
/// \brief Seeks and removes the particular multi-value entry of the key.
/// \return `True` if the given key-value pair is found and removed.
inline bool erase(const slice &key, const slice &value);
inline size_t put_multiple_samelength(const slice &key, const size_t value_length, const void *values_array,
size_t values_count, put_mode mode, bool allow_partial = false);
template <typename VALUE>
size_t put_multiple_samelength(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_samelength(key, sizeof(VALUE), values_array, values_count, mode, allow_partial);
}
template <typename VALUE>
void put_multiple_samelength(const slice &key, const ::std::vector<VALUE> &vector, put_mode mode) {
put_multiple_samelength(key, vector.data(), vector.size(), mode);
}
};
/// \brief Managed cursor.
@@ -4545,7 +4537,10 @@ public:
}
/// \brief Explicitly closes the cursor.
void close();
inline void close() {
error::success_or_throw(::mdbx_cursor_close2(handle_));
handle_ = nullptr;
}
cursor_managed(cursor_managed &&) = default;
cursor_managed &operator=(cursor_managed &&other) noexcept {
@@ -4558,6 +4553,12 @@ public:
return *this;
}
inline MDBX_cursor *withdraw_handle() noexcept {
MDBX_cursor *handle = handle_;
handle_ = nullptr;
return handle;
}
cursor_managed(const cursor_managed &) = delete;
cursor_managed &operator=(const cursor_managed &) = delete;
~cursor_managed() noexcept { ::mdbx_cursor_close(handle_); }
@@ -5604,10 +5605,9 @@ inline cursor_managed txn::open_cursor(map_handle map) const {
}
inline size_t txn::release_all_cursors(bool unbind) const {
int err = ::mdbx_txn_release_all_cursors(handle_, unbind);
if (MDBX_UNLIKELY(err < 0))
MDBX_CXX20_UNLIKELY error::throw_exception(err);
return size_t(err);
size_t count;
error::success_or_throw(::mdbx_txn_release_all_cursors_ex(handle_, unbind, &count));
return count;
}
inline ::mdbx::map_handle txn::open_map(const ::mdbx::slice &name, const ::mdbx::key_mode key_mode,
@@ -6167,9 +6167,9 @@ inline cursor::estimate_result cursor::estimate(move_operation operation) const
return estimate_result(*this, operation);
}
inline void cursor::renew(const ::mdbx::txn &txn) { error::success_or_throw(::mdbx_cursor_renew(txn, handle_)); }
inline void cursor::renew(::mdbx::txn &txn) { error::success_or_throw(::mdbx_cursor_renew(txn, handle_)); }
inline void cursor::bind(const ::mdbx::txn &txn, ::mdbx::map_handle map_handle) {
inline void cursor::bind(::mdbx::txn &txn, ::mdbx::map_handle map_handle) {
error::success_or_throw(::mdbx_cursor_bind(txn, handle_, map_handle.dbi));
}
@@ -6177,7 +6177,6 @@ inline void cursor::unbind() { error::success_or_throw(::mdbx_cursor_unbind(hand
inline txn cursor::txn() const {
MDBX_txn *txn = ::mdbx_cursor_txn(handle_);
error::throw_on_nullptr(txn, MDBX_EINVAL);
return ::mdbx::txn(txn);
}
@@ -6302,6 +6301,24 @@ inline bool cursor::erase(const slice &key, const slice &value) {
return data.done && erase();
}
inline size_t cursor::put_multiple_samelength(const slice &key, const size_t value_length, const void *values_array,
size_t values_count, put_mode mode, bool allow_partial) {
MDBX_val args[2] = {{const_cast<void *>(values_array), value_length}, {nullptr, values_count}};
const int err = ::mdbx_cursor_put(handle_, const_cast<slice *>(&key), args, MDBX_put_flags_t(mode) | MDBX_MULTIPLE);
switch (err) {
case MDBX_SUCCESS:
MDBX_CXX20_LIKELY break;
case MDBX_KEYEXIST:
if (allow_partial)
break;
mdbx_txn_break(txn());
MDBX_CXX17_FALLTHROUGH /* fallthrough */;
default:
MDBX_CXX20_UNLIKELY error::throw_exception(err);
}
return args[1].iov_len /* done item count */;
}
/// end cxx_api @}
} // namespace mdbx

View File

@@ -1,8 +1,8 @@
From 40efe497b511c322470aa9c084fe4c1759788c57 Mon Sep 17 00:00:00 2001
From 349c08cf21b66ecea851340133a1b845c25675f7 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?=
=?UTF-8?q?=D0=B5=D0=B2=20=28Leonid=20Yuriev=29?= <leo@yuriev.ru>
Date: Sun, 27 Oct 2024 22:34:19 +0300
Subject: [PATCH 1/1] package/libmdbx: new package (library/database).
Date: Tue, 22 Apr 2025 14:38:49 +0300
Subject: [PATCH] package/libmdbx: new package (library/database).
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -13,11 +13,9 @@ This patch adds libmdbx:
focused on creating unique lightweight solutions.
- libmdbx surpasses the legendary LMDB (Lightning Memory-Mapped Database)
in terms of reliability, features and performance.
- more information at https://gitflic.ru/project/erthink/libmdbx
- more information at https://libmdbx.dqdkfa.ru
The 0.12.12 "Dollezhal" is stable release of _libmdbx_ branch in memory
of the Soviet energy scientist Nikolai Antonovich Dollezhal on the 125th
anniversary of his birth.
The 0.13.6 "Бузина" (Elderberry) is stable release of _libmdbx_ branch with new superior features.
The complete ChangeLog: https://gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md
@@ -26,9 +24,9 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
DEVELOPERS | 3 +++
package/Config.in | 1 +
package/libmdbx/Config.in | 45 ++++++++++++++++++++++++++++++++++++
package/libmdbx/libmdbx.hash | 5 ++++
package/libmdbx/libmdbx.hash | 6 +++++
package/libmdbx/libmdbx.mk | 42 +++++++++++++++++++++++++++++++++
5 files changed, 96 insertions(+)
5 files changed, 97 insertions(+)
create mode 100644 package/libmdbx/Config.in
create mode 100644 package/libmdbx/libmdbx.hash
create mode 100644 package/libmdbx/libmdbx.mk
@@ -112,18 +110,19 @@ index 0000000000..a9a4ac45c5
+ !BR2_TOOLCHAIN_GCC_AT_LEAST_4_4
diff --git a/package/libmdbx/libmdbx.hash b/package/libmdbx/libmdbx.hash
new file mode 100644
index 0000000000..222e7caf5c
index 0000000000..ae5266716b
--- /dev/null
+++ b/package/libmdbx/libmdbx.hash
@@ -0,0 +1,5 @@
@@ -0,0 +1,6 @@
+# Hashes from: https://libmdbx.dqdkfa.ru/release/SHA256SUMS
+sha256 19c0eb33e1ed43ca2a94dceb06dd31946432d16f30a9751d3701c67efa22eb1a libmdbx-amalgamated-0.12.12.tar.xz
+sha256 57db987de6f7ccc66a66ae28a7bda9f9fbb48ac5fb9279bcca92fd5de13075d1 libmdbx-amalgamated-0.13.6.tar.xz
+
+# Locally calculated
+sha256 310fe25c858a9515fc8c8d7d1f24a67c9496f84a91e0a0e41ea9975b1371e569 LICENSE
+sha256 0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594 LICENSE
+sha256 651f71b46c6bb0046d2122df7f9def9cb24f4dc28c5b11cef059f66565cda30f NOTICE
diff --git a/package/libmdbx/libmdbx.mk b/package/libmdbx/libmdbx.mk
new file mode 100644
index 0000000000..da4a53913f
index 0000000000..571757262e
--- /dev/null
+++ b/package/libmdbx/libmdbx.mk
@@ -0,0 +1,42 @@
@@ -133,12 +132,12 @@ index 0000000000..da4a53913f
+#
+################################################################################
+
+LIBMDBX_VERSION = 0.12.12
+LIBMDBX_VERSION = 0.13.6
+LIBMDBX_SOURCE = libmdbx-amalgamated-$(LIBMDBX_VERSION).tar.xz
+LIBMDBX_SITE = https://libmdbx.dqdkfa.ru/release
+LIBMDBX_SUPPORTS_IN_SOURCE_BUILD = NO
+LIBMDBX_LICENSE = OLDAP-2.8
+LIBMDBX_LICENSE_FILES = LICENSE
+LIBMDBX_LICENSE = Apache-2.0
+LIBMDBX_LICENSE_FILES = LICENSE NOTICE
+LIBMDBX_REDISTRIBUTE = YES
+LIBMDBX_STRIP_COMPONENTS = 0
+LIBMDBX_INSTALL_STAGING = YES
@@ -170,5 +169,5 @@ index 0000000000..da4a53913f
+
+$(eval $(cmake-package))
--
2.47.0
2.49.0

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#define xMDBX_ALLOY 1 /* alloyed build */
#include "internals.h" /* must be included first */

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -141,7 +141,7 @@ __cold int mdbx_env_warmup(const MDBX_env *env, const MDBX_txn *txn, MDBX_warmup
return LOG_IFERR(MDBX_EINVAL);
if (txn) {
int err = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_ERROR);
int err = check_txn(txn, MDBX_TXN_FINISHED | MDBX_TXN_ERROR);
if (unlikely(err != MDBX_SUCCESS))
return LOG_IFERR(err);
}
@@ -342,7 +342,7 @@ __cold int mdbx_env_set_flags(MDBX_env *env, MDBX_env_flags_t flags, bool onoff)
if (unlikely(env->flags & MDBX_RDONLY))
return LOG_IFERR(MDBX_EACCESS);
const bool lock_needed = (env->flags & ENV_ACTIVE) && !env_txn0_owned(env);
const bool lock_needed = (env->flags & ENV_ACTIVE) && !env_owned_wrtxn(env);
bool should_unlock = false;
if (lock_needed) {
rc = lck_txn_lock(env, false);

View File

@@ -1,7 +1,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -571,11 +571,17 @@ retry_snap_meta:
uint8_t *const data_buffer = buffer + ceil_powerof2(meta_bytes, globals.sys_pagesize);
#if MDBX_USE_COPYFILERANGE
static bool copyfilerange_unavailable;
#if (defined(__linux__) || defined(__gnu_linux__))
if (globals.linux_kernel_version >= 0x05030000 && globals.linux_kernel_version < 0x05130000)
copyfilerange_unavailable = true;
#endif /* linux */
bool not_the_same_filesystem = false;
struct statfs statfs_info;
if (fstatfs(fd, &statfs_info) || statfs_info.f_type == /* ECRYPTFS_SUPER_MAGIC */ 0xf15f)
/* avoid use copyfilerange_unavailable() to ecryptfs due bugs */
not_the_same_filesystem = true;
if (!copyfilerange_unavailable) {
struct statfs statfs_info;
if (fstatfs(fd, &statfs_info) || statfs_info.f_type == /* ECRYPTFS_SUPER_MAGIC */ 0xf15f)
/* avoid use copyfilerange_unavailable() to ecryptfs due bugs */
not_the_same_filesystem = true;
}
#endif /* MDBX_USE_COPYFILERANGE */
for (size_t offset = meta_bytes; rc == MDBX_SUCCESS && offset < used_size;) {
@@ -597,7 +603,7 @@ retry_snap_meta:
continue;
}
rc = MDBX_ENODATA;
if (written == 0 || ignore_enosys(rc = errno) != MDBX_RESULT_TRUE)
if (written == 0 || ignore_enosys_and_eagain(rc = errno) != MDBX_RESULT_TRUE)
break;
sendfile_unavailable = true;
}
@@ -621,7 +627,7 @@ retry_snap_meta:
maybe useful for others FS */
EINVAL)
not_the_same_filesystem = true;
else if (ignore_enosys(rc) == MDBX_RESULT_TRUE)
else if (ignore_enosys_and_eagain(rc) == MDBX_RESULT_TRUE)
copyfilerange_unavailable = true;
else
break;
@@ -749,24 +755,66 @@ __cold static int copy2pathname(MDBX_txn *txn, const pathchar_t *dest_path, MDBX
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
#endif
);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
#if defined(_WIN32) || defined(_WIN64)
/* no locking required since the file opened with ShareMode == 0 */
#else
if (rc == MDBX_SUCCESS) {
MDBX_STRUCT_FLOCK lock_op;
memset(&lock_op, 0, sizeof(lock_op));
lock_op.l_type = F_WRLCK;
lock_op.l_whence = SEEK_SET;
lock_op.l_start = 0;
lock_op.l_len = OFF_T_MAX;
if (MDBX_FCNTL(newfd, MDBX_F_SETLK, &lock_op)
#if (defined(__linux__) || defined(__gnu_linux__)) && defined(LOCK_EX) && \
(!defined(__ANDROID_API__) || __ANDROID_API__ >= 24)
|| flock(newfd, LOCK_EX | LOCK_NB)
MDBX_STRUCT_FLOCK lock_op;
memset(&lock_op, 0, sizeof(lock_op));
lock_op.l_type = F_WRLCK;
lock_op.l_whence = SEEK_SET;
lock_op.l_start = 0;
lock_op.l_len = OFF_T_MAX;
const int err_fcntl = MDBX_FCNTL(newfd, MDBX_F_SETLK, &lock_op) ? errno : MDBX_SUCCESS;
const int err_flock =
#ifdef LOCK_EX
flock(newfd, LOCK_EX | LOCK_NB) ? errno : MDBX_SUCCESS;
#else
MDBX_ENOSYS;
#endif /* LOCK_EX */
const int err_check_fs_local =
/* avoid call osal_check_fs_local() on success */
(!err_fcntl && !err_flock && !MDBX_DEBUG) ? MDBX_SUCCESS :
#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 24
osal_check_fs_local(newfd, 0);
#else
MDBX_ENOSYS;
#endif
const bool flock_may_fail =
#if defined(__linux__) || defined(__gnu_linux__)
err_check_fs_local != 0;
#else
true;
#endif /* Linux */
)
rc = errno;
if (!err_fcntl &&
(err_flock == EWOULDBLOCK || err_flock == EAGAIN || ignore_enosys_and_eremote(err_flock) == MDBX_RESULT_TRUE)) {
rc = err_flock;
if (flock_may_fail) {
WARNING("ignore %s(%" MDBX_PRIsPATH ") error %d: since %s done, local/remote-fs check %d", "flock", dest_path,
err_flock, "fcntl-lock", err_check_fs_local);
rc = MDBX_SUCCESS;
}
} else if (!err_flock && err_check_fs_local == MDBX_RESULT_TRUE &&
ignore_enosys_and_eremote(err_fcntl) == MDBX_RESULT_TRUE) {
WARNING("ignore %s(%" MDBX_PRIsPATH ") error %d: since %s done, local/remote-fs check %d", "fcntl-lock", dest_path,
err_fcntl, "flock", err_check_fs_local);
} else if (err_fcntl || err_flock) {
ERROR("file-lock(%" MDBX_PRIsPATH ") failed: fcntl-lock %d, flock %d, local/remote-fs check %d", dest_path,
err_fcntl, err_flock, err_check_fs_local);
if (err_fcntl == ENOLCK || err_flock == ENOLCK)
rc = ENOLCK;
else if (err_fcntl == EWOULDBLOCK || err_flock == EWOULDBLOCK)
rc = EWOULDBLOCK;
else if (EWOULDBLOCK != EAGAIN && (err_fcntl == EAGAIN || err_flock == EAGAIN))
rc = EAGAIN;
else
rc = (err_fcntl && ignore_enosys_and_eremote(err_fcntl) != MDBX_RESULT_TRUE) ? err_fcntl : err_flock;
}
#endif /* Windows / POSIX */

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -12,8 +12,7 @@ MDBX_cursor *mdbx_cursor_create(void *context) {
couple->outer.signature = cur_signature_ready4dispose;
couple->outer.next = &couple->outer;
couple->userctx = context;
couple->outer.top_and_flags = z_poor_mark;
couple->inner.cursor.top_and_flags = z_poor_mark | z_inner;
cursor_reset(couple);
VALGRIND_MAKE_MEM_DEFINED(&couple->outer.backup, sizeof(couple->outer.backup));
VALGRIND_MAKE_MEM_DEFINED(&couple->outer.tree, sizeof(couple->outer.tree));
VALGRIND_MAKE_MEM_DEFINED(&couple->outer.clc, sizeof(couple->outer.clc));
@@ -23,62 +22,48 @@ MDBX_cursor *mdbx_cursor_create(void *context) {
return &couple->outer;
}
int mdbx_cursor_renew(const MDBX_txn *txn, MDBX_cursor *mc) {
int mdbx_cursor_renew(MDBX_txn *txn, MDBX_cursor *mc) {
return likely(mc) ? mdbx_cursor_bind(txn, mc, (kvx_t *)mc->clc - txn->env->kvs) : LOG_IFERR(MDBX_EINVAL);
}
int mdbx_cursor_reset(MDBX_cursor *mc) {
if (unlikely(!mc))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_ready4dispose && mc->signature != cur_signature_live))
return LOG_IFERR(MDBX_EBADSIGN);
cursor_couple_t *couple = (cursor_couple_t *)mc;
couple->outer.top_and_flags = z_poor_mark;
couple->inner.cursor.top_and_flags = z_poor_mark | z_inner;
return MDBX_SUCCESS;
}
int mdbx_cursor_bind(const MDBX_txn *txn, MDBX_cursor *mc, MDBX_dbi dbi) {
if (unlikely(!mc))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_ready4dispose && mc->signature != cur_signature_live))
return LOG_IFERR(MDBX_EBADSIGN);
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
int rc = cursor_check(mc, MDBX_TXN_FINISHED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = dbi_check(txn, dbi);
cursor_reset((cursor_couple_t *)mc);
return MDBX_SUCCESS;
}
int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *mc, MDBX_dbi dbi) {
if (unlikely(!mc))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_ready4dispose && mc->signature != cur_signature_live)) {
int rc = (mc->signature == cur_signature_wait4eot) ? MDBX_EINVAL : MDBX_EBADSIGN;
return LOG_IFERR(rc);
}
int rc = check_txn(txn, MDBX_TXN_FINISHED | MDBX_TXN_HAS_CHILD);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(dbi == FREE_DBI && !(txn->flags & MDBX_TXN_RDONLY)))
return LOG_IFERR(MDBX_EACCESS);
if (unlikely(mc->backup)) /* Cursor from parent transaction */ {
cASSERT(mc, mc->signature == cur_signature_live);
if (unlikely(cursor_dbi(mc) != dbi ||
/* paranoia */ mc->signature != cur_signature_live || mc->txn != txn))
return LOG_IFERR(MDBX_EINVAL);
rc = dbi_check(txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cASSERT(mc, mc->tree == &txn->dbs[dbi]);
cASSERT(mc, mc->clc == &txn->env->kvs[dbi].clc);
cASSERT(mc, cursor_dbi(mc) == dbi);
return likely(cursor_dbi(mc) == dbi &&
/* paranoia */ mc->signature == cur_signature_live && mc->txn == txn)
? MDBX_SUCCESS
: LOG_IFERR(MDBX_EINVAL) /* Disallow change DBI in nested
transactions */
;
}
if (unlikely(mc->backup)) /* Cursor from parent transaction */
LOG_IFERR(MDBX_EINVAL);
if (mc->signature == cur_signature_live) {
if (mc->txn == txn && cursor_dbi(mc) == dbi)
return MDBX_SUCCESS;
rc = mdbx_cursor_unbind(mc);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
return (rc == MDBX_BAD_TXN) ? MDBX_EINVAL : rc;
}
cASSERT(mc, mc->next == mc);
@@ -99,34 +84,47 @@ int mdbx_cursor_unbind(MDBX_cursor *mc) {
return (mc->signature == cur_signature_ready4dispose) ? MDBX_SUCCESS : LOG_IFERR(MDBX_EBADSIGN);
if (unlikely(mc->backup)) /* Cursor from parent transaction */
/* TODO: реализовать при переходе на двусвязный список курсоров */
return LOG_IFERR(MDBX_EINVAL);
eASSERT(nullptr, mc->txn && mc->txn->signature == txn_signature);
cASSERT(mc, mc->signature == cur_signature_live);
cASSERT(mc, !mc->backup);
int rc = check_txn(mc->txn, MDBX_TXN_FINISHED | MDBX_TXN_HAS_CHILD);
if (unlikely(rc != MDBX_SUCCESS)) {
for (const MDBX_txn *txn = mc->txn; rc == MDBX_BAD_TXN && check_txn(txn, MDBX_TXN_FINISHED) == MDBX_SUCCESS;
txn = txn->nested)
if (dbi_state(txn, cursor_dbi(mc)) == 0)
/* специальный случай: курсор прикреплён к родительской транзакции, но соответствующий dbi-дескриптор ещё
* не использовался во вложенной транзакции, т.е. курсор ещё не импортирован в дочернюю транзакцию и не имеет
* связанного сохранённого состояния (поэтому mc→backup равен nullptr). */
rc = MDBX_EINVAL;
return LOG_IFERR(rc);
}
if (unlikely(!mc->txn || mc->txn->signature != txn_signature)) {
ERROR("Wrong cursor's transaction %p 0x%x", __Wpedantic_format_voidptr(mc->txn), mc->txn ? mc->txn->signature : 0);
return LOG_IFERR(MDBX_PROBLEM);
}
if (mc->next != mc) {
const size_t dbi = (kvx_t *)mc->clc - mc->txn->env->kvs;
cASSERT(mc, cursor_dbi(mc) == dbi);
const size_t dbi = cursor_dbi(mc);
cASSERT(mc, dbi < mc->txn->n_dbi);
cASSERT(mc, &mc->txn->env->kvs[dbi].clc == mc->clc);
if (dbi < mc->txn->n_dbi) {
MDBX_cursor **prev = &mc->txn->cursors[dbi];
while (*prev && *prev != mc)
while (/* *prev && */ *prev != mc) {
ENSURE(mc->txn->env, (*prev)->signature == cur_signature_live || (*prev)->signature == cur_signature_wait4eot);
prev = &(*prev)->next;
}
cASSERT(mc, *prev == mc);
*prev = mc->next;
}
mc->next = mc;
}
cursor_drown((cursor_couple_t *)mc);
mc->signature = cur_signature_ready4dispose;
mc->flags = 0;
return MDBX_SUCCESS;
}
int mdbx_cursor_open(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) {
int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) {
if (unlikely(!ret))
return LOG_IFERR(MDBX_EINVAL);
*ret = nullptr;
@@ -145,44 +143,69 @@ int mdbx_cursor_open(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) {
return MDBX_SUCCESS;
}
void mdbx_cursor_close(MDBX_cursor *mc) {
if (likely(mc)) {
ENSURE(nullptr, mc->signature == cur_signature_live || mc->signature == cur_signature_ready4dispose);
MDBX_txn *const txn = mc->txn;
if (!mc->backup) {
mc->txn = nullptr;
/* Unlink from txn, if tracked. */
if (mc->next != mc) {
ENSURE(txn->env, check_txn(txn, 0) == MDBX_SUCCESS);
const size_t dbi = (kvx_t *)mc->clc - txn->env->kvs;
tASSERT(txn, dbi < txn->n_dbi);
if (dbi < txn->n_dbi) {
MDBX_cursor **prev = &txn->cursors[dbi];
while (*prev && *prev != mc)
prev = &(*prev)->next;
tASSERT(txn, *prev == mc);
*prev = mc->next;
}
mc->next = mc;
}
mc->signature = 0;
osal_free(mc);
} else {
/* Cursor closed before nested txn ends */
tASSERT(txn, mc->signature == cur_signature_live);
ENSURE(txn->env, check_txn_rw(txn, 0) == MDBX_SUCCESS);
mc->signature = cur_signature_wait4eot;
}
void mdbx_cursor_close(MDBX_cursor *cursor) {
if (likely(cursor)) {
int err = mdbx_cursor_close2(cursor);
if (unlikely(err != MDBX_SUCCESS))
mdbx_panic("%s:%d error %d (%s) while closing cursor", __func__, __LINE__, err, mdbx_liberr2str(err));
}
}
int mdbx_cursor_copy(const MDBX_cursor *src, MDBX_cursor *dest) {
if (unlikely(!src))
int mdbx_cursor_close2(MDBX_cursor *mc) {
if (unlikely(!mc))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(src->signature != cur_signature_live))
return LOG_IFERR((src->signature == cur_signature_ready4dispose) ? MDBX_EINVAL : MDBX_EBADSIGN);
int rc = mdbx_cursor_bind(src->txn, dest, cursor_dbi(src));
if (mc->signature == cur_signature_ready4dispose) {
if (unlikely(mc->txn || mc->backup))
return LOG_IFERR(MDBX_PANIC);
cursor_drown((cursor_couple_t *)mc);
mc->signature = 0;
osal_free(mc);
return MDBX_SUCCESS;
}
if (unlikely(mc->signature != cur_signature_live))
return LOG_IFERR(MDBX_EBADSIGN);
MDBX_txn *const txn = mc->txn;
int rc = check_txn(txn, MDBX_TXN_FINISHED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (mc->backup) {
/* Cursor closed before nested txn ends */
cursor_reset((cursor_couple_t *)mc);
mc->signature = cur_signature_wait4eot;
return MDBX_SUCCESS;
}
if (mc->next != mc) {
const size_t dbi = cursor_dbi(mc);
cASSERT(mc, dbi < mc->txn->n_dbi);
cASSERT(mc, &mc->txn->env->kvs[dbi].clc == mc->clc);
if (likely(dbi < txn->n_dbi)) {
MDBX_cursor **prev = &txn->cursors[dbi];
while (/* *prev && */ *prev != mc) {
ENSURE(txn->env, (*prev)->signature == cur_signature_live || (*prev)->signature == cur_signature_wait4eot);
prev = &(*prev)->next;
}
tASSERT(txn, *prev == mc);
*prev = mc->next;
}
mc->next = mc;
}
cursor_drown((cursor_couple_t *)mc);
mc->signature = 0;
osal_free(mc);
return MDBX_SUCCESS;
}
int mdbx_cursor_copy(const MDBX_cursor *src, MDBX_cursor *dest) {
int rc = cursor_check(src, MDBX_TXN_FINISHED | MDBX_TXN_HAS_CHILD);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = mdbx_cursor_bind(src->txn, dest, cursor_dbi(src));
if (unlikely(rc != MDBX_SUCCESS))
return rc;
@@ -207,43 +230,63 @@ again:
return MDBX_SUCCESS;
}
int mdbx_txn_release_all_cursors(const MDBX_txn *txn, bool unbind) {
int mdbx_txn_release_all_cursors_ex(const MDBX_txn *txn, bool unbind, size_t *count) {
int rc = check_txn(txn, MDBX_TXN_FINISHED | MDBX_TXN_HAS_CHILD);
if (likely(rc == MDBX_SUCCESS)) {
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
size_t n = 0;
do {
TXN_FOREACH_DBI_FROM(txn, i, MAIN_DBI) {
while (txn->cursors[i]) {
MDBX_cursor *mc = txn->cursors[i];
ENSURE(nullptr, mc->signature == cur_signature_live && (mc->next != mc) && !mc->backup);
rc = likely(rc < INT_MAX) ? rc + 1 : rc;
txn->cursors[i] = mc->next;
mc->next = mc;
if (unbind) {
mc->signature = cur_signature_ready4dispose;
mc->flags = 0;
} else {
mc->signature = 0;
osal_free(mc);
}
MDBX_cursor *mc = txn->cursors[i], *next = nullptr;
if (mc) {
txn->cursors[i] = nullptr;
do {
next = mc->next;
if (mc->signature == cur_signature_live) {
mc->signature = cur_signature_wait4eot;
cursor_drown((cursor_couple_t *)mc);
} else
ENSURE(nullptr, mc->signature == cur_signature_wait4eot);
if (mc->backup) {
MDBX_cursor *bk = mc->backup;
mc->next = bk->next;
mc->backup = bk->backup;
bk->backup = nullptr;
bk->signature = 0;
osal_free(bk);
} else {
mc->signature = cur_signature_ready4dispose;
mc->next = mc;
++n;
if (!unbind) {
mc->signature = 0;
osal_free(mc);
}
}
} while ((mc = next) != nullptr);
}
}
} else {
eASSERT(nullptr, rc < 0);
LOG_IFERR(rc);
}
return rc;
txn = txn->parent;
} while (txn);
if (count)
*count = n;
return MDBX_SUCCESS;
}
int mdbx_cursor_compare(const MDBX_cursor *l, const MDBX_cursor *r, bool ignore_multival) {
const int incomparable = INT16_MAX + 1;
if (unlikely(!l))
return r ? -incomparable * 9 : 0;
else if (unlikely(!r))
return incomparable * 9;
if (unlikely(l->signature != cur_signature_live))
return (r->signature == cur_signature_live) ? -incomparable * 8 : 0;
if (unlikely(r->signature != cur_signature_live))
return (l->signature == cur_signature_live) ? incomparable * 8 : 0;
if (unlikely(cursor_check_pure(l) != MDBX_SUCCESS))
return (cursor_check_pure(r) == MDBX_SUCCESS) ? -incomparable * 8 : 0;
if (unlikely(cursor_check_pure(r) != MDBX_SUCCESS))
return (cursor_check_pure(l) == MDBX_SUCCESS) ? incomparable * 8 : 0;
if (unlikely(l->clc != r->clc)) {
if (l->txn->env != r->txn->env)
@@ -309,13 +352,7 @@ int mdbx_cursor_compare(const MDBX_cursor *l, const MDBX_cursor *r, bool ignore_
}
int mdbx_cursor_count_ex(const MDBX_cursor *mc, size_t *count, MDBX_stat *ns, size_t bytes) {
if (unlikely(mc == nullptr))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_live))
return LOG_IFERR((mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL : MDBX_EBADSIGN);
int rc = check_txn(mc->txn, MDBX_TXN_BLOCKED);
int rc = cursor_check_ro(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
@@ -365,11 +402,9 @@ int mdbx_cursor_count(const MDBX_cursor *mc, size_t *count) {
}
int mdbx_cursor_on_first(const MDBX_cursor *mc) {
if (unlikely(mc == nullptr))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_live))
return LOG_IFERR((mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL : MDBX_EBADSIGN);
int rc = cursor_check_pure(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
for (intptr_t i = 0; i <= mc->top; ++i) {
if (mc->ki[i])
@@ -380,11 +415,9 @@ int mdbx_cursor_on_first(const MDBX_cursor *mc) {
}
int mdbx_cursor_on_first_dup(const MDBX_cursor *mc) {
if (unlikely(mc == nullptr))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_live))
return LOG_IFERR((mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL : MDBX_EBADSIGN);
int rc = cursor_check_pure(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (is_filled(mc) && mc->subcur) {
mc = &mc->subcur->cursor;
@@ -398,11 +431,9 @@ int mdbx_cursor_on_first_dup(const MDBX_cursor *mc) {
}
int mdbx_cursor_on_last(const MDBX_cursor *mc) {
if (unlikely(mc == nullptr))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_live))
return LOG_IFERR((mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL : MDBX_EBADSIGN);
int rc = cursor_check_pure(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
for (intptr_t i = 0; i <= mc->top; ++i) {
size_t nkeys = page_numkeys(mc->pg[i]);
@@ -414,11 +445,9 @@ int mdbx_cursor_on_last(const MDBX_cursor *mc) {
}
int mdbx_cursor_on_last_dup(const MDBX_cursor *mc) {
if (unlikely(mc == nullptr))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_live))
return LOG_IFERR((mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL : MDBX_EBADSIGN);
int rc = cursor_check_pure(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (is_filled(mc) && mc->subcur) {
mc = &mc->subcur->cursor;
@@ -433,29 +462,18 @@ int mdbx_cursor_on_last_dup(const MDBX_cursor *mc) {
}
int mdbx_cursor_eof(const MDBX_cursor *mc) {
if (unlikely(mc == nullptr))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_live))
return LOG_IFERR((mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL : MDBX_EBADSIGN);
int rc = cursor_check_pure(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
return is_eof(mc) ? MDBX_RESULT_TRUE : MDBX_RESULT_FALSE;
}
int mdbx_cursor_get(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, MDBX_cursor_op op) {
if (unlikely(mc == nullptr))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_live))
return LOG_IFERR((mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL : MDBX_EBADSIGN);
int rc = check_txn(mc->txn, MDBX_TXN_BLOCKED);
int rc = cursor_check_ro(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(cursor_dbi_changed(mc)))
return LOG_IFERR(MDBX_BAD_DBI);
return LOG_IFERR(cursor_ops(mc, key, data, op));
}
@@ -580,19 +598,13 @@ int mdbx_cursor_get_batch(MDBX_cursor *mc, size_t *count, MDBX_val *pairs, size_
return LOG_IFERR(MDBX_EINVAL);
*count = 0;
if (unlikely(mc == nullptr || limit < 4 || limit > INTPTR_MAX - 2))
if (unlikely(limit < 4 || limit > INTPTR_MAX - 2))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_live))
return LOG_IFERR((mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL : MDBX_EBADSIGN);
int rc = check_txn(mc->txn, MDBX_TXN_BLOCKED);
int rc = cursor_check_ro(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(cursor_dbi_changed(mc)))
return LOG_IFERR(MDBX_BAD_DBI);
if (unlikely(mc->subcur))
return LOG_IFERR(MDBX_INCOMPATIBLE) /* must be a non-dupsort table */;
@@ -661,11 +673,9 @@ bailout:
/*----------------------------------------------------------------------------*/
int mdbx_cursor_set_userctx(MDBX_cursor *mc, void *ctx) {
if (unlikely(!mc))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_ready4dispose && mc->signature != cur_signature_live))
return LOG_IFERR(MDBX_EBADSIGN);
int rc = cursor_check(mc, 0);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t *couple = container_of(mc, cursor_couple_t, outer);
couple->userctx = ctx;
@@ -687,11 +697,9 @@ MDBX_txn *mdbx_cursor_txn(const MDBX_cursor *mc) {
if (unlikely(!mc || mc->signature != cur_signature_live))
return nullptr;
MDBX_txn *txn = mc->txn;
if (unlikely(!txn || txn->signature != txn_signature))
if (unlikely(!txn || txn->signature != txn_signature || (txn->flags & MDBX_TXN_FINISHED)))
return nullptr;
if (unlikely(txn->flags & MDBX_TXN_FINISHED))
return nullptr;
return txn;
return (txn->flags & MDBX_TXN_HAS_CHILD) ? txn->env->txn : txn;
}
MDBX_dbi mdbx_cursor_dbi(const MDBX_cursor *mc) {
@@ -703,37 +711,17 @@ MDBX_dbi mdbx_cursor_dbi(const MDBX_cursor *mc) {
/*----------------------------------------------------------------------------*/
int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data, MDBX_put_flags_t flags) {
if (unlikely(mc == nullptr || key == nullptr || data == nullptr))
if (unlikely(key == nullptr || data == nullptr))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_live))
return LOG_IFERR((mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL : MDBX_EBADSIGN);
int rc = check_txn_rw(mc->txn, MDBX_TXN_BLOCKED);
int rc = cursor_check_rw(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(cursor_dbi_changed(mc)))
return LOG_IFERR(MDBX_BAD_DBI);
cASSERT(mc, cursor_is_tracked(mc));
/* Check this first so counter will always be zero on any early failures. */
if (unlikely(flags & MDBX_MULTIPLE)) {
if (unlikely(flags & MDBX_RESERVE))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(!(mc->tree->flags & MDBX_DUPFIXED)))
return LOG_IFERR(MDBX_INCOMPATIBLE);
const size_t dcount = data[1].iov_len;
if (unlikely(dcount < 2 || data->iov_len == 0))
return LOG_IFERR(MDBX_BAD_VALSIZE);
if (unlikely(mc->tree->dupfix_size != data->iov_len) && mc->tree->dupfix_size)
return LOG_IFERR(MDBX_BAD_VALSIZE);
if (unlikely(dcount > MAX_MAPSIZE / 2 / (BRANCH_NODE_MAX(MDBX_MAX_PAGESIZE) - NODESIZE))) {
/* checking for multiplication overflow */
if (unlikely(dcount > MAX_MAPSIZE / 2 / data->iov_len))
return LOG_IFERR(MDBX_TOO_LARGE);
}
rc = cursor_check_multiple(mc, key, data, flags);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
}
if (flags & MDBX_RESERVE) {
@@ -742,35 +730,21 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data, MDBX_p
data->iov_base = nullptr;
}
if (unlikely(mc->txn->flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED)))
return LOG_IFERR((mc->txn->flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN);
return LOG_IFERR(cursor_put_checklen(mc, key, data, flags));
}
int mdbx_cursor_del(MDBX_cursor *mc, MDBX_put_flags_t flags) {
if (unlikely(!mc))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_live))
return LOG_IFERR((mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL : MDBX_EBADSIGN);
int rc = check_txn_rw(mc->txn, MDBX_TXN_BLOCKED);
int rc = cursor_check_rw(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(cursor_dbi_changed(mc)))
return LOG_IFERR(MDBX_BAD_DBI);
return LOG_IFERR(cursor_del(mc, flags));
}
__cold int mdbx_cursor_ignord(MDBX_cursor *mc) {
if (unlikely(!mc))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_live))
return LOG_IFERR((mc->signature == cur_signature_ready4dispose) ? MDBX_EINVAL : MDBX_EBADSIGN);
int rc = cursor_check(mc, 0);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
mc->checking |= z_ignord;
if (mc->subcur)

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -11,6 +11,12 @@ __cold static intptr_t reasonable_db_maxsize(void) {
/* the 32-bit limit is good enough for fallback */
return cached_result = MAX_MAPSIZE32;
#if defined(__SANITIZE_ADDRESS__)
total_ram_pages >>= 4;
#endif /* __SANITIZE_ADDRESS__ */
if (RUNNING_ON_VALGRIND)
total_ram_pages >>= 4;
if (unlikely((size_t)total_ram_pages * 2 > MAX_MAPSIZE / (size_t)pagesize))
return cached_result = MAX_MAPSIZE;
assert(MAX_MAPSIZE >= (size_t)(total_ram_pages * pagesize * 2));
@@ -642,7 +648,7 @@ __cold int mdbx_env_close_ex(MDBX_env *env, bool dont_sync) {
#endif /* Windows */
}
if (env->basal_txn && env->basal_txn->owner == osal_thread_self())
if (env->basal_txn && (MDBX_TXN_CHECKOWNER ? env->basal_txn->owner == osal_thread_self() : !!env->basal_txn->owner))
lck_txn_unlock(env);
eASSERT(env, env->signature.weak == 0);
@@ -952,8 +958,7 @@ __cold int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t si
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
const bool txn0_owned = env->basal_txn && env_txn0_owned(env);
const bool inside_txn = txn0_owned && env->txn;
MDBX_txn *const txn_owned = env_owned_wrtxn(env);
bool should_unlock = false;
#if MDBX_DEBUG && 0 /* минимальные шаги для проверки/отладки уже не нужны */
@@ -969,7 +974,7 @@ __cold int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t si
if (unlikely(env->flags & MDBX_RDONLY))
return LOG_IFERR(MDBX_EACCESS);
if (!txn0_owned) {
if (!txn_owned) {
int err = lck_txn_lock(env, false);
if (unlikely(err != MDBX_SUCCESS))
return LOG_IFERR(err);
@@ -983,8 +988,7 @@ __cold int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t si
/* get untouched params from current TXN or DB */
if (pagesize <= 0 || pagesize >= INT_MAX)
pagesize = env->ps;
const geo_t *const geo =
inside_txn ? &env->txn->geo : &meta_recent(env, &env->basal_txn->tw.troika).ptr_c->geometry;
const geo_t *const geo = env->txn ? &env->txn->geo : &meta_recent(env, &env->basal_txn->tw.troika).ptr_c->geometry;
if (size_lower < 0)
size_lower = pgno2bytes(env, geo->lower);
if (size_now < 0)
@@ -1009,7 +1013,7 @@ __cold int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t si
size_now = usedbytes;
} else {
/* env NOT yet mapped */
if (unlikely(inside_txn))
if (unlikely(env->txn))
return LOG_IFERR(MDBX_PANIC);
/* is requested some auto-value for pagesize ? */
@@ -1198,8 +1202,7 @@ __cold int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t si
ENSURE(env, pagesize == (intptr_t)env->ps);
meta_t meta;
memset(&meta, 0, sizeof(meta));
if (!inside_txn) {
eASSERT(env, should_unlock);
if (!env->txn) {
const meta_ptr_t head = meta_recent(env, &env->basal_txn->tw.troika);
uint64_t timestamp = 0;
@@ -1289,7 +1292,7 @@ __cold int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t si
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
}
if (inside_txn) {
if (env->txn) {
env->txn->geo = new_geo;
env->txn->flags |= MDBX_TXN_DIRTY;
} else {
@@ -1414,17 +1417,17 @@ __cold int mdbx_env_stat_ex(const MDBX_env *env, const MDBX_txn *txn, MDBX_stat
if (unlikely(err != MDBX_SUCCESS))
return LOG_IFERR(err);
if (env->txn && env_txn0_owned(env))
MDBX_txn *txn_owned = env_owned_wrtxn(env);
if (txn_owned)
/* inside write-txn */
return LOG_IFERR(stat_acc(env->txn, dest, bytes));
return LOG_IFERR(stat_acc(txn_owned, dest, bytes));
MDBX_txn *tmp_txn;
err = mdbx_txn_begin((MDBX_env *)env, nullptr, MDBX_TXN_RDONLY, &tmp_txn);
err = mdbx_txn_begin((MDBX_env *)env, nullptr, MDBX_TXN_RDONLY, &txn_owned);
if (unlikely(err != MDBX_SUCCESS))
return LOG_IFERR(err);
const int rc = stat_acc(tmp_txn, dest, bytes);
err = mdbx_txn_abort(tmp_txn);
const int rc = stat_acc(txn_owned, dest, bytes);
err = mdbx_txn_abort(txn_owned);
if (unlikely(err != MDBX_SUCCESS))
return LOG_IFERR(err);
return LOG_IFERR(rc);

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -117,7 +117,6 @@ __cold int mdbx_thread_unregister(const MDBX_env *env) {
return MDBX_RESULT_TRUE /* not registered */;
eASSERT(env, r->pid.weak == env->pid);
eASSERT(env, r->tid.weak == osal_thread_self());
if (unlikely(r->pid.weak != env->pid || r->tid.weak != osal_thread_self()))
return LOG_IFERR(MDBX_BAD_RSLOT);
@@ -141,7 +140,7 @@ int mdbx_txn_lock(MDBX_env *env, bool dont_wait) {
if (unlikely(env->flags & MDBX_RDONLY))
return LOG_IFERR(MDBX_EACCESS);
if (unlikely(env->basal_txn->owner || (env->basal_txn->flags & MDBX_TXN_FINISHED) == 0))
if (dont_wait && unlikely(env->basal_txn->owner || (env->basal_txn->flags & MDBX_TXN_FINISHED) == 0))
return LOG_IFERR(MDBX_BUSY);
return LOG_IFERR(lck_txn_lock(env, dont_wait));
@@ -154,8 +153,10 @@ int mdbx_txn_unlock(MDBX_env *env) {
if (unlikely(env->flags & MDBX_RDONLY))
return LOG_IFERR(MDBX_EACCESS);
#if MDBX_TXN_CHECKOWNER
if (unlikely(env->basal_txn->owner != osal_thread_self()))
return LOG_IFERR(MDBX_THREAD_MISMATCH);
#endif /* MDBX_TXN_CHECKOWNER */
if (unlikely((env->basal_txn->flags & MDBX_TXN_FINISHED) == 0))
return LOG_IFERR(MDBX_BUSY);

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -199,9 +199,7 @@ __cold const char *mdbx_liberr2str(int errnum) {
return "MDBX_OUSTED: The parked read transaction was outed for the sake"
" of recycling old MVCC snapshots";
case MDBX_MVCC_RETARDED:
return "MDBX_MVCC_RETARDED: MVCC snapshot used by read transaction"
" is outdated and could not be copied"
" since corresponding meta-pages was overwritten";
return "MDBX_MVCC_RETARDED: MVCC snapshot used by parked transaction was bygone";
default:
return nullptr;
}

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -180,7 +180,7 @@ __cold int mdbx_env_set_option(MDBX_env *env, const MDBX_option_t option, uint64
if (unlikely(err != MDBX_SUCCESS))
return LOG_IFERR(err);
const bool lock_needed = ((env->flags & ENV_ACTIVE) && env->basal_txn && !env_txn0_owned(env));
const bool lock_needed = ((env->flags & ENV_ACTIVE) && env->basal_txn && !env_owned_wrtxn(env));
bool should_unlock = false;
switch (option) {
case MDBX_opt_sync_bytes:

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -16,12 +16,6 @@ __hot static int cursor_diff(const MDBX_cursor *const __restrict x, const MDBX_c
r->level = 0;
r->root_nkeys = 0;
if (unlikely(x->signature != cur_signature_live))
return (x->signature == cur_signature_ready4dispose) ? MDBX_EINVAL : MDBX_EBADSIGN;
if (unlikely(y->signature != cur_signature_live))
return (y->signature == cur_signature_ready4dispose) ? MDBX_EINVAL : MDBX_EBADSIGN;
int rc = check_txn(x->txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
@@ -146,12 +140,20 @@ __hot static ptrdiff_t estimate(const tree_t *tree, diff_t *const __restrict dr)
* Range-Estimation API */
__hot int mdbx_estimate_distance(const MDBX_cursor *first, const MDBX_cursor *last, ptrdiff_t *distance_items) {
if (unlikely(first == nullptr || last == nullptr || distance_items == nullptr))
if (unlikely(!distance_items))
return LOG_IFERR(MDBX_EINVAL);
int rc = cursor_check_pure(first);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = cursor_check_pure(last);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
*distance_items = 0;
diff_t dr;
int rc = cursor_diff(last, first, &dr);
rc = cursor_diff(last, first, &dr);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
@@ -172,14 +174,10 @@ __hot int mdbx_estimate_distance(const MDBX_cursor *first, const MDBX_cursor *la
__hot int mdbx_estimate_move(const MDBX_cursor *cursor, MDBX_val *key, MDBX_val *data, MDBX_cursor_op move_op,
ptrdiff_t *distance_items) {
if (unlikely(cursor == nullptr || distance_items == nullptr || move_op == MDBX_GET_CURRENT ||
move_op == MDBX_GET_MULTIPLE))
if (unlikely(!distance_items || move_op == MDBX_GET_CURRENT || move_op == MDBX_GET_MULTIPLE))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(cursor->signature != cur_signature_live))
return LOG_IFERR((cursor->signature == cur_signature_ready4dispose) ? MDBX_EINVAL : MDBX_EBADSIGN);
int rc = check_txn(cursor->txn, MDBX_TXN_BLOCKED);
int rc = cursor_check_ro(cursor);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
@@ -232,10 +230,6 @@ __hot int mdbx_estimate_move(const MDBX_cursor *cursor, MDBX_val *key, MDBX_val
__hot int mdbx_estimate_range(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *begin_key, const MDBX_val *begin_data,
const MDBX_val *end_key, const MDBX_val *end_data, ptrdiff_t *size_items) {
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!size_items))
return LOG_IFERR(MDBX_EINVAL);
@@ -248,6 +242,10 @@ __hot int mdbx_estimate_range(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val
if (unlikely(begin_key == MDBX_EPSILON && end_key == MDBX_EPSILON))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t begin;
/* LY: first, initialize cursor to refresh a DB in case it have DB_STALE */
rc = cursor_init(&begin.outer, txn, dbi);

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -51,15 +51,15 @@ __cold int mdbx_dbi_dupsort_depthmask(const MDBX_txn *txn, MDBX_dbi dbi, uint32_
}
int mdbx_canary_get(const MDBX_txn *txn, MDBX_canary *canary) {
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(canary == nullptr))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_PARKED);
if (unlikely(rc != MDBX_SUCCESS)) {
memset(canary, 0, sizeof(*canary));
return LOG_IFERR(rc);
}
if (unlikely(canary == nullptr))
return LOG_IFERR(MDBX_EINVAL);
*canary = txn->canary;
return MDBX_SUCCESS;
}
@@ -68,13 +68,13 @@ int mdbx_get(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *d
DKBUF_DEBUG;
DEBUG("===> get db %u key [%s]", dbi, DKEY_DEBUG(key));
if (unlikely(!key || !data))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!key || !data))
return LOG_IFERR(MDBX_EINVAL);
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
@@ -84,15 +84,12 @@ int mdbx_get(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *d
}
int mdbx_get_equal_or_great(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data) {
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!key || !data))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(txn->flags & MDBX_TXN_BLOCKED))
return LOG_IFERR(MDBX_BAD_TXN);
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, dbi);
@@ -106,13 +103,13 @@ int mdbx_get_ex(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data
DKBUF_DEBUG;
DEBUG("===> get db %u key [%s]", dbi, DKEY_DEBUG(key));
if (unlikely(!key || !data))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!key || !data))
return LOG_IFERR(MDBX_EINVAL);
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
@@ -179,7 +176,7 @@ int mdbx_canary_put(MDBX_txn *txn, const MDBX_canary *canary) {
* расположен в той-же странице памяти, в том числе для многостраничных
* P_LARGE страниц с длинными данными. */
int mdbx_is_dirty(const MDBX_txn *txn, const void *ptr) {
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
int rc = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_PARKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
@@ -215,18 +212,15 @@ int mdbx_is_dirty(const MDBX_txn *txn, const void *ptr) {
}
int mdbx_del(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, const MDBX_val *data) {
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!key))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(dbi <= FREE_DBI))
return LOG_IFERR(MDBX_BAD_DBI);
if (unlikely(txn->flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED)))
return LOG_IFERR((txn->flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN);
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, dbi);
@@ -254,10 +248,6 @@ int mdbx_del(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, const MDBX_val *d
}
int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data, MDBX_put_flags_t flags) {
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!key || !data))
return LOG_IFERR(MDBX_EINVAL);
@@ -268,13 +258,27 @@ int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data, M
MDBX_APPENDDUP | MDBX_CURRENT | MDBX_MULTIPLE)))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(txn->flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED)))
return LOG_IFERR((txn->flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN);
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(flags & MDBX_MULTIPLE)) {
rc = cursor_check_multiple(&cx.outer, key, data, flags);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
}
if (flags & MDBX_RESERVE) {
if (unlikely(cx.outer.tree->flags & (MDBX_DUPSORT | MDBX_REVERSEDUP | MDBX_INTEGERDUP | MDBX_DUPFIXED)))
return LOG_IFERR(MDBX_INCOMPATIBLE);
data->iov_base = nullptr;
}
cx.outer.next = txn->cursors[dbi];
txn->cursors[dbi] = &cx.outer;
@@ -330,10 +334,6 @@ int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data, M
int mdbx_replace_ex(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *new_data, MDBX_val *old_data,
MDBX_put_flags_t flags, MDBX_preserve_func preserver, void *preserver_context) {
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!key || !old_data || old_data == new_data))
return LOG_IFERR(MDBX_EINVAL);
@@ -350,6 +350,10 @@ int mdbx_replace_ex(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *
MDBX_APPENDDUP | MDBX_CURRENT)))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -9,7 +9,7 @@ __attribute__((__no_sanitize_thread__, __noinline__))
#endif
int mdbx_txn_straggler(const MDBX_txn *txn, int *percent)
{
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
int rc = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_PARKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR((rc > 0) ? -rc : rc);
@@ -101,11 +101,13 @@ int mdbx_txn_abort(MDBX_txn *txn) {
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
#if MDBX_TXN_CHECKOWNER
if ((txn->flags & (MDBX_TXN_RDONLY | MDBX_NOSTICKYTHREADS)) == MDBX_NOSTICKYTHREADS &&
unlikely(txn->owner != osal_thread_self())) {
mdbx_txn_break(txn);
return LOG_IFERR(MDBX_THREAD_MISMATCH);
}
#endif /* MDBX_TXN_CHECKOWNER */
return LOG_IFERR(txn_abort(txn));
}
@@ -198,9 +200,13 @@ int mdbx_txn_begin_ex(MDBX_env *env, MDBX_txn *parent, MDBX_txn_flags_t flags, M
MDBX_txn *txn = nullptr;
if (parent) {
/* Nested transactions: Max 1 child, write txns only, no writemap */
rc = check_txn_rw(parent, MDBX_TXN_RDONLY | MDBX_WRITEMAP | MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS)) {
if (rc == MDBX_BAD_TXN && (parent->flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED)) == 0) {
rc = check_txn(parent, MDBX_TXN_BLOCKED - MDBX_TXN_PARKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(parent->flags & (MDBX_TXN_RDONLY | MDBX_WRITEMAP))) {
rc = MDBX_BAD_TXN;
if ((parent->flags & MDBX_TXN_RDONLY) == 0) {
ERROR("%s mode is incompatible with nested transactions", "MDBX_WRITEMAP");
rc = MDBX_INCOMPATIBLE;
}
@@ -420,11 +426,13 @@ int mdbx_txn_commit_ex(MDBX_txn *txn, MDBX_commit_latency *latency) {
goto done;
}
if (!txn->parent && (txn->flags & MDBX_NOSTICKYTHREADS) && unlikely(txn->owner != osal_thread_self())) {
#if MDBX_TXN_CHECKOWNER
if ((txn->flags & MDBX_NOSTICKYTHREADS) && txn == env->basal_txn && unlikely(txn->owner != osal_thread_self())) {
txn->flags |= MDBX_TXN_ERROR;
rc = MDBX_THREAD_MISMATCH;
return LOG_IFERR(rc);
}
#endif /* MDBX_TXN_CHECKOWNER */
if (unlikely(txn->flags & MDBX_TXN_ERROR)) {
rc = MDBX_RESULT_TRUE;
@@ -453,20 +461,29 @@ int mdbx_txn_commit_ex(MDBX_txn *txn, MDBX_commit_latency *latency) {
eASSERT(env, dpl_check(txn));
if (txn->tw.dirtylist->length == 0 && !(txn->flags & MDBX_TXN_DIRTY) && parent->n_dbi == txn->n_dbi) {
TXN_FOREACH_DBI_ALL(txn, i) {
tASSERT(txn, (txn->dbi_state[i] & DBI_DIRTY) == 0);
if ((txn->dbi_state[i] & DBI_STALE) && !(parent->dbi_state[i] & DBI_STALE))
tASSERT(txn, memcmp(&parent->dbs[i], &txn->dbs[i], sizeof(tree_t)) == 0);
}
/* fast completion of pure nested transaction */
VERBOSE("fast-complete pure nested txn %" PRIaTXN, txn->txnid);
tASSERT(txn, memcmp(&parent->geo, &txn->geo, sizeof(parent->geo)) == 0);
tASSERT(txn, memcmp(&parent->canary, &txn->canary, sizeof(parent->canary)) == 0);
tASSERT(txn, !txn->tw.spilled.list || MDBX_PNL_GETSIZE(txn->tw.spilled.list) == 0);
tASSERT(txn, txn->tw.loose_count == 0);
/* fast completion of pure nested transaction */
VERBOSE("fast-complete pure nested txn %" PRIaTXN, txn->txnid);
end_mode = TXN_END_PURE_COMMIT | TXN_END_SLOT | TXN_END_FREE;
/* Update parent's DBs array */
eASSERT(env, parent->n_dbi == txn->n_dbi);
TXN_FOREACH_DBI_ALL(txn, dbi) {
tASSERT(txn, (txn->dbi_state[dbi] & (DBI_CREAT | DBI_DIRTY)) == 0);
if (txn->dbi_state[dbi] & DBI_FRESH) {
parent->dbs[dbi] = txn->dbs[dbi];
/* preserve parent's status */
const uint8_t state = txn->dbi_state[dbi] | DBI_FRESH;
DEBUG("dbi %zu dbi-state %s 0x%02x -> 0x%02x", dbi, (parent->dbi_state[dbi] != state) ? "update" : "still",
parent->dbi_state[dbi], state);
parent->dbi_state[dbi] = state;
}
}
txn_done_cursors(txn, true);
end_mode = TXN_END_PURE_COMMIT | TXN_END_SLOT | TXN_END_FREE | TXN_END_EOTDONE;
goto done;
}
@@ -528,15 +545,16 @@ int mdbx_txn_commit_ex(MDBX_txn *txn, MDBX_commit_latency *latency) {
/* Update parent's DBs array */
eASSERT(env, parent->n_dbi == txn->n_dbi);
TXN_FOREACH_DBI_ALL(txn, dbi) {
if (txn->dbi_state[dbi] & (DBI_CREAT | DBI_FRESH | DBI_DIRTY)) {
if (txn->dbi_state[dbi] != (parent->dbi_state[dbi] & ~(DBI_FRESH | DBI_CREAT | DBI_DIRTY))) {
eASSERT(env, (txn->dbi_state[dbi] & (DBI_CREAT | DBI_FRESH | DBI_DIRTY)) != 0 ||
(txn->dbi_state[dbi] | DBI_STALE) ==
(parent->dbi_state[dbi] & ~(DBI_FRESH | DBI_CREAT | DBI_DIRTY)));
parent->dbs[dbi] = txn->dbs[dbi];
/* preserve parent's status */
const uint8_t state = txn->dbi_state[dbi] | (parent->dbi_state[dbi] & (DBI_CREAT | DBI_FRESH | DBI_DIRTY));
DEBUG("dbi %zu dbi-state %s 0x%02x -> 0x%02x", dbi, (parent->dbi_state[dbi] != state) ? "update" : "still",
parent->dbi_state[dbi], state);
parent->dbi_state[dbi] = state;
} else {
eASSERT(env, txn->dbi_state[dbi] == (parent->dbi_state[dbi] & ~(DBI_FRESH | DBI_CREAT | DBI_DIRTY)));
}
}

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -78,7 +78,7 @@ __cold static int audit_ex_locked(MDBX_txn *txn, size_t retired_stored, bool don
if (db)
ctx.used += audit_db_used(db);
else if (dbi_state(txn, dbi))
WARNING("audit %s@%" PRIaTXN ": unable account dbi %zd / \"%*s\", state 0x%02x", txn->parent ? "nested-" : "",
WARNING("audit %s@%" PRIaTXN ": unable account dbi %zd / \"%.*s\", state 0x%02x", txn->parent ? "nested-" : "",
txn->txnid, dbi, (int)env->kvs[dbi].name.iov_len, (const char *)env->kvs[dbi].name.iov_base,
dbi_state(txn, dbi));
}

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -213,7 +213,7 @@ __cold static void MDBX_PRINTF_ARGS(5, 6)
issue->next = chk->usr->scope->issues;
chk->usr->scope->issues = issue;
} else
chk_error_rc(scope, ENOMEM, "adding issue");
chk_error_rc(scope, MDBX_ENOMEM, "adding issue");
}
va_list args;

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
@@ -200,6 +200,10 @@ static inline bool check_table_flags(unsigned flags) {
}
}
static inline int tbl_setup_ifneed(const MDBX_env *env, volatile kvx_t *const kvx, const tree_t *const db) {
return likely(kvx->clc.v.lmax) ? MDBX_SUCCESS : tbl_setup(env, kvx, db);
}
/*----------------------------------------------------------------------------*/
MDBX_NOTHROW_PURE_FUNCTION static inline size_t pgno2bytes(const MDBX_env *env, size_t pgno) {
@@ -348,7 +352,7 @@ MDBX_CONST_FUNCTION static inline lck_t *lckless_stub(const MDBX_env *env) {
}
#if !(defined(_WIN32) || defined(_WIN64))
MDBX_MAYBE_UNUSED static inline int ignore_enosys(int err) {
MDBX_CONST_FUNCTION static inline int ignore_enosys(int err) {
#ifdef ENOSYS
if (err == ENOSYS)
return MDBX_RESULT_TRUE;
@@ -369,10 +373,21 @@ MDBX_MAYBE_UNUSED static inline int ignore_enosys(int err) {
if (err == EOPNOTSUPP)
return MDBX_RESULT_TRUE;
#endif /* EOPNOTSUPP */
if (err == EAGAIN)
return MDBX_RESULT_TRUE;
return err;
}
MDBX_MAYBE_UNUSED MDBX_CONST_FUNCTION static inline int ignore_enosys_and_eagain(int err) {
return (err == EAGAIN) ? MDBX_RESULT_TRUE : ignore_enosys(err);
}
MDBX_MAYBE_UNUSED MDBX_CONST_FUNCTION static inline int ignore_enosys_and_einval(int err) {
return (err == EINVAL) ? MDBX_RESULT_TRUE : ignore_enosys(err);
}
MDBX_MAYBE_UNUSED MDBX_CONST_FUNCTION static inline int ignore_enosys_and_eremote(int err) {
return (err == MDBX_EREMOTE) ? MDBX_RESULT_TRUE : ignore_enosys(err);
}
#endif /* defined(_WIN32) || defined(_WIN64) */
static inline int check_env(const MDBX_env *env, const bool wanna_active) {
@@ -412,10 +427,11 @@ static __always_inline int check_txn(const MDBX_txn *txn, int bad_bits) {
return MDBX_EPERM;
if (unlikely(txn->flags & bad_bits)) {
if ((bad_bits & MDBX_TXN_RDONLY) && unlikely(txn->flags & MDBX_TXN_RDONLY))
return MDBX_EACCESS;
if ((bad_bits & MDBX_TXN_PARKED) == 0)
return MDBX_BAD_TXN;
else
return txn_check_badbits_parked(txn, bad_bits);
return txn_check_badbits_parked(txn, bad_bits);
}
}
@@ -433,14 +449,7 @@ static __always_inline int check_txn(const MDBX_txn *txn, int bad_bits) {
}
static inline int check_txn_rw(const MDBX_txn *txn, int bad_bits) {
int err = check_txn(txn, bad_bits & ~MDBX_TXN_PARKED);
if (unlikely(err))
return err;
if (unlikely(txn->flags & MDBX_TXN_RDONLY))
return MDBX_EACCESS;
return MDBX_SUCCESS;
return check_txn(txn, (bad_bits | MDBX_TXN_RDONLY) & ~MDBX_TXN_PARKED);
}
/*----------------------------------------------------------------------------*/

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,11 +1,11 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
__cold int cursor_check(const MDBX_cursor *mc) {
__cold int cursor_validate(const MDBX_cursor *mc) {
if (!mc->txn->tw.dirtylist) {
cASSERT(mc, (mc->txn->flags & MDBX_WRITEMAP) != 0 && !MDBX_AVOID_MSYNC);
} else {
@@ -81,10 +81,10 @@ __cold int cursor_check(const MDBX_cursor *mc) {
return MDBX_SUCCESS;
}
__cold int cursor_check_updating(MDBX_cursor *mc) {
__cold int cursor_validate_updating(MDBX_cursor *mc) {
const uint8_t checking = mc->checking;
mc->checking |= z_updating;
const int rc = cursor_check(mc);
const int rc = cursor_validate(mc);
mc->checking = checking;
return rc;
}
@@ -145,7 +145,7 @@ __hot int cursor_touch(MDBX_cursor *const mc, const MDBX_val *key, const MDBX_va
if (!cursor_is_gc(mc)) {
need += txn->dbs[FREE_DBI].height + (size_t)3;
/* 3) Named DBs also dirty the main DB */
if (cursor_is_main(mc))
if (!cursor_is_main(mc))
need += txn->dbs[MAIN_DBI].height + (size_t)3;
}
#if xMDBX_DEBUG_SPILLING != 2
@@ -168,7 +168,7 @@ __hot int cursor_touch(MDBX_cursor *const mc, const MDBX_val *key, const MDBX_va
return err;
}
if (likely(mc->top >= 0) && !is_modifable(mc->txn, mc->pg[mc->top])) {
if (likely(is_pointed(mc)) && ((mc->txn->flags & MDBX_TXN_SPILLS) || !is_modifable(mc->txn, mc->pg[mc->top]))) {
const int8_t top = mc->top;
mc->top = 0;
do {
@@ -184,15 +184,16 @@ __hot int cursor_touch(MDBX_cursor *const mc, const MDBX_val *key, const MDBX_va
/*----------------------------------------------------------------------------*/
int cursor_shadow(MDBX_cursor *parent_cursor, MDBX_txn *nested_txn, const size_t dbi) {
tASSERT(nested_txn, dbi > FREE_DBI && dbi < nested_txn->n_dbi);
const size_t size = parent_cursor->subcur ? sizeof(MDBX_cursor) + sizeof(subcur_t) : sizeof(MDBX_cursor);
for (MDBX_cursor *bk; parent_cursor; parent_cursor = bk->next) {
cASSERT(parent_cursor, parent_cursor != parent_cursor->next);
bk = parent_cursor;
if (parent_cursor->signature != cur_signature_live)
int cursor_shadow(MDBX_cursor *mc, MDBX_txn *nested, const size_t dbi) {
tASSERT(nested, dbi > FREE_DBI && dbi < nested->n_dbi);
const size_t size = mc->subcur ? sizeof(MDBX_cursor) + sizeof(subcur_t) : sizeof(MDBX_cursor);
for (MDBX_cursor *bk; mc; mc = bk->next) {
cASSERT(mc, mc != mc->next);
if (mc->signature != cur_signature_live) {
ENSURE(nested->env, mc->signature == cur_signature_wait4eot);
bk = mc;
continue;
}
bk = osal_malloc(size);
if (unlikely(!bk))
return MDBX_ENOMEM;
@@ -200,37 +201,37 @@ int cursor_shadow(MDBX_cursor *parent_cursor, MDBX_txn *nested_txn, const size_t
memset(bk, 0xCD, size);
VALGRIND_MAKE_MEM_UNDEFINED(bk, size);
#endif /* MDBX_DEBUG */
*bk = *parent_cursor;
parent_cursor->backup = bk;
/* Kill pointers into src to reduce abuse: The
* user may not use mc until dst ends. But we need a valid
* txn pointer here for cursor fixups to keep working. */
parent_cursor->txn = nested_txn;
parent_cursor->tree = &nested_txn->dbs[dbi];
parent_cursor->dbi_state = &nested_txn->dbi_state[dbi];
subcur_t *mx = parent_cursor->subcur;
if (mx != nullptr) {
*bk = *mc;
mc->backup = bk;
mc->txn = nested;
mc->tree = &nested->dbs[dbi];
mc->dbi_state = &nested->dbi_state[dbi];
subcur_t *mx = mc->subcur;
if (mx) {
*(subcur_t *)(bk + 1) = *mx;
mx->cursor.txn = nested_txn;
mx->cursor.dbi_state = parent_cursor->dbi_state;
mx->cursor.txn = nested;
mx->cursor.dbi_state = &nested->dbi_state[dbi];
}
parent_cursor->next = nested_txn->cursors[dbi];
nested_txn->cursors[dbi] = parent_cursor;
mc->next = nested->cursors[dbi];
nested->cursors[dbi] = mc;
}
return MDBX_SUCCESS;
}
void cursor_eot(MDBX_cursor *mc, const bool merge) {
MDBX_cursor *cursor_eot(MDBX_cursor *mc, MDBX_txn *txn, const bool merge) {
MDBX_cursor *const next = mc->next;
const unsigned stage = mc->signature;
MDBX_cursor *const bk = mc->backup;
ENSURE(mc->txn->env, stage == cur_signature_live || (stage == cur_signature_wait4eot && bk));
ENSURE(txn->env, stage == cur_signature_live || (stage == cur_signature_wait4eot && bk));
tASSERT(txn, mc->txn == txn);
if (bk) {
subcur_t *mx = mc->subcur;
cASSERT(mc, mc->txn->parent != nullptr);
tASSERT(txn, mc->txn->parent != nullptr);
tASSERT(txn, bk->txn == txn->parent);
/* Zap: Using uninitialized memory '*mc->backup'. */
MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(6001);
ENSURE(mc->txn->env, bk->signature == cur_signature_live);
cASSERT(mc, mx == bk->subcur);
ENSURE(txn->env, bk->signature == cur_signature_live);
tASSERT(txn, mx == bk->subcur);
if (merge) {
/* Update pointers to parent txn */
mc->next = bk->next;
@@ -239,24 +240,25 @@ void cursor_eot(MDBX_cursor *mc, const bool merge) {
mc->tree = bk->tree;
mc->dbi_state = bk->dbi_state;
if (mx) {
mx->cursor.txn = mc->txn;
mx->cursor.dbi_state = mc->dbi_state;
mx->cursor.txn = bk->txn;
mx->cursor.dbi_state = bk->dbi_state;
}
} else {
/* Restore from backup, i.e. rollback/abort nested txn */
*mc = *bk;
mc->signature = stage /* Promote (cur_signature_wait4eot) state to parent txn */;
if (mx)
*mx = *(subcur_t *)(bk + 1);
}
if (stage == cur_signature_wait4eot /* Cursor was closed by user */)
mc->signature = stage /* Promote closed state to parent txn */;
bk->signature = 0;
osal_free(bk);
} else {
ENSURE(mc->txn->env, stage == cur_signature_live);
mc->signature = cur_signature_ready4dispose /* Cursor may be reused */;
mc->next = mc;
cursor_drown((cursor_couple_t *)mc);
}
return next;
}
/*----------------------------------------------------------------------------*/
@@ -298,10 +300,7 @@ static __always_inline int couple_init(cursor_couple_t *couple, const MDBX_txn *
if (unlikely(*dbi_state & DBI_STALE))
return tbl_fetch(couple->outer.txn, cursor_dbi(&couple->outer));
if (unlikely(kvx->clc.k.lmax == 0))
return tbl_setup(txn->env, kvx, tree);
return MDBX_SUCCESS;
return tbl_setup_ifneed(txn->env, kvx, tree);
}
__cold int cursor_init4walk(cursor_couple_t *couple, const MDBX_txn *const txn, tree_t *const tree, kvx_t *const kvx) {
@@ -387,6 +386,7 @@ int cursor_dupsort_setup(MDBX_cursor *mc, const node_t *node, const page_t *mp)
}
mc->tree->dupfix_size = mx->nested_tree.dupfix_size;
mc->clc->v.lmin = mc->clc->v.lmax = mx->nested_tree.dupfix_size;
cASSERT(mc, mc->clc->v.lmax >= mc->clc->v.lmin);
}
DEBUG("Sub-db dbi -%zu root page %" PRIaPGNO, cursor_dbi(&mx->cursor), mx->nested_tree.root);
@@ -733,8 +733,17 @@ __hot int cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data, unsig
if (mc->clc->k.cmp(key, &current_key) != 0)
return MDBX_EKEYMISMATCH;
if (unlikely((flags & MDBX_MULTIPLE)))
goto drop_current;
if (unlikely((flags & MDBX_MULTIPLE))) {
if (unlikely(!mc->subcur))
return MDBX_EINVAL;
err = cursor_del(mc, flags & MDBX_ALLDUPS);
if (unlikely(err != MDBX_SUCCESS))
return err;
if (unlikely(data[1].iov_len == 0))
return MDBX_SUCCESS;
flags -= MDBX_CURRENT;
goto skip_check_samedata;
}
if (mc->subcur) {
node_t *node = page_node(mc->pg[mc->top], mc->ki[mc->top]);
@@ -744,7 +753,6 @@ __hot int cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data, unsig
* отличается, то вместо обновления требуется удаление и
* последующая вставка. */
if (mc->subcur->nested_tree.items > 1 || current_data.iov_len != data->iov_len) {
drop_current:
err = cursor_del(mc, flags & MDBX_ALLDUPS);
if (unlikely(err != MDBX_SUCCESS))
return err;
@@ -849,6 +857,8 @@ __hot int cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data, unsig
size_t *batch_dupfix_done = nullptr, batch_dupfix_given = 0;
if (unlikely(flags & MDBX_MULTIPLE)) {
batch_dupfix_given = data[1].iov_len;
if (unlikely(data[1].iov_len == 0))
return /* nothing todo */ MDBX_SUCCESS;
batch_dupfix_done = &data[1].iov_len;
*batch_dupfix_done = 0;
}
@@ -938,7 +948,7 @@ __hot int cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data, unsig
}
if (AUDIT_ENABLED()) {
err = cursor_check(mc);
err = cursor_validate(mc);
if (unlikely(err != MDBX_SUCCESS))
return err;
}
@@ -947,7 +957,7 @@ __hot int cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data, unsig
more:
if (AUDIT_ENABLED()) {
err = cursor_check(mc);
err = cursor_validate(mc);
if (unlikely(err != MDBX_SUCCESS))
return err;
}
@@ -1008,7 +1018,7 @@ __hot int cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data, unsig
memcpy(page_data(lp.page), data->iov_base, data->iov_len);
if (AUDIT_ENABLED()) {
err = cursor_check(mc);
err = cursor_validate(mc);
if (unlikely(err != MDBX_SUCCESS))
return err;
}
@@ -1274,7 +1284,7 @@ __hot int cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data, unsig
}
if (AUDIT_ENABLED()) {
err = cursor_check(mc);
err = cursor_validate(mc);
if (unlikely(err != MDBX_SUCCESS))
return err;
}
@@ -1292,7 +1302,7 @@ insert_node:;
if (page_room(mc->pg[mc->top]) < nsize) {
rc = page_split(mc, key, ref_data, P_INVALID, insert_key ? naf : naf | MDBX_SPLIT_REPLACE);
if (rc == MDBX_SUCCESS && AUDIT_ENABLED())
rc = insert_key ? cursor_check(mc) : cursor_check_updating(mc);
rc = insert_key ? cursor_validate(mc) : cursor_validate_updating(mc);
} else {
/* There is room already in this leaf page. */
if (is_dupfix_leaf(mc->pg[mc->top])) {
@@ -1410,11 +1420,12 @@ insert_node:;
data[0].iov_base = ptr_disp(data[0].iov_base, data[0].iov_len);
insert_key = insert_data = false;
old_singledup.iov_base = nullptr;
sub_root = nullptr;
goto more;
}
}
if (AUDIT_ENABLED())
rc = cursor_check(mc);
rc = cursor_validate(mc);
}
return rc;
@@ -1429,6 +1440,21 @@ insert_node:;
return rc;
}
int cursor_check_multiple(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data, unsigned flags) {
(void)key;
if (unlikely(flags & MDBX_RESERVE))
return MDBX_EINVAL;
if (unlikely(!(mc->tree->flags & MDBX_DUPFIXED)))
return MDBX_INCOMPATIBLE;
const size_t number = data[1].iov_len;
if (unlikely(number > MAX_MAPSIZE / 2 / (BRANCH_NODE_MAX(MDBX_MAX_PAGESIZE) - NODESIZE))) {
/* checking for multiplication overflow */
if (unlikely(number > MAX_MAPSIZE / 2 / data->iov_len))
return MDBX_TOO_LARGE;
}
return MDBX_SUCCESS;
}
__hot int cursor_put_checklen(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data, unsigned flags) {
cASSERT(mc, (mc->flags & z_inner) == 0);
if (unlikely(key->iov_len > mc->clc->k.lmax || key->iov_len < mc->clc->k.lmin)) {
@@ -1686,7 +1712,7 @@ del_key:
cASSERT(mc, rc == MDBX_SUCCESS);
if (AUDIT_ENABLED())
rc = cursor_check(mc);
rc = cursor_validate(mc);
return rc;
fail:
@@ -2047,27 +2073,24 @@ __hot int cursor_ops(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, const MDBX_
cASSERT(mc, is_poor(mc) && !is_filled(mc));
return rc;
case MDBX_SEEK_AND_GET_MULTIPLE:
if (unlikely(!key))
return MDBX_EINVAL;
rc = cursor_seek(mc, key, data, MDBX_SET).err;
if (unlikely(rc != MDBX_SUCCESS))
return rc;
__fallthrough /* fall through */;
case MDBX_GET_MULTIPLE:
if (unlikely(!data))
return MDBX_EINVAL;
if (unlikely((mc->tree->flags & MDBX_DUPFIXED) == 0))
return MDBX_INCOMPATIBLE;
if (unlikely(!is_pointed(mc))) {
if (unlikely(!key))
return MDBX_EINVAL;
if (unlikely((mc->flags & z_fresh) == 0))
return MDBX_ENODATA;
rc = cursor_seek(mc, key, data, MDBX_SET).err;
if (unlikely(rc != MDBX_SUCCESS))
return rc;
} else {
if (unlikely(!is_filled(mc)))
return MDBX_ENODATA;
if (key) {
const page_t *mp = mc->pg[mc->top];
const node_t *node = page_node(mp, mc->ki[mc->top]);
*key = get_key(node);
}
if (unlikely(!is_filled(mc)))
return MDBX_ENODATA;
if (key) {
const page_t *mp = mc->pg[mc->top];
const node_t *node = page_node(mp, mc->ki[mc->top]);
*key = get_key(node);
}
cASSERT(mc, is_filled(mc));
if (unlikely(!inner_filled(mc))) {
@@ -2102,15 +2125,6 @@ __hot int cursor_ops(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, const MDBX_
return MDBX_EINVAL;
if (unlikely(mc->subcur == nullptr))
return MDBX_INCOMPATIBLE;
if (unlikely(!is_pointed(mc))) {
if (unlikely((mc->flags & z_fresh) == 0))
return MDBX_ENODATA;
rc = outer_last(mc, key, data);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
mc->subcur->cursor.ki[mc->subcur->cursor.top] = 0;
goto fetch_multiple;
}
if (unlikely(!is_filled(mc) || !inner_filled(mc)))
return MDBX_ENODATA;
rc = cursor_sibling_left(&mc->subcur->cursor);
@@ -2343,3 +2357,40 @@ __hot int cursor_ops(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, const MDBX_
return MDBX_EINVAL;
}
}
int cursor_check(const MDBX_cursor *mc, int txn_bad_bits) {
if (unlikely(mc == nullptr))
return MDBX_EINVAL;
if (unlikely(mc->signature != cur_signature_live)) {
if (mc->signature != cur_signature_ready4dispose)
return MDBX_EBADSIGN;
return (txn_bad_bits > MDBX_TXN_FINISHED) ? MDBX_EINVAL : MDBX_SUCCESS;
}
/* проверяем что курсор в связном списке для отслеживания, исключение допускается только для read-only операций для
* служебных/временных курсоров на стеке. */
MDBX_MAYBE_UNUSED char stack_top[sizeof(void *)];
cASSERT(mc, cursor_is_tracked(mc) || (!(txn_bad_bits & MDBX_TXN_RDONLY) && stack_top < (char *)mc &&
(char *)mc - stack_top < (ptrdiff_t)globals.sys_pagesize * 4));
if (txn_bad_bits) {
int rc = check_txn(mc->txn, txn_bad_bits & ~MDBX_TXN_HAS_CHILD);
if (unlikely(rc != MDBX_SUCCESS)) {
cASSERT(mc, rc != MDBX_RESULT_TRUE);
return rc;
}
if (likely((mc->txn->flags & MDBX_TXN_HAS_CHILD) == 0))
return likely(!cursor_dbi_changed(mc)) ? MDBX_SUCCESS : MDBX_BAD_DBI;
cASSERT(mc, (mc->txn->flags & MDBX_TXN_RDONLY) == 0 && mc->txn != mc->txn->env->txn && mc->txn->env->txn);
rc = dbi_check(mc->txn->env->txn, cursor_dbi(mc));
if (unlikely(rc != MDBX_SUCCESS))
return rc;
cASSERT(mc, (mc->txn->flags & MDBX_TXN_RDONLY) == 0 && mc->txn == mc->txn->env->txn);
}
return MDBX_SUCCESS;
}

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
@@ -233,7 +233,7 @@ enum cursor_checking {
z_pagecheck = 0x80 /* perform page checking, see MDBX_VALIDATION */
};
MDBX_INTERNAL int __must_check_result cursor_check(const MDBX_cursor *mc);
MDBX_INTERNAL int __must_check_result cursor_validate(const MDBX_cursor *mc);
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline size_t cursor_dbi(const MDBX_cursor *mc) {
cASSERT(mc, mc->txn && mc->txn->signature == txn_signature);
@@ -292,20 +292,38 @@ MDBX_NOTHROW_PURE_FUNCTION static inline bool check_leaf_type(const MDBX_cursor
return (((page_type(mp) ^ mc->checking) & (z_branch | z_leaf | z_largepage | z_dupfix)) == 0);
}
MDBX_INTERNAL void cursor_eot(MDBX_cursor *mc, const bool merge);
MDBX_INTERNAL int cursor_shadow(MDBX_cursor *parent_cursor, MDBX_txn *nested_txn, const size_t dbi);
MDBX_INTERNAL int cursor_check(const MDBX_cursor *mc, int txn_bad_bits);
/* без необходимости доступа к данным, без активации припаркованных транзакций. */
static inline int cursor_check_pure(const MDBX_cursor *mc) {
return cursor_check(mc, MDBX_TXN_BLOCKED - MDBX_TXN_PARKED);
}
/* для чтения данных, с активацией припаркованных транзакций. */
static inline int cursor_check_ro(const MDBX_cursor *mc) { return cursor_check(mc, MDBX_TXN_BLOCKED); }
/* для записи данных. */
static inline int cursor_check_rw(const MDBX_cursor *mc) {
return cursor_check(mc, (MDBX_TXN_BLOCKED - MDBX_TXN_PARKED) | MDBX_TXN_RDONLY);
}
MDBX_INTERNAL MDBX_cursor *cursor_eot(MDBX_cursor *mc, MDBX_txn *txn, const bool merge);
MDBX_INTERNAL int cursor_shadow(MDBX_cursor *mc, MDBX_txn *nested, const size_t dbi);
MDBX_INTERNAL MDBX_cursor *cursor_cpstk(const MDBX_cursor *csrc, MDBX_cursor *cdst);
MDBX_INTERNAL int __must_check_result cursor_ops(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
const MDBX_cursor_op op);
MDBX_INTERNAL int __must_check_result cursor_check_multiple(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
unsigned flags);
MDBX_INTERNAL int __must_check_result cursor_put_checklen(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
unsigned flags);
MDBX_INTERNAL int __must_check_result cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data, unsigned flags);
MDBX_INTERNAL int __must_check_result cursor_check_updating(MDBX_cursor *mc);
MDBX_INTERNAL int __must_check_result cursor_validate_updating(MDBX_cursor *mc);
MDBX_INTERNAL int __must_check_result cursor_del(MDBX_cursor *mc, unsigned flags);
@@ -355,3 +373,19 @@ MDBX_MAYBE_UNUSED static inline void cursor_inner_refresh(const MDBX_cursor *mc,
}
MDBX_MAYBE_UNUSED MDBX_INTERNAL bool cursor_is_tracked(const MDBX_cursor *mc);
static inline void cursor_reset(cursor_couple_t *couple) {
couple->outer.top_and_flags = z_fresh_mark;
couple->inner.cursor.top_and_flags = z_fresh_mark | z_inner;
}
static inline void cursor_drown(cursor_couple_t *couple) {
couple->outer.top_and_flags = z_poor_mark;
couple->inner.cursor.top_and_flags = z_poor_mark | z_inner;
couple->outer.txn = nullptr;
couple->inner.cursor.txn = nullptr;
couple->outer.tree = nullptr;
/* сохраняем clc-указатель, так он используется для вычисления dbi в mdbx_cursor_renew(). */
couple->outer.dbi_state = nullptr;
couple->inner.cursor.dbi_state = nullptr;
}

101
src/dbi.c
View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -84,7 +84,7 @@ __noinline int dbi_import(MDBX_txn *txn, const size_t dbi) {
/* dbi-слот еще не инициализирован в транзакции, а хендл не использовался */
txn->cursors[dbi] = nullptr;
MDBX_txn *const parent = txn->parent;
if (parent) {
if (unlikely(parent)) {
/* вложенная пишущая транзакция */
int rc = dbi_check(parent, dbi);
/* копируем состояние table очищая new-флаги. */
@@ -107,26 +107,31 @@ __noinline int dbi_import(MDBX_txn *txn, const size_t dbi) {
txn->dbi_state[dbi] = DBI_LINDO;
} else {
eASSERT(env, txn->dbi_seqs[dbi] != env->dbi_seqs[dbi].weak);
if (unlikely((txn->dbi_state[dbi] & (DBI_VALID | DBI_OLDEN)) || txn->cursors[dbi])) {
/* хендл уже использовался в транзакции, но был закрыт или переоткрыт,
* либо при явном пере-открытии хендла есть висячие курсоры */
eASSERT(env, (txn->dbi_state[dbi] & DBI_STALE) == 0);
if (unlikely(txn->cursors[dbi])) {
/* хендл уже использовался в транзакции и остались висячие курсоры */
txn->dbi_seqs[dbi] = env->dbi_seqs[dbi].weak;
txn->dbi_state[dbi] = DBI_OLDEN | DBI_LINDO;
return txn->cursors[dbi] ? MDBX_DANGLING_DBI : MDBX_BAD_DBI;
return MDBX_DANGLING_DBI;
}
if (unlikely(txn->dbi_state[dbi] & (DBI_OLDEN | DBI_VALID))) {
/* хендл уже использовался в транзакции, но был закрыт или переоткрыт,
* висячих курсоров нет */
txn->dbi_seqs[dbi] = env->dbi_seqs[dbi].weak;
txn->dbi_state[dbi] = DBI_OLDEN | DBI_LINDO;
return MDBX_BAD_DBI;
}
}
/* хендл не использовался в транзакции, либо явно пере-отрывается при
* отсутствии висячих курсоров */
eASSERT(env, (txn->dbi_state[dbi] & DBI_LINDO) && !txn->cursors[dbi]);
eASSERT(env, (txn->dbi_state[dbi] & (DBI_LINDO | DBI_VALID)) == DBI_LINDO && !txn->cursors[dbi]);
/* читаем актуальные флаги и sequence */
struct dbi_snap_result snap = dbi_snap(env, dbi);
txn->dbi_seqs[dbi] = snap.sequence;
if (snap.flags & DB_VALID) {
txn->dbs[dbi].flags = snap.flags & DB_PERSISTENT_FLAGS;
txn->dbi_state[dbi] = DBI_LINDO | DBI_VALID | DBI_STALE;
txn->dbi_state[dbi] = (dbi >= CORE_DBS) ? DBI_LINDO | DBI_VALID | DBI_STALE : DBI_LINDO | DBI_VALID;
return MDBX_SUCCESS;
}
return MDBX_BAD_DBI;
@@ -380,7 +385,7 @@ static int dbi_open_locked(MDBX_txn *txn, unsigned user_flags, MDBX_dbi *dbi, MD
slot = (slot < scan) ? slot : scan;
continue;
}
if (!env->kvs[MAIN_DBI].clc.k.cmp(&name, &env->kvs[scan].name)) {
if (env->kvs[MAIN_DBI].clc.k.cmp(&name, &env->kvs[scan].name) == 0) {
slot = scan;
int err = dbi_check(txn, slot);
if (err == MDBX_BAD_DBI && txn->dbi_state[slot] == (DBI_OLDEN | DBI_LINDO)) {
@@ -536,54 +541,68 @@ int dbi_open(MDBX_txn *txn, const MDBX_val *const name, unsigned user_flags, MDB
#if MDBX_ENABLE_DBI_LOCKFREE
/* Is the DB already open? */
const MDBX_env *const env = txn->env;
size_t free_slot = env->n_dbi;
bool have_free_slot = env->n_dbi < env->max_dbi;
for (size_t i = CORE_DBS; i < env->n_dbi; ++i) {
retry:
if ((env->dbs_flags[i] & DB_VALID) == 0) {
free_slot = i;
have_free_slot = true;
continue;
}
const uint32_t snap_seq = atomic_load32(&env->dbi_seqs[i], mo_AcquireRelease);
const uint16_t snap_flags = env->dbs_flags[i];
struct dbi_snap_result snap = dbi_snap(env, i);
const MDBX_val snap_name = env->kvs[i].name;
if (user_flags != MDBX_ACCEDE &&
(((user_flags ^ snap_flags) & DB_PERSISTENT_FLAGS) || (keycmp && keycmp != env->kvs[i].clc.k.cmp) ||
(datacmp && datacmp != env->kvs[i].clc.v.cmp)))
continue;
const uint32_t main_seq = atomic_load32(&env->dbi_seqs[MAIN_DBI], mo_AcquireRelease);
MDBX_cmp_func *const snap_cmp = env->kvs[MAIN_DBI].clc.k.cmp;
if (unlikely(!(snap_flags & DB_VALID) || !snap_name.iov_base || !snap_name.iov_len || !snap_cmp))
continue;
if (unlikely(!(snap.flags & DB_VALID) || !snap_name.iov_base || !snap_name.iov_len || !snap_cmp))
/* похоже на столкновение с параллельно работающим обновлением */
goto slowpath_locking;
const bool name_match = snap_cmp(&snap_name, name) == 0;
osal_flush_incoherent_cpu_writeback();
if (unlikely(snap_seq != atomic_load32(&env->dbi_seqs[i], mo_AcquireRelease) ||
if (unlikely(snap.sequence != atomic_load32(&env->dbi_seqs[i], mo_AcquireRelease) ||
main_seq != atomic_load32(&env->dbi_seqs[MAIN_DBI], mo_AcquireRelease) ||
snap_flags != env->dbs_flags[i] || snap_name.iov_base != env->kvs[i].name.iov_base ||
snap.flags != env->dbs_flags[i] || snap_name.iov_base != env->kvs[i].name.iov_base ||
snap_name.iov_len != env->kvs[i].name.iov_len))
goto retry;
if (name_match) {
/* похоже на столкновение с параллельно работающим обновлением */
goto slowpath_locking;
if (!name_match)
continue;
osal_flush_incoherent_cpu_writeback();
if (user_flags != MDBX_ACCEDE &&
(((user_flags ^ snap.flags) & DB_PERSISTENT_FLAGS) || (keycmp && keycmp != env->kvs[i].clc.k.cmp) ||
(datacmp && datacmp != env->kvs[i].clc.v.cmp)))
/* есть подозрение что пользователь открывает таблицу с другими флагами/атрибутами
* или другими компараторами, поэтому уходим в безопасный режим */
goto slowpath_locking;
rc = dbi_check(txn, i);
if (rc == MDBX_BAD_DBI && txn->dbi_state[i] == (DBI_OLDEN | DBI_LINDO)) {
/* хендл использовался, стал невалидным,
* но теперь явно пере-открывается в этой транзакци */
eASSERT(env, !txn->cursors[i]);
txn->dbi_state[i] = DBI_LINDO;
rc = dbi_check(txn, i);
if (rc == MDBX_BAD_DBI && txn->dbi_state[i] == (DBI_OLDEN | DBI_LINDO)) {
/* хендл использовался, стал невалидным,
* но теперь явно пере-открывается в этой транзакци */
eASSERT(env, !txn->cursors[i]);
txn->dbi_state[i] = DBI_LINDO;
rc = dbi_check(txn, i);
}
if (likely(rc == MDBX_SUCCESS)) {
rc = dbi_bind(txn, i, user_flags, keycmp, datacmp);
if (likely(rc == MDBX_SUCCESS))
*dbi = (MDBX_dbi)i;
}
return rc;
}
if (likely(rc == MDBX_SUCCESS)) {
if (unlikely(snap.sequence != atomic_load32(&env->dbi_seqs[i], mo_AcquireRelease) ||
main_seq != atomic_load32(&env->dbi_seqs[MAIN_DBI], mo_AcquireRelease) ||
snap.flags != env->dbs_flags[i] || snap_name.iov_base != env->kvs[i].name.iov_base ||
snap_name.iov_len != env->kvs[i].name.iov_len))
/* похоже на столкновение с параллельно работающим обновлением */
goto slowpath_locking;
rc = dbi_bind(txn, i, user_flags, keycmp, datacmp);
if (likely(rc == MDBX_SUCCESS))
*dbi = (MDBX_dbi)i;
}
return rc;
}
/* Fail, if no free slot and max hit */
if (unlikely(free_slot >= env->max_dbi))
if (unlikely(!have_free_slot))
return MDBX_DBS_FULL;
slowpath_locking:
#endif /* MDBX_ENABLE_DBI_LOCKFREE */
rc = osal_fastmutex_acquire(&txn->env->dbi_lock);
@@ -683,7 +702,7 @@ __cold const tree_t *dbi_dig(const MDBX_txn *txn, const size_t dbi, tree_t *fall
case DBI_OLDEN:
return dig->dbs + dbi;
case 0:
return nullptr;
return fallback;
case DBI_VALID | DBI_STALE:
case DBI_OLDEN | DBI_STALE:
break;

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
@@ -53,6 +53,7 @@ static inline size_t dbi_bitmap_ctz(const MDBX_txn *txn, intptr_t bmi) {
I = (I - 1) | (bitmap_chunk - 1); \
bitmap_item = TXN->dbi_sparse[(1 + I) / bitmap_chunk]; \
if (!bitmap_item) \
/* coverity[const_overflow] */ \
I += bitmap_chunk; \
continue; \
} else if ((bitmap_item & 1) == 0) { \
@@ -101,7 +102,7 @@ static inline bool dbi_changed(const MDBX_txn *txn, const size_t dbi) {
const MDBX_env *const env = txn->env;
eASSERT(env, dbi_state(txn, dbi) & DBI_LINDO);
const uint32_t snap_seq = atomic_load32(&env->dbi_seqs[dbi], mo_AcquireRelease);
return snap_seq != txn->dbi_seqs[dbi];
return unlikely(snap_seq != txn->dbi_seqs[dbi]);
}
static inline int dbi_check(const MDBX_txn *txn, const size_t dbi) {

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -234,13 +234,14 @@ __cold int dxb_resize(MDBX_env *const env, const pgno_t used_pgno, const pgno_t
rc = MDBX_RESULT_TRUE;
#if defined(MADV_REMOVE)
if (env->flags & MDBX_WRITEMAP)
rc = madvise(ptr_disp(env->dxb_mmap.base, size_bytes), prev_size - size_bytes, MADV_REMOVE) ? ignore_enosys(errno)
: MDBX_SUCCESS;
rc = madvise(ptr_disp(env->dxb_mmap.base, size_bytes), prev_size - size_bytes, MADV_REMOVE)
? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
#endif /* MADV_REMOVE */
#if defined(MADV_DONTNEED)
if (rc == MDBX_RESULT_TRUE)
rc = madvise(ptr_disp(env->dxb_mmap.base, size_bytes), prev_size - size_bytes, MADV_DONTNEED)
? ignore_enosys(errno)
? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
#elif defined(POSIX_MADV_DONTNEED)
if (rc == MDBX_RESULT_TRUE)
@@ -368,7 +369,7 @@ void dxb_sanitize_tail(MDBX_env *env, MDBX_txn *txn) {
if (env->pid != osal_getpid()) {
/* resurrect after fork */
return;
} else if (env->txn && env_txn0_owned(env)) {
} else if (env_owned_wrtxn(env)) {
/* inside write-txn */
last = meta_recent(env, &env->basal_txn->tw.troika).ptr_v->geometry.first_unallocated;
} else if (env->flags & MDBX_RDONLY) {
@@ -426,7 +427,7 @@ __cold int dxb_set_readahead(const MDBX_env *env, const pgno_t edge, const bool
void *const ptr = ptr_disp(env->dxb_mmap.base, offset);
if (enable) {
#if defined(MADV_NORMAL)
err = madvise(ptr, length, MADV_NORMAL) ? ignore_enosys(errno) : MDBX_SUCCESS;
err = madvise(ptr, length, MADV_NORMAL) ? ignore_enosys_and_eagain(errno) : MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
#elif defined(POSIX_MADV_NORMAL)
@@ -454,7 +455,7 @@ __cold int dxb_set_readahead(const MDBX_env *env, const pgno_t edge, const bool
hint.ra_count = unlikely(length > INT_MAX && sizeof(length) > sizeof(hint.ra_count)) ? INT_MAX : (int)length;
(void)/* Ignore ENOTTY for DB on the ram-disk and so on */ fcntl(env->lazy_fd, F_RDADVISE, &hint);
#elif defined(MADV_WILLNEED)
err = madvise(ptr, length, MADV_WILLNEED) ? ignore_enosys(errno) : MDBX_SUCCESS;
err = madvise(ptr, length, MADV_WILLNEED) ? ignore_enosys_and_eagain(errno) : MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
#elif defined(POSIX_MADV_WILLNEED)
@@ -479,7 +480,7 @@ __cold int dxb_set_readahead(const MDBX_env *env, const pgno_t edge, const bool
} else {
mincore_clean_cache(env);
#if defined(MADV_RANDOM)
err = madvise(ptr, length, MADV_RANDOM) ? ignore_enosys(errno) : MDBX_SUCCESS;
err = madvise(ptr, length, MADV_RANDOM) ? ignore_enosys_and_eagain(errno) : MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
#elif defined(POSIX_MADV_RANDOM)
@@ -567,6 +568,7 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
return err;
}
size_t expected_filesize = 0;
const size_t used_bytes = pgno2bytes(env, header.geometry.first_unallocated);
const size_t used_aligned2os_bytes = ceil_powerof2(used_bytes, globals.sys_pagesize);
if ((env->flags & MDBX_RDONLY) /* readonly */
@@ -601,6 +603,8 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
/* pre-shrink if enabled */
env->geo_in_bytes.now = used_bytes + env->geo_in_bytes.shrink - used_bytes % env->geo_in_bytes.shrink;
/* сейчас БД еще не открыта, поэтому этот вызов не изменит геометрию, но проверит и скорректирует параметры
* с учетом реального размера страницы. */
err = mdbx_env_set_geometry(env, env->geo_in_bytes.lower, env->geo_in_bytes.now, env->geo_in_bytes.upper,
env->geo_in_bytes.grow, env->geo_in_bytes.shrink, header.pagesize);
if (unlikely(err != MDBX_SUCCESS)) {
@@ -608,27 +612,26 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
return (err == MDBX_EINVAL) ? MDBX_INCOMPATIBLE : err;
}
/* update meta fields */
/* altering fields to match geometry given from user */
expected_filesize = pgno_align2os_bytes(env, header.geometry.now);
header.geometry.now = bytes2pgno(env, env->geo_in_bytes.now);
header.geometry.lower = bytes2pgno(env, env->geo_in_bytes.lower);
header.geometry.upper = bytes2pgno(env, env->geo_in_bytes.upper);
header.geometry.grow_pv = pages2pv(bytes2pgno(env, env->geo_in_bytes.grow));
header.geometry.shrink_pv = pages2pv(bytes2pgno(env, env->geo_in_bytes.shrink));
VERBOSE("amended: root %" PRIaPGNO "/%" PRIaPGNO ", geo %" PRIaPGNO "/%" PRIaPGNO "-%" PRIaPGNO "/%" PRIaPGNO
VERBOSE("amending: root %" PRIaPGNO "/%" PRIaPGNO ", geo %" PRIaPGNO "/%" PRIaPGNO "-%" PRIaPGNO "/%" PRIaPGNO
" +%u -%u, txn_id %" PRIaTXN ", %s",
header.trees.main.root, header.trees.gc.root, header.geometry.lower, header.geometry.first_unallocated,
header.geometry.now, header.geometry.upper, pv2pages(header.geometry.grow_pv),
pv2pages(header.geometry.shrink_pv), unaligned_peek_u64(4, header.txnid_a), durable_caption(&header));
} else {
/* fetch back 'now/current' size, since it was ignored during comparison
* and may differ. */
/* fetch back 'now/current' size, since it was ignored during comparison and may differ. */
env->geo_in_bytes.now = pgno_align2os_bytes(env, header.geometry.now);
}
ENSURE(env, header.geometry.now >= header.geometry.first_unallocated);
} else {
/* geo-params are not pre-configured by user,
* get current values from the meta. */
/* geo-params are not pre-configured by user, get current values from the meta. */
env->geo_in_bytes.now = pgno2bytes(env, header.geometry.now);
env->geo_in_bytes.lower = pgno2bytes(env, header.geometry.lower);
env->geo_in_bytes.upper = pgno2bytes(env, header.geometry.upper);
@@ -638,17 +641,19 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
ENSURE(env, pgno_align2os_bytes(env, header.geometry.now) == env->geo_in_bytes.now);
ENSURE(env, env->geo_in_bytes.now >= used_bytes);
if (!expected_filesize)
expected_filesize = env->geo_in_bytes.now;
const uint64_t filesize_before = env->dxb_mmap.filesize;
if (unlikely(filesize_before != env->geo_in_bytes.now)) {
if (lck_rc != /* lck exclusive */ MDBX_RESULT_TRUE) {
VERBOSE("filesize mismatch (expect %" PRIuPTR "b/%" PRIaPGNO "p, have %" PRIu64 "b/%" PRIaPGNO "p), "
"assume other process working",
VERBOSE("filesize mismatch (expect %" PRIuPTR "b/%" PRIaPGNO "p, have %" PRIu64 "b/%" PRIu64
"p), assume other process working",
env->geo_in_bytes.now, bytes2pgno(env, env->geo_in_bytes.now), filesize_before,
bytes2pgno(env, (size_t)filesize_before));
filesize_before >> env->ps2ln);
} else {
WARNING("filesize mismatch (expect %" PRIuSIZE "b/%" PRIaPGNO "p, have %" PRIu64 "b/%" PRIaPGNO "p)",
env->geo_in_bytes.now, bytes2pgno(env, env->geo_in_bytes.now), filesize_before,
bytes2pgno(env, (size_t)filesize_before));
if (filesize_before != expected_filesize)
WARNING("filesize mismatch (expect %" PRIuSIZE "b/%" PRIaPGNO "p, have %" PRIu64 "b/%" PRIu64 "p)",
expected_filesize, bytes2pgno(env, expected_filesize), filesize_before, filesize_before >> env->ps2ln);
if (filesize_before < used_bytes) {
ERROR("last-page beyond end-of-file (last %" PRIaPGNO ", have %" PRIaPGNO ")",
header.geometry.first_unallocated, bytes2pgno(env, (size_t)filesize_before));
@@ -656,8 +661,9 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
}
if (env->flags & MDBX_RDONLY) {
if (filesize_before & (globals.sys_pagesize - 1)) {
ERROR("%s", "filesize should be rounded-up to system page");
if (filesize_before & (globals.sys_allocation_granularity - 1)) {
ERROR("filesize should be rounded-up to system allocation granularity %u",
globals.sys_allocation_granularity);
return MDBX_WANNA_RECOVERY;
}
WARNING("%s", "ignore filesize mismatch in readonly-mode");
@@ -676,19 +682,21 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
!(env->flags & MDBX_NORDAHEAD) && mdbx_is_readahead_reasonable(used_bytes, 0) == MDBX_RESULT_TRUE;
err = osal_mmap(env->flags, &env->dxb_mmap, env->geo_in_bytes.now, env->geo_in_bytes.upper,
(lck_rc && env->stuck_meta < 0) ? MMAP_OPTION_TRUNCATE : 0);
(lck_rc && env->stuck_meta < 0) ? MMAP_OPTION_TRUNCATE : 0, env->pathname.dxb);
if (unlikely(err != MDBX_SUCCESS))
return err;
#if defined(MADV_DONTDUMP)
err = madvise(env->dxb_mmap.base, env->dxb_mmap.limit, MADV_DONTDUMP) ? ignore_enosys(errno) : MDBX_SUCCESS;
err =
madvise(env->dxb_mmap.base, env->dxb_mmap.limit, MADV_DONTDUMP) ? ignore_enosys_and_eagain(errno) : MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
#endif /* MADV_DONTDUMP */
#if defined(MADV_DODUMP)
if (globals.runtime_flags & MDBX_DBG_DUMP) {
const size_t meta_length_aligned2os = pgno_align2os_bytes(env, NUM_METAS);
err = madvise(env->dxb_mmap.base, meta_length_aligned2os, MADV_DODUMP) ? ignore_enosys(errno) : MDBX_SUCCESS;
err = madvise(env->dxb_mmap.base, meta_length_aligned2os, MADV_DODUMP) ? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
}
@@ -927,7 +935,7 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
bytes2pgno(env, env->dxb_mmap.current));
err = madvise(ptr_disp(env->dxb_mmap.base, used_aligned2os_bytes), env->dxb_mmap.current - used_aligned2os_bytes,
MADV_REMOVE)
? ignore_enosys(errno)
? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
@@ -937,7 +945,7 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
NOTICE("open-MADV_%s %u..%u", "DONTNEED", env->lck->discarded_tail.weak, bytes2pgno(env, env->dxb_mmap.current));
err = madvise(ptr_disp(env->dxb_mmap.base, used_aligned2os_bytes), env->dxb_mmap.current - used_aligned2os_bytes,
MADV_DONTNEED)
? ignore_enosys(errno)
? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
@@ -1029,7 +1037,7 @@ int dxb_sync_locked(MDBX_env *env, unsigned flags, meta_t *const pending, troika
#endif /* MADV_FREE */
int err = madvise(ptr_disp(env->dxb_mmap.base, discard_edge_bytes), prev_discarded_bytes - discard_edge_bytes,
advise)
? ignore_enosys(errno)
? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
#else
int err = ignore_enosys(posix_madvise(ptr_disp(env->dxb_mmap.base, discard_edge_bytes),
@@ -1152,7 +1160,8 @@ int dxb_sync_locked(MDBX_env *env, unsigned flags, meta_t *const pending, troika
if (!head.is_steady && meta_is_steady(pending))
target = (meta_t *)head.ptr_c;
else {
WARNING("%s", "skip update meta");
NOTICE("skip update meta%" PRIaPGNO " for txn#%" PRIaTXN ", since it is already steady",
data_page(head.ptr_c)->pgno, head.txnid);
return MDBX_SUCCESS;
}
} else {

View File

@@ -1,11 +1,16 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
bool env_txn0_owned(const MDBX_env *env) {
return (env->flags & MDBX_NOSTICKYTHREADS) ? (env->basal_txn->owner != 0)
: (env->basal_txn->owner == osal_thread_self());
MDBX_txn *env_owned_wrtxn(const MDBX_env *env) {
if (likely(env->basal_txn)) {
const bool is_owned = (env->flags & MDBX_NOSTICKYTHREADS) ? (env->basal_txn->owner != 0)
: (env->basal_txn->owner == osal_thread_self());
if (is_owned)
return env->txn ? env->txn : env->basal_txn;
}
return nullptr;
}
int env_page_auxbuffer(MDBX_env *env) {
@@ -60,7 +65,7 @@ __cold int env_sync(MDBX_env *env, bool force, bool nonblock) {
if (unlikely(env->flags & MDBX_RDONLY))
return MDBX_EACCESS;
const bool txn0_owned = env_txn0_owned(env);
MDBX_txn *const txn_owned = env_owned_wrtxn(env);
bool should_unlock = false;
int rc = MDBX_RESULT_TRUE /* means "nothing to sync" */;
@@ -71,7 +76,7 @@ retry:;
goto bailout;
}
const troika_t troika = (txn0_owned | should_unlock) ? env->basal_txn->tw.troika : meta_tap(env);
const troika_t troika = (txn_owned || should_unlock) ? env->basal_txn->tw.troika : meta_tap(env);
const meta_ptr_t head = meta_recent(env, &troika);
const uint64_t unsynced_pages = atomic_load64(&env->lck->unsynced_pages, mo_Relaxed);
if (unsynced_pages == 0) {
@@ -104,7 +109,7 @@ retry:;
osal_monotime() - eoos_timestamp >= autosync_period))
flags &= MDBX_WRITEMAP /* clear flags for full steady sync */;
if (!txn0_owned) {
if (!txn_owned) {
if (!should_unlock) {
#if MDBX_ENABLE_PGOP_STAT
unsigned wops = 0;
@@ -163,8 +168,8 @@ retry:;
flags |= txn_shrink_allowed;
}
eASSERT(env, txn0_owned || should_unlock);
eASSERT(env, !txn0_owned || (flags & txn_shrink_allowed) == 0);
eASSERT(env, txn_owned || should_unlock);
eASSERT(env, !txn_owned || (flags & txn_shrink_allowed) == 0);
if (!head.is_steady && unlikely(env->stuck_meta >= 0) && troika.recent != (uint8_t)env->stuck_meta) {
NOTICE("skip %s since wagering meta-page (%u) is mispatch the recent "

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
@@ -28,12 +28,6 @@
typedef struct iov_ctx iov_ctx_t;
#include "osal.h"
#if UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul || defined(_WIN64)
#define MDBX_WORDBITS 64
#else
#define MDBX_WORDBITS 32
#endif /* MDBX_WORDBITS */
#include "options.h"
#include "atomics-types.h"

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -872,10 +872,8 @@ pgr_t gc_alloc_ex(const MDBX_cursor *const mc, const size_t num, uint8_t flags)
//---------------------------------------------------------------------------
if (unlikely(!is_gc_usable(txn, mc, flags))) {
eASSERT(env, (txn->flags & txn_gc_drained) || num > 1);
if (unlikely(!is_gc_usable(txn, mc, flags)))
goto no_gc;
}
eASSERT(env, (flags & (ALLOC_COALESCE | ALLOC_LIFO | ALLOC_SHOULD_SCAN)) == 0);
flags += (env->flags & MDBX_LIFORECLAIM) ? ALLOC_LIFO : 0;

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -146,20 +146,12 @@ static inline void zeroize_reserved(const MDBX_env *env, MDBX_val pnl) {
static int gcu_loose(MDBX_txn *txn, gcu_t *ctx) {
tASSERT(txn, txn->tw.loose_count > 0);
/* Return loose page numbers to tw.repnl,
* though usually none are left at this point.
/* Return loose page numbers to tw.repnl, though usually none are left at this point.
* The pages themselves remain in dirtylist. */
if (unlikely(!txn->tw.gc.retxl && txn->tw.gc.last_reclaimed < 1)) {
TRACE("%s: try allocate gc-slot for %zu loose-pages", dbg_prefix(ctx), txn->tw.loose_count);
int err = gc_alloc_ex(&ctx->cursor, 0, ALLOC_RESERVE).err;
if (err == MDBX_SUCCESS) {
TRACE("%s: retry since gc-slot for %zu loose-pages available", dbg_prefix(ctx), txn->tw.loose_count);
return MDBX_RESULT_TRUE;
}
/* Put loose page numbers in tw.retired_pages,
* since unable to return ones to tw.repnl. */
err = pnl_need(&txn->tw.retired_pages, txn->tw.loose_count);
/* Put loose page numbers in tw.retired_pages, since unable to return ones to tw.repnl. */
TRACE("%s: merge %zu loose-pages into %s-pages", dbg_prefix(ctx), txn->tw.loose_count, "retired");
int err = pnl_need(&txn->tw.retired_pages, txn->tw.loose_count);
if (unlikely(err != MDBX_SUCCESS))
return err;
for (page_t *lp = txn->tw.loose_pages; lp; lp = page_next(lp)) {
@@ -167,9 +159,9 @@ static int gcu_loose(MDBX_txn *txn, gcu_t *ctx) {
MDBX_ASAN_UNPOISON_MEMORY_REGION(&page_next(lp), sizeof(page_t *));
VALGRIND_MAKE_MEM_DEFINED(&page_next(lp), sizeof(page_t *));
}
TRACE("%s: append %zu loose-pages to retired-pages", dbg_prefix(ctx), txn->tw.loose_count);
} else {
/* Room for loose pages + temp PNL with same */
TRACE("%s: merge %zu loose-pages into %s-pages", dbg_prefix(ctx), txn->tw.loose_count, "reclaimed");
int err = pnl_need(&txn->tw.repnl, 2 * txn->tw.loose_count + 2);
if (unlikely(err != MDBX_SUCCESS))
return err;
@@ -185,7 +177,6 @@ static int gcu_loose(MDBX_txn *txn, gcu_t *ctx) {
MDBX_PNL_SETSIZE(loose, count);
pnl_sort(loose, txn->geo.first_unallocated);
pnl_merge(txn->tw.repnl, loose);
TRACE("%s: append %zu loose-pages to reclaimed-pages", dbg_prefix(ctx), txn->tw.loose_count);
}
/* filter-out list of dirty-pages from loose-pages */

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,7 +1,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,7 +1,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,7 +1,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#if !(defined(_WIN32) || defined(_WIN64))
/*----------------------------------------------------------------------------*
@@ -93,10 +93,11 @@ __cold static void choice_fcntl(void) {
static int lck_op(const mdbx_filehandle_t fd, int cmd, const int lck, const off_t offset, off_t len) {
STATIC_ASSERT(sizeof(off_t) >= sizeof(void *) && sizeof(off_t) >= sizeof(size_t));
#ifdef __ANDROID_API__
STATIC_ASSERT_MSG((sizeof(off_t) * 8 == MDBX_WORDBITS), "The bitness of system `off_t` type is mismatch. Please "
"fix build and/or NDK configuration.");
#endif /* Android */
#if defined(__ANDROID_API__) && __ANDROID_API__ < 24
STATIC_ASSERT_MSG((sizeof(off_t) * CHAR_BIT == MDBX_WORDBITS),
"The bitness of system `off_t` type is mismatch. Please "
"fix build and/or NDK configuration.");
#endif /* Android && API < 24 */
assert(offset >= 0 && len > 0);
assert((uint64_t)offset < (uint64_t)INT64_MAX && (uint64_t)len < (uint64_t)INT64_MAX &&
(uint64_t)(offset + len) > (uint64_t)offset);
@@ -132,7 +133,8 @@ static int lck_op(const mdbx_filehandle_t fd, int cmd, const int lck, const off_
}
rc = errno;
#if MDBX_USE_OFDLOCKS
if (rc == EINVAL && (cmd == MDBX_F_OFD_SETLK || cmd == MDBX_F_OFD_SETLKW || cmd == MDBX_F_OFD_GETLK)) {
if (ignore_enosys_and_einval(rc) == MDBX_RESULT_TRUE &&
(cmd == MDBX_F_OFD_SETLK || cmd == MDBX_F_OFD_SETLKW || cmd == MDBX_F_OFD_GETLK)) {
/* fallback to non-OFD locks */
if (cmd == MDBX_F_OFD_SETLK)
cmd = MDBX_F_SETLK;
@@ -460,6 +462,10 @@ __cold MDBX_INTERNAL int lck_destroy(MDBX_env *env, MDBX_env *inprocess_neighbor
jitter4testing(false);
}
#if MDBX_LOCKING == MDBX_LOCKING_SYSV
env->me_sysv_ipc.semid = -1;
#endif /* MDBX_LOCKING */
if (current_pid != env->pid) {
eASSERT(env, !inprocess_neighbor);
NOTICE("drown env %p after-fork pid %d -> %d", __Wpedantic_format_voidptr(env), env->pid, current_pid);
@@ -776,14 +782,14 @@ static int osal_ipclock_lock(MDBX_env *env, osal_ipclock_t *ipc, const bool dont
return rc;
}
int osal_ipclock_unlock(MDBX_env *env, osal_ipclock_t *ipc) {
static int osal_ipclock_unlock(MDBX_env *env, osal_ipclock_t *ipc) {
int err = MDBX_ENOSYS;
#if MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || MDBX_LOCKING == MDBX_LOCKING_POSIX2008
err = pthread_mutex_unlock(ipc);
#elif MDBX_LOCKING == MDBX_LOCKING_POSIX1988
err = sem_post(ipc) ? errno : MDBX_SUCCESS;
#elif MDBX_LOCKING == MDBX_LOCKING_SYSV
if (unlikely(*ipc != (pid_t)env->pid))
if (unlikely(*ipc != (pid_t)env->pid || env->me_sysv_ipc.key == -1))
err = EPERM;
else {
*ipc = 0;
@@ -823,7 +829,6 @@ MDBX_INTERNAL void lck_rdt_unlock(MDBX_env *env) {
int lck_txn_lock(MDBX_env *env, bool dont_wait) {
TRACE("%swait %s", dont_wait ? "dont-" : "", ">>");
eASSERT(env, env->basal_txn || (env->lck == lckless_stub(env) && (env->flags & MDBX_RDONLY)));
jitter4testing(true);
const int err = osal_ipclock_lock(env, &env->lck->wrt_lock, dont_wait);
int rc = err;
@@ -841,10 +846,8 @@ int lck_txn_lock(MDBX_env *env, bool dont_wait) {
void lck_txn_unlock(MDBX_env *env) {
TRACE("%s", ">>");
if (env->basal_txn) {
eASSERT(env, !env->basal_txn || env->basal_txn->owner == osal_thread_self());
eASSERT(env, env->basal_txn->owner == osal_thread_self());
env->basal_txn->owner = 0;
} else {
eASSERT(env, env->lck == lckless_stub(env) && (env->flags & MDBX_RDONLY));
}
int err = osal_ipclock_unlock(env, &env->lck->wrt_lock);
TRACE("<< err %d", err);

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#if defined(_WIN32) || defined(_WIN64)
@@ -87,7 +87,7 @@ int lck_txn_lock(MDBX_env *env, bool dontwait) {
}
}
eASSERT(env, !env->basal_txn->owner);
eASSERT(env, !env->basal_txn || !env->basal_txn->owner);
if (env->flags & MDBX_EXCLUSIVE)
goto done;
@@ -104,10 +104,11 @@ int lck_txn_lock(MDBX_env *env, bool dontwait) {
}
if (rc == MDBX_SUCCESS) {
done:
if (env->basal_txn)
env->basal_txn->owner = osal_thread_self();
/* Zap: Failing to release lock 'env->windowsbug_lock'
* in function 'mdbx_txn_lock' */
MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(26115);
env->basal_txn->owner = osal_thread_self();
return MDBX_SUCCESS;
}
@@ -116,14 +117,15 @@ int lck_txn_lock(MDBX_env *env, bool dontwait) {
}
void lck_txn_unlock(MDBX_env *env) {
eASSERT(env, env->basal_txn->owner == osal_thread_self());
eASSERT(env, !env->basal_txn || env->basal_txn->owner == osal_thread_self());
if ((env->flags & MDBX_EXCLUSIVE) == 0) {
const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
int err = funlock(fd4data, DXB_BODY);
if (err != MDBX_SUCCESS)
mdbx_panic("%s failed: err %u", __func__, err);
}
env->basal_txn->owner = 0;
if (env->basal_txn)
env->basal_txn->owner = 0;
LeaveCriticalSection(&env->windowsbug_lock);
}

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -62,19 +62,20 @@ __cold static int lck_setup_locked(MDBX_env *env) {
}
env->max_readers = (maxreaders <= MDBX_READERS_LIMIT) ? (unsigned)maxreaders : (unsigned)MDBX_READERS_LIMIT;
err = osal_mmap((env->flags & MDBX_EXCLUSIVE) | MDBX_WRITEMAP, &env->lck_mmap, (size_t)size, (size_t)size,
lck_seize_rc ? MMAP_OPTION_TRUNCATE | MMAP_OPTION_SEMAPHORE : MMAP_OPTION_SEMAPHORE);
err =
osal_mmap((env->flags & MDBX_EXCLUSIVE) | MDBX_WRITEMAP, &env->lck_mmap, (size_t)size, (size_t)size,
lck_seize_rc ? MMAP_OPTION_TRUNCATE | MMAP_OPTION_SEMAPHORE : MMAP_OPTION_SEMAPHORE, env->pathname.lck);
if (unlikely(err != MDBX_SUCCESS))
return err;
#ifdef MADV_DODUMP
err = madvise(env->lck_mmap.lck, size, MADV_DODUMP) ? ignore_enosys(errno) : MDBX_SUCCESS;
err = madvise(env->lck_mmap.lck, size, MADV_DODUMP) ? ignore_enosys_and_eagain(errno) : MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
#endif /* MADV_DODUMP */
#ifdef MADV_WILLNEED
err = madvise(env->lck_mmap.lck, size, MADV_WILLNEED) ? ignore_enosys(errno) : MDBX_SUCCESS;
err = madvise(env->lck_mmap.lck, size, MADV_WILLNEED) ? ignore_enosys_and_eagain(errno) : MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
#elif defined(POSIX_MADV_WILLNEED)

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -58,10 +58,11 @@ __cold void debug_log(int level, const char *function, int line, const char *fmt
__cold void log_error(const int err, const char *func, unsigned line) {
assert(err != MDBX_SUCCESS);
if (unlikely(globals.loglevel >= MDBX_LOG_DEBUG) &&
(globals.loglevel >= MDBX_LOG_TRACE || !(err == MDBX_RESULT_TRUE || err == MDBX_NOTFOUND))) {
if (unlikely(globals.loglevel >= MDBX_LOG_DEBUG)) {
const bool is_error = err != MDBX_RESULT_TRUE && err != MDBX_NOTFOUND;
char buf[256];
debug_log(MDBX_LOG_ERROR, func, line, "error %d (%s)\n", err, mdbx_strerror_r(err, buf, sizeof(buf)));
debug_log(is_error ? MDBX_LOG_ERROR : MDBX_LOG_VERBOSE, func, line, "%s %d (%s)\n",
is_error ? "error" : "condition", err, mdbx_strerror_r(err, buf, sizeof(buf)));
}
}

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,4 +1,4 @@
.\" Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015-2025 Leonid Yuriev <leo@yuriev.ru>.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_CHK 1 "2024-08-29" "MDBX 0.13"
.SH NAME

View File

@@ -1,4 +1,4 @@
.\" Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015-2025 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.

View File

@@ -1,4 +1,4 @@
.\" Copyright 2021-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2021-2025 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2014-2021 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_DROP 1 "2024-08-29" "MDBX 0.13"

View File

@@ -1,4 +1,4 @@
.\" Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015-2025 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.

View File

@@ -1,4 +1,4 @@
.\" Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015-2025 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.

View File

@@ -1,4 +1,4 @@
.\" Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015-2025 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2020-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2020-2025
///
/// \brief Non-inline part of the libmdbx C++ API
///
@@ -63,8 +63,8 @@ class trouble_location {
#endif
public:
MDBX_CXX11_CONSTEXPR trouble_location(unsigned line, const char *condition,
const char *function, const char *filename)
MDBX_CXX11_CONSTEXPR trouble_location(unsigned line, const char *condition, const char *function,
const char *filename)
:
#if TROUBLE_PROVIDE_LINENO
line_(line)
@@ -133,7 +133,7 @@ public:
//------------------------------------------------------------------------------
__cold std::string format_va(const char *fmt, va_list ap) {
__cold std::string format_va(const char *fmt, va_list ap) {
va_list ones;
va_copy(ones, ap);
#ifdef _MSC_VER
@@ -146,15 +146,14 @@ __cold std::string format_va(const char *fmt, va_list ap) {
result.reserve(size_t(needed + 1));
result.resize(size_t(needed), '\0');
assert(int(result.capacity()) > needed);
int actual = vsnprintf(const_cast<char *>(result.data()), result.capacity(),
fmt, ones);
int actual = vsnprintf(const_cast<char *>(result.data()), result.capacity(), fmt, ones);
assert(actual == needed);
(void)actual;
va_end(ones);
return result;
}
__cold std::string format(const char *fmt, ...) {
__cold std::string format(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
std::string result = format_va(fmt, ap);
@@ -175,17 +174,14 @@ public:
virtual ~bug() noexcept;
};
__cold bug::bug(const trouble_location &location) noexcept
: std::runtime_error(format("mdbx.bug: %s.%s at %s:%u", location.function(),
location.condition(), location.filename(),
location.line())),
__cold bug::bug(const trouble_location &location) noexcept
: std::runtime_error(format("mdbx.bug: %s.%s at %s:%u", location.function(), location.condition(),
location.filename(), location.line())),
location_(location) {}
__cold bug::~bug() noexcept {}
__cold bug::~bug() noexcept {}
[[noreturn]] __cold void raise_bug(const trouble_location &what_and_where) {
throw bug(what_and_where);
}
[[maybe_unused, noreturn]] __cold void raise_bug(const trouble_location &what_and_where) { throw bug(what_and_where); }
#define RAISE_BUG(line, condition, function, file) \
do { \
@@ -193,6 +189,7 @@ __cold bug::~bug() noexcept {}
raise_bug(bug); \
} while (0)
#undef ENSURE
#define ENSURE(condition) \
do \
if (MDBX_UNLIKELY(!(condition))) \
@@ -376,7 +373,7 @@ __cold std::string error::message() const {
__cold void error::throw_exception() const {
switch (code()) {
case MDBX_EINVAL:
throw std::invalid_argument("mdbx");
throw std::invalid_argument("MDBX_EINVAL");
case MDBX_ENOMEM:
throw std::bad_alloc();
case MDBX_SUCCESS:
@@ -1165,12 +1162,32 @@ bool from_base64::is_erroneous() const noexcept {
//------------------------------------------------------------------------------
template class LIBMDBX_API_TYPE buffer<legacy_allocator>;
#if defined(_MSC_VER)
#pragma warning(push)
/* warning C4251: 'mdbx::buffer<...>::silo_':
* struct 'mdbx::buffer<..>::silo' needs to have dll-interface to be used by clients of class 'mdbx::buffer<...>'
*
* Microsoft не хочет признавать ошибки и пересматривать приятные решения, поэтому MSVC продолжает кошмарить
* и стращать разработчиков предупреждениями, тем самым перекладывая ответственность на их плечи.
*
* В данном случае предупреждение выдаётся из-за инстанцирования std::string::allocator_type::pointer и
* std::pmr::string::allocator_type::pointer внутри mdbx::buffer<..>::silo. А так как эти типы являются частью
* стандартной библиотеки C++ они всегда будут доступны и без необходимости их инстанцирования и экспорта из libmdbx.
*
* Поэтому нет других вариантов как заглушить это предупреждение и еще раз плюнуть в сторону microsoft. */
#pragma warning(disable : 4251)
#endif /* MSVC */
MDBX_INSTALL_API_TEMPLATE(LIBMDBX_API_TYPE, buffer<legacy_allocator>);
#if defined(__cpp_lib_memory_resource) && __cpp_lib_memory_resource >= 201603L && _GLIBCXX_USE_CXX11_ABI
template class LIBMDBX_API_TYPE buffer<polymorphic_allocator>;
MDBX_INSTALL_API_TEMPLATE(LIBMDBX_API_TYPE, buffer<polymorphic_allocator>);
#endif /* __cpp_lib_memory_resource >= 201603L */
#if defined(_MSC_VER)
#pragma warning(pop)
#endif /* MSVC */
//------------------------------------------------------------------------------
static inline MDBX_env_flags_t mode2flags(env::mode mode) {
@@ -1590,15 +1607,6 @@ __cold bool txn::rename_map(const ::std::string &old_name, const ::std::string &
//------------------------------------------------------------------------------
void cursor_managed::close() {
if (MDBX_UNLIKELY(!handle_))
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_EINVAL);
::mdbx_cursor_close(handle_);
handle_ = nullptr;
}
//------------------------------------------------------------------------------
__cold ::std::ostream &operator<<(::std::ostream &out, const slice &it) {
out << "{";
if (!it.is_valid())

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,7 +1,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
/*******************************************************************************
*******************************************************************************
@@ -257,6 +257,14 @@
#error MDBX_HAVE_BUILTIN_CPU_SUPPORTS must be defined as 0 or 1
#endif /* MDBX_HAVE_BUILTIN_CPU_SUPPORTS */
/** if enabled then instead of the returned error `MDBX_REMOTE`, only a warning is issued, when
* the database being opened in non-read-only mode is located in a file system exported via NFS. */
#ifndef MDBX_ENABLE_NON_READONLY_EXPORT
#define MDBX_ENABLE_NON_READONLY_EXPORT 0
#elif !(MDBX_ENABLE_NON_READONLY_EXPORT == 0 || MDBX_ENABLE_NON_READONLY_EXPORT == 1)
#error MDBX_ENABLE_NON_READONLY_EXPORT must be defined as 0 or 1
#endif /* MDBX_ENABLE_NON_READONLY_EXPORT */
//------------------------------------------------------------------------------
/** Win32 File Locking API for \ref MDBX_LOCKING */

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
///
/// https://en.wikipedia.org/wiki/Operating_system_abstraction_layer
@@ -1745,7 +1745,7 @@ MDBX_INTERNAL int osal_check_fs_incore(mdbx_filehandle_t handle) {
return MDBX_RESULT_FALSE;
}
static int osal_check_fs_local(mdbx_filehandle_t handle, int flags) {
MDBX_INTERNAL int osal_check_fs_local(mdbx_filehandle_t handle, int flags) {
#if defined(_WIN32) || defined(_WIN64)
if (globals.running_under_Wine && !(flags & MDBX_EXCLUSIVE))
return ERROR_NOT_CAPABLE /* workaround for Wine */;
@@ -1760,7 +1760,7 @@ static int osal_check_fs_local(mdbx_filehandle_t handle, int flags) {
if ((RemoteProtocolInfo.Flags & REMOTE_PROTOCOL_INFO_FLAG_OFFLINE) && !(flags & MDBX_RDONLY))
return ERROR_FILE_OFFLINE;
if (!(RemoteProtocolInfo.Flags & REMOTE_PROTOCOL_INFO_FLAG_LOOPBACK) && !(flags & MDBX_EXCLUSIVE))
return ERROR_REMOTE_STORAGE_MEDIA_ERROR;
return MDBX_EREMOTE;
}
}
@@ -1779,7 +1779,7 @@ static int osal_check_fs_local(mdbx_filehandle_t handle, int flags) {
0, &GetExternalBacking_OutputBuffer, sizeof(GetExternalBacking_OutputBuffer));
if (NT_SUCCESS(rc)) {
if (!(flags & MDBX_EXCLUSIVE))
return ERROR_REMOTE_STORAGE_MEDIA_ERROR;
return MDBX_EREMOTE;
} else if (rc != STATUS_OBJECT_NOT_EXTERNALLY_BACKED && rc != STATUS_INVALID_DEVICE_REQUEST &&
rc != STATUS_NOT_SUPPORTED)
return ntstatus2errcode(rc);
@@ -1800,7 +1800,7 @@ static int osal_check_fs_local(mdbx_filehandle_t handle, int flags) {
if ((flags & MDBX_RDONLY) == 0) {
if (FileSystemFlags & (FILE_SEQUENTIAL_WRITE_ONCE | FILE_READ_ONLY_VOLUME | FILE_VOLUME_IS_COMPRESSED)) {
rc = ERROR_REMOTE_STORAGE_MEDIA_ERROR;
rc = MDBX_EREMOTE;
goto bailout;
}
}
@@ -1808,7 +1808,7 @@ static int osal_check_fs_local(mdbx_filehandle_t handle, int flags) {
if (imports.GetFinalPathNameByHandleW(handle, PathBuffer, INT16_MAX, FILE_NAME_NORMALIZED | VOLUME_NAME_NT)) {
if (_wcsnicmp(PathBuffer, L"\\Device\\Mup\\", 12) == 0) {
if (!(flags & MDBX_EXCLUSIVE)) {
rc = ERROR_REMOTE_STORAGE_MEDIA_ERROR;
rc = MDBX_EREMOTE;
goto bailout;
}
}
@@ -1836,7 +1836,7 @@ static int osal_check_fs_local(mdbx_filehandle_t handle, int flags) {
case DRIVE_REMOTE:
default:
if (!(flags & MDBX_EXCLUSIVE))
rc = ERROR_REMOTE_STORAGE_MEDIA_ERROR;
rc = MDBX_EREMOTE;
// fall through
case DRIVE_REMOVABLE:
case DRIVE_FIXED:
@@ -1968,11 +1968,11 @@ static int osal_check_fs_local(mdbx_filehandle_t handle, int flags) {
#endif /* ST/MNT_LOCAL */
#ifdef ST_EXPORTED
if ((st_flags & ST_EXPORTED) != 0 && !(flags & MDBX_RDONLY))
return MDBX_EREMOTE;
if ((st_flags & ST_EXPORTED) != 0 && !(flags & (MDBX_RDONLY | MDBX_EXCLUSIVE))))
return MDBX_RESULT_TRUE;
#elif defined(MNT_EXPORTED)
if ((mnt_flags & MNT_EXPORTED) != 0 && !(flags & MDBX_RDONLY))
return MDBX_EREMOTE;
if ((mnt_flags & MNT_EXPORTED) != 0 && !(flags & (MDBX_RDONLY | MDBX_EXCLUSIVE)))
return MDBX_RESULT_TRUE;
#endif /* ST/MNT_EXPORTED */
switch (type) {
@@ -2023,8 +2023,8 @@ static int check_mmap_limit(const size_t limit) {
return MDBX_SUCCESS;
}
MDBX_INTERNAL int osal_mmap(const int flags, osal_mmap_t *map, size_t size, const size_t limit,
const unsigned options) {
MDBX_INTERNAL int osal_mmap(const int flags, osal_mmap_t *map, size_t size, const size_t limit, const unsigned options,
const pathchar_t *pathname4logging) {
assert(size <= limit);
map->limit = 0;
map->current = 0;
@@ -2035,8 +2035,27 @@ MDBX_INTERNAL int osal_mmap(const int flags, osal_mmap_t *map, size_t size, cons
#endif /* Windows */
int err = osal_check_fs_local(map->fd, flags);
if (unlikely(err != MDBX_SUCCESS))
return err;
if (unlikely(err != MDBX_SUCCESS)) {
#if defined(_WIN32) || defined(_WIN64)
if (globals.running_under_Wine)
NOTICE("%s", "Please use native Linux application or WSL at least, instead of trouble-full Wine!");
#endif /* Windows */
switch (err) {
case MDBX_RESULT_TRUE:
#if MDBX_ENABLE_NON_READONLY_EXPORT
WARNING("%" MDBX_PRIsPATH " is exported via NFS, avoid using the file on a remote side!", pathname4logging);
break;
#else
ERROR("%" MDBX_PRIsPATH " is exported via NFS", pathname4logging);
return MDBX_EREMOTE;
#endif /* MDBX_PROHIBIT_NON_READONLY_EXPORT */
case MDBX_EREMOTE:
ERROR("%" MDBX_PRIsPATH " is on a remote file system, the %s is required", pathname4logging, "MDBX_EXCLUSIVE");
__fallthrough /* fall through */;
default:
return err;
}
}
err = check_mmap_limit(limit);
if (unlikely(err != MDBX_SUCCESS))
@@ -2837,7 +2856,7 @@ __cold static LSTATUS mdbx_RegGetValue(HKEY hKey, LPCSTR lpSubKey, LPCSTR lpValu
}
#endif
__cold MDBX_MAYBE_UNUSED static bool bootid_parse_uuid(bin128_t *s, const void *p, const size_t n) {
MDBX_MAYBE_UNUSED __cold static bool bootid_parse_uuid(bin128_t *s, const void *p, const size_t n) {
if (n > 31) {
unsigned bits = 0;
for (unsigned i = 0; i < n; ++i) /* try parse an UUID in text form */ {

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
///
/// https://en.wikipedia.org/wiki/Operating_system_abstraction_layer
@@ -193,7 +193,14 @@ typedef struct osal_mmap {
#elif defined(__ANDROID_API__)
#if __ANDROID_API__ < 24
/* https://android-developers.googleblog.com/2017/09/introducing-android-native-development.html
* https://android.googlesource.com/platform/bionic/+/master/docs/32-bit-abi.md */
#define MDBX_HAVE_PWRITEV 0
#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS != MDBX_WORDBITS
#error "_FILE_OFFSET_BITS != MDBX_WORDBITS and __ANDROID_API__ < 24" (_FILE_OFFSET_BITS != MDBX_WORDBITS)
#elif defined(__FILE_OFFSET_BITS) && __FILE_OFFSET_BITS != MDBX_WORDBITS
#error "__FILE_OFFSET_BITS != MDBX_WORDBITS and __ANDROID_API__ < 24" (__FILE_OFFSET_BITS != MDBX_WORDBITS)
#endif
#else
#define MDBX_HAVE_PWRITEV 1
#endif
@@ -464,7 +471,8 @@ MDBX_INTERNAL int osal_lockfile(mdbx_filehandle_t fd, bool wait);
#define MMAP_OPTION_TRUNCATE 1
#define MMAP_OPTION_SEMAPHORE 2
MDBX_INTERNAL int osal_mmap(const int flags, osal_mmap_t *map, size_t size, const size_t limit, const unsigned options);
MDBX_INTERNAL int osal_mmap(const int flags, osal_mmap_t *map, size_t size, const size_t limit, const unsigned options,
const pathchar_t *pathname4logging);
MDBX_INTERNAL int osal_munmap(osal_mmap_t *map);
#define MDBX_MRESIZE_MAY_MOVE 0x00000100
#define MDBX_MRESIZE_MAY_UNMAP 0x00000200
@@ -480,6 +488,7 @@ MDBX_INTERNAL int osal_resume_threads_after_remap(mdbx_handle_array_t *array);
MDBX_INTERNAL int osal_msync(const osal_mmap_t *map, size_t offset, size_t length, enum osal_syncmode_bits mode_bits);
MDBX_INTERNAL int osal_check_fs_rdonly(mdbx_filehandle_t handle, const pathchar_t *pathname, int err);
MDBX_INTERNAL int osal_check_fs_incore(mdbx_filehandle_t handle);
MDBX_INTERNAL int osal_check_fs_local(mdbx_filehandle_t handle, int flags);
MDBX_MAYBE_UNUSED static inline uint32_t osal_getpid(void) {
STATIC_ASSERT(sizeof(mdbx_pid_t) <= sizeof(uint32_t));

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -726,7 +726,7 @@ void recalculate_subpage_thresholds(MDBX_env *env) {
env->subpage_reserve_prereq = page_space(env);
else if (env->subpage_reserve_prereq < env->subpage_room_threshold + env->subpage_reserve_limit)
env->subpage_reserve_prereq = env->subpage_room_threshold + env->subpage_reserve_limit;
eASSERT(env, env->subpage_reserve_prereq > env->subpage_room_threshold + env->subpage_reserve_limit);
eASSERT(env, env->subpage_reserve_prereq >= env->subpage_room_threshold + env->subpage_reserve_limit);
}
size_t page_subleaf2_reserve(const MDBX_env *env, size_t host_page_room, size_t subpage_len, size_t item_len) {

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,7 +1,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
@@ -122,6 +122,8 @@
#pragma warning(disable : 6235) /* <expression> is always a constant */
#pragma warning(disable : 6237) /* <expression> is never evaluated and might \
have side effects */
#pragma warning(disable : 5286) /* implicit conversion from enum type 'type 1' to enum type 'type 2' */
#pragma warning(disable : 5287) /* operands are different enum types 'type 1' and 'type 2' */
#endif
#pragma warning(disable : 4710) /* 'xyz': function not inlined */
#pragma warning(disable : 4711) /* function 'xyz' selected for automatic \
@@ -433,11 +435,6 @@ __extern_C key_t ftok(const char *, int);
#if __ANDROID_API__ >= 21
#include <sys/sendfile.h>
#endif
#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS != MDBX_WORDBITS
#error "_FILE_OFFSET_BITS != MDBX_WORDBITS" (_FILE_OFFSET_BITS != MDBX_WORDBITS)
#elif defined(__FILE_OFFSET_BITS) && __FILE_OFFSET_BITS != MDBX_WORDBITS
#error "__FILE_OFFSET_BITS != MDBX_WORDBITS" (__FILE_OFFSET_BITS != MDBX_WORDBITS)
#endif
#endif /* Android */
#if defined(HAVE_SYS_STAT_H) || __has_include(<sys/stat.h>)
@@ -522,6 +519,12 @@ __extern_C key_t ftok(const char *, int);
#endif
#endif /* __BYTE_ORDER__ || __ORDER_LITTLE_ENDIAN__ || __ORDER_BIG_ENDIAN__ */
#if UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul || defined(_WIN64)
#define MDBX_WORDBITS 64
#else
#define MDBX_WORDBITS 32
#endif /* MDBX_WORDBITS */
/*----------------------------------------------------------------------------*/
/* Availability of CMOV or equivalent */

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
@@ -76,7 +76,7 @@ MDBX_INTERNAL int env_open(MDBX_env *env, mdbx_mode_t mode);
MDBX_INTERNAL int env_info(const MDBX_env *env, const MDBX_txn *txn, MDBX_envinfo *out, size_t bytes, troika_t *troika);
MDBX_INTERNAL int env_sync(MDBX_env *env, bool force, bool nonblock);
MDBX_INTERNAL int env_close(MDBX_env *env, bool resurrect_after_fork);
MDBX_INTERNAL bool env_txn0_owned(const MDBX_env *env);
MDBX_INTERNAL MDBX_txn *env_owned_wrtxn(const MDBX_env *env);
MDBX_INTERNAL int __must_check_result env_page_auxbuffer(MDBX_env *env);
MDBX_INTERNAL unsigned env_setup_pagesize(MDBX_env *env, const size_t pagesize);
@@ -95,7 +95,7 @@ MDBX_INTERNAL void recalculate_subpage_thresholds(MDBX_env *env);
/* table.c */
MDBX_INTERNAL int __must_check_result tbl_fetch(MDBX_txn *txn, size_t dbi);
MDBX_INTERNAL int __must_check_result tbl_setup(const MDBX_env *env, kvx_t *const kvx, const tree_t *const db);
MDBX_INTERNAL int __must_check_result tbl_setup(const MDBX_env *env, volatile kvx_t *const kvx, const tree_t *const db);
/* coherency.c */
MDBX_INTERNAL bool coherency_check_meta(const MDBX_env *env, const volatile meta_t *meta, bool report);

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
///
/// \file sort.h
/// \brief Маркосы реализующие сортировку и двоичный поиск

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,30 +1,39 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
int tbl_setup(const MDBX_env *env, kvx_t *const kvx, const tree_t *const db) {
int tbl_setup(const MDBX_env *env, volatile kvx_t *const kvx, const tree_t *const db) {
osal_memory_fence(mo_AcquireRelease, false);
if (unlikely(!check_table_flags(db->flags))) {
ERROR("incompatible or invalid db.flags (0x%x) ", db->flags);
return MDBX_INCOMPATIBLE;
}
if (unlikely(!kvx->clc.k.cmp)) {
kvx->clc.k.cmp = builtin_keycmp(db->flags);
kvx->clc.v.cmp = builtin_datacmp(db->flags);
size_t v_lmin = valsize_min(db->flags);
size_t v_lmax = env_valsize_max(env, db->flags);
if ((db->flags & (MDBX_DUPFIXED | MDBX_INTEGERDUP)) != 0 && db->dupfix_size) {
if (!MDBX_DISABLE_VALIDATION && unlikely(db->dupfix_size < v_lmin || db->dupfix_size > v_lmax)) {
ERROR("db.dupfix_size (%u) <> min/max value-length (%zu/%zu)", db->dupfix_size, v_lmin, v_lmax);
return MDBX_CORRUPTED;
}
v_lmin = v_lmax = db->dupfix_size;
}
kvx->clc.k.lmin = keysize_min(db->flags);
kvx->clc.k.lmax = env_keysize_max(env, db->flags);
kvx->clc.v.lmin = valsize_min(db->flags);
kvx->clc.v.lmax = env_valsize_max(env, db->flags);
if ((db->flags & (MDBX_DUPFIXED | MDBX_INTEGERDUP)) != 0 && db->dupfix_size) {
if (!MDBX_DISABLE_VALIDATION && unlikely(db->dupfix_size < kvx->clc.v.lmin || db->dupfix_size > kvx->clc.v.lmax)) {
ERROR("db.dupfix_size (%u) <> min/max value-length (%zu/%zu)", db->dupfix_size, kvx->clc.v.lmin, kvx->clc.v.lmax);
return MDBX_CORRUPTED;
}
kvx->clc.v.lmin = kvx->clc.v.lmax = db->dupfix_size;
if (unlikely(!kvx->clc.k.cmp)) {
kvx->clc.v.cmp = builtin_datacmp(db->flags);
kvx->clc.k.cmp = builtin_keycmp(db->flags);
}
kvx->clc.v.lmin = v_lmin;
osal_memory_fence(mo_Relaxed, true);
kvx->clc.v.lmax = v_lmax;
osal_memory_fence(mo_AcquireRelease, true);
eASSERT(env, kvx->clc.k.lmax >= kvx->clc.k.lmin);
eASSERT(env, kvx->clc.v.lmax >= kvx->clc.v.lmin);
return MDBX_SUCCESS;
}
@@ -38,7 +47,7 @@ int tbl_fetch(MDBX_txn *txn, size_t dbi) {
rc = tree_search(&couple.outer, &kvx->name, 0);
if (unlikely(rc != MDBX_SUCCESS)) {
bailout:
NOTICE("dbi %zu refs to inaccessible table `%*s` for txn %" PRIaTXN " (err %d)", dbi, (int)kvx->name.iov_len,
NOTICE("dbi %zu refs to inaccessible table `%.*s` for txn %" PRIaTXN " (err %d)", dbi, (int)kvx->name.iov_len,
(const char *)kvx->name.iov_base, txn->txnid, rc);
return (rc == MDBX_NOTFOUND) ? MDBX_BAD_DBI : rc;
}
@@ -50,7 +59,7 @@ int tbl_fetch(MDBX_txn *txn, size_t dbi) {
goto bailout;
}
if (unlikely((node_flags(nsr.node) & (N_DUP | N_TREE)) != N_TREE)) {
NOTICE("dbi %zu refs to not a named table `%*s` for txn %" PRIaTXN " (%s)", dbi, (int)kvx->name.iov_len,
NOTICE("dbi %zu refs to not a named table `%.*s` for txn %" PRIaTXN " (%s)", dbi, (int)kvx->name.iov_len,
(const char *)kvx->name.iov_base, txn->txnid, "wrong flags");
return MDBX_INCOMPATIBLE; /* not a named DB */
}
@@ -60,7 +69,7 @@ int tbl_fetch(MDBX_txn *txn, size_t dbi) {
return rc;
if (unlikely(data.iov_len != sizeof(tree_t))) {
NOTICE("dbi %zu refs to not a named table `%*s` for txn %" PRIaTXN " (%s)", dbi, (int)kvx->name.iov_len,
NOTICE("dbi %zu refs to not a named table `%.*s` for txn %" PRIaTXN " (%s)", dbi, (int)kvx->name.iov_len,
(const char *)kvx->name.iov_base, txn->txnid, "wrong rec-size");
return MDBX_INCOMPATIBLE; /* not a named DB */
}
@@ -70,7 +79,7 @@ int tbl_fetch(MDBX_txn *txn, size_t dbi) {
* have dropped and recreated the DB with other flags. */
tree_t *const db = &txn->dbs[dbi];
if (unlikely((db->flags & DB_PERSISTENT_FLAGS) != flags)) {
NOTICE("dbi %zu refs to the re-created table `%*s` for txn %" PRIaTXN
NOTICE("dbi %zu refs to the re-created table `%.*s` for txn %" PRIaTXN
" with different flags (present 0x%X != wanna 0x%X)",
dbi, (int)kvx->name.iov_len, (const char *)kvx->name.iov_base, txn->txnid, db->flags & DB_PERSISTENT_FLAGS,
flags);
@@ -86,10 +95,13 @@ int tbl_fetch(MDBX_txn *txn, size_t dbi) {
return MDBX_CORRUPTED;
}
#endif /* !MDBX_DISABLE_VALIDATION */
rc = tbl_setup(txn->env, kvx, db);
rc = tbl_setup_ifneed(txn->env, kvx, db);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
if (unlikely(dbi_changed(txn, dbi)))
return MDBX_BAD_DBI;
txn->dbi_state[dbi] &= ~DBI_STALE;
return MDBX_SUCCESS;
}

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
///
///

View File

@@ -1,7 +1,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
///
/// mdbx_copy.c - memory-mapped database backup tool
///

View File

@@ -1,7 +1,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2021-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2021-2025
///
/// mdbx_drop.c - memory-mapped database delete tool
///

View File

@@ -1,7 +1,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
///
/// mdbx_dump.c - memory-mapped database dump tool
///

View File

@@ -1,7 +1,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
///
/// mdbx_load.c - memory-mapped database load tool
///

View File

@@ -1,7 +1,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
///
/// mdbx_stat.c - memory-mapped database status tool
///

View File

@@ -1,7 +1,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
@@ -880,7 +880,7 @@ retry:
if (nkeys >= minkeys) {
mc->ki[mc->top] = (indx_t)ki_top;
if (AUDIT_ENABLED())
return cursor_check_updating(mc);
return cursor_validate_updating(mc);
return MDBX_SUCCESS;
}
@@ -920,7 +920,7 @@ int page_split(MDBX_cursor *mc, const MDBX_val *const newkey, MDBX_val *const ne
const size_t newindx = mc->ki[mc->top];
size_t nkeys = page_numkeys(mp);
if (AUDIT_ENABLED()) {
rc = cursor_check_updating(mc);
rc = cursor_validate_updating(mc);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
}
@@ -979,7 +979,7 @@ int page_split(MDBX_cursor *mc, const MDBX_val *const newkey, MDBX_val *const ne
mc->top = 1;
prev_top = 0;
if (AUDIT_ENABLED()) {
rc = cursor_check_updating(mc);
rc = cursor_validate_updating(mc);
if (unlikely(rc != MDBX_SUCCESS))
goto done;
}
@@ -1092,10 +1092,10 @@ int page_split(MDBX_cursor *mc, const MDBX_val *const newkey, MDBX_val *const ne
}
if (AUDIT_ENABLED()) {
rc = cursor_check_updating(mc);
rc = cursor_validate_updating(mc);
if (unlikely(rc != MDBX_SUCCESS))
goto done;
rc = cursor_check_updating(mn);
rc = cursor_validate_updating(mn);
if (unlikely(rc != MDBX_SUCCESS))
goto done;
}
@@ -1221,7 +1221,7 @@ int page_split(MDBX_cursor *mc, const MDBX_val *const newkey, MDBX_val *const ne
goto done;
cASSERT(mc, mc->top - top == mc->tree->height - height);
if (AUDIT_ENABLED()) {
rc = cursor_check_updating(mc);
rc = cursor_validate_updating(mc);
if (unlikely(rc != MDBX_SUCCESS))
goto done;
}
@@ -1474,7 +1474,7 @@ done:
mc->txn->flags |= MDBX_TXN_ERROR;
else {
if (AUDIT_ENABLED())
rc = cursor_check_updating(mc);
rc = cursor_validate_updating(mc);
if (unlikely(naf & MDBX_RESERVE)) {
node_t *node = page_node(mc->pg[mc->top], mc->ki[mc->top]);
if (!(node_flags(node) & N_BIG))
@@ -1524,7 +1524,7 @@ int tree_propagate_key(MDBX_cursor *mc, const MDBX_val *key) {
node_del(mc, 0);
int err = page_split(mc, key, nullptr, pgno, MDBX_SPLIT_REPLACE);
if (err == MDBX_SUCCESS && AUDIT_ENABLED())
err = cursor_check_updating(mc);
err = cursor_validate_updating(mc);
return err;
}

View File

@@ -1,7 +1,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"

View File

@@ -1,5 +1,5 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once

Some files were not shown because too many files have changed in this diff Show More