mdbx: Merge branch 'master' into 'nexenta' branch.

Change-Id: Iade70bae46bc4ea4baac36b3ed86434519959274
This commit is contained in:
Leo Yuriev 2017-03-16 15:23:15 +03:00
commit c0c4742dba
36 changed files with 1713 additions and 474 deletions

15
.travis.yml Normal file
View File

@ -0,0 +1,15 @@
language: c
sudo: false
dist: trusty
cache: bundler
notifications:
email: false
compiler:
- gcc
- clang
os:
- linux
script: if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then make all lmdb check; fi

19
CHANGES
View File

@ -1,16 +1,29 @@
MDBX
Add MDB_PREV_MULTIPLE
Fix MDB_CP_COMPACT (ITS#8209)
Add error MDB_PROBLEM, replace some MDB_CORRUPTED
Backport fixes for ITS#8406
Workarounds for glibc bugs: #21031 and 21032.
LMDB 0.9.19 Release Engineering
LMDB 0.9.20 Release Engineering
Fix mdb_load with escaped plaintext (ITS#8558)
Fix mdb_cursor_last / mdb_put interaction (ITS#8557)
LMDB 0.9.19 Release (2016/12/28)
Fix mdb_env_cwalk cursor init (ITS#8424)
Fix robust mutexes on Solaris 10/11 (ITS#8339)
Fix MDB_GET_BOTH on non-dup record (ITS#8393)
Optimize mdb_drop
Fix xcursors after mdb_cursor_del (ITS#8406)
Fix MDB_NEXT_DUP after mdb_cursor_del (ITS#8412)
Fix mdb_cursor_put resetting C_EOF (ITS#8489)
Fix mdb_env_copyfd2 to return EPIPE on SIGPIPE (ITS#8504)
Fix mdb_env_copy with empty DB (ITS#8209)
Fix behaviors with fork (ITS#8505)
Fix mdb_dbi_open with mainDB cursors (ITS#8542)
Fix F_NOCACHE on MacOS, error is non-fatal (ITS#7682)
Documentation
Cleanup doxygen nits
Note reserved vs actual mem/disk usage
LMDB 0.9.18 Release (2016/02/05)
already done for mdbx - Fix robust mutex detection on glibc 2.10-11 (ITS#8330)

View File

@ -1,6 +1,6 @@
Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
Copyright (c) 2015,2016 Peter-Service R&D LLC.
Copyright 2011-2016 Howard Chu, Symas Corp.
Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
Copyright 2011-2017 Howard Chu, Symas Corp.
Copyright 2015,2016 Peter-Service R&D LLC.
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -253,7 +253,7 @@ IDL_PROPERTY_SUPPORT = YES
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
DISTRIBUTE_GROUP_DOC = NO
DISTRIBUTE_GROUP_DOC = YES
# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
# the same type (for instance a group of public functions) to be put as a

View File

@ -24,8 +24,8 @@ suffix ?=
CC ?= gcc
XCFLAGS ?= -DNDEBUG=1 -DMDB_DEBUG=0
CFLAGS ?= -O2 -g3 -Wall -Werror -Wextra
CFLAGS += -pthread $(XCFLAGS)
CFLAGS ?= -O2 -g3 -Wall -Werror -Wextra -ffunction-sections
CFLAGS += -std=gnu99 -pthread $(XCFLAGS)
# LY: for ability to built with modern glibc,
# but then run with the old
@ -207,13 +207,15 @@ bench: bench-lmdb.txt bench-mdbx.txt
endif
ci-rule = ( CC=$$(which $1); if [ -n "$$CC" ]; then \
echo -n "probe by $2 ($$CC): " && \
echo -n "probe by $2 ($$(readlink -f $$(which $$CC))): " && \
$(MAKE) clean >$1.log 2>$1.err && \
$(MAKE) CC=$$(readlink -f $$CC) XCFLAGS="-UNDEBUG -DMDB_DEBUG=2" all check 1>$1.log 2>$1.err && echo "OK" \
|| ( echo "FAILED"; cat $1.err >&2; exit 1 ); \
else echo "no $2 ($1) for probe"; fi; )
ci:
@if [ "$(CC)" != "gcc" ]; then \
@if [ "$$(readlink -f $$(which $(CC)))" != "$$(readlink -f $$(which gcc || echo /bin/false))" -a \
"$$(readlink -f $$(which $(CC)))" != "$$(readlink -f $$(which clang || echo /bin/false))" -a \
"$$(readlink -f $$(which $(CC)))" != "$$(readlink -f $$(which icc || echo /bin/false))" ]; then \
$(call ci-rule,$(CC),default C compiler); \
fi
@$(call ci-rule,gcc,GCC)

375
README.md Normal file
View File

@ -0,0 +1,375 @@
libmdbx
======================================
Extended LMDB, aka "Расширенная LMDB".
*The Future will Positive. Всё будет хорошо.*
[![Build Status](https://travis-ci.org/ReOpen/libmdbx.svg?branch=master)](https://travis-ci.org/ReOpen/libmdbx)
English version by Google [is here](https://translate.googleusercontent.com/translate_c?act=url&ie=UTF8&sl=ru&tl=en&u=https://github.com/ReOpen/libmdbx/tree/master).
## Кратко
_libmdbx_ - это встраиваемый key-value движок хранения со специфическим
набором возможностей, которые при правильном применении позволяют
создавать уникальные решения с чемпионской производительностью, идеально
сочетаясь с технологией [MRAM](https://en.wikipedia.org/wiki/Magnetoresistive_random-access_memory).
_libmdbx_ обновляет совместно используемый набор данных, никак не мешая
при этом параллельным операциям чтения, не применяя атомарных операций к
самим данным, и обеспечивая согласованность при аварийной остановке в
любой момент. Поэтому _libmdbx_ позволяя строить системы с линейным
масштабированием производительности чтения/поиска по ядрам CPU и
амортизационной стоимостью любых операций Olog(N).
### История
_libmdbx_ является потомком "Lightning Memory-Mapped Database",
известной под аббревиатурой
[LMDB](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database).
Изначально доработка производилась в составе проекта
[ReOpenLDAP](https://github.com/ReOpen/ReOpenLDAP). Примерно за год
работы внесенные изменения приобрели самостоятельную ценность. Осенью
2015 доработанный движок был выделен в отдельный проект, который был
[представлен на конференции Highload++
2015](http://www.highload.ru/2015/abstracts/1831.html).
Характеристики и ключевые особенности
=====================================
_libmdbx_ наследует все ключевые возможности и особенности от
своего прародителя [LMDB](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database),
с устранением описанных далее проблем и архитектурных недочетов.
### Общее для оригинальной _LMDB_ и _libmdbx_
1. Данные хранятся в упорядоченном отображении (ordered map), ключи всегда
отсортированы, поддерживается выборка диапазонов (range lookups).
2. Данные отображается в память каждого работающего с БД процесса.
Ключам и данным обеспечивается прямой доступ без необходимости их
копирования, так как они защищены транзакцией чтения и не изменяются.
3. Транзакции согласно
[ACID](https://ru.wikipedia.org/wiki/ACID), посредством
[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).
Изменения строго последовательны и не блокируются чтением,
конфликты между транзакциями не возможны.
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),
без [атомарных операций](https://ru.wikipedia.org/wiki/%D0%90%D1%82%D0%BE%D0%BC%D0%B0%D1%80%D0%BD%D0%B0%D1%8F_%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F).
Читатели не блокируются операциями записи и не конкурируют
между собой, чтение масштабируется линейно по ядрам CPU.
5. Эффективное хранение дубликатов (ключей с несколькими
значениями), без дублирования ключей, с сортировкой значений, в
том числе целочисленных (для вторичных индексов).
6. Эффективная поддержка ключей фиксированной длины, в том числе целочисленных.
7. Амортизационная стоимость любой операции Olog(N),
[WAF](https://en.wikipedia.org/wiki/Write_amplification) и RAF также Olog(N).
8. Нет [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) и журнала
транзакций, после сбоев не требуется восстановление. Не требуется компактификация
или какое-либо периодическое обслуживание. Поддерживается резервное копирование
"по горячему", на работающей БД без приостановки изменения данных.
9. Отсутствует какое-либо внутреннее управление памятью или кэшированием. Всё
необходимое штатно выполняет ядро ОС.
### Недостатки и Компромиссы
1. Единовременно может выполняться не более одной транзакция изменения данных
(один писатель). Зато все изменения всегда последовательны, не может быть
конфликтов или ошибок при откате транзакций.
2. Отсутствие [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging)
обуславливает относительно большой
[WAF](https://en.wikipedia.org/wiki/Write_amplification). Поэтому фиксация
изменений на диске может быть дорогой и является главным ограничителем для
производительности по записи. В качестве компромисса предлагается несколько
режимов ленивой и/или периодической фиксации. В том числе режим `MAPASYNC`,
при котором изменения происходят только в памяти и асинхронно фиксируются на
диске ядром ОС.
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)
для реализации [MVCC](https://ru.wikipedia.org/wiki/MVCC) выполняется на
уровне страниц в [B+ дереве](https://ru.wikipedia.org/wiki/B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE).
Поэтому изменение данных амортизационно требует копирования Olog(N) страниц,
что расходует [пропускную способность оперативной
памяти](https://en.wikipedia.org/wiki/Memory_bandwidth) и является основным
ограничителем производительности в режиме `MAPASYNC`.
4. В _LMDB_ существует проблема долгих чтений (приостановленных читателей),
которая приводит к деградации производительности и переполнению БД.
В _libmdbx_ предложены средства для предотвращения, выхода из проблемной
ситуации и устранения её последствий. Подробности ниже.
5. В _LMDB_ есть вероятность разрушения БД в режиме `WRITEMAP+MAPASYNC`.
В _libmdbx_ для `WRITEMAP+MAPASYNC` гарантируется как сохранность базы,
так и согласованность данных. При этом также, в качестве альтернативы,
предложен режим `UTTERLY_NOSYNC`. Подробности ниже.
#### Проблема долгих чтений
Понимание проблемы требует некоторых пояснений, которые
изложены ниже, но могут быть сложны для быстрого восприятия.
Поэтому, тезисно:
* Изменение данных на фоне долгой операции чтения может
приводить к исчерпанию места в БД.
* После чего любая попытка обновить данные будет приводить к
ошибке `MAP_FULL` до завершения долгой операции чтения.
* Характерными примерами долгих чтений являются горячее
резервное копирования и отладка клиентского приложения при
активной транзакции чтения.
* В оригинальной _LMDB_ после этого будет наблюдаться
устойчивая деградация производительности всех механизмов
обратной записи на диск (в I/O контроллере, в гипервизоре,
в ядре ОС).
* В _libmdbx_ предусмотрен механизм аварийного прерывания таких
операций, а также режим `LIFO RECLAIM` устраняющий последующую
деградацию производительности.
Операции чтения выполняются в контексте снимка данных (версии
БД), который был актуальным на момент старта транзакции чтения.
Такой читаемый снимок поддерживается неизменным до завершения
операции. В свою очередь, это не позволяет повторно
использовать страницы БД в последующих версиях (снимках БД).
Другими словами, если обновление данных выполняется на фоне
долгой операции чтения, то вместо повторного использования
"старых" ненужных страниц будут выделяться новые, так как
"старые" страницы составляют снимок БД, который еще
используется долгой операцией чтения.
В результате, при интенсивном изменении данных и достаточно
длительной операции чтения, в БД могут быть исчерпаны свободные
страницы, что не позволит создавать новые снимки/версии БД.
Такая ситуация будет сохраняться до завершения операции чтения,
которая использует старый снимок данных и препятствует
повторному использованию страниц БД.
Однако, на этом проблемы не заканчиваются. После описанной
ситуации, все дополнительные страницы, которые были выделены
пока переработка старых была невозможна, будут участвовать в
цикле выделения/освобождения до конца жизни экземпляра БД. В
оригинальной _LMDB_ этот цикл использования страниц работает по
принципу [FIFO](https://ru.wikipedia.org/wiki/FIFO). Поэтому
увеличение количества циркулирующий страниц, с точки зрения
механизмов кэширования и/или обратной записи, выглядит как
увеличение рабочего набор данных. Проще говоря, однократное
попадание в ситуацию "уснувшего читателя" приводит к
устойчивому эффекту вымывания I/O кэша при всех последующих
изменениях данных.
Для устранения описанных проблемы в _libmdbx_ сделаны
существенные доработки, подробности ниже. Иллюстрации к
проблеме "долгих чтений" можно найти в [слайдах
презентации](http://www.slideshare.net/leoyuriev/lmdb).
Там же приведен пример количественной оценки прироста
производительности за счет эффективной работы
[BBWC](https://en.wikipedia.org/wiki/BBWC) при включении `LIFO
RECLAIM` в _libmdbx_.
#### Вероятность разрушения БД в режиме `WRITEMAP+MAPASYNC`
При работе в режиме `WRITEMAP+MAPSYNC` запись измененных
страниц выполняется ядром ОС, что имеет ряд преимуществ. Так
например, при крахе приложения, ядро ОС сохранит все изменения.
Однако, при аварийном отключении питания или сбое в ядре ОС, на
диске будет сохранена только часть измененных страниц БД. При
этом с большой вероятностью может оказаться так, что будут
сохранены мета-страницы со ссылками на страницы с новыми
версиями данных, но не сами новые данные. В этом случае БД
будет безвозвратна разрушена, даже если до аварии производилась
полная синхронизация данных (посредством `mdb_env_sync()`).
В _libmdbx_ эта проблема устранена, подробности ниже.
Доработки _libmdbx_
===================
1. Режим `LIFO RECLAIM`.
Для повторного использования выбираются не самые старые, а
самые новые страницы из доступных. За счет этого цикл
использования страниц всегда имеет минимальную длину и не
зависит от общего числа выделенных страниц.
В результате механизмы кэширования и обратной записи работают с
максимально возможной эффективностью. В случае использования
контроллера дисков или системы хранения с
[BBWC](https://en.wikipedia.org/wiki/BBWC) возможно
многократное увеличение производительности по записи
(обновлению данных).
2. Обработчик `OOM-KICK`.
Посредством `mdbx_env_set_oomfunc()` может быть установлен
внешний обработчик (callback), который будет вызван при
исчерпания свободных страниц из-за долгой операцией чтения.
Обработчику будет передан PID и pthread_id. В свою очередь
обработчик может предпринять одно из действий:
* отправить сигнал kill (#9), если долгое чтение выполняется
сторонним процессом;
* отменить или перезапустить проблемную операцию чтения, если
операция выполняется одним из потоков текущего процесса;
* подождать некоторое время, в расчете что проблемная операция
чтения будет штатно завершена;
* перервать текущую операцию изменения данных с возвратом кода
ошибки.
3. Гарантия сохранности БД в режиме `WRITEMAP+MAPSYNC`.
При работе в режиме `WRITEMAP+MAPSYNC` запись измененных
страниц выполняется ядром ОС, что имеет ряд преимуществ. Так
например, при крахе приложения, ядро ОС сохранит все изменения.
Однако, при аварийном отключении питания или сбое в ядре ОС, на
диске будет сохранена только часть измененных страниц БД. При
этом с большой вероятностью может оказаться так, что будут
сохранены мета-страницы со ссылками на страницы с новыми
версиями данных, но не сами новые данные. В этом случае БД
будет безвозвратна разрушена, даже если до аварии производилась
полная синхронизация данных (посредством `mdb_env_sync()`).
В _libmdbx_ эта проблема устранена путем полной переработки
пути записи данных:
* В режиме `WRITEMAP+MAPSYNC` _libmdbx_ не обновляет
мета-страницы непосредственно, а поддерживает их теневые копии
с переносом изменений после фиксации данных.
* При завершении транзакций, в зависимости от состояния
синхронности данных между диском и оперативной память,
_libmdbx_ помечает точки фиксации либо как сильные (strong),
либо как слабые (weak). Так например, в режиме
`WRITEMAP+MAPSYNC` завершаемые транзакции помечаются как
слабые, а при явной синхронизации данных как сильные.
* При открытии БД выполняется автоматический откат к последней
сильной фиксации. Этим обеспечивается гарантия сохранности БД.
К сожалению, такая гарантия надежности не дается бесплатно. Для
сохранности данных, страницы формирующие крайний снимок с
сильной фиксацией, не должны повторно использоваться
(перезаписываться) до формирования следующей сильной точки
фиксации. Таким образом, крайняя точка фиксации создает
описанный выше эффект "долгого чтения". Разница же здесь в том,
что при исчерпании свободных страниц ситуация будет
автоматически исправлена, посредством записи изменений на диск
и формированием новой сильной точки фиксации.
В последующих версиях _libmdbx_ будут предусмотрены средства
для асинхронной записи данных на диск с автоматическим
формированием сильных точек фиксации.
4. Возможность автоматического формирования контрольных точек
(сброса данных на диск) при накоплении заданного объёма изменений,
устанавливаемого функцией `mdbx_env_set_syncbytes()`.
5. Возможность получить отставание текущей транзакции чтения от
последней версии данных в БД посредством `mdbx_txn_straggler()`.
6. Утилита mdbx_chk для проверки БД и функция `mdbx_env_pgwalk()` для
обхода всех страниц БД.
7. Управление отладкой и получение отладочных сообщений посредством
`mdbx_setup_debug()`.
8. Возможность связать с каждой завершаемой транзакцией до 3
дополнительных маркеров посредством `mdbx_canary_put()`, и прочитать их
в транзакции чтения посредством `mdbx_canary_get()`.
9. Возможность узнать есть ли за текущей позицией курсора строка данных
посредством `mdbx_cursor_eof()`.
10. Возможность явно запросить обновление существующей записи, без
создания новой посредством флажка `MDB_CURRENT` для `mdbx_put()`.
11. Возможность обновить или удалить запись с получением предыдущего
значения данных посредством `mdbx_replace()`.
12. Поддержка ключей и значений нулевой длины. Включая сортированные
дубликаты, в том числе вне зависимости от порядка их добавления или
обновления.
13. Исправленный вариант `mdbx_cursor_count()`, возвращающий корректное
количество дубликатов для всех типов таблиц и любого положения курсора.
14. Возможность открыть БД в эксклюзивном режиме посредством
`mdbx_env_open_ex()`, например в целях её проверки.
15. Возможность закрыть БД в "грязном" состоянии (без сброса данных и
формирования сильной точки фиксации) посредством `mdbx_env_close_ex()`.
16. Возможность получить посредством `mdbx_env_info()` дополнительную
информацию, включая номер самой старой версии БД (снимка данных),
который используется одним из читателей.
17. Функция `mdbx_del()` не игнорирует дополнительный (уточняющий)
аргумент `data` для таблиц без дубликатов (без флажка `MDB_DUPSORT`), а
при его ненулевом значении всегда использует его для сверки с удаляемой
записью.
18. Возможность открыть dbi-таблицу, одновременно с установкой
компараторов для ключей и данных, посредством `mdbx_dbi_open_ex()`.
19. Возможность посредством `mdbx_is_dirty()` определить находятся ли
некоторый ключ или данные в "грязной" странице БД. Таким образом избегаю
лишнего копирования данных перед выполнением модифицирующих операций
(значения в размещенные "грязных" страницах могут быть перезаписаны при
изменениях, иначе они будут неизменны).
20. Корректное обновление текущей записи, в том числе сортированного
дубликата, при использовании режима `MDB_CURRENT` в `mdbx_cursor_put()`.
21. Все курсоры, как в транзакциях только для чтения, так и в пишущих,
могут быть переиспользованы посредством `mdbx_cursor_renew()` и ДОЛЖНЫ
ОСВОБОЖДАТЬСЯ ЯВНО.
>
> ## _ВАЖНО_, Обратите внимание!
>
> Это единственное изменение в API, которое значимо меняет
> семантику управления курсорами и может приводить к утечкам
> памяти. Следует отметить, что это изменение вынужденно.
> Так устраняется неоднозначность с массой тяжких последствий:
>
> - обращение к уже освобожденной памяти;
> - попытки повторного освобождения памяти;
> - memory corruption and segfaults.
22. Дополнительный код ошибки `MDBX_EMULTIVAL`, который возвращается из
`mdbx_put()` и `mdbx_replace()` при попытке выполнять неоднозначное
обновление или удаления одного из нескольких значений с одним ключом,
т.е. когда невозможно однозначно идентифицировать одно целевое значение
из нескольких.
23. Возможность посредством `mdbx_get_ex()` получить значение по
заданному ключу, одновременно с количеством дубликатов.
24. Наличие функций mdbx_cursor_on_first() и mdbx_cursor_on_last(),
которые позволяют быстро выяснить стоит ли курсор на первой/последней
позиции.
25. При завершении читающих транзакций, открытые в них DBI-хендлы не
закрываются и не теряются при завершении таких транзакций посредством
mdb_txn_abort() или mdb_txn_reset(). Что позволяет избавится от ряда
сложно обнаруживаемых ошибок.

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View File

@ -1,7 +1,7 @@
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2015 Howard Chu, Symas Corp.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015-2017 Howard Chu, Symas Corp.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

65
lmdb.h
View File

@ -1,7 +1,7 @@
/** @file lmdb.h
* @brief Reliable Lightning memory-mapped database library
* @brief Extended Lightning memory-mapped database library
*
* @mainpage Reliable Lightning Memory-Mapped Database Manager (MDBX)
* @mainpage Extended Lightning Memory-Mapped Database (MDBX)
*
* @section intro_sec Introduction
* MDBX is a Btree-based database management library modeled loosely on the
@ -66,6 +66,11 @@
* This does not use actual memory or disk space, but users may need
* to understand the difference so they won't be scared off.
*
* - An LMDB configuration will often reserve considerable \b unused
* memory address space and maybe file size for future growth.
* This does not use actual memory or disk space, but users may need
* to understand the difference so they won't be scared off.
*
* - By default, in versions before 0.9.10, unused portions of the data
* file might receive garbage data from memory freed by other code.
* (This does not happen when using the #MDB_WRITEMAP flag.) As of
@ -120,17 +125,17 @@
* @author Leonid Yuriev, 'ReOpen' initiative <https://github.com/ReOpen>.
* Howard Chu, Symas Corp. All rights reserved.
*
* @copyright 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* 2011-2016 Howard Chu, Symas Corp. All rights reserved.
* @copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* 2011-2017 Howard Chu, Symas Corp. All rights reserved.
*
* ---
*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015,2016 Peter-Service R&D LLC.
* @par Derived From:
* This code is derived from LMDB engine written by Howard Chu, Symas Corporation.
*
* Copyright 2011-2016 Howard Chu, Symas Corp. All rights reserved.
* Copyright 2011-2017 Howard Chu, Symas Corp. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
@ -204,7 +209,7 @@ typedef int mdb_filehandle_t;
MDB_VERINT(MDB_VERSION_MAJOR,MDB_VERSION_MINOR,MDB_VERSION_PATCH)
/** The release date of this library version */
#define MDB_VERSION_DATE "2016-06-09"
#define MDB_VERSION_DATE "2017-02-17"
/** A stringifier for the version info */
#define MDB_VERSTR(a,b,c,d) "MDBX " #a "." #b "." #c ": (" d ", https://github.com/ReOpen/libmdbx)"
@ -349,7 +354,8 @@ typedef void (MDB_rel_func)(MDB_val *item, void *oldptr, void *newptr, void *rel
* For mdb_cursor_del: remove all duplicate data items.
*/
#define MDB_NODUPDATA 0x20
/** For mdb_cursor_put: overwrite the current key/data pair */
/** For mdb_cursor_put: overwrite the current key/data pair
* MDBX allows this flag for mdb_put() for explicit overwrite/update without insertion. */
#define MDB_CURRENT 0x40
/** For put: Just reserve space for data, don't copy it. Return a
* pointer to the reserved space.
@ -1033,8 +1039,16 @@ size_t mdb_txn_id(MDB_txn *txn);
*
* The transaction handle is freed. It and its cursors must not be used
* again after this call, except with #mdb_cursor_renew().
* @note Earlier documentation incorrectly said all cursors would be freed.
*
* @note MDBX-mode:
* A cursor must be closed explicitly always, before
* or after its transaction ends. It can be reused with
* #mdb_cursor_renew() before finally closing it.
*
* @note LMDB-compatible mode:
* Earlier documentation incorrectly said all cursors would be freed.
* Only write-transactions free cursors.
*
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
* @return A non-zero error value on failure and 0 on success. Some possible
* errors are:
@ -1051,8 +1065,16 @@ int mdb_txn_commit(MDB_txn *txn);
*
* The transaction handle is freed. It and its cursors must not be used
* again after this call, except with #mdb_cursor_renew().
* @note Earlier documentation incorrectly said all cursors would be freed.
*
* @note MDBX-mode:
* A cursor must be closed explicitly always, before
* or after its transaction ends. It can be reused with
* #mdb_cursor_renew() before finally closing it.
*
* @note LMDB-compatible mode:
* Earlier documentation incorrectly said all cursors would be freed.
* Only write-transactions free cursors.
*
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
*/
int mdb_txn_abort(MDB_txn *txn);
@ -1144,8 +1166,9 @@ int mdb_txn_renew(MDB_txn *txn);
* This flag may only be used in combination with #MDB_DUPSORT. This option
* tells the library that the data items for this database are all the same
* size, which allows further optimizations in storage and retrieval. When
* all data items are the same size, the #MDB_GET_MULTIPLE and #MDB_NEXT_MULTIPLE
* cursor operations may be used to retrieve multiple items at once.
* all data items are the same size, the #MDB_GET_MULTIPLE, #MDB_NEXT_MULTIPLE
* and #MDB_PREV_MULTIPLE cursor operations may be used to retrieve multiple
* items at once.
* <li>#MDB_INTEGERDUP
* This option specifies that duplicate data items are binary integers,
* similar to #MDB_INTEGERKEY keys.
@ -1380,12 +1403,20 @@ int mdb_put(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data,
/** @brief Delete items from a database.
*
* This function removes key/data pairs from the database.
*
* MDBX-mode:
* The data parameter is NOT ignored regardless the database does
* support sorted duplicate data items or not. If the data parameter
* is non-NULL only the matching data item will be deleted.
*
* LMDB-compatible mode:
* If the database does not support sorted duplicate data items
* (#MDB_DUPSORT) the data parameter is ignored.
* If the database supports sorted duplicates and the data parameter
* is NULL, all of the duplicate data items for the key will be
* deleted. Otherwise, if the data parameter is non-NULL
* only the matching data item will be deleted.
*
* This function will return #MDB_NOTFOUND if the specified key/data
* pair is not in the database.
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
@ -1407,6 +1438,13 @@ int mdb_del(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data);
* A cursor cannot be used when its database handle is closed. Nor
* when its transaction has ended, except with #mdb_cursor_renew().
* It can be discarded with #mdb_cursor_close().
*
* MDBX-mode:
* A cursor must be closed explicitly always, before
* or after its transaction ends. It can be reused with
* #mdb_cursor_renew() before finally closing it.
*
* LMDB-compatible mode:
* A cursor in a write-transaction can be closed before its transaction
* ends, and will otherwise be closed when its transaction ends.
* A cursor in a read-only transaction must be closed explicitly, before
@ -1414,6 +1452,7 @@ int mdb_del(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data);
* #mdb_cursor_renew() before finally closing it.
* @note Earlier documentation said that cursors in every transaction
* were closed when the transaction committed or aborted.
*
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
* @param[in] dbi A database handle returned by #mdb_dbi_open()
* @param[out] cursor Address where the new #MDB_cursor handle will be stored

1003
mdb.c

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
/* mdbx_chk.c - memory-mapped database check tool */
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015,2016 Peter-Service R&D LLC.
*
* This file is part of libmdbx.
*
@ -52,7 +52,7 @@ flagbit dbflags[] = {
static volatile sig_atomic_t gotsignal;
static void signal_hanlder( int sig ) {
static void signal_handler( int sig ) {
(void) sig;
gotsignal = 1;
}
@ -264,7 +264,7 @@ static int pgvisitor(size_t pgno, unsigned pgnumber, void* ctx, const char* dbi,
problem_add("page", pgno, "illegal header-length", "%zu < %i < %zu",
sizeof(long), header_bytes, stat.base.ms_psize - sizeof(long));
if (payload_bytes < 1) {
if (nentries > 0) {
if (nentries > 1) {
problem_add("page", pgno, "zero size-of-entry", "payload %i bytes, %i entries",
payload_bytes, nentries);
if ((size_t) header_bytes + unused_bytes < page_size) {
@ -432,7 +432,6 @@ static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent)
fflush(NULL);
}
skipped_subdb++;
mdbx_dbi_close(env, dbi);
return MDB_SUCCESS;
}
@ -444,14 +443,12 @@ static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent)
rc = mdbx_dbi_flags(txn, dbi, &flags);
if (rc) {
error(" - mdbx_dbi_flags failed, error %d %s\n", rc, mdbx_strerror(rc));
mdbx_dbi_close(env, dbi);
return rc;
}
rc = mdbx_stat(txn, dbi, &ms, sizeof(ms));
if (rc) {
error(" - mdbx_stat failed, error %d %s\n", rc, mdbx_strerror(rc));
mdbx_dbi_close(env, dbi);
return rc;
}
@ -475,7 +472,6 @@ static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent)
rc = mdbx_cursor_open(txn, dbi, &mc);
if (rc) {
error(" - mdbx_cursor_open failed, error %d %s\n", rc, mdbx_strerror(rc));
mdbx_dbi_close(env, dbi);
return rc;
}
@ -491,9 +487,7 @@ static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent)
goto bailout;
}
if (key.mv_size == 0) {
problem_add("entry", record_count, "key with zero length", NULL);
} else if (key.mv_size > maxkeysize) {
if (key.mv_size > maxkeysize) {
problem_add("entry", record_count, "key length exceeds max-key-size",
"%zu > %zu", key.mv_size, maxkeysize);
} else if ((flags & MDB_INTEGERKEY)
@ -565,7 +559,6 @@ bailout:
}
mdbx_cursor_close(mc);
mdbx_dbi_close(env, dbi);
return rc || problems_count;
}
@ -660,13 +653,13 @@ int main(int argc, char *argv[])
usage(prog);
#ifdef SIGPIPE
signal(SIGPIPE, signal_hanlder);
signal(SIGPIPE, signal_handler);
#endif
#ifdef SIGHUP
signal(SIGHUP, signal_hanlder);
signal(SIGHUP, signal_handler);
#endif
signal(SIGINT, signal_hanlder);
signal(SIGTERM, signal_hanlder);
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
envname = argv[optind];
print("Running mdbx_chk for '%s' in %s mode...\n",
@ -686,7 +679,11 @@ int main(int argc, char *argv[])
}
maxkeysize = rc;
mdbx_env_set_maxdbs(env, 3);
rc = mdbx_env_set_maxdbs(env, MAX_DBI);
if (rc < 0) {
error("mdbx_env_set_maxdbs failed, error %d %s\n", rc, mdbx_strerror(rc));
goto bailout;
}
rc = mdbx_env_open_ex(env, envname, envflags, 0664, &exclusive);
if (rc) {
@ -747,7 +744,7 @@ int main(int argc, char *argv[])
meta_lt(info.me_meta1_txnid, info.me_meta1_sign,
info.me_meta2_txnid, info.me_meta2_sign) ? "tail" : "head");
if (info.me_meta1_txnid > info.base.me_last_txnid)
print(", rolled-back %zu (%zu >>> %zu)\n",
print(", rolled-back %zu (%zu >>> %zu)",
info.me_meta1_txnid - info.base.me_last_txnid,
info.me_meta1_txnid, info.base.me_last_txnid);
print("\n");
@ -757,7 +754,7 @@ int main(int argc, char *argv[])
meta_lt(info.me_meta2_txnid, info.me_meta2_sign,
info.me_meta1_txnid, info.me_meta1_sign) ? "tail" : "head");
if (info.me_meta2_txnid > info.base.me_last_txnid)
print(", rolled-back %zu (%zu >>> %zu)\n",
print(", rolled-back %zu (%zu >>> %zu)",
info.me_meta2_txnid - info.base.me_last_txnid,
info.me_meta2_txnid, info.base.me_last_txnid);
print("\n");

View File

@ -1,4 +1,6 @@
.\" Copyright 2012-2016 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2012-2017 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDB_COPY 1 "2014/06/20" "LMDB 0.9.14"
.SH NAME

View File

@ -1,9 +1,9 @@
/* mdb_copy.c - memory-mapped database backup tool */
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2012-2016 Howard Chu, Symas Corp.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2012-2017 Howard Chu, Symas Corp.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
.\" Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright (c) 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2014-2016 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2014-2017 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDB_DUMP 1 "2014/06/20" "LMDB 0.9.14"
.SH NAME

View File

@ -1,9 +1,9 @@
/* mdb_dump.c - memory-mapped database dump tool */
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2011-2016 Howard Chu, Symas Corp.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2011-2017 Howard Chu, Symas Corp.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,8 @@
.TH MDB_LOAD 1 "2014/06/20" "LMDB 0.9.14"
.\" Copyright 2014-2016 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2014-2017 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDB_LOAD 1 "2014/06/20" "LMDB 0.9.14"
.SH NAME
mdb_load \- LMDB environment import tool
.SH SYNOPSIS

View File

@ -1,9 +1,9 @@
/* mdb_load.c - memory-mapped database load tool */
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2011-2016 Howard Chu, Symas Corp.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2011-2017 Howard Chu, Symas Corp.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -252,7 +252,8 @@ badend:
c2 += 2;
}
} else {
c1++; c2++;
/* copies are redundant when no escapes were used */
*c1++ = *c2++;
}
}
} else {

View File

@ -1,6 +1,6 @@
.\" Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright (c) 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2012-2016 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2012-2017 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDB_STAT 1 "2014/06/20" "LMDB 0.9.14"
.SH NAME

View File

@ -1,9 +1,9 @@
/* mdb_stat.c - memory-mapped database status tool */
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2011-2016 Howard Chu, Symas Corp.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2011-2017 Howard Chu, Symas Corp.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

447
mdbx.c
View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -118,7 +118,7 @@ mdbx_env_set_syncbytes(MDB_env *env, size_t bytes)
return MDB_VERSION_MISMATCH;
env->me_sync_threshold = bytes;
return env->me_map ? mdb_env_sync(env, 0) : 0;
return env->me_map ? mdb_env_sync(env, 0) : MDB_SUCCESS;
}
void __cold
@ -172,7 +172,7 @@ typedef struct mdb_walk_ctx {
/** Depth-first tree traversal. */
static int __cold
mdb_env_walk(mdb_walk_ctx_t *ctx, const char* dbi, pgno_t pg, int flags, int deep)
mdb_env_walk(mdb_walk_ctx_t *ctx, const char* dbi, pgno_t pg, int deep)
{
MDB_page *mp;
int rc, i, nkeys;
@ -182,7 +182,12 @@ mdb_env_walk(mdb_walk_ctx_t *ctx, const char* dbi, pgno_t pg, int flags, int dee
if (pg == P_INVALID)
return MDB_SUCCESS; /* empty db */
rc = mdb_page_get(ctx->mw_txn, pg, &mp, NULL);
MDB_cursor mc;
memset(&mc, 0, sizeof(mc));
mc.mc_snum = 1;
mc.mc_txn = ctx->mw_txn;
rc = mdb_page_get(&mc, pg, &mp, NULL);
if (rc)
return rc;
if (pg != mp->mp_p.p_pgno)
@ -220,7 +225,7 @@ mdb_env_walk(mdb_walk_ctx_t *ctx, const char* dbi, pgno_t pg, int flags, int dee
}
for (align_bytes = i = 0; i < nkeys;
align_bytes += ((payload_size + align_bytes) & 1), i++) {
align_bytes += ((payload_size + align_bytes) & 1), i++) {
MDB_node *node;
if (IS_LEAF2(mp)) {
@ -233,15 +238,13 @@ mdb_env_walk(mdb_walk_ctx_t *ctx, const char* dbi, pgno_t pg, int flags, int dee
payload_size += NODESIZE + node->mn_ksize;
if (IS_BRANCH(mp)) {
rc = mdb_env_walk(ctx, dbi, NODEPGNO(node), flags, deep);
rc = mdb_env_walk(ctx, dbi, NODEPGNO(node), deep);
if (rc)
return rc;
continue;
}
assert(IS_LEAF(mp));
if (node->mn_ksize < 1)
return MDB_CORRUPTED;
if (node->mn_flags & F_BIGDATA) {
MDB_page *omp;
pgno_t *opg;
@ -249,7 +252,7 @@ mdb_env_walk(mdb_walk_ctx_t *ctx, const char* dbi, pgno_t pg, int flags, int dee
payload_size += sizeof(pgno_t);
opg = NODEDATA(node);
rc = mdb_page_get(ctx->mw_txn, *opg, &omp, NULL);
rc = mdb_page_get(&mc, *opg, &omp, NULL);
if (rc)
return rc;
if (*opg != omp->mp_p.p_pgno)
@ -276,8 +279,6 @@ mdb_env_walk(mdb_walk_ctx_t *ctx, const char* dbi, pgno_t pg, int flags, int dee
MDB_db *db = NODEDATA(node);
char* name = NULL;
if (NODEDSZ(node) < 1)
return MDB_CORRUPTED;
if (! (node->mn_flags & F_DUPDATA)) {
name = NODEKEY(node);
int namelen = (char*) db - name;
@ -285,7 +286,7 @@ mdb_env_walk(mdb_walk_ctx_t *ctx, const char* dbi, pgno_t pg, int flags, int dee
name[namelen] = 0;
}
rc = mdb_env_walk(ctx, (name && name[0]) ? name : dbi,
db->md_root, node->mn_flags & F_DUPDATA, deep + 1);
db->md_root, deep + 1);
if (rc)
return rc;
}
@ -313,14 +314,430 @@ mdbx_env_pgwalk(MDB_txn *txn, MDBX_pgvisitor_func* visitor, void* user)
rc = visitor(0, 2, user, "lmdb", "meta", 2, sizeof(MDB_meta)*2, PAGEHDRSZ*2,
(txn->mt_env->me_psize - sizeof(MDB_meta) - PAGEHDRSZ) *2);
if (! rc)
rc = mdb_env_walk(&ctx, "free", txn->mt_dbs[FREE_DBI].md_root, 0, 0);
rc = mdb_env_walk(&ctx, "free", txn->mt_dbs[FREE_DBI].md_root, 0);
if (! rc)
rc = mdb_env_walk(&ctx, "main", txn->mt_dbs[MAIN_DBI].md_root, 0, 0);
rc = mdb_env_walk(&ctx, "main", txn->mt_dbs[MAIN_DBI].md_root, 0);
if (! rc)
rc = visitor(P_INVALID, 0, user, NULL, NULL, 0, 0, 0, 0);
return rc;
}
int mdbx_canary_put(MDB_txn *txn, const mdbx_canary* canary)
{
if (unlikely(!txn))
return EINVAL;
if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE))
return MDB_VERSION_MISMATCH;
if (unlikely(F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)))
return EACCES;
if (likely(canary)) {
txn->mt_canary.x = canary->x;
txn->mt_canary.y = canary->y;
txn->mt_canary.z = canary->z;
}
txn->mt_canary.v = txn->mt_txnid;
return MDB_SUCCESS;
}
size_t mdbx_canary_get(MDB_txn *txn, mdbx_canary* canary)
{
if(unlikely(!txn || txn->mt_signature != MDBX_MT_SIGNATURE))
return 0;
if (likely(canary))
*canary = txn->mt_canary;
return txn->mt_txnid;
}
int mdbx_cursor_on_first(MDB_cursor *mc)
{
if (unlikely(mc == NULL))
return EINVAL;
if (unlikely(mc->mc_signature != MDBX_MC_SIGNATURE))
return MDB_VERSION_MISMATCH;
if (!(mc->mc_flags & C_INITIALIZED))
return MDBX_RESULT_FALSE;
unsigned i;
for(i = 0; i < mc->mc_snum; ++i) {
if (mc->mc_ki[i])
return MDBX_RESULT_FALSE;
}
return MDBX_RESULT_TRUE;
}
int mdbx_cursor_on_last(MDB_cursor *mc)
{
if (unlikely(mc == NULL))
return EINVAL;
if (unlikely(mc->mc_signature != MDBX_MC_SIGNATURE))
return MDB_VERSION_MISMATCH;
if (!(mc->mc_flags & C_INITIALIZED))
return MDBX_RESULT_FALSE;
unsigned i;
for(i = 0; i < mc->mc_snum; ++i) {
unsigned nkeys = NUMKEYS(mc->mc_pg[i]);
if (mc->mc_ki[i] < nkeys - 1)
return MDBX_RESULT_FALSE;
}
return MDBX_RESULT_TRUE;
}
int mdbx_cursor_eof(MDB_cursor *mc)
{
if (unlikely(mc == NULL))
return EINVAL;
if (unlikely(mc->mc_signature != MDBX_MC_SIGNATURE))
return MDB_VERSION_MISMATCH;
if ((mc->mc_flags & C_INITIALIZED) == 0)
return MDBX_RESULT_TRUE;
if (mc->mc_snum == 0)
return MDBX_RESULT_TRUE;
if ((mc->mc_flags & C_EOF)
&& mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top]))
return MDBX_RESULT_TRUE;
return MDBX_RESULT_FALSE;
}
static int mdbx_is_samedata(const MDB_val* a, const MDB_val* b) {
return a->iov_len == b->iov_len
&& memcmp(a->iov_base, b->iov_base, a->iov_len) == 0;
}
/* Позволяет обновить или удалить существующую запись с получением
* в old_data предыдущего значения данных. При этом если new_data равен
* нулю, то выполняется удаление, иначе обновление/вставка.
*
* Текущее значение может находиться в уже измененной (грязной) странице.
* В этом случае страница будет перезаписана при обновлении, а само старое
* значение утрачено. Поэтому исходно в old_data должен быть передан
* дополнительный буфер для копирования старого значения.
* Если переданный буфер слишком мал, то функция вернет -1, установив
* old_data->iov_len в соответствующее значение.
*
* Для не-уникальных ключей также возможен второй сценарий использования,
* когда посредством old_data из записей с одинаковым ключом для
* удаления/обновления выбирается конкретная. Для выбора этого сценария
* во flags следует одновременно указать MDB_CURRENT и MDB_NOOVERWRITE.
* Именно эта комбинация выбрана, так как она лишена смысла, и этим позволяет
* идентифицировать запрос такого сценария.
*
* Функция может быть замещена соответствующими операциями с курсорами
* после двух доработок (TODO):
* - внешняя аллокация курсоров, в том числе на стеке (без malloc).
* - получения статуса страницы по адресу (знать о P_DIRTY).
*/
int mdbx_replace(MDB_txn *txn, MDB_dbi dbi,
MDB_val *key, MDB_val *new_data, MDB_val *old_data, unsigned flags)
{
MDB_cursor mc;
MDB_xcursor mx;
if (unlikely(!key || !old_data || !txn || old_data == new_data))
return EINVAL;
if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE))
return MDB_VERSION_MISMATCH;
if (unlikely(old_data->iov_base == NULL && old_data->iov_len))
return EINVAL;
if (unlikely(new_data == NULL && !(flags & MDB_CURRENT)))
return EINVAL;
if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)))
return EINVAL;
if (unlikely(flags & ~(MDB_NOOVERWRITE|MDB_NODUPDATA|MDB_RESERVE|MDB_APPEND|MDB_APPENDDUP|MDB_CURRENT)))
return EINVAL;
if (unlikely(txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED)))
return (txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN;
mdb_cursor_init(&mc, txn, dbi, &mx);
mc.mc_next = txn->mt_cursors[dbi];
txn->mt_cursors[dbi] = &mc;
int rc;
MDB_val present_key = *key;
if (F_ISSET(flags, MDB_CURRENT | MDB_NOOVERWRITE)) {
/* в old_data значение для выбора конкретного дубликата */
if (unlikely(!(txn->mt_dbs[dbi].md_flags & MDB_DUPSORT))) {
rc = EINVAL;
goto bailout;
}
/* убираем лишний бит, он был признаком запрошенного режима */
flags -= MDB_NOOVERWRITE;
rc = mdbx_cursor_get(&mc, &present_key, old_data, MDB_GET_BOTH);
if (rc != MDB_SUCCESS)
goto bailout;
if (new_data) {
/* обновление конкретного дубликата */
if (mdbx_is_samedata(old_data, new_data))
/* если данные совпадают, то ничего делать не надо */
goto bailout;
#if 0 /* LY: исправлено в mdbx_cursor_put(), здесь в качестве памятки */
MDB_node *leaf = NODEPTR(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]);
if (F_ISSET(leaf->mn_flags, F_DUPDATA)
&& mc.mc_xcursor->mx_db.md_entries > 1) {
/* Если у ключа больше одного значения, то
* сначала удаляем найденое "старое" значение.
*
* Этого можно не делать, так как MDBX уже
* обучен корректно обрабатывать такие ситуации.
*
* Однако, следует помнить, что в LMDB при
* совпадении размера данных, значение будет
* просто перезаписано с нарушением
* упорядоченности, что сломает поиск. */
rc = mdbx_cursor_del(&mc, 0);
if (rc != MDB_SUCCESS)
goto bailout;
flags -= MDB_CURRENT;
}
#endif
}
} else {
/* в old_data буфер для сохранения предыдущего значения */
if (unlikely(new_data && old_data->iov_base == new_data->iov_base))
return EINVAL;
MDB_val present_data;
rc = mdbx_cursor_get(&mc, &present_key, &present_data, MDB_SET_KEY);
if (unlikely(rc != MDB_SUCCESS)) {
old_data->iov_base = NULL;
old_data->iov_len = rc;
if (rc != MDB_NOTFOUND || (flags & MDB_CURRENT))
goto bailout;
} else if (flags & MDB_NOOVERWRITE) {
rc = MDB_KEYEXIST;
*old_data = present_data;
goto bailout;
} else {
MDB_page *page = mc.mc_pg[mc.mc_top];
if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT) {
if (flags & MDB_CURRENT) {
/* для не-уникальных ключей позволяем update/delete только если ключ один */
MDB_node *leaf = NODEPTR(page, mc.mc_ki[mc.mc_top]);
if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
mdb_tassert(txn, XCURSOR_INITED(&mc) && mc.mc_xcursor->mx_db.md_entries > 1);
if (mc.mc_xcursor->mx_db.md_entries > 1) {
rc = MDBX_EMULTIVAL;
goto bailout;
}
}
/* если данные совпадают, то ничего делать не надо */
if (new_data && mdbx_is_samedata(&present_data, new_data)) {
*old_data = *new_data;
goto bailout;
}
/* В оригинальной LMDB фладок MDB_CURRENT здесь приведет
* к замене данных без учета MDB_DUPSORT сортировки,
* но здесь это в любом случае допустимо, так как мы
* проверили что для ключа есть только одно значение. */
} else if ((flags & MDB_NODUPDATA) && mdbx_is_samedata(&present_data, new_data)) {
/* если данные совпадают и установлен MDB_NODUPDATA */
rc = MDB_KEYEXIST;
goto bailout;
}
} else {
/* если данные совпадают, то ничего делать не надо */
if (new_data && mdbx_is_samedata(&present_data, new_data)) {
*old_data = *new_data;
goto bailout;
}
flags |= MDB_CURRENT;
}
if (page->mp_flags & P_DIRTY) {
if (unlikely(old_data->iov_len < present_data.iov_len)) {
old_data->iov_base = NULL;
old_data->iov_len = present_data.iov_len;
rc = MDBX_RESULT_TRUE;
goto bailout;
}
memcpy(old_data->iov_base, present_data.iov_base, present_data.iov_len);
old_data->iov_len = present_data.iov_len;
} else {
*old_data = present_data;
}
}
}
if (likely(new_data))
rc = mdbx_cursor_put(&mc, key, new_data, flags);
else
rc = mdbx_cursor_del(&mc, 0);
bailout:
txn->mt_cursors[dbi] = mc.mc_next;
return rc;
}
int
mdbx_get_ex(MDB_txn *txn, MDB_dbi dbi,
MDB_val *key, MDB_val *data, int* values_count)
{
DKBUF;
mdb_debug("===> get db %u key [%s]", dbi, DKEY(key));
if (unlikely(!key || !data || !txn))
return EINVAL;
if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE))
return MDB_VERSION_MISMATCH;
if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)))
return EINVAL;
if (unlikely(txn->mt_flags & MDB_TXN_BLOCKED))
return MDB_BAD_TXN;
MDB_cursor mc;
MDB_xcursor mx;
mdb_cursor_init(&mc, txn, dbi, &mx);
int exact = 0;
int rc = mdb_cursor_set(&mc, key, data, MDB_SET_KEY, &exact);
if (unlikely(rc != MDB_SUCCESS)) {
if (rc == MDB_NOTFOUND && values_count)
*values_count = 0;
return rc;
}
if (values_count) {
*values_count = 1;
if (mc.mc_xcursor != NULL) {
MDB_node *leaf = NODEPTR(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]);
if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
mdb_tassert(txn, mc.mc_xcursor == &mx
&& (mx.mx_cursor.mc_flags & C_INITIALIZED));
*values_count = mx.mx_db.md_entries;
}
}
}
return MDB_SUCCESS;
}
/* Функция сообщает находится ли указанный адрес в "грязной" странице у
* заданной пишущей транзакции. В конечном счете это позволяет избавиться от
* лишнего копирования данных из НЕ-грязных страниц.
*
* "Грязные" страницы - это те, которые уже были изменены в ходе пишущей
* транзакции. Соответственно, какие-либо дальнейшие изменения могут привести
* к перезаписи таких страниц. Поэтому все функции, выполняющие изменения, в
* качестве аргументов НЕ должны получать указатели на данные в таких
* страницах. В свою очередь "НЕ грязные" страницы перед модификацией будут
* скопированы.
*
* Другими словами, данные из "грязных" страниц должны быть либо скопированы
* перед передачей в качестве аргументов для дальнейших модификаций, либо
* отвергнуты на стадии проверки корректности аргументов.
*
* Таким образом, функция позволяет как избавится от лишнего копирования,
* так и выполнить более полную проверку аргументов.
*
* ВАЖНО: Передаваемый указатель должен указывать на начало данных. Только
* так гарантируется что актуальный заголовок страницы будет физически
* расположен в той-же странице памяти, в том числе для многостраничных
* P_OVERFLOW страниц с длинными данными. */
int mdbx_is_dirty(const MDB_txn *txn, const void* ptr)
{
if (unlikely(!txn))
return EINVAL;
if(unlikely(txn->mt_signature != MDBX_MT_SIGNATURE))
return MDB_VERSION_MISMATCH;
if (unlikely(txn->mt_flags & MDB_TXN_RDONLY))
return MDB_BAD_TXN;
const MDB_env *env = txn->mt_env;
const uintptr_t mask = ~(uintptr_t) (env->me_psize - 1);
const MDB_page *page = (const MDB_page *) ((uintptr_t) ptr & mask);
/* LY: Тут не всё хорошо с абсолютной достоверностью результата,
* так как флажок P_DIRTY в LMDB может означать не совсем то,
* что было исходно задумано, детали см в логике кода mdb_page_touch().
*
* Более того, в режиме БЕЗ WRITEMAP грязные страницы выделяются через
* malloc(), т.е. находятся вне mmap-диаппазона.
*
* Тем не менее, однозначно страница "не грязная" если:
* - адрес находится внутри mmap-диаппазона и в заголовке страницы
* нет флажка P_DIRTY, то однозначно страница "не грязная".
* - адрес вне mmap-диаппазона и его нет среди списка "грязных" страниц.
*/
if (env->me_map < (char*) page) {
const size_t used_size = env->me_psize * txn->mt_next_pgno;
if (env->me_map + used_size > (char*) page) {
/* страница внутри диапазона */
if (page->mp_flags & P_DIRTY)
return MDBX_RESULT_TRUE;
return MDBX_RESULT_FALSE;
}
/* Гипотетически здесь возможна ситуация, когда указатель адресует что-то
* в пределах mmap, но за границей распределенных страниц. Это тяжелая
* ошибка, которой не возможно добиться без каких-то мега-нарушений.
* Поэтому не проверяем этот случай кроме как assert-ом, ибо бестолку. */
mdb_tassert(txn, env->me_map + env->me_mapsize > (char*) page);
}
/* Страница вне mmap-диаппазона */
if (env->me_flags & MDB_WRITEMAP)
/* Если MDB_WRITEMAP, то результат уже ясен. */
return MDBX_RESULT_FALSE;
/* Смотрим список грязных страниц у заданной транзакции. */
MDB_ID2 *list = txn->mt_u.dirty_list;
if (list) {
unsigned i, n = list[0].mid;
for (i = 1; i <= n; i++) {
const MDB_page *dirty = list[i].mptr;
if (dirty == page)
return MDBX_RESULT_TRUE;
}
}
/* При вложенных транзакциях, страница может быть в dirty-списке
* родительской транзакции, но в этом случае она будет скопирована перед
* изменением в текущей транзакции, т.е. относительно заданной транзакции
* проверяемый адрес "не грязный". */
return MDBX_RESULT_FALSE;
}
int mdbx_dbi_open_ex(MDB_txn *txn, const char *name, unsigned flags,
MDB_dbi *pdbi, MDB_cmp_func *keycmp, MDB_cmp_func *datacmp)
{
int rc = mdbx_dbi_open(txn, name, flags, pdbi);
if (likely(rc == MDB_SUCCESS)) {
MDB_dbi dbi = *pdbi;
unsigned flags = txn->mt_dbs[dbi].md_flags;
txn->mt_dbxs[dbi].md_cmp = keycmp ? keycmp : mdbx_default_keycmp(flags);
txn->mt_dbxs[dbi].md_dcmp = datacmp ? datacmp : mdbx_default_datacmp(flags);
}
return rc;
}
/* attribute support functions for Nexenta ***********************************/
static __inline int

42
mdbx.h
View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -211,6 +211,44 @@ typedef int MDBX_pgvisitor_func(size_t pgno, unsigned pgnumber, void* ctx,
const char* dbi, const char *type, int nentries,
int payload_bytes, int header_bytes, int unused_bytes);
int mdbx_env_pgwalk(MDB_txn *txn, MDBX_pgvisitor_func* visitor, void* ctx);
typedef struct mdbx_canary {
size_t x, y, z, v;
} mdbx_canary;
int mdbx_canary_put(MDB_txn *txn, const mdbx_canary* canary);
size_t mdbx_canary_get(MDB_txn *txn, mdbx_canary* canary);
/* Returns:
* - MDBX_RESULT_TRUE when no more data available
* or cursor not positioned;
* - MDBX_RESULT_FALSE when data available;
* - Otherwise the error code. */
int mdbx_cursor_eof(MDB_cursor *mc);
/* Returns: MDBX_RESULT_TRUE, MDBX_RESULT_FALSE or Error code. */
int mdbx_cursor_on_first(MDB_cursor *mc);
/* Returns: MDBX_RESULT_TRUE, MDBX_RESULT_FALSE or Error code. */
int mdbx_cursor_on_last(MDB_cursor *mc);
#define MDBX_EMULTIVAL (MDB_LAST_ERRCODE - 42)
#define MDBX_RESULT_FALSE MDB_SUCCESS
#define MDBX_RESULT_TRUE (-1)
int mdbx_replace(MDB_txn *txn, MDB_dbi dbi,
MDB_val *key, MDB_val *new_data, MDB_val *old_data, unsigned flags);
/* Same as mdbx_get(), but:
* 1) if values_count is not NULL, then returns the count
* of multi-values/duplicates for a given key.
* 2) updates the key for pointing to the actual key's data inside DB. */
int mdbx_get_ex(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data, int* values_count);
int mdbx_is_dirty(const MDB_txn *txn, const void* ptr);
int mdbx_dbi_open_ex(MDB_txn *txn, const char *name, unsigned flags,
MDB_dbi *dbi, MDB_cmp_func *keycmp, MDB_cmp_func *datacmp);
/** @} */
#ifdef __cplusplus

6
midl.c
View File

@ -2,9 +2,9 @@
* @brief ldap bdb back-end ID List functions */
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2000-2016 The OpenLDAP Foundation.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2000-2017 The OpenLDAP Foundation.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

6
midl.h
View File

@ -10,9 +10,9 @@
*/
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2000-2016 The OpenLDAP Foundation.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2000-2017 The OpenLDAP Foundation.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View File

@ -1,9 +1,9 @@
/* mtest.c - memory-mapped database tester/toy */
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2011-2016 Howard Chu, Symas Corp.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2011-2017 Howard Chu, Symas Corp.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -23,6 +23,8 @@
#include <sys/stat.h>
#include "mdbx.h"
#include <pthread.h>
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
@ -32,6 +34,18 @@
# define DBPATH "./testdb"
#endif
void* thread_entry(void *ctx)
{
MDB_env *env = ctx;
MDB_txn *txn;
int rc;
E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn));
mdb_txn_abort(txn);
return NULL;
}
int main(int argc,char * argv[])
{
int i = 0, j = 0, rc;
@ -60,7 +74,7 @@ int main(int argc,char * argv[])
}
E(mdb_env_create(&env));
E(mdb_env_set_maxreaders(env, 1));
E(mdb_env_set_maxreaders(env, 42));
E(mdb_env_set_mapsize(env, 10485760));
E(stat("/proc/self/exe", &exe_stat)?errno:0);
@ -184,6 +198,11 @@ int main(int argc,char * argv[])
mdb_cursor_close(cur2);
E(mdb_txn_commit(txn));
for(i = 0; i < 41; ++i) {
pthread_t thread;
pthread_create(&thread, NULL, thread_entry, env);
}
printf("Restarting cursor outside txn\n");
E(mdb_txn_begin(env, NULL, 0, &txn));
E(mdb_cursor_open(txn, dbi, &cursor));

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View File

@ -1,9 +1,9 @@
/* mtest2.c - memory-mapped database tester/toy */
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2011-2016 Howard Chu, Symas Corp.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2011-2017 Howard Chu, Symas Corp.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View File

@ -1,9 +1,9 @@
/* mtest3.c - memory-mapped database tester/toy */
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2011-2016 Howard Chu, Symas Corp.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2011-2017 Howard Chu, Symas Corp.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View File

@ -1,9 +1,9 @@
/* mtest4.c - memory-mapped database tester/toy */
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2011-2016 Howard Chu, Symas Corp.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2011-2017 Howard Chu, Symas Corp.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View File

@ -1,9 +1,9 @@
/* mtest5.c - memory-mapped database tester/toy */
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2011-2016 Howard Chu, Symas Corp.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2011-2017 Howard Chu, Symas Corp.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View File

@ -1,9 +1,9 @@
/* mtest6.c - memory-mapped database tester/toy */
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2011-2016 Howard Chu, Symas Corp.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2011-2017 Howard Chu, Symas Corp.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -57,7 +57,7 @@
#endif /* __must_check_result */
#ifndef __hot
# if defined(NDEBUG) && (defined(__GNUC__) && !defined(__clang__))
# if defined(__OPTIMIZE__) && (defined(__GNUC__) && !defined(__clang__))
# define __hot __attribute__((hot, optimize("O3")))
# elif defined(__GNUC__)
/* cland case, just put frequently used functions in separate section */
@ -68,7 +68,7 @@
#endif /* __hot */
#ifndef __cold
# if defined(NDEBUG) && (defined(__GNUC__) && !defined(__clang__))
# if defined(__OPTIMIZE__) && (defined(__GNUC__) && !defined(__clang__))
# define __cold __attribute__((cold, optimize("Os")))
# elif defined(__GNUC__)
/* cland case, just put infrequently used functions in separate section */
@ -79,7 +79,7 @@
#endif /* __cold */
#ifndef __flatten
# if defined(NDEBUG) && (defined(__GNUC__) || defined(__clang__))
# if defined(__OPTIMIZE__) && (defined(__GNUC__) || defined(__clang__))
# define __flatten __attribute__((flatten))
# else
# define __flatten

View File

@ -4,9 +4,9 @@
*/
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2012-2016 Howard Chu, Symas Corp.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2012-2017 Howard Chu, Symas Corp.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View File

@ -4,9 +4,9 @@
*/
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2012-2016 Howard Chu, Symas Corp.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2012-2017 Howard Chu, Symas Corp.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015 Vladimir Romanov <https://www.linkedin.com/in/vladimirromanov>, Yota Lab.
* Copyright 2016-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015 Vladimir Romanov <https://www.linkedin.com/in/vladimirromanov>, Yota Lab.
*
* This file is part of libmdbx.
*
@ -16,7 +16,6 @@
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <sys/time.h>

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2016 Leonid Yuriev <leo@yuriev.ru>.
* Copyright (c) 2015 Vladimir Romanov <https://www.linkedin.com/in/vladimirromanov>, Yota Lab.
* Copyright 2016-2017 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015 Vladimir Romanov <https://www.linkedin.com/in/vladimirromanov>, Yota Lab.
*
* This file is part of libmdbx.
*
@ -16,7 +16,6 @@
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <sys/time.h>