mirror of
https://github.com/isar/libmdbx.git
synced 2025-12-18 17:52:22 +08:00
Compare commits
140 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
566b0f93c7 | ||
|
|
df3a18d0cf | ||
|
|
675b462601 | ||
|
|
7c990542e3 | ||
|
|
9093435ce6 | ||
|
|
7ef7a1f983 | ||
|
|
ac275d246b | ||
|
|
d2f4697df3 | ||
|
|
b107842c3d | ||
|
|
cb428e613f | ||
|
|
5c69cb322a | ||
|
|
6318ca701a | ||
|
|
9c1f3aa4b5 | ||
|
|
759d0442d4 | ||
|
|
6cb9305b31 | ||
|
|
68f9dc18be | ||
|
|
f5aa804a2d | ||
|
|
7468ce6ada | ||
|
|
decf34fd2b | ||
|
|
f1b933c541 | ||
|
|
230fbd64f5 | ||
|
|
e8bfffc9f6 | ||
|
|
6abaa67eb8 | ||
|
|
266937e72d | ||
|
|
5c44dd201c | ||
|
|
f4384800b5 | ||
|
|
a971c76aff | ||
|
|
b6f918aa1c | ||
|
|
ab2f661c97 | ||
|
|
5548ef20f6 | ||
|
|
679c1eb939 | ||
|
|
76a588f91b | ||
|
|
6b5515908b | ||
|
|
214fa153e2 | ||
|
|
819551ce13 | ||
|
|
a22c0c5c48 | ||
|
|
9540cabf5f | ||
|
|
0e3b093eb5 | ||
|
|
5d38add405 | ||
|
|
b55a41f604 | ||
|
|
29bed7cf5d | ||
|
|
8d0eceee9f | ||
|
|
56a6377622 | ||
|
|
19dc93fc76 | ||
|
|
5f1d8dcb3e | ||
|
|
3d2b221256 | ||
|
|
ca1808d57f | ||
|
|
aa98d6a88e | ||
|
|
b9b14f0061 | ||
|
|
e3324cef91 | ||
|
|
bb664152b8 | ||
|
|
5d9fb63fb8 | ||
|
|
1e0a1014a4 | ||
|
|
35349cf538 | ||
|
|
8157d07b00 | ||
|
|
3c3628c798 | ||
|
|
3a0dbee58c | ||
|
|
e11d419d20 | ||
|
|
59343d9106 | ||
|
|
2127d3b7d7 | ||
|
|
065aef35ea | ||
|
|
9653c8f45b | ||
|
|
7ed769e9c6 | ||
|
|
52a19fecca | ||
|
|
3282adf8bd | ||
|
|
529f2c2380 | ||
|
|
1dfe1e872e | ||
|
|
767ba21977 | ||
|
|
0a9c9840da | ||
|
|
9c177de034 | ||
|
|
5f37ea60d2 | ||
|
|
c457804fad | ||
|
|
6c036add8b | ||
|
|
5fd319bbc2 | ||
|
|
682233ba28 | ||
|
|
c5936eb5da | ||
|
|
d8890bc169 | ||
|
|
73d52c1963 | ||
|
|
58729a2fbd | ||
|
|
5dfe3433a8 | ||
|
|
1720762080 | ||
|
|
91570a084f | ||
|
|
0fff8d0704 | ||
|
|
484b488f92 | ||
|
|
2fbdaccf60 | ||
|
|
753b2270fd | ||
|
|
33ceba0a5a | ||
|
|
2476fba287 | ||
|
|
2b6a768750 | ||
|
|
b6dcdcf2dc | ||
|
|
175e4a2e1b | ||
|
|
f9d7eb5525 | ||
|
|
69895e2b55 | ||
|
|
15bd9cfc89 | ||
|
|
d8f9f3ba58 | ||
|
|
4150f411dc | ||
|
|
32ca9691c3 | ||
|
|
4f59864ef5 | ||
|
|
f82b760b6e | ||
|
|
d6b359756c | ||
|
|
4d454d6e80 | ||
|
|
44467d0883 | ||
|
|
49e6bd9296 | ||
|
|
805d84480d | ||
|
|
7504a8f8f2 | ||
|
|
94a2abaf31 | ||
|
|
0604accecf | ||
|
|
bc2f1c59cb | ||
|
|
40f655e2da | ||
|
|
5e714ed946 | ||
|
|
d313008d82 | ||
|
|
9277daa185 | ||
|
|
1792bdc763 | ||
|
|
90635e7248 | ||
|
|
1ec13c63ab | ||
|
|
c712147eeb | ||
|
|
23600241e1 | ||
|
|
22c6763d57 | ||
|
|
c585fcd613 | ||
|
|
0ef0f49e2e | ||
|
|
80de77b1ee | ||
|
|
822213f75d | ||
|
|
aa2ff20faf | ||
|
|
fcdd2e2db3 | ||
|
|
75122b311d | ||
|
|
79572b4850 | ||
|
|
24f2b9b099 | ||
|
|
b46d2def80 | ||
|
|
21630ea115 | ||
|
|
6d346d8630 | ||
|
|
b59937adb8 | ||
|
|
11e1346f9d | ||
|
|
a59c5f9316 | ||
|
|
27a2166be7 | ||
|
|
c615e4d0a6 | ||
|
|
03685aba5a | ||
|
|
4a0a32a54b | ||
|
|
36abcc57f0 | ||
|
|
88d782e5eb | ||
|
|
d3daa23c63 |
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
302
ChangeLog.md
302
ChangeLog.md
@@ -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'.
|
||||
|
||||
92
GNUmakefile
92
GNUmakefile
@@ -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
32
NOTICE
@@ -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.
|
||||
|
||||
91
README.md
91
README.md
@@ -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 -->
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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`.
|
||||
Всё будет хорошо!
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
143
mdbx.h
@@ -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++
421
mdbx.h++
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
390
src/api-cursor.c
390
src/api-cursor.c
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
37
src/cogs.h
37
src/cogs.h
@@ -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);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
203
src/cursor.c
203
src/cursor.c
@@ -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, ¤t_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;
|
||||
}
|
||||
|
||||
44
src/cursor.h
44
src/cursor.h
@@ -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
101
src/dbi.c
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
67
src/dxb.c
67
src/dxb.c
@@ -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 {
|
||||
|
||||
23
src/env.c
23
src/env.c
@@ -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 "
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
21
src/gc-put.c
21
src/gc-put.c
@@ -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 */
|
||||
|
||||
2
src/gc.h
2
src/gc.h
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
11
src/lck.c
11
src/lck.c
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
62
src/mdbx.c++
62
src/mdbx.c++
@@ -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())
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
51
src/osal.c
51
src/osal.c
@@ -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 */ {
|
||||
|
||||
13
src/osal.h
13
src/osal.h
@@ -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));
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 Маркосы реализующие сортировку и двоичный поиск
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
50
src/table.c
50
src/table.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
///
|
||||
|
||||
///
|
||||
|
||||
@@ -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
|
||||
///
|
||||
|
||||
@@ -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
|
||||
///
|
||||
|
||||
@@ -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
|
||||
///
|
||||
|
||||
@@ -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
|
||||
///
|
||||
|
||||
@@ -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
|
||||
///
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user