diff options
Diffstat (limited to 'libs/libmdbx/src')
53 files changed, 898 insertions, 216 deletions
diff --git a/libs/libmdbx/src/COPYRIGHT b/libs/libmdbx/src/COPYRIGHT index 7c2fd24c78..46e0961046 100644 --- a/libs/libmdbx/src/COPYRIGHT +++ b/libs/libmdbx/src/COPYRIGHT @@ -1,4 +1,4 @@ -Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>. +Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru>. Copyright 2011-2015 Howard Chu, Symas Corp. Copyright 2015,2016 Peter-Service R&D LLC. All rights reserved. diff --git a/libs/libmdbx/src/README-RU.md b/libs/libmdbx/src/README-RU.md index 5dd062c1a6..97888cbf88 100644 --- a/libs/libmdbx/src/README-RU.md +++ b/libs/libmdbx/src/README-RU.md @@ -12,7 +12,7 @@ and [by Yandex](https://translate.yandex.ru/translate?url=https%3A%2F%2Fgithub.c ### Project Status -**Сейчас MDBX _активно перерабатывается_** и к середине 2018 ожидается +**Сейчас MDBX _активно перерабатывается_** предстоит большое изменение как API, так и формата базы данных. К сожалению, обновление приведет к потере совместимости с предыдущими версиями. @@ -171,31 +171,18 @@ Amplification Factor) и RAF (Read Amplification Factor) также Olog(N). Доработки и усовершенствования относительно LMDB ================================================ -1. Утилита `mdbx_chk` для проверки целостности структуры БД. - -2. Автоматическое динамическое управление размером БД согласно +1. Автоматическое динамическое управление размером БД согласно параметрам задаваемым функцией `mdbx_env_set_geometry()`, включая шаг приращения и порог уменьшения размера БД, а также выбор размера страницы. Соответственно, это позволяет снизить фрагментированность файла БД на диске и освободить место, в том числе в **Windows**. -3. Автоматическая без-затратная компактификация БД путем возврата +2. Автоматическая без-затратная компактификация БД путем возврата освобождающихся страниц в область нераспределенного резерва в конце файла данных. При этом уменьшается количество страниц находящихся в памяти и участвующих в в обмене с диском. -4. Поддержка ключей и значений нулевой длины, включая сортированные -дубликаты. - -5. Возможность связать с каждой завершаемой транзакцией до 3 -дополнительных маркеров посредством `mdbx_canary_put()`, и прочитать их -в транзакции чтения посредством `mdbx_canary_get()`. - -6. Возможность посредством `mdbx_replace()` обновить или удалить запись -с получением предыдущего значения данных, а также адресно изменить -конкретное multi-значение. - -7. Режим `LIFO RECLAIM`. +3. Режим `LIFO RECLAIM`. Для повторного использования выбираются не самые старые, а самые новые страницы из доступных. За счет этого цикл @@ -209,9 +196,27 @@ Amplification Factor) и RAF (Read Amplification Factor) также Olog(N). многократное увеличение производительности по записи (обновлению данных). -8. Генерация последовательностей посредством `mdbx_dbi_sequence()`. +4. Быстрая оценка количества элементов попадающих в запрашиваемый +диапазон значений ключа посредством функций `mdbx_estimate_range()`, +`mdbx_estimate_move()` и `mdbx_estimate_distance()` для выбора +оптимального плана выполнения запроса. + +5. Утилита `mdbx_chk` для проверки целостности структуры БД. + +6. Поддержка ключей и значений нулевой длины, включая сортированные +дубликаты. + +7. Возможность связать с каждой завершаемой транзакцией до 3 +дополнительных маркеров посредством `mdbx_canary_put()`, и прочитать их +в транзакции чтения посредством `mdbx_canary_get()`. + +8. Возможность посредством `mdbx_replace()` обновить или удалить запись +с получением предыдущего значения данных, а также адресно изменить +конкретное multi-значение. + +9. Генерация последовательностей посредством `mdbx_dbi_sequence()`. -9. Обработчик `OOM-KICK`. +10. Обработчик `OOM-KICK`. Посредством `mdbx_env_set_oomfunc()` может быть установлен внешний обработчик (callback), который будет вызван при @@ -232,83 +237,83 @@ Amplification Factor) и RAF (Read Amplification Factor) также Olog(N). * прервать текущую операцию изменения данных с возвратом кода ошибки. -10. Возможность открыть БД в эксклюзивном режиме посредством флага -`MDBX_EXCLUSIVE`. +11. Возможность открыть БД в эксклюзивном режиме посредством флага + `MDBX_EXCLUSIVE`, в том числе на сетевом носителе. -11. Возможность получить отставание текущей транзакции чтения от +12. Возможность получить отставание текущей транзакции чтения от последней версии данных в БД посредством `mdbx_txn_straggler()`. -12. Возможность явно запросить обновление существующей записи, без +13. Возможность явно запросить обновление существующей записи, без создания новой посредством флажка `MDBX_CURRENT` для `mdbx_put()`. -13. Исправленный вариант `mdbx_cursor_count()`, возвращающий корректное +14. Исправленный вариант `mdbx_cursor_count()`, возвращающий корректное количество дубликатов для всех типов таблиц и любого положения курсора. -14. Возможность получить посредством `mdbx_env_info()` дополнительную +15. Возможность получить посредством `mdbx_env_info()` дополнительную информацию, включая номер самой старой версии БД (снимка данных), который используется одним из читателей. -15. Функция `mdbx_del()` не игнорирует дополнительный (уточняющий) +16. Функция `mdbx_del()` не игнорирует дополнительный (уточняющий) аргумент `data` для таблиц без дубликатов (без флажка `MDBX_DUPSORT`), а при его ненулевом значении всегда использует его для сверки с удаляемой записью. -16. Возможность открыть dbi-таблицу, одновременно с установкой +17. Возможность открыть dbi-таблицу, одновременно с установкой компараторов для ключей и данных, посредством `mdbx_dbi_open_ex()`. -17. Возможность посредством `mdbx_is_dirty()` определить находятся ли +18. Возможность посредством `mdbx_is_dirty()` определить находятся ли некоторый ключ или данные в "грязной" странице БД. Таким образом, избегая лишнего копирования данных перед выполнением модифицирующих операций (значения, размещенные в "грязных" страницах, могут быть перезаписаны при изменениях, иначе они будут неизменны). -18. Корректное обновление текущей записи, в том числе сортированного +19. Корректное обновление текущей записи, в том числе сортированного дубликата, при использовании режима `MDBX_CURRENT` в `mdbx_cursor_put()`. -19. Возможность узнать есть ли за текущей позицией курсора строка данных +20. Возможность узнать есть ли за текущей позицией курсора строка данных посредством `mdbx_cursor_eof()`. -20. Дополнительный код ошибки `MDBX_EMULTIVAL`, который возвращается из +21. Дополнительный код ошибки `MDBX_EMULTIVAL`, который возвращается из `mdbx_put()` и `mdbx_replace()` при попытке выполнить неоднозначное обновление или удаления одного из нескольких значений с одним ключом. -21. Возможность посредством `mdbx_get_ex()` получить значение по +22. Возможность посредством `mdbx_get_ex()` получить значение по заданному ключу, одновременно с количеством дубликатов. -22. Наличие функций `mdbx_cursor_on_first()` и `mdbx_cursor_on_last()`, +23. Наличие функций `mdbx_cursor_on_first()` и `mdbx_cursor_on_last()`, которые позволяют быстро выяснить стоит ли курсор на первой/последней позиции. -23. Возможность автоматического формирования контрольных точек (сброса +24. Возможность автоматического формирования контрольных точек (сброса данных на диск) при накоплении заданного объёма изменений, устанавливаемого функцией `mdbx_env_set_syncbytes()`. -24. Управление отладкой и получение отладочных сообщений посредством +25. Управление отладкой и получение отладочных сообщений посредством `mdbx_setup_debug()`. -25. Функция `mdbx_env_pgwalk()` для обхода всех страниц БД. +26. Функция `mdbx_env_pgwalk()` для обхода всех страниц БД. -26. Три мета-страницы вместо двух, что позволяет гарантированно +27. Три мета-страницы вместо двух, что позволяет гарантированно консистентно обновлять слабые контрольные точки фиксации без риска повредить крайнюю сильную точку фиксации. -27. Гарантия сохранности БД в режиме `WRITEMAP+MAPSYNC`. +28. Гарантия сохранности БД в режиме `WRITEMAP+MAPSYNC`. > В текущей версии _libmdbx_ вам предоставляется выбор между безопасным > режимом (по умолчанию) асинхронной фиксации, и режимом `UTTERLY_NOSYNC` > когда при системной аварии есть шанс полного разрушения БД как в LMDB. > Для подробностей смотрите раздел > [Сохранность данных в режиме асинхронной фиксации](#Сохранность-данных-в-режиме-асинхронной-фиксации). -28. Возможность закрыть БД в "грязном" состоянии (без сброса данных и +29. Возможность закрыть БД в "грязном" состоянии (без сброса данных и формирования сильной точки фиксации) посредством `mdbx_env_close_ex()`. -29. При завершении читающих транзакций, открытые в них DBI-хендлы не +30. При завершении читающих транзакций, открытые в них DBI-хендлы не закрываются и не теряются при завершении таких транзакций посредством `mdbx_txn_abort()` или `mdbx_txn_reset()`. Что позволяет избавится от ряда сложно обнаруживаемых ошибок. -30. Все курсоры, как в транзакциях только для чтения, так и в пишущих, +31. Все курсоры, как в транзакциях только для чтения, так и в пишущих, могут быть переиспользованы посредством `mdbx_cursor_renew()` и ДОЛЖНЫ ОСВОБОЖДАТЬСЯ ЯВНО. > @@ -682,11 +687,11 @@ $ objdump -f -h -j .text libmdbx.so libmdbx.so: file format elf64-x86-64 architecture: i386:x86-64, flags 0x00000150: HAS_SYMS, DYNAMIC, D_PAGED -start address 0x000030e0 +start address 0x0000000000003870 Sections: Idx Name Size VMA LMA File off Algn - 11 .text 00014d84 00000000000030e0 00000000000030e0 000030e0 2**4 + 11 .text 000173d4 0000000000003870 0000000000003870 00003870 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE ``` diff --git a/libs/libmdbx/src/README.md b/libs/libmdbx/src/README.md index a2a6ab7ac3..78cc906763 100644 --- a/libs/libmdbx/src/README.md +++ b/libs/libmdbx/src/README.md @@ -170,30 +170,18 @@ regular maintenance. Backups can be made on the fly on working DB Improvements over LMDB ====================== -1. `mdbx_chk` tool for DB integrity check. - -2. Automatic dynamic DB size management according to the parameters -specified by `mdbx_env_set_geometry()` function. Including including +1. Automatic dynamic DB size management according to the parameters +specified by `mdbx_env_set_geometry()` function. Including growth step and truncation threshold, as well as the choice of page size. -3. Automatic returning of freed pages into unallocated space at the end -of database file with optionally automatic shrinking it. This reduces +2. Automatic returning of freed pages into unallocated space at the end +of database file, with optionally automatic shrinking it. This reduces amount of pages resides in RAM and circulated in disk I/O. In fact _libmdbx_ constantly performs DB compactification, without spending additional resources for that. -4. Support for keys and values of zero length, including sorted -duplicates. - -5. Ability to assign up to 3 markers to commiting transaction with -`mdbx_canary_put()` and then get them in read transaction by -`mdbx_canary_get()`. - -6. Ability to update or delete record and get previous value via -`mdbx_replace()` Also can update specific multi-value. - -7. `LIFO RECLAIM` mode: +3. `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 of total @@ -204,9 +192,25 @@ duplicates. [BBWC](https://en.wikipedia.org/wiki/Disk_buffer#Write_acceleration) this may greatly improve write performance. -8. Sequence generation via `mdbx_dbi_sequence()`. +4. Fast estimation of range query result size via functions `mdbx_estimate_range()`, +`mdbx_estimate_move()` and `mdbx_estimate_distance()`. E.g. for selection the +optimal query execution plan. + +5. `mdbx_chk` tool for DB integrity check. + +6. Support for keys and values of zero length, including sorted +duplicates. + +7. Ability to assign up to 3 markers to commiting transaction with +`mdbx_canary_put()` and then get them in read transaction by +`mdbx_canary_get()`. + +8. Ability to update or delete record and get previous value via +`mdbx_replace()`. Also can update specific multi-value. + +9. Sequence generation via `mdbx_dbi_sequence()`. -9. `OOM-KICK` callback. +10. `OOM-KICK` callback. `mdbx_env_set_oomfunc()` allows to set a callback, which will be called in the event of DB space exhausting during long-time read transaction in @@ -224,75 +228,75 @@ duplicates. * abort current write transaction with returning error code. -10. Ability to open DB in exclusive mode with `MDBX_EXCLUSIVE` flag. +11. Ability to open DB in exclusive mode with `MDBX_EXCLUSIVE` flag. -11. Ability to get how far current read-only snapshot is from latest +12. Ability to get how far current read-only snapshot is from latest version of the DB by `mdbx_txn_straggler()`. -12. Ability to explicitly request update of present record without +13. Ability to explicitly request update of present record without creating new record. Implemented as `MDBX_CURRENT` flag for `mdbx_put()`. -13. Fixed `mdbx_cursor_count()`, which returns correct count of +14. Fixed `mdbx_cursor_count()`, which returns correct count of duplicated for all table types and any cursor position. -14. `mdbx_env_info()` to getting additional info, including number of +15. `mdbx_env_info()` to getting additional info, including number of the oldest snapshot of DB, which is used by one of the readers. -15. `mdbx_del()` doesn't ignore additional argument (specifier) `data` +16. `mdbx_del()` doesn't ignore additional argument (specifier) `data` for tables without duplicates (without flag `MDBX_DUPSORT`), if `data` is not null then always uses it to verify record, which is being deleted. -16. Ability to open dbi-table with simultaneous setup of comparators for +17. Ability to open dbi-table with simultaneous setup of comparators for keys and values, via `mdbx_dbi_open_ex()`. -17. `mdbx_is_dirty()`to find out if key or value is on dirty page, that +18. `mdbx_is_dirty()`to find out if key or value is on dirty page, that useful to avoid copy-out before updates. -18. Correct update of current record in `MDBX_CURRENT` mode of +19. Correct update of current record in `MDBX_CURRENT` mode of `mdbx_cursor_put()`, including sorted duplicated. -19. Check if there is a row with data after current cursor position via +20. Check if there is a row with data after current cursor position via `mdbx_cursor_eof()`. -20. Additional error code `MDBX_EMULTIVAL`, which is returned by +21. Additional error code `MDBX_EMULTIVAL`, which is returned by `mdbx_put()` and `mdbx_replace()` in case is ambiguous update or delete. -21. Ability to get value by key and duplicates count by `mdbx_get_ex()`. +22. Ability to get value by key and duplicates count by `mdbx_get_ex()`. -22. Functions `mdbx_cursor_on_first()` and `mdbx_cursor_on_last()`, +23. Functions `mdbx_cursor_on_first()` and `mdbx_cursor_on_last()`, which allows to know if cursor is currently on first or last position respectively. -23. Automatic creation of synchronization points (flush changes to +24. Automatic creation of synchronization points (flush changes to persistent storage) when changes reach set threshold (threshold can be set by `mdbx_env_set_syncbytes()`). -24. Control over debugging and receiving of debugging messages via +25. Control over debugging and receiving of debugging messages via `mdbx_setup_debug()`. -25. Function `mdbx_env_pgwalk()` for page-walking all pages in DB. +26. Function `mdbx_env_pgwalk()` for page-walking all pages in DB. -26. Three meta-pages instead of two, this allows to guarantee +27. Three meta-pages instead of two, this allows to guarantee consistently update weak sync-points without risking to corrupt last steady sync-point. -27. Guarantee of DB integrity in `WRITEMAP+MAPSYNC` mode: +28. Guarantee of DB integrity in `WRITEMAP+MAPSYNC` mode: > Current _libmdbx_ gives a choice of safe async-write mode (default) > and `UTTERLY_NOSYNC` mode which may result in full > DB corruption during system crash as with LMDB. For details see > [Data safety in async-write mode](#data-safety-in-async-write-mode). -28. Ability to close DB in "dirty" state (without data flush and +29. Ability to close DB in "dirty" state (without data flush and creation of steady synchronization point) via `mdbx_env_close_ex()`. -29. If read transaction is aborted via `mdbx_txn_abort()` or +30. If read transaction is aborted via `mdbx_txn_abort()` or `mdbx_txn_reset()` then DBI-handles, which were opened in it, aren't closed or deleted. This allows to avoid several types of hard-to-debug errors. -30. All cursors in all read and write transactions can be reused by +31. All cursors in all read and write transactions can be reused by `mdbx_cursor_renew()` and MUST be freed explicitly. > ## Caution, please pay attention! > @@ -594,11 +598,11 @@ $ objdump -f -h -j .text libmdbx.so libmdbx.so: file format elf64-x86-64 architecture: i386:x86-64, flags 0x00000150: HAS_SYMS, DYNAMIC, D_PAGED -start address 0x000030e0 +start address 0x0000000000003870 Sections: Idx Name Size VMA LMA File off Algn - 11 .text 00014d84 00000000000030e0 00000000000030e0 000030e0 2**4 + 11 .text 000173d4 0000000000003870 0000000000003870 00003870 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE ``` diff --git a/libs/libmdbx/src/dll.vcxproj b/libs/libmdbx/src/dll.vcxproj index 63a1c766d6..c0743890ce 100644 --- a/libs/libmdbx/src/dll.vcxproj +++ b/libs/libmdbx/src/dll.vcxproj @@ -21,6 +21,7 @@ <PropertyGroup Label="Globals"> <ProjectGuid>{6D19209B-ECE7-4B9C-941C-0AA2B484F199}</ProjectGuid> <Keyword>Win32Proj</Keyword> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> diff --git a/libs/libmdbx/src/libmdbx.files b/libs/libmdbx/src/libmdbx.files index 653b03970e..76ea29fb5a 100644 --- a/libs/libmdbx/src/libmdbx.files +++ b/libs/libmdbx/src/libmdbx.files @@ -3,6 +3,7 @@ README-RU.md pcrf_test/CMakeLists.txt src/tools/CMakeLists.txt test/CMakeLists.txt +test/append.cc test/copy.cc tutorial/CMakeLists.txt tutorial/sample-mdbx.c diff --git a/libs/libmdbx/src/mdbx.h b/libs/libmdbx/src/mdbx.h index c42f62aafa..3b76bacc7d 100644 --- a/libs/libmdbx/src/mdbx.h +++ b/libs/libmdbx/src/mdbx.h @@ -1,6 +1,6 @@ /* LICENSE AND COPYRUSTING ***************************************************** * - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -60,8 +60,8 @@ /* IMPENDING CHANGES WARNING *************************************************** * - * MDBX is under active development, database format and API aren't stable - * at least until 2018Q2. New version won't be backwards compatible. Main focus + * MDBX is under active non-public development, database format and API + * will be refined. New version won't be backwards compatible. Main focus * of the rework is to provide clear and robust API and new features. * ******************************************************************************/ @@ -615,7 +615,7 @@ LIBMDBX_API int mdbx_env_create(MDBX_env **penv); * - MDBX_NOTLS * Don't use Thread-Local Storage. Tie reader locktable slots to * MDBX_txn objects instead of to threads. I.e. mdbx_txn_reset() keeps - * the slot reseved for the MDBX_txn object. A thread may use parallel + * the slot reserved for the MDBX_txn object. A thread may use parallel * read-only transactions. A read-only transaction may span threads if * the user synchronizes its use. Applications that multiplex many * user threads over individual OS threads need this option. Such an @@ -1700,6 +1700,53 @@ LIBMDBX_API int mdbx_cursor_on_first(MDBX_cursor *mc); /* Returns: MDBX_RESULT_TRUE, MDBX_RESULT_FALSE or Error code. */ LIBMDBX_API int mdbx_cursor_on_last(MDBX_cursor *mc); +/* Estimates the distance between cursors as the number of elements. + * Both cursors must be initialized for the same DBI. + * + * [in] cursor_a The first cursor for estimation. + * [in] cursor_b The second cursor for estimation. + * [out] distance_items A pointer to store estimated distance value, + * i.e. *distance_items = distance(a - b). + * + * Returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_estimate_distance(const MDBX_cursor *first, + const MDBX_cursor *last, + ptrdiff_t *distance_items); + +/* Estimates the move distance, i.e. between the current cursor position and + * next position after the specified move-operation with given key and data. + * Current cursor position and state are preserved. + * + * [in] cursor Cursor for estimation. + * [in,out] key The key for a retrieved item. + * [in,out] data The data of a retrieved item. + * [in] op A cursor operation MDBX_cursor_op. + * [out] distance_items A pointer to store estimated move distance + * as the number of elements. + * + * Returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_estimate_move(const MDBX_cursor *cursor, MDBX_val *key, + MDBX_val *data, MDBX_cursor_op move_op, + ptrdiff_t *distance_items); + +/* Estimates the size of a range in the number of elements. + * + * [in] txn A transaction handle returned by mdbx_txn_begin(). + * [in] dbi A database handle returned by mdbx_dbi_open(). + * [in] begin_key The key of range beginning or NULL for explicit FIRST. + * [in] begin_data Optional additional data to seeking among sorted + * duplicates. Only for MDBX_DUPSORT, NULL otherwise. + * [in] end_key The key of range ending or NULL for explicit LAST. + * [in] end_data Optional additional data to seeking among sorted + * duplicates. Only for MDBX_DUPSORT, NULL otherwise. + * [out] distance_items A pointer to store range estimation result. + * + * Returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, + MDBX_val *begin_key, MDBX_val *begin_data, + MDBX_val *end_key, MDBX_val *end_data, + ptrdiff_t *size_items); + LIBMDBX_API int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *new_data, MDBX_val *old_data, unsigned flags); diff --git a/libs/libmdbx/src/src/bits.h b/libs/libmdbx/src/src/bits.h index 3d3e392180..3325c327ae 100644 --- a/libs/libmdbx/src/src/bits.h +++ b/libs/libmdbx/src/src/bits.h @@ -1,5 +1,5 @@ /* - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -29,6 +29,12 @@ #ifndef _GNU_SOURCE # define _GNU_SOURCE 1 #endif +#ifndef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 200112L +#endif +#ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 500 +#endif #ifndef _FILE_OFFSET_BITS # define _FILE_OFFSET_BITS 64 #endif diff --git a/libs/libmdbx/src/src/defs.h b/libs/libmdbx/src/src/defs.h index fb60b310ef..024322a1f2 100644 --- a/libs/libmdbx/src/src/defs.h +++ b/libs/libmdbx/src/src/defs.h @@ -1,5 +1,5 @@ /* - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/src/lck-posix.c b/libs/libmdbx/src/src/lck-posix.c index 2633d44791..e8d4539a1b 100644 --- a/libs/libmdbx/src/src/lck-posix.c +++ b/libs/libmdbx/src/src/lck-posix.c @@ -1,5 +1,5 @@ /* - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -20,8 +20,8 @@ #ifndef MDBX_USE_ROBUST /* Howard Chu: Android currently lacks Robust Mutex support */ #if defined(EOWNERDEAD) && \ - !defined(ANDROID) /* LY: glibc before 2.10 has a troubles with Robust \ - Mutex too. */ \ + !defined(__ANDROID__) /* LY: glibc before 2.10 has a troubles \ + with Robust Mutex too. */ \ && __GLIBC_PREREQ(2, 10) #define MDBX_USE_ROBUST 1 #else diff --git a/libs/libmdbx/src/src/lck-windows.c b/libs/libmdbx/src/src/lck-windows.c index 24fa09965c..822ba9c293 100644 --- a/libs/libmdbx/src/src/lck-windows.c +++ b/libs/libmdbx/src/src/lck-windows.c @@ -1,5 +1,5 @@ /* - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/src/mdbx.c b/libs/libmdbx/src/src/mdbx.c index 3203d30a30..84b2c1b125 100644 --- a/libs/libmdbx/src/src/mdbx.c +++ b/libs/libmdbx/src/src/mdbx.c @@ -1,5 +1,5 @@ /* - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -406,7 +406,7 @@ __cold void mdbx_rthc_global_init(void) { } /* dtor called for thread, i.e. for all mdbx's environment objects */ -void mdbx_rthc_thread_dtor(void *ptr) { +__cold void mdbx_rthc_thread_dtor(void *ptr) { mdbx_rthc_lock(); mdbx_trace(">> pid %d, thread 0x%" PRIxPTR ", rthc %p", mdbx_getpid(), (uintptr_t)mdbx_thread_self(), ptr); @@ -2214,8 +2214,8 @@ static int __must_check_result mdbx_page_dirty(MDBX_txn *txn, MDBX_page *mp) { return MDBX_SUCCESS; } -static int mdbx_mapresize(MDBX_env *env, const pgno_t size_pgno, - const pgno_t limit_pgno) { +__cold static int mdbx_mapresize(MDBX_env *env, const pgno_t size_pgno, + const pgno_t limit_pgno) { #ifdef USE_VALGRIND const size_t prev_mapsize = env->me_mapsize; void *const prev_mapaddr = env->me_map; @@ -2770,7 +2770,8 @@ done: * [in] dst page to copy into * [in] src page to copy from * [in] psize size of a page */ -static void mdbx_page_copy(MDBX_page *dst, MDBX_page *src, unsigned psize) { +__hot static void mdbx_page_copy(MDBX_page *dst, MDBX_page *src, + unsigned psize) { STATIC_ASSERT(UINT16_MAX > MAX_PAGESIZE - PAGEHDRSZ); STATIC_ASSERT(MIN_PAGESIZE > PAGEHDRSZ + NODESIZE * 42); enum { Align = sizeof(pgno_t) }; @@ -2797,8 +2798,9 @@ static void mdbx_page_copy(MDBX_page *dst, MDBX_page *src, unsigned psize) { * [in] mp the page being referenced. It must not be dirty. * [out] ret the writable page, if any. * ret is unchanged if mp wasn't spilled. */ -static int __must_check_result mdbx_page_unspill(MDBX_txn *txn, MDBX_page *mp, - MDBX_page **ret) { +__hot static int __must_check_result mdbx_page_unspill(MDBX_txn *txn, + MDBX_page *mp, + MDBX_page **ret) { MDBX_env *env = txn->mt_env; const MDBX_txn *tx2; unsigned x; @@ -2856,7 +2858,7 @@ static int __must_check_result mdbx_page_unspill(MDBX_txn *txn, MDBX_page *mp, * [in] mc cursor pointing to the page to be touched * * Returns 0 on success, non-zero on failure. */ -static int mdbx_page_touch(MDBX_cursor *mc) { +__hot static int mdbx_page_touch(MDBX_cursor *mc) { MDBX_page *mp = mc->mc_pg[mc->mc_top], *np; MDBX_txn *txn = mc->mc_txn; MDBX_cursor *m2, *m3; @@ -2963,7 +2965,7 @@ fail: return rc; } -static int mdbx_env_sync_ex(MDBX_env *env, int force, int nonblock) { +__cold static int mdbx_env_sync_ex(MDBX_env *env, int force, int nonblock) { if (unlikely(!env)) return MDBX_EINVAL; @@ -3034,7 +3036,7 @@ static int mdbx_env_sync_ex(MDBX_env *env, int force, int nonblock) { return MDBX_SUCCESS; } -int mdbx_env_sync(MDBX_env *env, int force) { +__cold int mdbx_env_sync(MDBX_env *env, int force) { return mdbx_env_sync_ex(env, force, false); } @@ -5127,7 +5129,7 @@ static int __cold mdbx_read_header(MDBX_env *env, MDBX_meta *meta, /* LY: check and silently put mm_geo.now into [geo.lower...geo.upper]. * - * Copy-with-compaction by previous version of libmfbx could produce DB-file + * Copy-with-compaction by previous version of libmdbx could produce DB-file * less than meta.geo.lower bound, in case actual filling is low or no data * at all. This is not a problem as there is no damage or loss of data. * Therefore it is better not to consider such situation as an error, but @@ -5643,11 +5645,10 @@ static int __cold mdbx_env_map(MDBX_env *env, size_t usedsize) { return MDBX_SUCCESS; } -LIBMDBX_API int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, - intptr_t size_now, intptr_t size_upper, - intptr_t growth_step, - intptr_t shrink_threshold, - intptr_t pagesize) { +__cold LIBMDBX_API int +mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t size_now, + intptr_t size_upper, intptr_t growth_step, + intptr_t shrink_threshold, intptr_t pagesize) { if (unlikely(!env)) return MDBX_EINVAL; @@ -5723,25 +5724,32 @@ LIBMDBX_API int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, } } + if (pagesize == 0) + pagesize = MIN_PAGESIZE; + else if (pagesize == INTPTR_MAX) + pagesize = MAX_PAGESIZE; + if (pagesize < (intptr_t)MIN_PAGESIZE || pagesize > (intptr_t)MAX_PAGESIZE || !mdbx_is_power2(pagesize)) { rc = MDBX_EINVAL; goto bailout; } - if (size_lower < 0) { + if (size_lower <= 0) { size_lower = MIN_MAPSIZE; if (MIN_MAPSIZE / pagesize < MIN_PAGENO) size_lower = MIN_PAGENO * pagesize; } - if (size_now < 0) { + if (size_now <= 0) { size_now = DEFAULT_MAPSIZE; if (size_now < size_lower) size_now = size_lower; + if (size_upper >= size_lower && size_now > size_upper) + size_now = size_upper; } - if (size_upper < 0) { + if (size_upper <= 0) { if ((size_t)size_now >= MAX_MAPSIZE / 2) size_upper = MAX_MAPSIZE; else if (MAX_MAPSIZE != MAX_MAPSIZE32 && @@ -6489,8 +6497,10 @@ int __cold mdbx_env_open(MDBX_env *env, const char *path, unsigned flags, int oflags; if (F_ISSET(flags, MDBX_RDONLY)) oflags = O_RDONLY; - else + else if (mode != 0) oflags = O_RDWR | O_CREAT; + else + oflags = O_RDWR; rc = mdbx_openfile(dxb_pathname, oflags, mode, &env->me_fd, (env->me_flags & MDBX_EXCLUSIVE) ? true : false); @@ -6711,7 +6721,9 @@ int __cold mdbx_env_close_ex(MDBX_env *env, int dont_sync) { return rc; } -int mdbx_env_close(MDBX_env *env) { return mdbx_env_close_ex(env, false); } +__cold int mdbx_env_close(MDBX_env *env) { + return mdbx_env_close_ex(env, false); +} /* Compare two items pointing at aligned unsigned int's. */ static int __hot mdbx_cmp_int_ai(const MDBX_val *a, const MDBX_val *b) { @@ -6993,8 +7005,8 @@ static int mdbx_cursor_push(MDBX_cursor *mc, MDBX_page *mp) { * 0=mapped page. * * Returns 0 on success, non-zero on failure. */ -static int mdbx_page_get(MDBX_cursor *mc, pgno_t pgno, MDBX_page **ret, - int *lvl) { +__hot static int mdbx_page_get(MDBX_cursor *mc, pgno_t pgno, MDBX_page **ret, + int *lvl) { MDBX_txn *txn = mc->mc_txn; MDBX_env *env = txn->mt_env; MDBX_page *p = NULL; @@ -7062,7 +7074,8 @@ done: /* Finish mdbx_page_search() / mdbx_page_search_lowest(). * The cursor is at the root page, set up the rest of it. */ -static int mdbx_page_search_root(MDBX_cursor *mc, MDBX_val *key, int flags) { +__hot static int mdbx_page_search_root(MDBX_cursor *mc, MDBX_val *key, + int flags) { MDBX_page *mp = mc->mc_pg[mc->mc_top]; int rc; DKBUF; @@ -7145,7 +7158,7 @@ static int mdbx_page_search_root(MDBX_cursor *mc, MDBX_val *key, int flags) { * before calling mdbx_page_search_root(), because the callers * are all in situations where the current page is known to * be underfilled. */ -static int mdbx_page_search_lowest(MDBX_cursor *mc) { +__hot static int mdbx_page_search_lowest(MDBX_cursor *mc) { MDBX_page *mp = mc->mc_pg[mc->mc_top]; mdbx_cassert(mc, IS_BRANCH(mp)); MDBX_node *node = NODEPTR(mp, 0); @@ -7174,7 +7187,7 @@ static int mdbx_page_search_lowest(MDBX_cursor *mc) { * lookups. * * Returns 0 on success, non-zero on failure. */ -static int mdbx_page_search(MDBX_cursor *mc, MDBX_val *key, int flags) { +__hot static int mdbx_page_search(MDBX_cursor *mc, MDBX_val *key, int flags) { int rc; pgno_t root; @@ -7689,8 +7702,8 @@ static int mdbx_cursor_prev(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, } /* Set the cursor on a specific data item. */ -static int mdbx_cursor_set(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, - MDBX_cursor_op op, int *exactp) { +__hot static int mdbx_cursor_set(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, + MDBX_cursor_op op, int *exactp) { int rc; MDBX_page *mp; MDBX_node *leaf = NULL; @@ -8300,7 +8313,7 @@ int mdbx_cursor_put(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, if (rc > 0) { rc = MDBX_NOTFOUND; mc->mc_ki[mc->mc_top]++; - } else { + } else if (unlikely(rc < 0 || (flags & MDBX_APPENDDUP) == 0)) { /* new key is <= last key */ rc = MDBX_EKEYMISMATCH; } @@ -10882,7 +10895,7 @@ static int mdbx_page_split(MDBX_cursor *mc, const MDBX_val *newkey, * This yields better packing during sequential inserts. */ int dir; - if (nkeys < 20 || nsize > pmax / 16 || newindx >= nkeys) { + if (nkeys < 32 || nsize > pmax / 16 || newindx >= nkeys) { /* Find split point */ psize = 0; if (newindx <= split_indx || newindx >= nkeys) { @@ -13159,11 +13172,362 @@ int mdbx_cursor_eof(MDBX_cursor *mc) { return MDBX_RESULT_FALSE; } +//------------------------------------------------------------------------------ + +struct diff_result { + ptrdiff_t diff; + int level; + int root_nkeys; +}; + +/* calculates: r = x - y */ +__hot static int cursor_diff(const MDBX_cursor *const __restrict x, + const MDBX_cursor *const __restrict y, + struct diff_result *const __restrict r) { + r->diff = 0; + r->level = 0; + r->root_nkeys = 0; + + if (unlikely(y->mc_signature != MDBX_MC_SIGNATURE || + x->mc_signature != MDBX_MC_SIGNATURE)) + return MDBX_EBADSIGN; + + if (unlikely(y->mc_dbi != x->mc_dbi)) + return MDBX_EINVAL; + + if (unlikely(!(y->mc_flags & x->mc_flags & C_INITIALIZED))) + return MDBX_ENODATA; + + while (likely(r->level < y->mc_snum && r->level < x->mc_snum)) { + if (unlikely(y->mc_pg[r->level] != x->mc_pg[r->level])) + return MDBX_PROBLEM; + + int nkeys = NUMKEYS(y->mc_pg[r->level]); + assert(nkeys > 0); + if (r->level == 0) + r->root_nkeys = nkeys; + + const int limit_ki = nkeys - 1; + const int x_ki = x->mc_ki[r->level]; + const int y_ki = y->mc_ki[r->level]; + r->diff = ((x_ki < limit_ki) ? x_ki : limit_ki) - + ((y_ki < limit_ki) ? y_ki : limit_ki); + if (r->diff == 0) { + r->level += 1; + continue; + } + + while (unlikely(r->diff == 1) && + likely(r->level + 1 < y->mc_snum && r->level + 1 < x->mc_snum)) { + r->level += 1; + /* DB'PAGEs: 0------------------>MAX + * + * CURSORs: y < x + * STACK[i ]: | + * STACK[+1]: ...y++N|0++x... + */ + nkeys = NUMKEYS(y->mc_pg[r->level]); + r->diff = (nkeys - y->mc_ki[r->level]) + x->mc_ki[r->level]; + assert(r->diff > 0); + } + + while (unlikely(r->diff == -1) && + likely(r->level + 1 < y->mc_snum && r->level + 1 < x->mc_snum)) { + r->level += 1; + /* DB'PAGEs: 0------------------>MAX + * + * CURSORs: x < y + * STACK[i ]: | + * STACK[+1]: ...x--N|0--y... + */ + nkeys = NUMKEYS(x->mc_pg[r->level]); + r->diff = -(nkeys - x->mc_ki[r->level]) - y->mc_ki[r->level]; + assert(r->diff < 0); + } + + return MDBX_SUCCESS; + } + + r->diff = mdbx_cmp2int(x->mc_flags & C_EOF, y->mc_flags & C_EOF); + return MDBX_SUCCESS; +} + +__hot static ptrdiff_t estimate(const MDBX_db *db, + struct diff_result *const __restrict dr) { + /* root: branch-page => scale = leaf-factor * branch-factor^(N-1) + * level-1: branch-page(s) => scale = leaf-factor * branch-factor^2 + * level-2: branch-page(s) => scale = leaf-factor * branch-factor + * level-N: branch-page(s) => scale = leaf-factor + * last-level: leaf-page(s) => scale = 1 + */ + ptrdiff_t btree_power = db->md_depth - 2 - dr->level; + if (btree_power < 0) + return dr->diff; + + ptrdiff_t estimated = + (ptrdiff_t)db->md_entries * dr->diff / (ptrdiff_t)db->md_leaf_pages; + if (btree_power == 0) + return estimated; + + if (db->md_depth < 4) { + assert(dr->level == 0 && btree_power == 1); + return (ptrdiff_t)db->md_entries * dr->diff / (ptrdiff_t)dr->root_nkeys; + } + + /* average_branchpage_fillfactor = total(branch_entries) / branch_pages + total(branch_entries) = leaf_pages + branch_pages - 1 (root page) */ + const size_t log2_fixedpoint = sizeof(size_t) - 1; + const size_t half = UINT64_C(1) << (log2_fixedpoint - 1); + const size_t factor = + ((db->md_leaf_pages + db->md_branch_pages - 1) << log2_fixedpoint) / + db->md_branch_pages; + while (1) { + switch ((size_t)btree_power) { + default: { + const size_t square = (factor * factor + half) >> log2_fixedpoint; + const size_t quad = (square * square + half) >> log2_fixedpoint; + do { + estimated = estimated * quad + half; + estimated >>= log2_fixedpoint; + btree_power -= 4; + } while (btree_power >= 4); + continue; + } + case 3: + estimated = estimated * factor + half; + estimated >>= log2_fixedpoint; + __fallthrough /* fall through */; + case 2: + estimated = estimated * factor + half; + estimated >>= log2_fixedpoint; + __fallthrough /* fall through */; + case 1: + estimated = estimated * factor + half; + estimated >>= log2_fixedpoint; + __fallthrough /* fall through */; + case 0: + if (unlikely(estimated > (ptrdiff_t)db->md_entries)) + return (ptrdiff_t)db->md_entries; + if (unlikely(estimated < -(ptrdiff_t)db->md_entries)) + return -(ptrdiff_t)db->md_entries; + return estimated; + } + } +} + +__hot int mdbx_estimate_distance(const MDBX_cursor *first, + const MDBX_cursor *last, + ptrdiff_t *distance_items) { + if (unlikely(first == NULL || last == NULL || distance_items == NULL)) + return MDBX_EINVAL; + + *distance_items = 0; + struct diff_result dr; + int rc = cursor_diff(last, first, &dr); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + + if (unlikely(dr.diff == 0) && + F_ISSET(first->mc_db->md_flags & first->mc_db->md_flags, + MDBX_DUPSORT | C_INITIALIZED)) { + first = &first->mc_xcursor->mx_cursor; + last = &last->mc_xcursor->mx_cursor; + rc = cursor_diff(first, last, &dr); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + } + + if (likely(dr.diff != 0)) + *distance_items = estimate(first->mc_db, &dr); + + return MDBX_SUCCESS; +} + +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 == NULL || distance_items == NULL || + move_op == MDBX_GET_CURRENT || move_op == MDBX_GET_MULTIPLE)) + return MDBX_EINVAL; + + if (unlikely(cursor->mc_signature != MDBX_MC_SIGNATURE)) + return MDBX_EBADSIGN; + + if (!(cursor->mc_flags & C_INITIALIZED)) + return MDBX_ENODATA; + + MDBX_cursor_couple next; + mdbx_cursor_copy(cursor, &next.outer); + next.outer.mc_xcursor = NULL; + if (cursor->mc_db->md_flags & MDBX_DUPSORT) { + next.outer.mc_xcursor = &next.inner; + int rc = mdbx_xcursor_init0(&next.outer); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + MDBX_xcursor *mx = &container_of(cursor, MDBX_cursor_couple, outer)->inner; + mdbx_cursor_copy(&mx->mx_cursor, &next.inner.mx_cursor); + } + + MDBX_val stub = {0, 0}; + if (data == NULL) { + const unsigned mask = + 1 << MDBX_GET_BOTH | 1 << MDBX_GET_BOTH_RANGE | 1 << MDBX_SET_KEY; + if (unlikely(mask & (1 << move_op))) + return MDBX_EINVAL; + data = &stub; + } + + if (key == NULL) { + const unsigned mask = 1 << MDBX_GET_BOTH | 1 << MDBX_GET_BOTH_RANGE | + 1 << MDBX_SET_KEY | 1 << MDBX_SET | + 1 << MDBX_SET_RANGE; + if (unlikely(mask & (1 << move_op))) + return MDBX_EINVAL; + key = &stub; + } + + int rc = mdbx_cursor_get(&next.outer, key, data, move_op); + if (unlikely(rc != MDBX_SUCCESS && + (rc != MDBX_NOTFOUND || !(next.outer.mc_flags & C_INITIALIZED)))) + return rc; + + return mdbx_estimate_distance(cursor, &next.outer, distance_items); +} + static int mdbx_is_samedata(const MDBX_val *a, const MDBX_val *b) { return a->iov_len == b->iov_len && memcmp(a->iov_base, b->iov_base, a->iov_len) == 0; } +int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *begin_key, + MDBX_val *begin_data, MDBX_val *end_key, + MDBX_val *end_data, ptrdiff_t *size_items) { + + if (unlikely(!txn || !size_items)) + return MDBX_EINVAL; + + if (unlikely(!begin_key && begin_data)) + return MDBX_EINVAL; + + if (unlikely(!end_key && end_data)) + return MDBX_EINVAL; + + if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) + return MDBX_EBADSIGN; + + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + + if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_USRVALID))) + return MDBX_EINVAL; + + if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED)) + return MDBX_BAD_TXN; + + MDBX_cursor_couple begin; + /* LY: first, initialize cursor to refresh a DB in case it have DB_STALE */ + int rc = mdbx_cursor_init(&begin.outer, txn, dbi); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + + if (unlikely(begin.outer.mc_db->md_entries == 0)) { + *size_items = 0; + return MDBX_SUCCESS; + } + + if (!begin_key) { + if (unlikely(!end_key)) { + /* LY: FIRST..LAST case */ + *size_items = (ptrdiff_t)begin.outer.mc_db->md_entries; + return MDBX_SUCCESS; + } + MDBX_val stub = {0, 0}; + rc = mdbx_cursor_first(&begin.outer, &stub, &stub); + } else if (end_key && !begin_data && !end_data && + (begin_key == end_key || mdbx_is_samedata(begin_key, end_key))) { + /* LY: single key case */ + int exact = 0; + rc = mdbx_cursor_set(&begin.outer, begin_key, NULL, MDBX_SET, &exact); + if (unlikely(rc != MDBX_SUCCESS)) { + *size_items = 0; + return (rc == MDBX_NOTFOUND) ? MDBX_SUCCESS : rc; + } + *size_items = 1; + if (begin.outer.mc_xcursor != NULL) { + MDBX_node *leaf = NODEPTR(begin.outer.mc_pg[begin.outer.mc_top], + begin.outer.mc_ki[begin.outer.mc_top]); + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + /* LY: return the number of duplicates for given key */ + mdbx_tassert(txn, begin.outer.mc_xcursor == &begin.inner && + (begin.inner.mx_cursor.mc_flags & C_INITIALIZED)); + *size_items = + (sizeof(*size_items) >= sizeof(begin.inner.mx_db.md_entries) || + begin.inner.mx_db.md_entries <= SIZE_MAX) + ? (size_t)begin.inner.mx_db.md_entries + : SIZE_MAX; + } + } + return MDBX_SUCCESS; + } else { + rc = mdbx_cursor_set(&begin.outer, begin_key, begin_data, + begin_data ? MDBX_GET_BOTH_RANGE : MDBX_SET_RANGE, + NULL); + } + + if (unlikely(rc != MDBX_SUCCESS)) { + if (rc != MDBX_NOTFOUND || !(begin.outer.mc_flags & C_INITIALIZED)) + return rc; + } + + MDBX_cursor_couple end; + rc = mdbx_cursor_init(&end.outer, txn, dbi); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + if (!end_key) { + MDBX_val stub = {0, 0}; + rc = mdbx_cursor_last(&end.outer, &stub, &stub); + } else { + rc = mdbx_cursor_set(&end.outer, end_key, end_data, + end_data ? MDBX_GET_BOTH_RANGE : MDBX_SET_RANGE, NULL); + } + if (unlikely(rc != MDBX_SUCCESS)) { + if (rc != MDBX_NOTFOUND || !(end.outer.mc_flags & C_INITIALIZED)) + return rc; + } + + rc = mdbx_estimate_distance(&begin.outer, &end.outer, size_items); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + assert(*size_items >= -(ptrdiff_t)begin.outer.mc_db->md_entries && + *size_items <= (ptrdiff_t)begin.outer.mc_db->md_entries); + +#if 0 /* LY: Was decided to returns as-is (i.e. negative) the estimation \ + * results for an inverted ranges. */ + + /* Commit 8ddfd1f34ad7cf7a3c4aa75d2e248ca7e639ed63 + Change-Id: If59eccf7311123ab6384c4b93f9b1fed5a0a10d1 */ + + if (*size_items < 0) { + /* LY: inverted range case */ + *size_items += (ptrdiff_t)begin.outer.mc_db->md_entries; + } else if (*size_items == 0 && begin_key && end_key) { + int cmp = begin.outer.mc_dbx->md_cmp(&origin_begin_key, &origin_end_key); + if (cmp == 0 && (begin.inner.mx_cursor.mc_flags & C_INITIALIZED) && + begin_data && end_data) + cmp = begin.outer.mc_dbx->md_dcmp(&origin_begin_data, &origin_end_data); + if (cmp > 0) { + /* LY: inverted range case with empty scope */ + *size_items = (ptrdiff_t)begin.outer.mc_db->md_entries; + } + } + assert(*size_items >= 0 && + *size_items <= (ptrdiff_t)begin.outer.mc_db->md_entries); +#endif + + return MDBX_SUCCESS; +} + +//------------------------------------------------------------------------------ + /* Позволяет обновить или удалить существующую запись с получением * в old_data предыдущего значения данных. При этом если new_data равен * нулю, то выполняется удаление, иначе обновление/вставка. diff --git a/libs/libmdbx/src/src/osal.c b/libs/libmdbx/src/src/osal.c index 4650519348..e3997e2a45 100644 --- a/libs/libmdbx/src/src/osal.c +++ b/libs/libmdbx/src/src/osal.c @@ -1,7 +1,7 @@ /* https://en.wikipedia.org/wiki/Operating_system_abstraction_layer */ /* - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -153,8 +153,10 @@ typedef struct _FILE_PROVIDER_EXTERNAL_INFO_V1 { /*----------------------------------------------------------------------------*/ -#ifndef _MSC_VER -/* Prototype should match libc runtime. ISO POSIX (2003) & LSB 3.1 */ +#if !defined(_MSC_VER) && \ + /* workaround for avoid musl libc wrong prototype */ ( \ + defined(__GLIBC__) || defined(__GNU_LIBRARY__)) +/* Prototype should match libc runtime. ISO POSIX (2003) & LSB 1.x-3.x */ __nothrow __noreturn void __assert_fail(const char *assertion, const char *file, unsigned line, const char *function); #endif /* _MSC_VER */ @@ -673,34 +675,27 @@ int mdbx_filesync(mdbx_filehandle_t fd, bool filesize_changed) { #if defined(_WIN32) || defined(_WIN64) (void)filesize_changed; return FlushFileBuffers(fd) ? MDBX_SUCCESS : GetLastError(); -#elif __GLIBC_PREREQ(2, 16) || _BSD_SOURCE || _XOPEN_SOURCE || \ - (__GLIBC_PREREQ(2, 8) && _POSIX_C_SOURCE >= 200112L) - for (;;) { -/* LY: It is no reason to use fdatasync() here, even in case - * no such bug in a kernel. Because "no-bug" mean that a kernel - * internally do nearly the same, e.g. fdatasync() == fsync() - * when no-kernel-bug and file size was changed. - * - * So, this code is always safe and without appreciable - * performance degradation. - * - * For more info about of a corresponding fdatasync() bug - * see http://www.spinics.net/lists/linux-ext4/msg33714.html */ -#if _POSIX_C_SOURCE >= 199309L || _XOPEN_SOURCE >= 500 || \ - defined(_POSIX_SYNCHRONIZED_IO) - if (!filesize_changed && fdatasync(fd) == 0) - return MDBX_SUCCESS; +#else + int rc; + do { +#if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0 + /* LY: This code is always safe and without appreciable performance + * degradation, even on a kernel with fdatasync's bug. + * + * For more info about of a corresponding fdatasync() bug + * see http://www.spinics.net/lists/linux-ext4/msg33714.html */ + if (!filesize_changed) { + if (fdatasync(fd) == 0) + return MDBX_SUCCESS; + } else #else (void)filesize_changed; #endif - if (fsync(fd) == 0) + if (fsync(fd) == 0) return MDBX_SUCCESS; - int rc = errno; - if (rc != EINTR) - return rc; - } -#else -#error FIXME + rc = errno; + } while (rc == EINTR); + return rc; #endif } diff --git a/libs/libmdbx/src/src/osal.h b/libs/libmdbx/src/src/osal.h index 101605be05..0208a52254 100644 --- a/libs/libmdbx/src/src/osal.h +++ b/libs/libmdbx/src/src/osal.h @@ -1,7 +1,7 @@ /* https://en.wikipedia.org/wiki/Operating_system_abstraction_layer */ /* - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/src/tools/mdbx_chk.c b/libs/libmdbx/src/src/tools/mdbx_chk.c index 31119ba66e..64349cd392 100644 --- a/libs/libmdbx/src/src/tools/mdbx_chk.c +++ b/libs/libmdbx/src/src/tools/mdbx_chk.c @@ -1,7 +1,7 @@ /* mdbx_chk.c - memory-mapped database check tool */ /* - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -92,7 +92,8 @@ MDBX_envinfo envinfo; MDBX_stat envstat; size_t maxkeysize, userdb_count, skipped_subdb; uint64_t reclaimable_pages, gc_pages, lastpgno, unused_pages; -unsigned verbose, quiet; +unsigned verbose; +char ignore_wrong_order, quiet; const char *only_subdb; struct problem { @@ -615,6 +616,8 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler, saved_list = problems_push(); prev_key.iov_base = NULL; + prev_key.iov_len = 0; + prev_data.iov_base = NULL; prev_data.iov_len = 0; rc = mdbx_cursor_get(mc, &key, &data, MDBX_FIRST); while (rc == MDBX_SUCCESS) { @@ -648,18 +651,25 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler, } int cmp = mdbx_cmp(txn, dbi_handle, &prev_key, &key); - if (cmp > 0) { - problem_add("entry", record_count, "broken ordering of entries", NULL); - } else if (cmp == 0) { + if (cmp == 0) { ++dups; - if (!(flags & MDBX_DUPSORT)) + if ((flags & MDBX_DUPSORT) == 0) { problem_add("entry", record_count, "duplicated entries", NULL); - else if (flags & MDBX_INTEGERDUP) { + if (data.iov_len == prev_data.iov_len && + memcmp(data.iov_base, prev_data.iov_base, data.iov_len) == 0) { + problem_add("entry", record_count, "complete duplicate", NULL); + } + } else { cmp = mdbx_dcmp(txn, dbi_handle, &prev_data, &data); - if (cmp > 0) - problem_add("entry", record_count, - "broken ordering of multi-values", NULL); + if (cmp == 0) { + problem_add("entry", record_count, "complete duplicate", NULL); + } else if (cmp > 0 && !ignore_wrong_order) { + problem_add("entry", record_count, "wrong order of multi-values", + NULL); + } } + } else if (cmp > 0 && !ignore_wrong_order) { + problem_add("entry", record_count, "wrong order of entries", NULL); } } else if (verbose) { if (flags & MDBX_INTEGERKEY) @@ -714,7 +724,8 @@ static void usage(char *prog) { " -w\t\tlock DB for writing while checking\n" " -d\t\tdisable page-by-page traversal of b-tree\n" " -s subdb\tprocess a specific subdatabase only\n" - " -c\t\tforce cooperative mode (don't try exclusive)\n", + " -c\t\tforce cooperative mode (don't try exclusive)\n" + " -i\t\tignore wrong order errors (for custom comparators case)\n", prog); exit(EXIT_INTERRUPTED); } @@ -898,7 +909,7 @@ int main(int argc, char *argv[]) { usage(prog); } - for (int i; (i = getopt(argc, argv, "Vvqnwcds:")) != EOF;) { + for (int i; (i = getopt(argc, argv, "Vvqnwcdsi:")) != EOF;) { switch (i) { case 'V': printf("%s (%s, build %s)\n", mdbx_version.git.describe, @@ -928,6 +939,9 @@ int main(int argc, char *argv[]) { usage(prog); only_subdb = optarg; break; + case 'i': + ignore_wrong_order = 1; + break; default: usage(prog); } diff --git a/libs/libmdbx/src/src/tools/mdbx_chk.vcxproj b/libs/libmdbx/src/src/tools/mdbx_chk.vcxproj index f87c687526..e6c2686286 100644 --- a/libs/libmdbx/src/src/tools/mdbx_chk.vcxproj +++ b/libs/libmdbx/src/src/tools/mdbx_chk.vcxproj @@ -22,6 +22,7 @@ <ProjectGuid>{15030120-5F7F-48F9-ABE5-DFC814F2A4BE}</ProjectGuid> <Keyword>Win32Proj</Keyword> <RootNamespace>mdbx_chk</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> diff --git a/libs/libmdbx/src/src/tools/mdbx_copy.1 b/libs/libmdbx/src/src/tools/mdbx_copy.1 index db6c453abd..74d94b6b98 100644 --- a/libs/libmdbx/src/src/tools/mdbx_copy.1 +++ b/libs/libmdbx/src/src/tools/mdbx_copy.1 @@ -1,4 +1,4 @@ -.\" Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>. +.\" Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru>. .\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>. .\" Copying restrictions apply. See COPYRIGHT/LICENSE. diff --git a/libs/libmdbx/src/src/tools/mdbx_copy.c b/libs/libmdbx/src/src/tools/mdbx_copy.c index ee3f739d4d..9b0c833a37 100644 --- a/libs/libmdbx/src/src/tools/mdbx_copy.c +++ b/libs/libmdbx/src/src/tools/mdbx_copy.c @@ -1,7 +1,7 @@ /* mdbx_copy.c - memory-mapped database backup tool */ /* - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/src/tools/mdbx_copy.vcxproj b/libs/libmdbx/src/src/tools/mdbx_copy.vcxproj index 09bdc78fe7..d47513c204 100644 --- a/libs/libmdbx/src/src/tools/mdbx_copy.vcxproj +++ b/libs/libmdbx/src/src/tools/mdbx_copy.vcxproj @@ -22,6 +22,7 @@ <ProjectGuid>{15030120-5F7F-48F9-ABE5-DFC814F2A4BD}</ProjectGuid> <Keyword>Win32Proj</Keyword> <RootNamespace>mdbx_copy</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> diff --git a/libs/libmdbx/src/src/tools/mdbx_dump.1 b/libs/libmdbx/src/src/tools/mdbx_dump.1 index ccfcc0c9da..93d29a7cf3 100644 --- a/libs/libmdbx/src/src/tools/mdbx_dump.1 +++ b/libs/libmdbx/src/src/tools/mdbx_dump.1 @@ -1,4 +1,4 @@ -.\" Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>. +.\" Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru>. .\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>. .\" Copying restrictions apply. See COPYRIGHT/LICENSE. diff --git a/libs/libmdbx/src/src/tools/mdbx_dump.c b/libs/libmdbx/src/src/tools/mdbx_dump.c index d16215622e..07951548dd 100644 --- a/libs/libmdbx/src/src/tools/mdbx_dump.c +++ b/libs/libmdbx/src/src/tools/mdbx_dump.c @@ -1,7 +1,7 @@ /* mdbx_dump.c - memory-mapped database dump tool */ /* - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/src/tools/mdbx_dump.vcxproj b/libs/libmdbx/src/src/tools/mdbx_dump.vcxproj index 1e2ff8450f..6978a2c22d 100644 --- a/libs/libmdbx/src/src/tools/mdbx_dump.vcxproj +++ b/libs/libmdbx/src/src/tools/mdbx_dump.vcxproj @@ -22,6 +22,7 @@ <ProjectGuid>{15030120-5F7F-48F9-ABE5-DFC814F2A4BC}</ProjectGuid> <Keyword>Win32Proj</Keyword> <RootNamespace>mdbx_dump</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> diff --git a/libs/libmdbx/src/src/tools/mdbx_load.1 b/libs/libmdbx/src/src/tools/mdbx_load.1 index 7a18a6c018..e23ec78eee 100644 --- a/libs/libmdbx/src/src/tools/mdbx_load.1 +++ b/libs/libmdbx/src/src/tools/mdbx_load.1 @@ -1,4 +1,4 @@ -.\" Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>. +.\" Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru>. .\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>. .\" Copying restrictions apply. See COPYRIGHT/LICENSE. @@ -39,6 +39,13 @@ option below. .BR \-V Write the library version number to the standard output, and exit. .TP +.BR \-a +Append all records in the order they appear in the input. The input is assumed to already be +in correctly sorted order and no sorting or checking for redundant values will be performed. +This option must be used to reload data that was produced by running +.B mdbx_dump +on a database that uses custom compare functions. +.TP .BR \-f \ file Read from the specified file instead of from the standard input. .TP diff --git a/libs/libmdbx/src/src/tools/mdbx_load.c b/libs/libmdbx/src/src/tools/mdbx_load.c index 697e3e166f..9789e83a62 100644 --- a/libs/libmdbx/src/src/tools/mdbx_load.c +++ b/libs/libmdbx/src/src/tools/mdbx_load.c @@ -1,7 +1,7 @@ -/* mdbx_load.c - memory-mapped database load tool */ +/* mdbx_load.c - memory-mapped database load tool */ /* - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -57,6 +57,7 @@ static int Eof; static MDBX_envinfo envinfo; static MDBX_val kbuf, dbuf; +static MDBX_val k0buf; #define STRLENOF(s) (sizeof(s) - 1) @@ -304,11 +305,18 @@ static int readline(MDBX_val *out, MDBX_val *buf) { } static void usage(void) { - fprintf(stderr, "usage: %s [-V] [-f input] [-n] [-s name] [-N] [-T] dbpath\n", + fprintf(stderr, + "usage: %s [-V] [-a] [-f input] [-n] [-s name] [-N] [-T] dbpath\n", prog); exit(EXIT_FAILURE); } +static int anyway_greater(const MDBX_val *a, const MDBX_val *b) { + (void)a; + (void)b; + return 1; +} + int main(int argc, char *argv[]) { int i, rc; MDBX_env *env = NULL; @@ -316,28 +324,32 @@ int main(int argc, char *argv[]) { MDBX_cursor *mc = NULL; MDBX_dbi dbi; char *envname = NULL; - int envflags = 0, putflags = 0; + int envflags = MDBX_UTTERLY_NOSYNC, putflags = 0; + int append = 0; + MDBX_val prevk; prog = argv[0]; - - if (argc < 2) { + if (argc < 2) usage(); - } - /* -f: load file instead of stdin + /* -a: append records in input order + * -f: load file instead of stdin * -n: use NOSUBDIR flag on env_open * -s: load into named subDB * -N: use NOOVERWRITE on puts * -T: read plaintext * -V: print version and exit */ - while ((i = getopt(argc, argv, "f:ns:NTV")) != EOF) { + while ((i = getopt(argc, argv, "af:ns:NTV")) != EOF) { switch (i) { case 'V': printf("%s (%s, build %s)\n", mdbx_version.git.describe, mdbx_version.git.datetime, mdbx_build.datetime); exit(EXIT_SUCCESS); break; + case 'a': + append = 1; + break; case 'f': if (freopen(optarg, "r", stdin) == NULL) { fprintf(stderr, "%s: %s: reopen: %s\n", prog, optarg, @@ -381,6 +393,7 @@ int main(int argc, char *argv[]) { dbuf.iov_len = 4096; dbuf.iov_base = mdbx_malloc(dbuf.iov_len); + /* read first header for mapsize= */ if (!(mode & NOHDR)) readhdr(); @@ -418,8 +431,17 @@ int main(int argc, char *argv[]) { goto env_close; } - kbuf.iov_len = mdbx_env_get_maxkeysize(env) * 2 + 2; - kbuf.iov_base = mdbx_malloc(kbuf.iov_len); + kbuf.iov_len = mdbx_env_get_maxkeysize(env); + if (kbuf.iov_len >= SIZE_MAX / 4) { + fprintf(stderr, "mdbx_env_get_maxkeysize failed, returns %zu\n", + kbuf.iov_len); + goto env_close; + } + kbuf.iov_len = (kbuf.iov_len + 1) * 2; + kbuf.iov_base = malloc(kbuf.iov_len * 2); + k0buf.iov_len = kbuf.iov_len; + k0buf.iov_base = (char *)kbuf.iov_base + kbuf.iov_len; + prevk.iov_base = k0buf.iov_base; while (!Eof) { if (user_break) { @@ -427,9 +449,6 @@ int main(int argc, char *argv[]) { break; } - MDBX_val key, data; - int batch = 0; - rc = mdbx_txn_begin(env, NULL, 0, &txn); if (rc) { fprintf(stderr, "mdbx_txn_begin failed, error %d %s\n", rc, @@ -437,7 +456,9 @@ int main(int argc, char *argv[]) { goto env_close; } - rc = mdbx_dbi_open(txn, subname, dbi_flags | MDBX_CREATE, &dbi); + rc = mdbx_dbi_open_ex(txn, subname, dbi_flags | MDBX_CREATE, &dbi, + append ? anyway_greater : NULL, + append ? anyway_greater : NULL); if (rc) { fprintf(stderr, "mdbx_open failed, error %d %s\n", rc, mdbx_strerror(rc)); goto txn_abort; @@ -450,11 +471,15 @@ int main(int argc, char *argv[]) { goto txn_abort; } + int batch = 0; + prevk.iov_len = 0; while (1) { + MDBX_val key; rc = readline(&key, &kbuf); if (rc) /* rc == EOF */ break; + MDBX_val data; rc = readline(&data, &dbuf); if (rc) { fprintf(stderr, "%s: line %" PRIiSIZE ": failed to read key value\n", @@ -462,7 +487,18 @@ int main(int argc, char *argv[]) { goto txn_abort; } - rc = mdbx_cursor_put(mc, &key, &data, putflags); + int appflag = 0; + if (append) { + appflag = MDBX_APPEND; + if (dbi_flags & MDBX_DUPSORT) { + if (prevk.iov_len == key.iov_len && + memcmp(prevk.iov_base, key.iov_base, key.iov_len) == 0) + appflag = MDBX_APPEND | MDBX_APPENDDUP; + else + memcpy(prevk.iov_base, key.iov_base, prevk.iov_len = key.iov_len); + } + } + rc = mdbx_cursor_put(mc, &key, &data, putflags | appflag); if (rc == MDBX_KEYEXIST && putflags) continue; if (rc) { @@ -501,6 +537,8 @@ int main(int argc, char *argv[]) { goto env_close; } mdbx_dbi_close(env, dbi); + + /* try read next header */ if (!(mode & NOHDR)) readhdr(); } diff --git a/libs/libmdbx/src/src/tools/mdbx_load.vcxproj b/libs/libmdbx/src/src/tools/mdbx_load.vcxproj index 483d9b6104..05a100fc64 100644 --- a/libs/libmdbx/src/src/tools/mdbx_load.vcxproj +++ b/libs/libmdbx/src/src/tools/mdbx_load.vcxproj @@ -22,6 +22,7 @@ <ProjectGuid>{15030120-5F7F-48F9-ABE5-DFC814F2A4BB}</ProjectGuid> <Keyword>Win32Proj</Keyword> <RootNamespace>mdbx_load</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> diff --git a/libs/libmdbx/src/src/tools/mdbx_stat.1 b/libs/libmdbx/src/src/tools/mdbx_stat.1 index ca427f7a66..50a30b4f97 100644 --- a/libs/libmdbx/src/src/tools/mdbx_stat.1 +++ b/libs/libmdbx/src/src/tools/mdbx_stat.1 @@ -1,4 +1,4 @@ -.\" Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>. +.\" Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru>. .\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>. .\" Copying restrictions apply. See COPYRIGHT/LICENSE. diff --git a/libs/libmdbx/src/src/tools/mdbx_stat.c b/libs/libmdbx/src/src/tools/mdbx_stat.c index 95a6cdd23c..e459121d76 100644 --- a/libs/libmdbx/src/src/tools/mdbx_stat.c +++ b/libs/libmdbx/src/src/tools/mdbx_stat.c @@ -1,7 +1,7 @@ /* mdbx_stat.c - memory-mapped database status tool */ /* - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/src/tools/mdbx_stat.vcxproj b/libs/libmdbx/src/src/tools/mdbx_stat.vcxproj index 53f649bfd9..4027491d39 100644 --- a/libs/libmdbx/src/src/tools/mdbx_stat.vcxproj +++ b/libs/libmdbx/src/src/tools/mdbx_stat.vcxproj @@ -22,6 +22,7 @@ <ProjectGuid>{15030120-5F7F-48F9-ABE5-DFC814F2A4BF}</ProjectGuid> <Keyword>Win32Proj</Keyword> <RootNamespace>mdbx_stat</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> diff --git a/libs/libmdbx/src/test/CMakeLists.txt b/libs/libmdbx/src/test/CMakeLists.txt index ca7dd794cd..88fd09e01b 100644 --- a/libs/libmdbx/src/test/CMakeLists.txt +++ b/libs/libmdbx/src/test/CMakeLists.txt @@ -27,6 +27,7 @@ add_executable(${TARGET} try.cc utils.cc utils.h + append.cc ) target_link_libraries(${TARGET} diff --git a/libs/libmdbx/src/test/append.cc b/libs/libmdbx/src/test/append.cc new file mode 100644 index 0000000000..3ce53eb292 --- /dev/null +++ b/libs/libmdbx/src/test/append.cc @@ -0,0 +1,132 @@ +/* + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> + * and other libmdbx authors: please see AUTHORS file. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "test.h" + +bool testcase_append::run() { + db_open(); + + txn_begin(false); + MDBX_dbi dbi = db_table_open(true); + int rc = mdbx_drop(txn_guard.get(), dbi, false); + if (unlikely(rc != MDBX_SUCCESS)) + failure_perror("mdbx_drop(delete=false)", rc); + + keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */); + /* LY: тест наполнения таблиц в append-режиме, + * при котором записи добавляются строго в конец (в порядке сортировки) */ + const unsigned flags = (config.params.table_flags & MDBX_DUPSORT) + ? MDBX_APPEND | MDBX_APPENDDUP + : MDBX_APPEND; + keyvalue_maker.make_ordered(); + + key = keygen::alloc(config.params.keylen_max); + data = keygen::alloc(config.params.datalen_max); + keygen::buffer last_key = keygen::alloc(config.params.keylen_max); + keygen::buffer last_data = keygen::alloc(config.params.datalen_max); + last_key->value.iov_base = last_key->bytes; + last_key->value.iov_len = 0; + last_data->value.iov_base = last_data->bytes; + last_data->value.iov_len = 0; + + simple_checksum inserted_checksum; + uint64_t inserted_number = 0; + uint64_t serial_count = 0; + unsigned txn_nops = 0; + while (should_continue()) { + const keygen::serial_t serial = serial_count; + if (!keyvalue_maker.increment(serial_count, 1)) { + // дошли до границы пространства ключей + break; + } + + log_trace("append: append-a %" PRIu64, serial); + generate_pair(serial, key, data); + int cmp = inserted_number ? mdbx_cmp(txn_guard.get(), dbi, &key->value, + &last_key->value) + : 1; + if (cmp == 0 && (config.params.table_flags & MDBX_DUPSORT)) + cmp = mdbx_dcmp(txn_guard.get(), dbi, &data->value, &last_data->value); + + rc = mdbx_put(txn_guard.get(), dbi, &key->value, &data->value, flags); + if (cmp > 0) { + if (unlikely(rc != MDBX_SUCCESS)) + failure_perror("mdbx_put(appenda-a)", rc); + memcpy(last_key->value.iov_base, key->value.iov_base, + last_key->value.iov_len = key->value.iov_len); + memcpy(last_data->value.iov_base, data->value.iov_base, + last_data->value.iov_len = data->value.iov_len); + ++inserted_number; + inserted_checksum.push((uint32_t)inserted_number, key->value); + inserted_checksum.push(10639, data->value); + } else { + if (unlikely(rc != MDBX_EKEYMISMATCH)) + failure_perror("mdbx_put(appenda-a) != MDBX_EKEYMISMATCH", rc); + } + + if (++txn_nops >= config.params.batch_write) { + txn_restart(false, false); + txn_nops = 0; + } + + report(1); + } + + txn_restart(false, true); + //---------------------------------------------------------------------------- + cursor_open(dbi); + + MDBX_val check_key, check_data; + rc = mdbx_cursor_get(cursor_guard.get(), &check_key, &check_data, MDBX_FIRST); + if (unlikely(rc != MDBX_SUCCESS)) + failure_perror("mdbx_cursor_get(MDBX_FIRST)", rc); + + simple_checksum read_checksum; + uint64_t read_count = 0; + while (rc == MDBX_SUCCESS) { + ++read_count; + read_checksum.push((uint32_t)read_count, check_key); + read_checksum.push(10639, check_data); + + rc = + mdbx_cursor_get(cursor_guard.get(), &check_key, &check_data, MDBX_NEXT); + } + + if (unlikely(rc != MDBX_NOTFOUND)) + failure_perror("mdbx_cursor_get(MDBX_NEXT) != EOF", rc); + + if (unlikely(read_count != inserted_number)) + failure("read_count(%" PRIu64 ") != inserted_number(%" PRIu64 ")", + read_count, inserted_number); + + if (unlikely(read_checksum.value != inserted_checksum.value)) + failure("read_checksum(0x%016" PRIu64 ") " + "!= inserted_checksum(0x%016" PRIu64 ")", + read_checksum.value, inserted_checksum.value); + + cursor_close(); + //---------------------------------------------------------------------------- + if (txn_guard) + txn_end(false); + + if (dbi) { + if (config.params.drop_table && !mode_readonly()) { + txn_begin(false); + db_table_drop(dbi); + txn_end(false); + } else + db_table_close(dbi); + } + return true; +} diff --git a/libs/libmdbx/src/test/base.h b/libs/libmdbx/src/test/base.h index b23f776aa3..0b4d26e51b 100644 --- a/libs/libmdbx/src/test/base.h +++ b/libs/libmdbx/src/test/base.h @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/test/cases.cc b/libs/libmdbx/src/test/cases.cc index 13d475763a..1d41efc82b 100644 --- a/libs/libmdbx/src/test/cases.cc +++ b/libs/libmdbx/src/test/cases.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -69,6 +69,7 @@ void testcase_setup(const char *casename, actor_params ¶ms, configure_actor(last_space_id, ac_hill, nullptr, params); configure_actor(last_space_id, ac_try, nullptr, params); configure_actor(last_space_id, ac_copy, nullptr, params); + configure_actor(last_space_id, ac_append, nullptr, params); log_notice("<<< testcase_setup(%s): done", casename); } else { failure("unknown testcase `%s`", casename); diff --git a/libs/libmdbx/src/test/chrono.cc b/libs/libmdbx/src/test/chrono.cc index f734668628..38cb321a81 100644 --- a/libs/libmdbx/src/test/chrono.cc +++ b/libs/libmdbx/src/test/chrono.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/test/chrono.h b/libs/libmdbx/src/test/chrono.h index c2bd5627a6..11675195ac 100644 --- a/libs/libmdbx/src/test/chrono.h +++ b/libs/libmdbx/src/test/chrono.h @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/test/config.cc b/libs/libmdbx/src/test/config.cc index 619bd35727..bfae5c14df 100644 --- a/libs/libmdbx/src/test/config.cc +++ b/libs/libmdbx/src/test/config.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/test/config.h b/libs/libmdbx/src/test/config.h index 1886a8ea57..d6eaea2e54 100644 --- a/libs/libmdbx/src/test/config.h +++ b/libs/libmdbx/src/test/config.h @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -27,7 +27,8 @@ enum actor_testcase { ac_deadwrite, ac_jitter, ac_try, - ac_copy + ac_copy, + ac_append }; enum actor_status { diff --git a/libs/libmdbx/src/test/dead.cc b/libs/libmdbx/src/test/dead.cc index 3dd1ee7b24..a1a8b5f9de 100644 --- a/libs/libmdbx/src/test/dead.cc +++ b/libs/libmdbx/src/test/dead.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/test/hill.cc b/libs/libmdbx/src/test/hill.cc index 856aeb9356..5b083e1fcc 100644 --- a/libs/libmdbx/src/test/hill.cc +++ b/libs/libmdbx/src/test/hill.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/test/jitter.cc b/libs/libmdbx/src/test/jitter.cc index 48f9bd998e..82d1d764ff 100644 --- a/libs/libmdbx/src/test/jitter.cc +++ b/libs/libmdbx/src/test/jitter.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/test/keygen.cc b/libs/libmdbx/src/test/keygen.cc index c7a706065f..5876fd8cec 100644 --- a/libs/libmdbx/src/test/keygen.cc +++ b/libs/libmdbx/src/test/keygen.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -167,6 +167,15 @@ void maker::setup(const config::actor_params_pod &actor, unsigned actor_id, base = 0; } +void maker::make_ordered() { + mapping.mesh = 0; + mapping.rotate = 0; +} + +bool maker::is_unordered() const { + return (mapping.mesh >= serial_minwith || mapping.rotate) != 0; +} + bool maker::increment(serial_t &serial, int delta) { if (serial > mask(mapping.width)) { log_extra("keygen-increment: %" PRIu64 " > %" PRIu64 ", overflow", serial, diff --git a/libs/libmdbx/src/test/keygen.h b/libs/libmdbx/src/test/keygen.h index bbd97b29d1..890397b8ca 100644 --- a/libs/libmdbx/src/test/keygen.h +++ b/libs/libmdbx/src/test/keygen.h @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -121,6 +121,8 @@ public: serial_t value_age); void setup(const config::actor_params_pod &actor, unsigned actor_id, unsigned thread_number); + void make_ordered(); + bool is_unordered() const; bool increment(serial_t &serial, int delta); }; diff --git a/libs/libmdbx/src/test/log.cc b/libs/libmdbx/src/test/log.cc index 0e325e3add..79544e11bb 100644 --- a/libs/libmdbx/src/test/log.cc +++ b/libs/libmdbx/src/test/log.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/test/log.h b/libs/libmdbx/src/test/log.h index ecdd91bf88..7d6b4012f1 100644 --- a/libs/libmdbx/src/test/log.h +++ b/libs/libmdbx/src/test/log.h @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/test/main.cc b/libs/libmdbx/src/test/main.cc index 749d48f07d..f3ee76b62f 100644 --- a/libs/libmdbx/src/test/main.cc +++ b/libs/libmdbx/src/test/main.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -341,6 +341,10 @@ int main(int argc, char *const argv[]) { configure_actor(last_space_id, ac_copy, value, params); continue; } + if (config::parse_option(argc, argv, narg, "append", nullptr)) { + configure_actor(last_space_id, ac_append, value, params); + continue; + } if (config::parse_option(argc, argv, narg, "failfast", global::config::failfast)) continue; diff --git a/libs/libmdbx/src/test/osal-unix.cc b/libs/libmdbx/src/test/osal-unix.cc index 6e6d7a1c5c..fd691e354f 100644 --- a/libs/libmdbx/src/test/osal-unix.cc +++ b/libs/libmdbx/src/test/osal-unix.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/test/osal-windows.cc b/libs/libmdbx/src/test/osal-windows.cc index f7f1de56e0..5858e89530 100644 --- a/libs/libmdbx/src/test/osal-windows.cc +++ b/libs/libmdbx/src/test/osal-windows.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/test/osal.h b/libs/libmdbx/src/test/osal.h index 3ccc7bbec1..5acf7ad094 100644 --- a/libs/libmdbx/src/test/osal.h +++ b/libs/libmdbx/src/test/osal.h @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/test/test.cc b/libs/libmdbx/src/test/test.cc index 6bba425a67..e34bd7f0e8 100644 --- a/libs/libmdbx/src/test/test.cc +++ b/libs/libmdbx/src/test/test.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -33,6 +33,8 @@ const char *testcase2str(const actor_testcase testcase) { return "try"; case ac_copy: return "copy"; + case ac_append: + return "append"; } } @@ -185,9 +187,33 @@ void testcase::txn_end(bool abort) { log_trace("<< txn_end(%s)", abort ? "abort" : "commit"); } +void testcase::cursor_open(unsigned dbi) { + log_trace(">> cursor_open(%u)", dbi); + assert(!cursor_guard); + assert(txn_guard); + + MDBX_cursor *cursor = nullptr; + int rc = mdbx_cursor_open(txn_guard.get(), dbi, &cursor); + if (unlikely(rc != MDBX_SUCCESS)) + failure_perror("mdbx_cursor_open()", rc); + cursor_guard.reset(cursor); + + log_trace("<< cursor_open(%u)", dbi); +} + +void testcase::cursor_close() { + log_trace(">> cursor_close()"); + assert(cursor_guard); + MDBX_cursor *cursor = cursor_guard.release(); + mdbx_cursor_close(cursor); + log_trace("<< cursor_close()"); +} + void testcase::txn_restart(bool abort, bool readonly, unsigned flags) { if (txn_guard) txn_end(abort); + if (cursor_guard) + cursor_close(); txn_begin(readonly, flags); } @@ -396,7 +422,7 @@ void testcase::db_table_drop(MDBX_dbi handle) { if (config.params.drop_table) { int rc = mdbx_drop(txn_guard.get(), handle, true); if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_drop()", rc); + failure_perror("mdbx_drop(delete=true)", rc); log_trace("<< testcase::db_table_drop"); } else { log_trace("<< testcase::db_table_drop: not needed"); @@ -458,6 +484,9 @@ bool test_execute(const actor_config &config) { case ac_copy: test.reset(new testcase_copy(config, pid)); break; + case ac_append: + test.reset(new testcase_append(config, pid)); + break; default: test.reset(new testcase(config, pid)); break; diff --git a/libs/libmdbx/src/test/test.h b/libs/libmdbx/src/test/test.h index d145ec2e38..e726023279 100644 --- a/libs/libmdbx/src/test/test.h +++ b/libs/libmdbx/src/test/test.h @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -107,6 +107,8 @@ protected: void txn_begin(bool readonly, unsigned flags = 0); void txn_end(bool abort); void txn_restart(bool abort, bool readonly, unsigned flags = 0); + void cursor_open(unsigned dbi); + void cursor_close(); void txn_inject_writefault(void); void txn_inject_writefault(MDBX_txn *txn); void fetch_canary(); @@ -158,6 +160,13 @@ public: bool run(); }; +class testcase_append : public testcase { +public: + testcase_append(const actor_config &config, const mdbx_pid_t pid) + : testcase(config, pid) {} + bool run(); +}; + class testcase_deadread : public testcase { public: testcase_deadread(const actor_config &config, const mdbx_pid_t pid) diff --git a/libs/libmdbx/src/test/test.vcxproj b/libs/libmdbx/src/test/test.vcxproj index ad647f3874..9eb62cdcf5 100644 --- a/libs/libmdbx/src/test/test.vcxproj +++ b/libs/libmdbx/src/test/test.vcxproj @@ -27,6 +27,7 @@ <ProjectGuid>{30E29CE6-E6FC-4D32-AA07-46A55FAF3A31}</ProjectGuid> <Keyword>Win32Proj</Keyword> <RootNamespace>mdbxtest</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> @@ -180,6 +181,7 @@ <ClInclude Include="utils.h" /> </ItemGroup> <ItemGroup> + <ClCompile Include="append.cc" /> <ClCompile Include="cases.cc" /> <ClCompile Include="chrono.cc" /> <ClCompile Include="config.cc" /> diff --git a/libs/libmdbx/src/test/utils.cc b/libs/libmdbx/src/test/utils.cc index 622c4d09bd..326455a693 100644 --- a/libs/libmdbx/src/test/utils.cc +++ b/libs/libmdbx/src/test/utils.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/libs/libmdbx/src/test/utils.h b/libs/libmdbx/src/test/utils.h index 42d497e86e..7bf3abd305 100644 --- a/libs/libmdbx/src/test/utils.h +++ b/libs/libmdbx/src/test/utils.h @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru> + * Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru> * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -288,6 +288,7 @@ struct simple_checksum { void push(uint32_t data) { value += data * UINT64_C(9386433910765580089) + 1; value ^= value >> 41; + value *= UINT64_C(0xBD9CACC22C6E9571); } void push(uint64_t data) { @@ -304,11 +305,15 @@ struct simple_checksum { } void push(const double &data) { push(&data, sizeof(double)); } - void push(const char *cstr) { push(cstr, strlen(cstr)); } - void push(const std::string &str) { push(str.data(), str.size()); } + void push(unsigned salt, const MDBX_val &val) { + push(val.iov_len); + push(salt); + push(val.iov_base, val.iov_len); + } + #if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) void push(const HANDLE &handle) { push(&handle, sizeof(handle)); } #endif /* _WINDOWS */ diff --git a/libs/libmdbx/src/tutorial/sample-bdb.txt b/libs/libmdbx/src/tutorial/sample-bdb.txt index 1015d06460..440efddb57 100644 --- a/libs/libmdbx/src/tutorial/sample-bdb.txt +++ b/libs/libmdbx/src/tutorial/sample-bdb.txt @@ -4,7 +4,7 @@ */ /* - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>. + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru>. * Copyright 2012-2015 Howard Chu, Symas Corp. * Copyright 2015,2016 Peter-Service R&D LLC. * All rights reserved. diff --git a/libs/libmdbx/src/tutorial/sample-mdbx.c b/libs/libmdbx/src/tutorial/sample-mdbx.c index aaafbc31cf..991ab69806 100644 --- a/libs/libmdbx/src/tutorial/sample-mdbx.c +++ b/libs/libmdbx/src/tutorial/sample-mdbx.c @@ -5,7 +5,7 @@ /* * Copyright 2017 Ilya Shipitsin <chipitsine@gmail.com>. - * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>. + * Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru>. * Copyright 2012-2015 Howard Chu, Symas Corp. * All rights reserved. * |