diff --git a/.gitignore b/.gitignore index 981df32c..2ea34c80 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ Win32/ build-* cmake-build-* core +example libmdbx.creator.user mdbx-dll.VC.VC.opendb mdbx-dll.VC.db @@ -25,7 +26,7 @@ mdbx_copy mdbx_dump mdbx_load mdbx_stat -ootest/test +mdbx_test test.log test/test.vcxproj.user test/tmp.db diff --git a/README-RU.md b/README-RU.md index 095a2598..23354555 100644 --- a/README-RU.md +++ b/README-RU.md @@ -14,28 +14,28 @@ and [by Yandex](https://translate.yandex.ru/translate?url=https%3A%2F%2Fgithub.c **Сейчас MDBX _активно перерабатывается_** и к середине 2018 -ожидается большое измените как API, так и формата базы данных. +ожидается большое изменение как API, так и формата базы данных. К сожалению, обновление приведет к потере совместимости с предыдущими версиями. -Цель этой революции в обеспечении более четкого надежного -API и добавлении новых функции, а также в наделении базы данных +Цель этой революции - обеспечение более четкого надежного +API и добавление новых функции, а также наделение базы данных новыми свойствами. В настоящее время MDBX предназначена для Linux, а также поддерживает Windows (начиная с Windows Server 2008) в качестве -дополнительно платформы. Поддержка других ОС может быть +дополнительной платформы. Поддержка других ОС может быть обеспечена на коммерческой основе. Однако такие усовершенствования (т. е. pull-requests) могут быть приняты в мейнстрим только в том случае, если будет доступен соответствующий публичный и бесплатный сервис непрерывной -интеграции (aka Countinious Integration). +интеграции (aka Continuous Integration). ## Содержание - [Обзор](#Обзор) - [Сравнение с другими СУБД](#Сравнение-с-другими-СУБД) - - [История & Acknowledgements](#История) + - [История & Acknowledgments](#История) - [Основные свойства](#Основные-свойства) - [Сравнение производительности](#Сравнение-производительности) - [Интегральная производительность](#Интегральная-производительность) @@ -62,8 +62,8 @@ _libmdbx_ позволяет множеству процессов совмес _libmdbx_ обеспечивает [serializability](https://en.wikipedia.org/wiki/Serializability) -изменений и согласованность данных после аварий. При этом транзакции -изменяющие данные никак не мешают операциям чтения и выполняются строго +изменений и согласованность данных после аварий. При этом транзакции, +изменяющие данные, никак не мешают операциям чтения и выполняются строго последовательно с использованием единственного [мьютекса](https://en.wikipedia.org/wiki/Mutual_exclusion). @@ -101,7 +101,7 @@ Tables](https://github.com/leo-yuriev/libfpta), aka ["Позитивные Technologies](https://www.ptsecurity.ru). -#### Acknowledgements +#### Acknowledgments Howard Chu (Symas Corporation) - the author of LMDB, from which originated the MDBX in 2015. @@ -113,7 +113,7 @@ which was used for begin development of LMDB. Основные свойства ================= -_libmdbx_ наследует все ключевые возможности и особенности от +_libmdbx_ наследует все ключевые возможности и особенности своего прародителя [LMDB](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database), но с устранением ряда описываемых далее проблем и архитектурных недочетов. @@ -129,7 +129,7 @@ _libmdbx_ наследует все ключевые возможности и [MVCC](https://ru.wikipedia.org/wiki/MVCC) и [COW](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BF%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D1%80%D0%B8_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B8). Изменения строго последовательны и не блокируются чтением, - конфликты между транзакциями не возможны. + конфликты между транзакциями невозможны. При этом гарантируется чтение только зафиксированных данных, см [relaxing serializability](https://en.wikipedia.org/wiki/Serializability). 4. Чтение и поиск [без блокировок](https://ru.wikipedia.org/wiki/%D0%9D%D0%B5%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8%D1%80%D1%83%D1%8E%D1%89%D0%B0%D1%8F_%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F), @@ -193,7 +193,7 @@ github](https://github.com/pmwkaa/ioarena/tree/HL%2B%2B2015). 1. Такое сравнение не совсем правомочно, его следует делать с движками ориентированными на хранение данных в памяти ([Tarantool](https://tarantool.io/), [Redis](https://redis.io/)). - 2. Превосходство libmdbx становится еще более подавляющем, что мешает + 2. Превосходство libmdbx становится еще более подавляющим, что мешает восприятию информации. ![Comparison #1: Integral Performance](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-1.png) @@ -217,7 +217,7 @@ github](https://github.com/pmwkaa/ioarena/tree/HL%2B%2B2015). - Логарифмическая шкала справа и желтые интервальные отрезки соответствуют времени выполнения транзакций. При этом каждый отрезок - показывает минимальное и максимальное время затраченное на выполнение + показывает минимальное и максимальное время, затраченное на выполнение транзакций, а крестиком отмечено среднеквадратичное значение. Выполняется **10.000 транзакций в режиме синхронной фиксации данных** на @@ -243,7 +243,7 @@ github](https://github.com/pmwkaa/ioarena/tree/HL%2B%2B2015). - Логарифмическая шкала справа и желтые интервальные отрезки соответствуют времени выполнения транзакций. При этом каждый отрезок - показывает минимальное и максимальное время затраченное на выполнение + показывает минимальное и максимальное время, затраченное на выполнение транзакций, а крестиком отмечено среднеквадратичное значение. Выполняется **100.000 транзакций в режиме отложенной фиксации данных** @@ -274,7 +274,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко - Логарифмическая шкала справа и желтые интервальные отрезки соответствуют времени выполнения транзакций. При этом каждый отрезок - показывает минимальное и максимальное время затраченное на выполнение + показывает минимальное и максимальное время, затраченное на выполнение транзакций, а крестиком отмечено среднеквадратичное значение. Выполняется **1.000.000 транзакций в режиме асинхронной фиксации @@ -283,7 +283,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко консистентны на момент завершения одной из транзакций, но допускается потеря изменений из значительного количества последних транзакций. Во всех движках при этом включался режим предполагающий минимальную -нагрузку на диск по-записи, и соответственно минимальную гарантию +нагрузку на диск по записи, и соответственно минимальную гарантию сохранности данных. В _libmdbx_ при этом используется режим асинхронной записи измененных страниц на диск посредством ядра ОС и системного вызова [msync(MS_ASYNC)](https://linux.die.net/man/2/msync). @@ -348,7 +348,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко > _libmdbx_ фиксация транзакции также требует от 2 IOPS. Однако, в БД с > журналом кол-во IOPS будет меняться в зависимости от файловой системы, > но не от кол-ва записей или их объема. Тогда как в _libmdbx_ кол-во - > будет расти логарифмически от кол-во записей/строк в БД (по высоте + > будет расти логарифмически от кол-ва записей/строк в БД (по высоте > b+tree). 3. [COW](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BF%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D1%80%D0%B8_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B8) @@ -364,7 +364,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко > значительного копирования данных в памяти и массы других затратных операций. > Поэтому обусловленное этим недостатком падение производительности становится > заметным только при отказе от фиксации изменений на диске. - > Соответственно, корректнее сказать что _libmdbx_ позволяет + > Соответственно, корректнее сказать, что _libmdbx_ позволяет > получить персистентность ценой минимального падения производительности. > Если же нет необходимости оперативно сохранять данные, то логичнее > использовать `std::map`. @@ -457,7 +457,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко Однако, при аварийном отключении питания или сбое в ядре ОС, на диске может быть сохранена только часть измененных страниц БД. При этом с большой -вероятностью может оказаться так, что будут сохранены мета-страницы со +вероятностью может оказаться, что будут сохранены мета-страницы со ссылками на страницы с новыми версиями данных, но не сами новые данные. В этом случае БД будет безвозвратна разрушена, даже если до аварии производилась полная синхронизация данных (посредством @@ -471,11 +471,11 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко с переносом изменений после фиксации данных. * При завершении транзакций, в зависимости от состояния - синхронности данных между диском и оперативной память, + синхронности данных между диском и оперативной памятью, _libmdbx_ помечает точки фиксации либо как сильные (strong), либо как слабые (weak). Так например, в режиме `WRITEMAP+MAPSYNC` завершаемые транзакции помечаются как - слабые, а при явной синхронизации данных как сильные. + слабые, а при явной синхронизации данных - как сильные. * В _libmdbx_ поддерживается не две, а три отдельные мета-страницы. Это позволяет выполнять фиксацию транзакций с формированием как @@ -489,20 +489,20 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко сильной фиксации. Этим обеспечивается гарантия сохранности БД. Такая гарантия надежности не дается бесплатно. Для -сохранности данных, страницы формирующие крайний снимок с +сохранности данных, страницы, формирующие крайний снимок с сильной фиксацией, не должны повторно использоваться (перезаписываться) до формирования следующей сильной точки фиксации. Таким образом, крайняя точка фиксации создает описанный выше эффект "долгого чтения". Разница же здесь в том, что при исчерпании свободных страниц ситуация будет автоматически исправлена, посредством записи изменений на диск -и формированием новой сильной точки фиксации. +и формирования новой сильной точки фиксации. Таким образом, в режиме безопасной асинхронной фиксации _libmdbx_ будет всегда использовать новые страницы до исчерпания места в БД или до явного формирования сильной точки фиксации посредством `mdbx_env_sync()`. -При этом суммарный трафик записи на диск будет примерно такой-же, -как если бы отдельно фиксировалась каждая транзакций. +При этом суммарный трафик записи на диск будет примерно такой же, +как если бы отдельно фиксировалась каждая транзакция. В текущей версии _libmdbx_ вам предоставляется выбор между безопасным режимом (по умолчанию) асинхронной фиксации, и режимом `UTTERLY_NOSYNC` когда @@ -535,9 +535,9 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко Посредством `mdbx_env_set_oomfunc()` может быть установлен внешний обработчик (callback), который будет вызван при - исчерпания свободных страниц из-за долгой операцией чтения. + исчерпании свободных страниц из-за долгой операцией чтения. Обработчику будет передан PID и pthread_id виновника. - В свою очередь обработчик может предпринять одно из действий: + В свою очередь обработчик может предпринять одно из действий: * нейтрализовать виновника (отправить сигнал kill #9), если долгое чтение выполняется сторонним процессом; @@ -545,7 +545,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко * отменить или перезапустить проблемную операцию чтения, если операция выполняется одним из потоков текущего процесса; - * подождать некоторое время, в расчете что проблемная операция + * подождать некоторое время, в расчете на то, что проблемная операция чтения будет штатно завершена; * прервать текущую операцию изменения данных с возвратом кода @@ -612,7 +612,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко 19. Возможность посредством `mdbx_is_dirty()` определить находятся ли некоторый ключ или данные в "грязной" странице БД. Таким образом, избегая лишнего копирования данных перед выполнением модифицирующих -операций (значения в размещенные "грязных" страницах могут быть +операций (значения, размещенные в "грязных" страницах, могут быть перезаписаны при изменениях, иначе они будут неизменны). 20. Корректное обновление текущей записи, в том числе сортированного @@ -632,7 +632,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко > > - обращение к уже освобожденной памяти; > - попытки повторного освобождения памяти; - > - memory corruption and segfaults. + > - повреждение памяти и ошибки сегментации. 22. Дополнительный код ошибки `MDBX_EMULTIVAL`, который возвращается из `mdbx_put()` и `mdbx_replace()` при попытке выполнить неоднозначное @@ -666,7 +666,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко цикле обновления данных и записи на диск. Фактически _libmdbx_ выполняет постоянную компактификацию данных, но не затрачивая на это дополнительных ресурсов, а только освобождая их. При освобождении места -в БД и установки соответствующих параметров геометрии базы данных, также будет +в БД и установке соответствующих параметров геометрии базы данных, также будет уменьшаться размер файла на диске, в том числе в **Windows**. -------------------------------------------------------------------------------- diff --git a/README.md b/README.md index 5d9bbc6b..ba6f6b80 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,15 @@ libmdbx ## Project Status for now - - The stable versions (the _stable/*_ branches) of are frozen, i.e. no new features or API changes, but only bug fixes. - - The next (the _devel_ branch) version **is under active development**, i.e. current API and set of features are extreme volatile. - - The immediate goal of development is formation of the stable API and the stable internal database format, which allows realise all planned features. - - Planned features: - 1. Integrity check by Merkle tree; - 2. Support for raw block devices; + - The stable versions ([_stable/0.0_](https://github.com/leo-yuriev/libmdbx/tree/stable/0.0) and [_stable/0.1_](https://github.com/leo-yuriev/libmdbx/tree/stable/0.1) branches) of _MDBX_ are frozen, i.e. no new features or API changes, but only bug fixes. + - The next version ([_devel_](https://github.com/leo-yuriev/libmdbx/tree/devel) branch) **is under active non-public development**, i.e. current API and set of features are extreme volatile. + - The immediate goal of development is formation of the stable API and the stable internal database format, which allows realise all PLANNED FEATURES: + 1. Integrity check by [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree); + 2. Support for [raw block devices](https://en.wikipedia.org/wiki/Raw_device); 3. Separate place (HDD) for large data items; - 4. Using "roaring bitmaps" inside garbage collector; - 5. Non-linear page reclaiming (like PostgreSQL's Vacuum); - 6. Asynchronous lazy data flushing to disk(s); + 4. Using "[Roaring bitmaps](http://roaringbitmap.org/about/)" inside garbage collector; + 5. Non-sequential reclaiming, like PostgreSQL's [Vacuum](https://www.postgresql.org/docs/9.1/static/sql-vacuum.html); + 6. [Asynchronous lazy data flushing](https://sites.fas.harvard.edu/~cs265/papers/kathuria-2008.pdf) to disk(s); 7. etc... ----- @@ -34,7 +33,7 @@ Integration service will be available. - [Overview](#overview) - [Comparison with other DBs](#comparison-with-other-dbs) - - [History & Acknowledgements](#history) + - [History & Acknowledgments](#history) - [Main features](#main-features) - [Performance comparison](#performance-comparison) - [Integral performance](#integral-performance) @@ -74,7 +73,7 @@ Because _libmdbx_ is currently overhauled, I think it's better to just link ### History -_libmdbx_ design is based on [Lightning Memory-Mapped Database](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database). +The _libmdbx_ design is based on [Lightning Memory-Mapped Database](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database). Initial development was going in [ReOpenLDAP](https://github.com/leo-yuriev/ReOpenLDAP) project, about a year later it received separate development effort and in autumn 2015 was isolated to separate project, which was [presented at Highload++ 2015 conference](http://www.highload.ru/2015/abstracts/1831.html). @@ -82,7 +81,7 @@ received separate development effort and in autumn 2015 was isolated to separate Since early 2017 _libmdbx_ is used in [Fast Positive Tables](https://github.com/leo-yuriev/libfpta), by [Positive Technologies](https://www.ptsecurity.com). -#### Acknowledgements +#### Acknowledgments Howard Chu (Symas Corporation) - the author of LMDB, from which originated the MDBX in 2015. @@ -278,7 +277,7 @@ scanning data directory. #### Long-time read transactions problem Garbage collection problem exists in all databases one way or another (e.g. VACUUM in PostgreSQL). -But in _libmbdx_ and LMDB it's even more important because of high performance and deliberate +But in _libmdbx_ and LMDB it's even more important because of high performance and deliberate simplification of internals with emphasis on performance. * Altering data during long read operation may exhaust available space on persistent storage. @@ -314,7 +313,7 @@ _libmdbx_ addresses the problem, details below. Illustrations to this problem ca In `WRITEMAP+MAPSYNC` mode dirty pages are written to persistent storage by kernel. This means that in case of application crash OS kernel will write all dirty data to disk and nothing will be lost. But in case of hardware malfunction or OS kernel fatal error only some dirty data might be synced to disk, and there is high probability that pages with metadata saved, -will point to non-saved, hence non-existent, data pages. In such situation DB is completely corrupted and can't be +will point to non-saved, hence non-existent, data pages. In such situation, DB is completely corrupted and can't be repaired even if there was full sync before the crash via `mdbx_env_sync(). _libmdbx_ addresses this by fully reimplementing write path of data: @@ -337,7 +336,7 @@ synchronization point. So last steady synchronization point creates "long-time r of memory exhaustion the problem will be immediately addressed by flushing changes to persistent storage and forming new steady synchronization point. -So in async-write mode _libmdbx_ will always use new pages until memory is exhausted or `mdbx_env_sync()`is invoked. Total +So in async-write mode _libmdbx_ will always use new pages until memory is exhausted or `mdbx_env_sync()` is invoked. Total disk usage will be almost the same as in sync-write mode. Current _libmdbx_ gives a choice of safe async-write mode (default) and `UTTERLY_NOSYNC` mode which may result in full DB @@ -353,7 +352,7 @@ Improvements over LMDB 1. `LIFO RECLAIM` mode: The newest pages are picked for reuse instead of the oldest. - This allows to minimize reclaim loop and make it execution time independent from total page count. + This allows to minimize reclaim loop and make it execution time independent of total page count. This results in OS kernel cache mechanisms working with maximum efficiency. In case of using disk controllers or storages with @@ -365,7 +364,7 @@ Improvements over LMDB `mdbx_env_set_oomfunc()` allows to set a callback, which will be called in the event of memory exhausting during long-time read transaction. Callback will be invoked with PID and pthread_id of offending thread as parameters. - Callback can do any of this things to remedy the problem: + Callback can do any of these things to remedy the problem: * wait for read transaction to finish normally; @@ -409,7 +408,7 @@ Improvements over LMDB 15. Ability to close DB in "dirty" state (without data flush and creation of steady synchronization point) via `mdbx_env_close_ex()`. -16. Ability to get addition info, including number of the oldest snapshot of DB, which is used by one of the readers. +16. Ability to get additional info, including number of the oldest snapshot of DB, which is used by one of the readers. Implemented via `mdbx_env_info()`. 17. `mdbx_del()` doesn't ignore additional argument (specifier) `data` @@ -418,7 +417,7 @@ Improvements over LMDB 18. Ability to open dbi-table with simultaneous setup of comparators for keys and values, via `mdbx_dbi_open_ex()`. -19. Ability to find out if key or value are in dirty page. This may be useful to make a decision to avoid +19. Ability to find out if key or value is in dirty page. This may be useful to make a decision to avoid excessive CoW before updates. Implemented via `mdbx_is_dirty()`. 20. Correct update of current record in `MDBX_CURRENT` mode of `mdbx_cursor_put()`, including sorted duplicated. @@ -449,12 +448,12 @@ Improvements over LMDB 27. Advanced dynamic control over DB size, including ability to choose page size via `mdbx_env_set_geometry()`, including on Windows. -28. Three meta-pages instead two, this allows to guarantee consistently update weak sync-points without risking to +28. Three meta-pages instead of two, this allows to guarantee consistently update weak sync-points without risking to corrupt last steady sync-point. -29. Automatic reclaim of freed pages to specific reserved space in the end of database file. This lowers amount of pages, +29. Automatic reclaim of freed pages to specific reserved space at the end of database file. This lowers amount of pages, loaded to memory, used in update/flush loop. In fact _libmdbx_ constantly performs compactification of data, - but doesn't use addition resources for that. Space reclaim of DB and setup of database geometry parameters also decreases + but doesn't use additional resources for that. Space reclaim of DB and setup of database geometry parameters also decreases size of the database on disk, including on Windows. -------------------------------------------------------------------------------- diff --git a/TODO.md b/TODO.md index 32c4f0ac..810b18dc 100644 --- a/TODO.md +++ b/TODO.md @@ -70,7 +70,7 @@ - [x] Привести в порядок volatile. - [x] контроль meta.mapsize. - [x] переработка формата: заголовки страниц, meta, clk... -- [x] зачистка Doxygen и бесполезных коментариев. +- [x] зачистка Doxygen и бесполезных комментариев. - [x] Добавить поле типа контрольной суммы. - [x] Добавить поле/флаг размера pgno_t. - [x] Поменять сигнатуры. diff --git a/src/mdbx.c b/src/mdbx.c index 311a10a1..0b753972 100644 --- a/src/mdbx.c +++ b/src/mdbx.c @@ -962,63 +962,80 @@ enum { #define MDBX_END_SLOT 0x80 /* release any reader slot if MDBX_NOTLS */ static int mdbx_txn_end(MDBX_txn *txn, unsigned mode); -static int mdbx_page_get(MDBX_cursor *mc, pgno_t pgno, MDBX_page **mp, - int *lvl); -static int mdbx_page_search_root(MDBX_cursor *mc, MDBX_val *key, int modify); +static int __must_check_result mdbx_page_get(MDBX_cursor *mc, pgno_t pgno, + MDBX_page **mp, int *lvl); +static int __must_check_result mdbx_page_search_root(MDBX_cursor *mc, + MDBX_val *key, int modify); #define MDBX_PS_MODIFY 1 #define MDBX_PS_ROOTONLY 2 #define MDBX_PS_FIRST 4 #define MDBX_PS_LAST 8 -static int mdbx_page_search(MDBX_cursor *mc, MDBX_val *key, int flags); -static int mdbx_page_merge(MDBX_cursor *csrc, MDBX_cursor *cdst); +static int __must_check_result mdbx_page_search(MDBX_cursor *mc, MDBX_val *key, + int flags); +static int __must_check_result mdbx_page_merge(MDBX_cursor *csrc, + MDBX_cursor *cdst); #define MDBX_SPLIT_REPLACE MDBX_APPENDDUP /* newkey is not new */ -static int mdbx_page_split(MDBX_cursor *mc, MDBX_val *newkey, MDBX_val *newdata, - pgno_t newpgno, unsigned nflags); +static int __must_check_result mdbx_page_split(MDBX_cursor *mc, + MDBX_val *newkey, + MDBX_val *newdata, + pgno_t newpgno, unsigned nflags); -static int mdbx_read_header(MDBX_env *env, MDBX_meta *meta); -static int mdbx_sync_locked(MDBX_env *env, unsigned flags, - MDBX_meta *const pending); +static int __must_check_result mdbx_read_header(MDBX_env *env, MDBX_meta *meta); +static int __must_check_result mdbx_sync_locked(MDBX_env *env, unsigned flags, + MDBX_meta *const pending); static void mdbx_env_close0(MDBX_env *env); static MDBX_node *mdbx_node_search(MDBX_cursor *mc, MDBX_val *key, int *exactp); -static int mdbx_node_add(MDBX_cursor *mc, unsigned indx, MDBX_val *key, - MDBX_val *data, pgno_t pgno, unsigned flags); +static int __must_check_result mdbx_node_add(MDBX_cursor *mc, unsigned indx, + MDBX_val *key, MDBX_val *data, + pgno_t pgno, unsigned flags); static void mdbx_node_del(MDBX_cursor *mc, size_t ksize); static void mdbx_node_shrink(MDBX_page *mp, unsigned indx); -static int mdbx_node_move(MDBX_cursor *csrc, MDBX_cursor *cdst, int fromleft); -static int mdbx_node_read(MDBX_cursor *mc, MDBX_node *leaf, MDBX_val *data); +static int __must_check_result mdbx_node_move(MDBX_cursor *csrc, + MDBX_cursor *cdst, int fromleft); +static int __must_check_result mdbx_node_read(MDBX_cursor *mc, MDBX_node *leaf, + MDBX_val *data); static size_t mdbx_leaf_size(MDBX_env *env, MDBX_val *key, MDBX_val *data); static size_t mdbx_branch_size(MDBX_env *env, MDBX_val *key); -static int mdbx_rebalance(MDBX_cursor *mc); -static int mdbx_update_key(MDBX_cursor *mc, MDBX_val *key); +static int __must_check_result mdbx_rebalance(MDBX_cursor *mc); +static int __must_check_result mdbx_update_key(MDBX_cursor *mc, MDBX_val *key); static void mdbx_cursor_pop(MDBX_cursor *mc); -static int mdbx_cursor_push(MDBX_cursor *mc, MDBX_page *mp); +static int __must_check_result mdbx_cursor_push(MDBX_cursor *mc, MDBX_page *mp); -static int mdbx_cursor_del0(MDBX_cursor *mc); -static int mdbx_del0(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, - unsigned flags); -static int mdbx_cursor_sibling(MDBX_cursor *mc, int move_right); -static int mdbx_cursor_next(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, - MDBX_cursor_op op); -static int mdbx_cursor_prev(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, - MDBX_cursor_op op); -static int mdbx_cursor_set(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, - MDBX_cursor_op op, int *exactp); -static int mdbx_cursor_first(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data); -static int mdbx_cursor_last(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data); +static int __must_check_result mdbx_cursor_del0(MDBX_cursor *mc); +static int __must_check_result mdbx_del0(MDBX_txn *txn, MDBX_dbi dbi, + MDBX_val *key, MDBX_val *data, + unsigned flags); +static int __must_check_result mdbx_cursor_sibling(MDBX_cursor *mc, + int move_right); +static int __must_check_result mdbx_cursor_next(MDBX_cursor *mc, MDBX_val *key, + MDBX_val *data, + MDBX_cursor_op op); +static int __must_check_result mdbx_cursor_prev(MDBX_cursor *mc, MDBX_val *key, + MDBX_val *data, + MDBX_cursor_op op); +static int __must_check_result mdbx_cursor_set(MDBX_cursor *mc, MDBX_val *key, + MDBX_val *data, + MDBX_cursor_op op, int *exactp); +static int __must_check_result mdbx_cursor_first(MDBX_cursor *mc, MDBX_val *key, + MDBX_val *data); +static int __must_check_result mdbx_cursor_last(MDBX_cursor *mc, MDBX_val *key, + MDBX_val *data); -static void mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, MDBX_dbi dbi, - MDBX_xcursor *mx); -static void mdbx_xcursor_init0(MDBX_cursor *mc); -static void mdbx_xcursor_init1(MDBX_cursor *mc, MDBX_node *node); -static void mdbx_xcursor_init2(MDBX_cursor *mc, MDBX_xcursor *src_mx, - int force); +static int __must_check_result mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, + MDBX_dbi dbi, MDBX_xcursor *mx); +static int __must_check_result mdbx_xcursor_init0(MDBX_cursor *mc); +static int __must_check_result mdbx_xcursor_init1(MDBX_cursor *mc, + MDBX_node *node); +static int __must_check_result mdbx_xcursor_init2(MDBX_cursor *mc, + MDBX_xcursor *src_mx, + int force); -static int mdbx_drop0(MDBX_cursor *mc, int subs); +static int __must_check_result mdbx_drop0(MDBX_cursor *mc, int subs); static MDBX_cmp_func mdbx_cmp_memn, mdbx_cmp_memnr, mdbx_cmp_int_ai, mdbx_cmp_int_a2, mdbx_cmp_int_ua; @@ -1305,13 +1322,15 @@ static void mdbx_cursor_chk(MDBX_cursor *mc) { /* Count all the pages in each DB and in the freelist and make sure * it matches the actual number of pages being used. * All named DBs must be open for a correct count. */ -static void mdbx_audit(MDBX_txn *txn) { +static int mdbx_audit(MDBX_txn *txn) { MDBX_cursor mc; MDBX_val key, data; int rc; pgno_t freecount = 0; - mdbx_cursor_init(&mc, txn, FREE_DBI, NULL); + rc = mdbx_cursor_init(&mc, txn, FREE_DBI, NULL); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; while ((rc = mdbx_cursor_get(&mc, &key, &data, MDBX_NEXT)) == 0) freecount += *(pgno_t *)data.iov_base; mdbx_tassert(txn, rc == MDBX_NOTFOUND); @@ -1321,7 +1340,9 @@ static void mdbx_audit(MDBX_txn *txn) { MDBX_xcursor mx; if (!(txn->mt_dbflags[i] & DB_VALID)) continue; - mdbx_cursor_init(&mc, txn, i, &mx); + rc = mdbx_cursor_init(&mc, txn, i, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; if (txn->mt_dbs[i].md_root == P_INVALID) continue; count += txn->mt_dbs[i].md_branch_pages + txn->mt_dbs[i].md_leaf_pages + @@ -1348,7 +1369,9 @@ static void mdbx_audit(MDBX_txn *txn) { " total: %" PRIaPGNO " next_pgno: %" PRIaPGNO "\n", txn->mt_txnid, freecount, count + NUM_METAS, freecount + count + NUM_METAS, txn->mt_next_pgno); + return MDBX_CORRUPTED; } + return MDBX_SUCCESS; } int mdbx_cmp(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a, @@ -2179,7 +2202,9 @@ static int mdbx_page_alloc(MDBX_cursor *mc, unsigned num, MDBX_page **mp, /* Prepare to fetch more and coalesce */ oldest = (flags & MDBX_LIFORECLAIM) ? mdbx_find_oldest(txn) : env->me_oldest[0]; - mdbx_cursor_init(&recur, txn, FREE_DBI, NULL); + rc = mdbx_cursor_init(&recur, txn, FREE_DBI, NULL); + if (unlikely(rc != MDBX_SUCCESS)) + goto fail; if (flags & MDBX_LIFORECLAIM) { /* Begin from oldest reader if any */ if (oldest > 2) { @@ -3458,7 +3483,9 @@ static int mdbx_freelist_save(MDBX_txn *txn) { unsigned cleanup_reclaimed_pos = 0, refill_reclaimed_pos = 0; const bool lifo = (env->me_flags & MDBX_LIFORECLAIM) != 0; - mdbx_cursor_init(&mc, txn, FREE_DBI, NULL); + rc = mdbx_cursor_init(&mc, txn, FREE_DBI, NULL); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; /* MDBX_RESERVE cancels meminit in ovpage malloc (when no WRITEMAP) */ const intptr_t clean_limit = @@ -4180,7 +4207,9 @@ int mdbx_txn_commit(MDBX_txn *txn) { MDBX_val data; data.iov_len = sizeof(MDBX_db); - mdbx_cursor_init(&mc, txn, MAIN_DBI, NULL); + rc = mdbx_cursor_init(&mc, txn, MAIN_DBI, NULL); + if (unlikely(rc != MDBX_SUCCESS)) + goto fail; for (i = CORE_DBS; i < txn->mt_numdbs; i++) { if (txn->mt_dbflags[i] & DB_DIRTY) { if (unlikely(TXN_DBI_CHANGED(txn, i))) { @@ -6390,9 +6419,11 @@ static int mdbx_page_search(MDBX_cursor *mc, MDBX_val *key, int flags) { MDBX_cursor mc2; if (unlikely(TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi))) return MDBX_BAD_DBI; - mdbx_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, NULL); + rc = mdbx_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, NULL); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; rc = mdbx_page_search(&mc2, &mc->mc_dbx->md_name, 0); - if (rc) + if (unlikely(rc != MDBX_SUCCESS)) return rc; { MDBX_val data; @@ -6576,7 +6607,9 @@ int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data) { if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED)) return MDBX_BAD_TXN; - mdbx_cursor_init(&mc, txn, dbi, &mx); + int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; return mdbx_cursor_set(&mc, key, data, MDBX_SET, &exact); } @@ -6631,7 +6664,9 @@ static int mdbx_cursor_sibling(MDBX_cursor *mc, int move_right) { return rc; } - mdbx_cursor_push(mc, mp); + rc = mdbx_cursor_push(mc, mp); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; if (!move_right) mc->mc_ki[mc->mc_top] = NUMKEYS(mp) - 1; @@ -6711,7 +6746,9 @@ skip: leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { - mdbx_xcursor_init1(mc, leaf); + rc = mdbx_xcursor_init1(mc, leaf); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; } if (data) { if (unlikely((rc = mdbx_node_read(mc, leaf, data)) != MDBX_SUCCESS)) @@ -6799,7 +6836,9 @@ static int mdbx_cursor_prev(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { - mdbx_xcursor_init1(mc, leaf); + rc = mdbx_xcursor_init1(mc, leaf); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; } if (data) { if (unlikely((rc = mdbx_node_read(mc, leaf, data)) != MDBX_SUCCESS)) @@ -6966,7 +7005,9 @@ set1: } if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { - mdbx_xcursor_init1(mc, leaf); + rc = mdbx_xcursor_init1(mc, leaf); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; } if (likely(data)) { if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { @@ -7041,8 +7082,9 @@ static int mdbx_cursor_first(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data) { if (likely(data)) { if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { - mdbx_cassert(mc, mc->mc_xcursor != nullptr); - mdbx_xcursor_init1(mc, leaf); + rc = mdbx_xcursor_init1(mc, leaf); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; rc = mdbx_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL); if (unlikely(rc)) return rc; @@ -7085,8 +7127,9 @@ static int mdbx_cursor_last(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data) { if (likely(data)) { if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { - mdbx_cassert(mc, mc->mc_xcursor != nullptr); - mdbx_xcursor_init1(mc, leaf); + rc = mdbx_xcursor_init1(mc, leaf); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; rc = mdbx_cursor_last(&mc->mc_xcursor->mx_cursor, data, NULL); if (unlikely(rc)) return rc; @@ -7139,7 +7182,9 @@ int mdbx_cursor_get(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, if (data) { if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { if (unlikely(!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED))) { - mdbx_xcursor_init1(mc, leaf); + rc = mdbx_xcursor_init1(mc, leaf); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; rc = mdbx_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL); if (unlikely(rc)) return rc; @@ -7281,7 +7326,9 @@ static int mdbx_cursor_touch(MDBX_cursor *mc) { MDBX_xcursor mcx; if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi)) return MDBX_BAD_DBI; - mdbx_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, &mcx); + rc = mdbx_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, &mcx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; rc = mdbx_page_search(&mc2, &mc->mc_dbx->md_name, MDBX_PS_MODIFY); if (unlikely(rc)) return rc; @@ -7462,7 +7509,9 @@ int mdbx_cursor_put(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, return rc2; } assert(np->mp_flags & P_LEAF); - mdbx_cursor_push(mc, np); + rc2 = mdbx_cursor_push(mc, np); + if (unlikely(rc2 != MDBX_SUCCESS)) + return rc2; mc->mc_db->md_root = np->mp_pgno; mc->mc_db->md_depth++; *mc->mc_dbflag |= DB_DIRTY; @@ -7810,7 +7859,9 @@ new_sub: ? MDBX_CURRENT | MDBX_NOOVERWRITE | MDBX_NOSPILL : MDBX_CURRENT | MDBX_NOSPILL; } else { - mdbx_xcursor_init1(mc, leaf); + rc2 = mdbx_xcursor_init1(mc, leaf); + if (unlikely(rc2 != MDBX_SUCCESS)) + return rc2; xflags = (flags & MDBX_NODUPDATA) ? MDBX_NOOVERWRITE | MDBX_NOSPILL : MDBX_NOSPILL; } @@ -7839,7 +7890,9 @@ new_sub: continue; if (m2->mc_pg[i] == mp) { if (m2->mc_ki[i] == mc->mc_ki[i]) { - mdbx_xcursor_init2(m2, mx, dupdata_flag); + rc2 = mdbx_xcursor_init2(m2, mx, dupdata_flag); + if (unlikely(rc2 != MDBX_SUCCESS)) + return rc2; } else if (!insert_key && m2->mc_ki[i] < nkeys) { XCURSOR_REFRESH(m2, mp, m2->mc_ki[i]); } @@ -8359,8 +8412,10 @@ static void mdbx_node_shrink(MDBX_page *mp, unsigned indx) { * depend only on the parent DB. * * [in] mc The main cursor whose sorted-dups cursor is to be initialized. */ -static void mdbx_xcursor_init0(MDBX_cursor *mc) { +static int mdbx_xcursor_init0(MDBX_cursor *mc) { MDBX_xcursor *mx = mc->mc_xcursor; + if (unlikely(mx == nullptr)) + return MDBX_CORRUPTED; mx->mx_cursor.mc_xcursor = NULL; mx->mx_cursor.mc_txn = mc->mc_txn; @@ -8375,6 +8430,7 @@ static void mdbx_xcursor_init0(MDBX_cursor *mc) { mx->mx_dbx.md_name.iov_base = NULL; mx->mx_dbx.md_cmp = mc->mc_dbx->md_dcmp; mx->mx_dbx.md_dcmp = NULL; + return MDBX_SUCCESS; } /* Final setup of a sorted-dups cursor. @@ -8382,8 +8438,10 @@ static void mdbx_xcursor_init0(MDBX_cursor *mc) { * [in] mc The main cursor whose sorted-dups cursor is to be initialized. * [in] node The data containing the MDBX_db record for the sorted-dup database. */ -static void mdbx_xcursor_init1(MDBX_cursor *mc, MDBX_node *node) { +static int mdbx_xcursor_init1(MDBX_cursor *mc, MDBX_node *node) { MDBX_xcursor *mx = mc->mc_xcursor; + if (unlikely(mx == nullptr)) + return MDBX_CORRUPTED; mdbx_cassert(mc, mc->mc_txn->mt_txnid >= mc->mc_txn->mt_env->me_oldest[0]); if (node->mn_flags & F_SUBDATA) { @@ -8422,6 +8480,8 @@ static void mdbx_xcursor_init1(MDBX_cursor *mc, MDBX_node *node) { sizeof(size_t)) mx->mx_dbx.md_cmp = mdbx_cmp_clong; #endif */ + + return MDBX_SUCCESS; } /* Fixup a sorted-dups cursor due to underlying update. @@ -8431,9 +8491,11 @@ static void mdbx_xcursor_init1(MDBX_cursor *mc, MDBX_node *node) { * [in] mc The main cursor whose sorted-dups cursor is to be fixed up. * [in] src_mx The xcursor of an up-to-date cursor. * [in] new_dupdata True if converting from a non-F_DUPDATA item. */ -static void mdbx_xcursor_init2(MDBX_cursor *mc, MDBX_xcursor *src_mx, - int new_dupdata) { +static int mdbx_xcursor_init2(MDBX_cursor *mc, MDBX_xcursor *src_mx, + int new_dupdata) { MDBX_xcursor *mx = mc->mc_xcursor; + if (unlikely(mx == nullptr)) + return MDBX_CORRUPTED; mdbx_cassert(mc, mc->mc_txn->mt_txnid >= mc->mc_txn->mt_env->me_oldest[0]); if (new_dupdata) { @@ -8444,17 +8506,18 @@ static void mdbx_xcursor_init2(MDBX_cursor *mc, MDBX_xcursor *src_mx, mx->mx_dbflag = DB_VALID | DB_USRVALID | DB_DUPDATA; mx->mx_dbx.md_cmp = src_mx->mx_dbx.md_cmp; } else if (!(mx->mx_cursor.mc_flags & C_INITIALIZED)) { - return; + return MDBX_SUCCESS; } mx->mx_db = src_mx->mx_db; mx->mx_cursor.mc_pg[0] = src_mx->mx_cursor.mc_pg[0]; mdbx_debug("Sub-db -%u root page %" PRIaPGNO "", mx->mx_cursor.mc_dbi, mx->mx_db.md_root); + return MDBX_SUCCESS; } /* Initialize a cursor for a given transaction and database. */ -static void mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, MDBX_dbi dbi, - MDBX_xcursor *mx) { +static int mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, MDBX_dbi dbi, + MDBX_xcursor *mx) { mc->mc_signature = MDBX_MC_SIGNATURE; mc->mc_next = NULL; mc->mc_backup = NULL; @@ -8469,16 +8532,23 @@ static void mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, MDBX_dbi dbi, mc->mc_flags = 0; mc->mc_ki[0] = 0; mc->mc_xcursor = NULL; + if (txn->mt_dbs[dbi].md_flags & MDBX_DUPSORT) { mdbx_tassert(txn, mx != NULL); mx->mx_cursor.mc_signature = MDBX_MC_SIGNATURE; mc->mc_xcursor = mx; - mdbx_xcursor_init0(mc); + int rc = mdbx_xcursor_init0(mc); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; } + mdbx_cassert(mc, mc->mc_txn->mt_txnid >= mc->mc_txn->mt_env->me_oldest[0]); + int rc = MDBX_SUCCESS; if (unlikely(*mc->mc_dbflag & DB_STALE)) { - mdbx_page_search(mc, NULL, MDBX_PS_ROOTONLY); + rc = mdbx_page_search(mc, NULL, MDBX_PS_ROOTONLY); + rc = (rc != MDBX_NOTFOUND) ? rc : MDBX_SUCCESS; } + return rc; } int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) { @@ -8507,7 +8577,11 @@ int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) { size += sizeof(MDBX_xcursor); if (likely((mc = malloc(size)) != NULL)) { - mdbx_cursor_init(mc, txn, dbi, (MDBX_xcursor *)(mc + 1)); + int rc = mdbx_cursor_init(mc, txn, dbi, (MDBX_xcursor *)(mc + 1)); + if (unlikely(rc != MDBX_SUCCESS)) { + free(mc); + return rc; + } if (txn->mt_cursors) { mc->mc_next = txn->mt_cursors[dbi]; txn->mt_cursors[dbi] = mc; @@ -8518,7 +8592,6 @@ int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) { } *ret = mc; - return MDBX_SUCCESS; } @@ -8557,8 +8630,7 @@ int mdbx_cursor_renew(MDBX_txn *txn, MDBX_cursor *mc) { if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED)) return MDBX_BAD_TXN; - mdbx_cursor_init(mc, txn, mc->mc_dbi, mc->mc_xcursor); - return MDBX_SUCCESS; + return mdbx_cursor_init(mc, txn, mc->mc_dbi, mc->mc_xcursor); } /* Return the count of duplicate data items for the current key */ @@ -9399,8 +9471,9 @@ static int mdbx_cursor_del0(MDBX_cursor *mc) { if (!(node->mn_flags & F_SUBDATA)) m3->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(node); } else { - mdbx_xcursor_init1(m3, node); - m3->mc_xcursor->mx_cursor.mc_flags |= C_DEL; + rc = mdbx_xcursor_init1(m3, node); + if (likely(rc == MDBX_SUCCESS)) + m3->mc_xcursor->mx_cursor.mc_flags |= C_DEL; } } } @@ -9446,7 +9519,9 @@ static int mdbx_del0(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, mdbx_debug("====> delete db %u key [%s], data [%s]", dbi, DKEY(key), DVAL(data)); - mdbx_cursor_init(&mc, txn, dbi, &mx); + rc = mdbx_cursor_init(&mc, txn, dbi, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; if (data) { op = MDBX_GET_BOTH; @@ -9935,11 +10010,12 @@ int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, if (unlikely(txn->mt_flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED))) return (txn->mt_flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN; - mdbx_cursor_init(&mc, txn, dbi, &mx); + int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; mc.mc_next = txn->mt_cursors[dbi]; txn->mt_cursors[dbi] = &mc; - int rc = MDBX_SUCCESS; /* LY: support for update (explicit overwrite) */ if (flags & MDBX_CURRENT) { rc = mdbx_cursor_get(&mc, key, NULL, MDBX_SET); @@ -10264,7 +10340,9 @@ static int __cold mdbx_env_compact(MDBX_env *env, mdbx_filehandle_t fd) { MDBX_cursor mc; MDBX_val key, data; - mdbx_cursor_init(&mc, txn, FREE_DBI, NULL); + rc = mdbx_cursor_init(&mc, txn, FREE_DBI, NULL); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; while ((rc = mdbx_cursor_get(&mc, &key, &data, MDBX_NEXT)) == 0) freecount += *(pgno_t *)data.iov_base; if (unlikely(rc != MDBX_NOTFOUND)) @@ -10685,8 +10763,10 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags, key.iov_len = len; key.iov_base = (void *)table_name; MDBX_cursor mc; - mdbx_cursor_init(&mc, txn, MAIN_DBI, NULL); - int rc = mdbx_cursor_set(&mc, &key, &data, MDBX_SET, &exact); + int rc = mdbx_cursor_init(&mc, txn, MAIN_DBI, NULL); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + rc = mdbx_cursor_set(&mc, &key, &data, MDBX_SET, &exact); if (unlikely(rc != MDBX_SUCCESS)) { if (rc != MDBX_NOTFOUND || !(user_flags & MDBX_CREATE)) return rc; @@ -10822,7 +10902,9 @@ int __cold mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *arg, MDBX_cursor mc; MDBX_xcursor mx; /* Stale, must read the DB's root. cursor_init does it for us. */ - mdbx_cursor_init(&mc, txn, dbi, &mx); + int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; } return mdbx_stat0(txn->mt_env, &txn->mt_dbs[dbi], arg); } @@ -10926,7 +11008,9 @@ static int mdbx_drop0(MDBX_cursor *mc, int subs) { if (!mc->mc_db->md_overflow_pages && !subs) break; } else if (subs && (ni->mn_flags & F_SUBDATA)) { - mdbx_xcursor_init1(mc, ni); + rc = mdbx_xcursor_init1(mc, ni); + if (unlikely(rc != MDBX_SUCCESS)) + goto done; rc = mdbx_drop0(&mc->mc_xcursor->mx_cursor, 0); if (unlikely(rc)) goto done; @@ -11762,11 +11846,12 @@ int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *new_data, MDBX_cursor mc; MDBX_xcursor mx; - mdbx_cursor_init(&mc, txn, dbi, &mx); + int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; mc.mc_next = txn->mt_cursors[dbi]; txn->mt_cursors[dbi] = &mc; - int rc; MDBX_val present_key = *key; if (F_ISSET(flags, MDBX_CURRENT | MDBX_NOOVERWRITE)) { /* в old_data значение для выбора конкретного дубликата */ @@ -11889,10 +11974,12 @@ int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, MDBX_cursor mc; MDBX_xcursor mx; - mdbx_cursor_init(&mc, txn, dbi, &mx); + int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; int exact = 0; - int rc = mdbx_cursor_set(&mc, key, data, MDBX_SET_KEY, &exact); + rc = mdbx_cursor_set(&mc, key, data, MDBX_SET_KEY, &exact); if (unlikely(rc != MDBX_SUCCESS)) { if (rc == MDBX_NOTFOUND && values_count) *values_count = 0; @@ -12136,8 +12223,10 @@ int mdbx_set_attr(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, MDBX_cursor mc; MDBX_xcursor mx; MDBX_val old_data; - mdbx_cursor_init(&mc, txn, dbi, &mx); - int rc = mdbx_cursor_set(&mc, key, &old_data, MDBX_SET, NULL); + int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + rc = mdbx_cursor_set(&mc, key, &old_data, MDBX_SET, NULL); if (unlikely(rc != MDBX_SUCCESS)) { if (rc == MDBX_NOTFOUND && data) { mc.mc_next = txn->mt_cursors[dbi];