mirror of
https://github.com/isar/libmdbx.git
synced 2025-01-21 18:28:20 +08:00
mdbx: Merge branch 'master' into 'nexenta' branch.
Change-Id: Iade70bae46bc4ea4baac36b3ed86434519959274
This commit is contained in:
commit
c0c4742dba
15
.travis.yml
Normal file
15
.travis.yml
Normal 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
19
CHANGES
@ -1,16 +1,29 @@
|
|||||||
MDBX
|
MDBX
|
||||||
Add MDB_PREV_MULTIPLE
|
Add MDB_PREV_MULTIPLE
|
||||||
Fix MDB_CP_COMPACT (ITS#8209)
|
|
||||||
Add error MDB_PROBLEM, replace some MDB_CORRUPTED
|
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 mdb_env_cwalk cursor init (ITS#8424)
|
||||||
Fix robust mutexes on Solaris 10/11 (ITS#8339)
|
Fix robust mutexes on Solaris 10/11 (ITS#8339)
|
||||||
Fix MDB_GET_BOTH on non-dup record (ITS#8393)
|
Fix MDB_GET_BOTH on non-dup record (ITS#8393)
|
||||||
Optimize mdb_drop
|
Optimize mdb_drop
|
||||||
Fix xcursors after mdb_cursor_del (ITS#8406)
|
Fix xcursors after mdb_cursor_del (ITS#8406)
|
||||||
Fix MDB_NEXT_DUP after mdb_cursor_del (ITS#8412)
|
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)
|
LMDB 0.9.18 Release (2016/02/05)
|
||||||
already done for mdbx - Fix robust mutex detection on glibc 2.10-11 (ITS#8330)
|
already done for mdbx - Fix robust mutex detection on glibc 2.10-11 (ITS#8330)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
Copyright 2011-2017 Howard Chu, Symas Corp.
|
||||||
Copyright 2011-2016 Howard Chu, Symas Corp.
|
Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
2
Doxyfile
2
Doxyfile
@ -253,7 +253,7 @@ IDL_PROPERTY_SUPPORT = YES
|
|||||||
# member in the group (if any) for the other members of the group. By default
|
# member in the group (if any) for the other members of the group. By default
|
||||||
# all members of a group must be documented explicitly.
|
# 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
|
# 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
|
# the same type (for instance a group of public functions) to be put as a
|
||||||
|
10
Makefile
10
Makefile
@ -24,8 +24,8 @@ suffix ?=
|
|||||||
|
|
||||||
CC ?= gcc
|
CC ?= gcc
|
||||||
XCFLAGS ?= -DNDEBUG=1 -DMDB_DEBUG=0
|
XCFLAGS ?= -DNDEBUG=1 -DMDB_DEBUG=0
|
||||||
CFLAGS ?= -O2 -g3 -Wall -Werror -Wextra
|
CFLAGS ?= -O2 -g3 -Wall -Werror -Wextra -ffunction-sections
|
||||||
CFLAGS += -pthread $(XCFLAGS)
|
CFLAGS += -std=gnu99 -pthread $(XCFLAGS)
|
||||||
|
|
||||||
# LY: for ability to built with modern glibc,
|
# LY: for ability to built with modern glibc,
|
||||||
# but then run with the old
|
# but then run with the old
|
||||||
@ -207,13 +207,15 @@ bench: bench-lmdb.txt bench-mdbx.txt
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ci-rule = ( CC=$$(which $1); if [ -n "$$CC" ]; then \
|
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) 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" \
|
$(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 ); \
|
|| ( echo "FAILED"; cat $1.err >&2; exit 1 ); \
|
||||||
else echo "no $2 ($1) for probe"; fi; )
|
else echo "no $2 ($1) for probe"; fi; )
|
||||||
ci:
|
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); \
|
$(call ci-rule,$(CC),default C compiler); \
|
||||||
fi
|
fi
|
||||||
@$(call ci-rule,gcc,GCC)
|
@$(call ci-rule,gcc,GCC)
|
||||||
|
375
README.md
Normal file
375
README.md
Normal 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(). Что позволяет избавится от ряда
|
||||||
|
сложно обнаруживаемых ошибок.
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2015-2017 Howard Chu, Symas Corp.
|
||||||
* Copyright 2015 Howard Chu, Symas Corp.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
65
lmdb.h
65
lmdb.h
@ -1,7 +1,7 @@
|
|||||||
/** @file lmdb.h
|
/** @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
|
* @section intro_sec Introduction
|
||||||
* MDBX is a Btree-based database management library modeled loosely on the
|
* 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
|
* This does not use actual memory or disk space, but users may need
|
||||||
* to understand the difference so they won't be scared off.
|
* 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
|
* - By default, in versions before 0.9.10, unused portions of the data
|
||||||
* file might receive garbage data from memory freed by other code.
|
* file might receive garbage data from memory freed by other code.
|
||||||
* (This does not happen when using the #MDB_WRITEMAP flag.) As of
|
* (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>.
|
* @author Leonid Yuriev, 'ReOpen' initiative <https://github.com/ReOpen>.
|
||||||
* Howard Chu, Symas Corp. All rights reserved.
|
* Howard Chu, Symas Corp. All rights reserved.
|
||||||
*
|
*
|
||||||
* @copyright 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* @copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* 2011-2016 Howard Chu, Symas Corp. All rights reserved.
|
* 2011-2017 Howard Chu, Symas Corp. All rights reserved.
|
||||||
*
|
*
|
||||||
* ---
|
* ---
|
||||||
*
|
*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* @par Derived From:
|
* @par Derived From:
|
||||||
* This code is derived from LMDB engine written by Howard Chu, Symas Corporation.
|
* 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
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted only as authorized by the OpenLDAP
|
* 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)
|
MDB_VERINT(MDB_VERSION_MAJOR,MDB_VERSION_MINOR,MDB_VERSION_PATCH)
|
||||||
|
|
||||||
/** The release date of this library version */
|
/** 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 */
|
/** A stringifier for the version info */
|
||||||
#define MDB_VERSTR(a,b,c,d) "MDBX " #a "." #b "." #c ": (" d ", https://github.com/ReOpen/libmdbx)"
|
#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.
|
* For mdb_cursor_del: remove all duplicate data items.
|
||||||
*/
|
*/
|
||||||
#define MDB_NODUPDATA 0x20
|
#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
|
#define MDB_CURRENT 0x40
|
||||||
/** For put: Just reserve space for data, don't copy it. Return a
|
/** For put: Just reserve space for data, don't copy it. Return a
|
||||||
* pointer to the reserved space.
|
* 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
|
* The transaction handle is freed. It and its cursors must not be used
|
||||||
* again after this call, except with #mdb_cursor_renew().
|
* 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.
|
* Only write-transactions free cursors.
|
||||||
|
*
|
||||||
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
|
* @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
|
* @return A non-zero error value on failure and 0 on success. Some possible
|
||||||
* errors are:
|
* 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
|
* The transaction handle is freed. It and its cursors must not be used
|
||||||
* again after this call, except with #mdb_cursor_renew().
|
* 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.
|
* Only write-transactions free cursors.
|
||||||
|
*
|
||||||
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
|
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
|
||||||
*/
|
*/
|
||||||
int mdb_txn_abort(MDB_txn *txn);
|
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
|
* 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
|
* tells the library that the data items for this database are all the same
|
||||||
* size, which allows further optimizations in storage and retrieval. When
|
* 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
|
* all data items are the same size, the #MDB_GET_MULTIPLE, #MDB_NEXT_MULTIPLE
|
||||||
* cursor operations may be used to retrieve multiple items at once.
|
* and #MDB_PREV_MULTIPLE cursor operations may be used to retrieve multiple
|
||||||
|
* items at once.
|
||||||
* <li>#MDB_INTEGERDUP
|
* <li>#MDB_INTEGERDUP
|
||||||
* This option specifies that duplicate data items are binary integers,
|
* This option specifies that duplicate data items are binary integers,
|
||||||
* similar to #MDB_INTEGERKEY keys.
|
* 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.
|
/** @brief Delete items from a database.
|
||||||
*
|
*
|
||||||
* This function removes key/data pairs from the 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
|
* If the database does not support sorted duplicate data items
|
||||||
* (#MDB_DUPSORT) the data parameter is ignored.
|
* (#MDB_DUPSORT) the data parameter is ignored.
|
||||||
* If the database supports sorted duplicates and the data parameter
|
* If the database supports sorted duplicates and the data parameter
|
||||||
* is NULL, all of the duplicate data items for the key will be
|
* is NULL, all of the duplicate data items for the key will be
|
||||||
* deleted. Otherwise, if the data parameter is non-NULL
|
* deleted. Otherwise, if the data parameter is non-NULL
|
||||||
* only the matching data item will be deleted.
|
* only the matching data item will be deleted.
|
||||||
|
*
|
||||||
* This function will return #MDB_NOTFOUND if the specified key/data
|
* This function will return #MDB_NOTFOUND if the specified key/data
|
||||||
* pair is not in the database.
|
* pair is not in the database.
|
||||||
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
|
* @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
|
* A cursor cannot be used when its database handle is closed. Nor
|
||||||
* when its transaction has ended, except with #mdb_cursor_renew().
|
* when its transaction has ended, except with #mdb_cursor_renew().
|
||||||
* It can be discarded with #mdb_cursor_close().
|
* 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
|
* A cursor in a write-transaction can be closed before its transaction
|
||||||
* ends, and will otherwise be closed when its transaction ends.
|
* ends, and will otherwise be closed when its transaction ends.
|
||||||
* A cursor in a read-only transaction must be closed explicitly, before
|
* 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.
|
* #mdb_cursor_renew() before finally closing it.
|
||||||
* @note Earlier documentation said that cursors in every transaction
|
* @note Earlier documentation said that cursors in every transaction
|
||||||
* were closed when the transaction committed or aborted.
|
* were closed when the transaction committed or aborted.
|
||||||
|
*
|
||||||
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
|
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
|
||||||
* @param[in] dbi A database handle returned by #mdb_dbi_open()
|
* @param[in] dbi A database handle returned by #mdb_dbi_open()
|
||||||
* @param[out] cursor Address where the new #MDB_cursor handle will be stored
|
* @param[out] cursor Address where the new #MDB_cursor handle will be stored
|
||||||
|
35
mdb_chk.c
35
mdb_chk.c
@ -1,8 +1,8 @@
|
|||||||
/* mdbx_chk.c - memory-mapped database check tool */
|
/* mdbx_chk.c - memory-mapped database check tool */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
*
|
*
|
||||||
* This file is part of libmdbx.
|
* This file is part of libmdbx.
|
||||||
*
|
*
|
||||||
@ -52,7 +52,7 @@ flagbit dbflags[] = {
|
|||||||
|
|
||||||
static volatile sig_atomic_t gotsignal;
|
static volatile sig_atomic_t gotsignal;
|
||||||
|
|
||||||
static void signal_hanlder( int sig ) {
|
static void signal_handler( int sig ) {
|
||||||
(void) sig;
|
(void) sig;
|
||||||
gotsignal = 1;
|
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",
|
problem_add("page", pgno, "illegal header-length", "%zu < %i < %zu",
|
||||||
sizeof(long), header_bytes, stat.base.ms_psize - sizeof(long));
|
sizeof(long), header_bytes, stat.base.ms_psize - sizeof(long));
|
||||||
if (payload_bytes < 1) {
|
if (payload_bytes < 1) {
|
||||||
if (nentries > 0) {
|
if (nentries > 1) {
|
||||||
problem_add("page", pgno, "zero size-of-entry", "payload %i bytes, %i entries",
|
problem_add("page", pgno, "zero size-of-entry", "payload %i bytes, %i entries",
|
||||||
payload_bytes, nentries);
|
payload_bytes, nentries);
|
||||||
if ((size_t) header_bytes + unused_bytes < page_size) {
|
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);
|
fflush(NULL);
|
||||||
}
|
}
|
||||||
skipped_subdb++;
|
skipped_subdb++;
|
||||||
mdbx_dbi_close(env, dbi);
|
|
||||||
return MDB_SUCCESS;
|
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);
|
rc = mdbx_dbi_flags(txn, dbi, &flags);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
error(" - mdbx_dbi_flags failed, error %d %s\n", rc, mdbx_strerror(rc));
|
error(" - mdbx_dbi_flags failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||||
mdbx_dbi_close(env, dbi);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = mdbx_stat(txn, dbi, &ms, sizeof(ms));
|
rc = mdbx_stat(txn, dbi, &ms, sizeof(ms));
|
||||||
if (rc) {
|
if (rc) {
|
||||||
error(" - mdbx_stat failed, error %d %s\n", rc, mdbx_strerror(rc));
|
error(" - mdbx_stat failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||||
mdbx_dbi_close(env, dbi);
|
|
||||||
return rc;
|
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);
|
rc = mdbx_cursor_open(txn, dbi, &mc);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
error(" - mdbx_cursor_open failed, error %d %s\n", rc, mdbx_strerror(rc));
|
error(" - mdbx_cursor_open failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||||
mdbx_dbi_close(env, dbi);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,9 +487,7 @@ static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent)
|
|||||||
goto bailout;
|
goto bailout;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.mv_size == 0) {
|
if (key.mv_size > maxkeysize) {
|
||||||
problem_add("entry", record_count, "key with zero length", NULL);
|
|
||||||
} else if (key.mv_size > maxkeysize) {
|
|
||||||
problem_add("entry", record_count, "key length exceeds max-key-size",
|
problem_add("entry", record_count, "key length exceeds max-key-size",
|
||||||
"%zu > %zu", key.mv_size, maxkeysize);
|
"%zu > %zu", key.mv_size, maxkeysize);
|
||||||
} else if ((flags & MDB_INTEGERKEY)
|
} else if ((flags & MDB_INTEGERKEY)
|
||||||
@ -565,7 +559,6 @@ bailout:
|
|||||||
}
|
}
|
||||||
|
|
||||||
mdbx_cursor_close(mc);
|
mdbx_cursor_close(mc);
|
||||||
mdbx_dbi_close(env, dbi);
|
|
||||||
return rc || problems_count;
|
return rc || problems_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -660,13 +653,13 @@ int main(int argc, char *argv[])
|
|||||||
usage(prog);
|
usage(prog);
|
||||||
|
|
||||||
#ifdef SIGPIPE
|
#ifdef SIGPIPE
|
||||||
signal(SIGPIPE, signal_hanlder);
|
signal(SIGPIPE, signal_handler);
|
||||||
#endif
|
#endif
|
||||||
#ifdef SIGHUP
|
#ifdef SIGHUP
|
||||||
signal(SIGHUP, signal_hanlder);
|
signal(SIGHUP, signal_handler);
|
||||||
#endif
|
#endif
|
||||||
signal(SIGINT, signal_hanlder);
|
signal(SIGINT, signal_handler);
|
||||||
signal(SIGTERM, signal_hanlder);
|
signal(SIGTERM, signal_handler);
|
||||||
|
|
||||||
envname = argv[optind];
|
envname = argv[optind];
|
||||||
print("Running mdbx_chk for '%s' in %s mode...\n",
|
print("Running mdbx_chk for '%s' in %s mode...\n",
|
||||||
@ -686,7 +679,11 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
maxkeysize = rc;
|
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);
|
rc = mdbx_env_open_ex(env, envname, envflags, 0664, &exclusive);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
@ -747,7 +744,7 @@ int main(int argc, char *argv[])
|
|||||||
meta_lt(info.me_meta1_txnid, info.me_meta1_sign,
|
meta_lt(info.me_meta1_txnid, info.me_meta1_sign,
|
||||||
info.me_meta2_txnid, info.me_meta2_sign) ? "tail" : "head");
|
info.me_meta2_txnid, info.me_meta2_sign) ? "tail" : "head");
|
||||||
if (info.me_meta1_txnid > info.base.me_last_txnid)
|
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,
|
||||||
info.me_meta1_txnid, info.base.me_last_txnid);
|
info.me_meta1_txnid, info.base.me_last_txnid);
|
||||||
print("\n");
|
print("\n");
|
||||||
@ -757,7 +754,7 @@ int main(int argc, char *argv[])
|
|||||||
meta_lt(info.me_meta2_txnid, info.me_meta2_sign,
|
meta_lt(info.me_meta2_txnid, info.me_meta2_sign,
|
||||||
info.me_meta1_txnid, info.me_meta1_sign) ? "tail" : "head");
|
info.me_meta1_txnid, info.me_meta1_sign) ? "tail" : "head");
|
||||||
if (info.me_meta2_txnid > info.base.me_last_txnid)
|
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,
|
||||||
info.me_meta2_txnid, info.base.me_last_txnid);
|
info.me_meta2_txnid, info.base.me_last_txnid);
|
||||||
print("\n");
|
print("\n");
|
||||||
|
@ -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.
|
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||||
.TH MDB_COPY 1 "2014/06/20" "LMDB 0.9.14"
|
.TH MDB_COPY 1 "2014/06/20" "LMDB 0.9.14"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
/* mdb_copy.c - memory-mapped database backup tool */
|
/* mdb_copy.c - memory-mapped database backup tool */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2012-2017 Howard Chu, Symas Corp.
|
||||||
* Copyright 2012-2016 Howard Chu, Symas Corp.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.\" Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
.\" Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
.\" Copyright (c) 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
|
.\" Copyright 2014-2017 Howard Chu, Symas Corp. All Rights Reserved.
|
||||||
.\" Copyright 2014-2016 Howard Chu, Symas Corp. All Rights Reserved.
|
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
|
||||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||||
.TH MDB_DUMP 1 "2014/06/20" "LMDB 0.9.14"
|
.TH MDB_DUMP 1 "2014/06/20" "LMDB 0.9.14"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
/* mdb_dump.c - memory-mapped database dump tool */
|
/* mdb_dump.c - memory-mapped database dump tool */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2011-2017 Howard Chu, Symas Corp.
|
||||||
* Copyright 2011-2016 Howard Chu, Symas Corp.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
.TH MDB_LOAD 1 "2014/06/20" "LMDB 0.9.14"
|
.\" Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
.\" Copyright 2014-2016 Howard Chu, Symas Corp. All Rights Reserved.
|
.\" 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.
|
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||||
|
.TH MDB_LOAD 1 "2014/06/20" "LMDB 0.9.14"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
mdb_load \- LMDB environment import tool
|
mdb_load \- LMDB environment import tool
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
/* mdb_load.c - memory-mapped database load tool */
|
/* mdb_load.c - memory-mapped database load tool */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2011-2017 Howard Chu, Symas Corp.
|
||||||
* Copyright 2011-2016 Howard Chu, Symas Corp.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -252,7 +252,8 @@ badend:
|
|||||||
c2 += 2;
|
c2 += 2;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c1++; c2++;
|
/* copies are redundant when no escapes were used */
|
||||||
|
*c1++ = *c2++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.\" Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
.\" Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
.\" Copyright (c) 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
|
.\" Copyright 2012-2017 Howard Chu, Symas Corp. All Rights Reserved.
|
||||||
.\" Copyright 2012-2016 Howard Chu, Symas Corp. All Rights Reserved.
|
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
|
||||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||||
.TH MDB_STAT 1 "2014/06/20" "LMDB 0.9.14"
|
.TH MDB_STAT 1 "2014/06/20" "LMDB 0.9.14"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
/* mdb_stat.c - memory-mapped database status tool */
|
/* mdb_stat.c - memory-mapped database status tool */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2011-2017 Howard Chu, Symas Corp.
|
||||||
* Copyright 2011-2016 Howard Chu, Symas Corp.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
447
mdbx.c
447
mdbx.c
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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;
|
return MDB_VERSION_MISMATCH;
|
||||||
|
|
||||||
env->me_sync_threshold = bytes;
|
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
|
void __cold
|
||||||
@ -172,7 +172,7 @@ typedef struct mdb_walk_ctx {
|
|||||||
|
|
||||||
/** Depth-first tree traversal. */
|
/** Depth-first tree traversal. */
|
||||||
static int __cold
|
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;
|
MDB_page *mp;
|
||||||
int rc, i, nkeys;
|
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)
|
if (pg == P_INVALID)
|
||||||
return MDB_SUCCESS; /* empty db */
|
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)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
if (pg != mp->mp_p.p_pgno)
|
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;
|
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;
|
MDB_node *node;
|
||||||
|
|
||||||
if (IS_LEAF2(mp)) {
|
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;
|
payload_size += NODESIZE + node->mn_ksize;
|
||||||
|
|
||||||
if (IS_BRANCH(mp)) {
|
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)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(IS_LEAF(mp));
|
assert(IS_LEAF(mp));
|
||||||
if (node->mn_ksize < 1)
|
|
||||||
return MDB_CORRUPTED;
|
|
||||||
if (node->mn_flags & F_BIGDATA) {
|
if (node->mn_flags & F_BIGDATA) {
|
||||||
MDB_page *omp;
|
MDB_page *omp;
|
||||||
pgno_t *opg;
|
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);
|
payload_size += sizeof(pgno_t);
|
||||||
opg = NODEDATA(node);
|
opg = NODEDATA(node);
|
||||||
rc = mdb_page_get(ctx->mw_txn, *opg, &omp, NULL);
|
rc = mdb_page_get(&mc, *opg, &omp, NULL);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
if (*opg != omp->mp_p.p_pgno)
|
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);
|
MDB_db *db = NODEDATA(node);
|
||||||
char* name = NULL;
|
char* name = NULL;
|
||||||
|
|
||||||
if (NODEDSZ(node) < 1)
|
|
||||||
return MDB_CORRUPTED;
|
|
||||||
if (! (node->mn_flags & F_DUPDATA)) {
|
if (! (node->mn_flags & F_DUPDATA)) {
|
||||||
name = NODEKEY(node);
|
name = NODEKEY(node);
|
||||||
int namelen = (char*) db - name;
|
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;
|
name[namelen] = 0;
|
||||||
}
|
}
|
||||||
rc = mdb_env_walk(ctx, (name && name[0]) ? name : dbi,
|
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)
|
if (rc)
|
||||||
return 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,
|
rc = visitor(0, 2, user, "lmdb", "meta", 2, sizeof(MDB_meta)*2, PAGEHDRSZ*2,
|
||||||
(txn->mt_env->me_psize - sizeof(MDB_meta) - PAGEHDRSZ) *2);
|
(txn->mt_env->me_psize - sizeof(MDB_meta) - PAGEHDRSZ) *2);
|
||||||
if (! rc)
|
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)
|
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)
|
if (! rc)
|
||||||
rc = visitor(P_INVALID, 0, user, NULL, NULL, 0, 0, 0, 0);
|
rc = visitor(P_INVALID, 0, user, NULL, NULL, 0, 0, 0, 0);
|
||||||
return rc;
|
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 ***********************************/
|
/* attribute support functions for Nexenta ***********************************/
|
||||||
|
|
||||||
static __inline int
|
static __inline int
|
||||||
|
42
mdbx.h
42
mdbx.h
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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,
|
const char* dbi, const char *type, int nentries,
|
||||||
int payload_bytes, int header_bytes, int unused_bytes);
|
int payload_bytes, int header_bytes, int unused_bytes);
|
||||||
int mdbx_env_pgwalk(MDB_txn *txn, MDBX_pgvisitor_func* visitor, void* ctx);
|
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
|
#ifdef __cplusplus
|
||||||
|
6
midl.c
6
midl.c
@ -2,9 +2,9 @@
|
|||||||
* @brief ldap bdb back-end ID List functions */
|
* @brief ldap bdb back-end ID List functions */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2000-2017 The OpenLDAP Foundation.
|
||||||
* Copyright 2000-2016 The OpenLDAP Foundation.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
6
midl.h
6
midl.h
@ -10,9 +10,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2000-2017 The OpenLDAP Foundation.
|
||||||
* Copyright 2000-2016 The OpenLDAP Foundation.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
27
mtest0.c
27
mtest0.c
@ -1,9 +1,9 @@
|
|||||||
/* mtest.c - memory-mapped database tester/toy */
|
/* mtest.c - memory-mapped database tester/toy */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2011-2017 Howard Chu, Symas Corp.
|
||||||
* Copyright 2011-2016 Howard Chu, Symas Corp.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -23,6 +23,8 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include "mdbx.h"
|
#include "mdbx.h"
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
|
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
|
||||||
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
|
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
|
||||||
#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
|
#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
|
||||||
@ -32,6 +34,18 @@
|
|||||||
# define DBPATH "./testdb"
|
# define DBPATH "./testdb"
|
||||||
#endif
|
#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 main(int argc,char * argv[])
|
||||||
{
|
{
|
||||||
int i = 0, j = 0, rc;
|
int i = 0, j = 0, rc;
|
||||||
@ -60,7 +74,7 @@ int main(int argc,char * argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
E(mdb_env_create(&env));
|
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(mdb_env_set_mapsize(env, 10485760));
|
||||||
|
|
||||||
E(stat("/proc/self/exe", &exe_stat)?errno:0);
|
E(stat("/proc/self/exe", &exe_stat)?errno:0);
|
||||||
@ -184,6 +198,11 @@ int main(int argc,char * argv[])
|
|||||||
mdb_cursor_close(cur2);
|
mdb_cursor_close(cur2);
|
||||||
E(mdb_txn_commit(txn));
|
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");
|
printf("Restarting cursor outside txn\n");
|
||||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||||
|
4
mtest1.c
4
mtest1.c
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
6
mtest2.c
6
mtest2.c
@ -1,9 +1,9 @@
|
|||||||
/* mtest2.c - memory-mapped database tester/toy */
|
/* mtest2.c - memory-mapped database tester/toy */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2011-2017 Howard Chu, Symas Corp.
|
||||||
* Copyright 2011-2016 Howard Chu, Symas Corp.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
6
mtest3.c
6
mtest3.c
@ -1,9 +1,9 @@
|
|||||||
/* mtest3.c - memory-mapped database tester/toy */
|
/* mtest3.c - memory-mapped database tester/toy */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2011-2017 Howard Chu, Symas Corp.
|
||||||
* Copyright 2011-2016 Howard Chu, Symas Corp.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
6
mtest4.c
6
mtest4.c
@ -1,9 +1,9 @@
|
|||||||
/* mtest4.c - memory-mapped database tester/toy */
|
/* mtest4.c - memory-mapped database tester/toy */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2011-2017 Howard Chu, Symas Corp.
|
||||||
* Copyright 2011-2016 Howard Chu, Symas Corp.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
6
mtest5.c
6
mtest5.c
@ -1,9 +1,9 @@
|
|||||||
/* mtest5.c - memory-mapped database tester/toy */
|
/* mtest5.c - memory-mapped database tester/toy */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2011-2017 Howard Chu, Symas Corp.
|
||||||
* Copyright 2011-2016 Howard Chu, Symas Corp.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
6
mtest6.c
6
mtest6.c
@ -1,9 +1,9 @@
|
|||||||
/* mtest6.c - memory-mapped database tester/toy */
|
/* mtest6.c - memory-mapped database tester/toy */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2011-2017 Howard Chu, Symas Corp.
|
||||||
* Copyright 2011-2016 Howard Chu, Symas Corp.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
10
reopen.h
10
reopen.h
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -57,7 +57,7 @@
|
|||||||
#endif /* __must_check_result */
|
#endif /* __must_check_result */
|
||||||
|
|
||||||
#ifndef __hot
|
#ifndef __hot
|
||||||
# if defined(NDEBUG) && (defined(__GNUC__) && !defined(__clang__))
|
# if defined(__OPTIMIZE__) && (defined(__GNUC__) && !defined(__clang__))
|
||||||
# define __hot __attribute__((hot, optimize("O3")))
|
# define __hot __attribute__((hot, optimize("O3")))
|
||||||
# elif defined(__GNUC__)
|
# elif defined(__GNUC__)
|
||||||
/* cland case, just put frequently used functions in separate section */
|
/* cland case, just put frequently used functions in separate section */
|
||||||
@ -68,7 +68,7 @@
|
|||||||
#endif /* __hot */
|
#endif /* __hot */
|
||||||
|
|
||||||
#ifndef __cold
|
#ifndef __cold
|
||||||
# if defined(NDEBUG) && (defined(__GNUC__) && !defined(__clang__))
|
# if defined(__OPTIMIZE__) && (defined(__GNUC__) && !defined(__clang__))
|
||||||
# define __cold __attribute__((cold, optimize("Os")))
|
# define __cold __attribute__((cold, optimize("Os")))
|
||||||
# elif defined(__GNUC__)
|
# elif defined(__GNUC__)
|
||||||
/* cland case, just put infrequently used functions in separate section */
|
/* cland case, just put infrequently used functions in separate section */
|
||||||
@ -79,7 +79,7 @@
|
|||||||
#endif /* __cold */
|
#endif /* __cold */
|
||||||
|
|
||||||
#ifndef __flatten
|
#ifndef __flatten
|
||||||
# if defined(NDEBUG) && (defined(__GNUC__) || defined(__clang__))
|
# if defined(__OPTIMIZE__) && (defined(__GNUC__) || defined(__clang__))
|
||||||
# define __flatten __attribute__((flatten))
|
# define __flatten __attribute__((flatten))
|
||||||
# else
|
# else
|
||||||
# define __flatten
|
# define __flatten
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2012-2017 Howard Chu, Symas Corp.
|
||||||
* Copyright 2012-2016 Howard Chu, Symas Corp.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2012-2017 Howard Chu, Symas Corp.
|
||||||
* Copyright 2012-2016 Howard Chu, Symas Corp.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
4
wbench.c
4
wbench.c
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015,2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015,2016 Peter-Service R&D LLC.
|
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2016-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015 Vladimir Romanov <https://www.linkedin.com/in/vladimirromanov>, Yota Lab.
|
* Copyright 2015 Vladimir Romanov <https://www.linkedin.com/in/vladimirromanov>, Yota Lab.
|
||||||
*
|
*
|
||||||
* This file is part of libmdbx.
|
* This file is part of libmdbx.
|
||||||
*
|
*
|
||||||
@ -16,7 +16,6 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* 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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Leonid Yuriev <leo@yuriev.ru>.
|
* Copyright 2016-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||||
* Copyright (c) 2015 Vladimir Romanov <https://www.linkedin.com/in/vladimirromanov>, Yota Lab.
|
* Copyright 2015 Vladimir Romanov <https://www.linkedin.com/in/vladimirromanov>, Yota Lab.
|
||||||
*
|
*
|
||||||
* This file is part of libmdbx.
|
* This file is part of libmdbx.
|
||||||
*
|
*
|
||||||
@ -16,7 +16,6 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* 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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user