forked from blue/lmdbal
Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
ef86d0adf9 | |||
e88efb458f | |||
68ea7df6a9 | |||
56d35d4832 | |||
43d4900809 | |||
02946bbe98 | |||
7aaab5e807 | |||
649d967963 | |||
5670453314 | |||
2add0b1ae2 | |||
50a13d7290 | |||
75010c0bc6 | |||
4c5e4bb6ac | |||
f5d6fb9141 | |||
1083df36ee | |||
551495843d | |||
c3246fd2b5 | |||
8b7d548df6 | |||
7c26b09056 | |||
29b126c30e | |||
93ed15e1da | |||
75aafd2750 | |||
4fd3799c19 | |||
def2b8bc4d |
80
.gitea/workflows/aur.yml
Normal file
80
.gitea/workflows/aur.yml
Normal file
@ -0,0 +1,80 @@
|
||||
name: Release to AUR
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
package-name:
|
||||
required: true
|
||||
type: string
|
||||
description:
|
||||
required: true
|
||||
type: string
|
||||
depends:
|
||||
required: true
|
||||
type: string
|
||||
flags:
|
||||
required: true
|
||||
type: string
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
package-name:
|
||||
description: "The name under which the package is going to be packaged to AUR"
|
||||
type: string
|
||||
default: "lmdbal"
|
||||
description:
|
||||
description: "The description of the package"
|
||||
type: string
|
||||
default: "LMDB Abstraction Layer"
|
||||
depends:
|
||||
description: "Additional dependencies"
|
||||
type: string
|
||||
default: ""
|
||||
flags:
|
||||
description: "Additional CMake flags"
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
jobs:
|
||||
aur:
|
||||
name: Releasing ${{ inputs.package-name }} to AUR
|
||||
runs-on: archlinux
|
||||
steps:
|
||||
- name: Download the release tarball
|
||||
run: curl -sL ${{ gitea.server_url }}/${{ gitea.repository }}/archive/${{ gitea.event.release.tag_name }}.tar.gz --output tarball.tar.gz
|
||||
|
||||
- name: Calculate SHA256 for the tarball
|
||||
run: echo "tbSum=$(sha256sum tarball.tar.gz | cut -d ' ' -f 1)" >> $GITHUB_ENV
|
||||
|
||||
- name: Unarchive tarball
|
||||
run: tar -xvzf tarball.tar.gz
|
||||
|
||||
- name: Clone the AUR repository
|
||||
run: |
|
||||
echo "${{ secrets.DEPLOY_TO_AUR_PRIVATE_KEY }}" > key
|
||||
chmod 600 key
|
||||
GIT_SSH_COMMAND="ssh -i key -o 'IdentitiesOnly yes' -o 'StrictHostKeyChecking no'" git clone ssh://aur@aur.archlinux.org/${{ inputs.package-name }}.git aur
|
||||
chmod 777 -R aur
|
||||
cd aur
|
||||
git config user.name ${{ secrets.DEPLOY_TO_AUR_USER_NAME }}
|
||||
git config user.email ${{ secrets.DEPLOY_TO_AUR_EMAIL }}
|
||||
git checkout -b master || git checkout master
|
||||
|
||||
- name: Copy PKGBUILD to the directory
|
||||
run: cp lmdbal/packaging/Archlinux/PKGBUILD aur/
|
||||
|
||||
- name: Patch PKGBUILD file, and generate .SRCINFO
|
||||
working-directory: aur
|
||||
run: |
|
||||
sed -i "/sha256sums=/c\sha256sums=('${{ env.tbSum }}')" PKGBUILD
|
||||
sed -i "/pkgname=lmdbal/c\pkgname=${{ inputs.package-name }}" PKGBUILD
|
||||
sed -i "/pkgver=1.0.0/c\pkgver=${{ gitea.event.release.tag_name }}" PKGBUILD
|
||||
sed -i "/pkgdesc=\"LMDB Abstraction Layer\"/c\pkgdesc=\"${{ inputs.description }}\"" PKGBUILD
|
||||
sed -i "/depends=( 'lmdb' )/c\depends=( 'lmdb' ${{ inputs.depends }} )" PKGBUILD
|
||||
sed -i '/cmake . -D/s/$/ ${{ inputs.flags }}/' PKGBUILD
|
||||
sudo -u build makepkg --printsrcinfo > .SRCINFO
|
||||
|
||||
- name: Commit package to aur
|
||||
working-directory: aur
|
||||
run: |
|
||||
git add PKGBUILD .SRCINFO
|
||||
git commit -m "${{ gitea.event.release.body }}"
|
||||
GIT_SSH_COMMAND="ssh -i ../key -o 'IdentitiesOnly yes' -o 'StrictHostKeyChecking no'" git push origin master
|
@ -6,8 +6,22 @@ on:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
Archlinux:
|
||||
test-qt5:
|
||||
name: Test LMDBAL with qt5
|
||||
uses: ./.gitea/workflows/test.yml
|
||||
with:
|
||||
flags: -D QT_VERSION_MAJOR=5 -D LMDBAL_NAME=LMDBAL-QT5
|
||||
|
||||
test-qt6:
|
||||
name: Test LMDBAL with qt6
|
||||
uses: ./.gitea/workflows/test.yml
|
||||
with:
|
||||
flags: -D QT_VERSION_MAJOR=6 -D LMDBAL_NAME=LMDBAL-QT6
|
||||
|
||||
docs:
|
||||
name: Release documentation
|
||||
runs-on: archlinux
|
||||
needs: [test-qt5, test-qt6]
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
@ -17,16 +31,12 @@ jobs:
|
||||
|
||||
- name: Configure
|
||||
working-directory: ./build
|
||||
run: cmake .. -D BUILD_TESTS=True -D BUILD_DOC_HTML=True -D BUILD_DOC_XML=True -D BUILD_DOC_MAN=True -D BUILD_DOXYGEN_AWESOME=True -D QT_VERSION_MAJOR=5
|
||||
run: cmake .. BUILD_DOC_HTML=True -D BUILD_DOC_XML=True -D BUILD_DOC_MAN=True -D BUILD_DOXYGEN_AWESOME=True
|
||||
|
||||
- name: Build
|
||||
working-directory: ./build
|
||||
run: cmake --build .
|
||||
|
||||
- name: Run tests
|
||||
working-directory: ./build/test
|
||||
run: ./runUnitTests
|
||||
|
||||
- name: Copy docs via scp
|
||||
uses: appleboy/scp-action@master
|
||||
# working-directory: ./build/doc //doesn't work
|
||||
|
@ -5,42 +5,29 @@ on:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
Archlinux:
|
||||
runs-on: archlinux
|
||||
steps:
|
||||
- name: Download the release tarball
|
||||
run: curl -sL ${{ gitea.server_url }}/${{ gitea.repository }}/archive/${{ gitea.event.release.tag_name }}.tar.gz --output tarball.tar.gz
|
||||
qt5:
|
||||
name: Release qt5 build to AUR
|
||||
uses: ./.gitea/workflows/aur.yml
|
||||
with:
|
||||
package-name: lmdbal-qt5
|
||||
description: LMDB Abstraction Layer, qt5 version
|
||||
depends: 'qt5-base'
|
||||
flags: -D QT_VERSION_MAJOR=5 -D LMDBAL_NAME=LMDBALQT5
|
||||
secrets:
|
||||
DEPLOY_TO_AUR_PRIVATE_KEY: ${{ secrets.DEPLOY_TO_AUR_PRIVATE_KEY }}
|
||||
DEPLOY_TO_AUR_USER_NAME: ${{ secrets.DEPLOY_TO_AUR_USER_NAME }}
|
||||
DEPLOY_TO_AUR_EMAIL: ${{ secrets.DEPLOY_TO_AUR_EMAIL }}
|
||||
|
||||
- name: Calculate SHA256 for the tarball
|
||||
run: echo "tbSum=$(sha256sum tarball.tar.gz | cut -d ' ' -f 1)" >> $GITHUB_ENV
|
||||
|
||||
- name: Unarchive tarball
|
||||
run: tar -xvzf tarball.tar.gz
|
||||
|
||||
- name: Clone the AUR repository
|
||||
run: |
|
||||
echo "${{ secrets.DEPLOY_TO_AUR_PRIVATE_KEY }}" > key
|
||||
chmod 600 key
|
||||
GIT_SSH_COMMAND="ssh -i key -o 'IdentitiesOnly yes' -o 'StrictHostKeyChecking no'" git clone ssh://aur@aur.archlinux.org/lmdbal.git aur
|
||||
chmod 777 -R aur
|
||||
cd aur
|
||||
git config user.name ${{ secrets.DEPLOY_TO_AUR_USER_NAME }}
|
||||
git config user.email ${{ secrets.DEPLOY_TO_AUR_EMAIL }}
|
||||
|
||||
|
||||
- name: Copy PKGBUILD to the directory
|
||||
run: cp lmdbal/packaging/Archlinux/PKGBUILD aur/
|
||||
|
||||
- name: Put SHA256 sum to PKGBUILD file, and generate .SRCINFO
|
||||
working-directory: aur
|
||||
run: |
|
||||
sed -i "/sha256sums=/c\sha256sums=('${{ env.tbSum }}')" PKGBUILD
|
||||
sudo -u build makepkg --printsrcinfo > .SRCINFO
|
||||
|
||||
- name: Commit package to aur
|
||||
working-directory: aur
|
||||
run: |
|
||||
git add PKGBUILD .SRCINFO
|
||||
git commit -m "${{ gitea.event.release.body }}"
|
||||
GIT_SSH_COMMAND="ssh -i ../key -o 'IdentitiesOnly yes' -o 'StrictHostKeyChecking no'" git push
|
||||
qt6:
|
||||
name: Release qt6 build to AUR
|
||||
uses: ./.gitea/workflows/aur.yml
|
||||
with:
|
||||
package-name: lmdbal-qt6
|
||||
description: LMDB Abstraction Layer, qt6 version
|
||||
depends: 'qt6-base'
|
||||
flags: -D QT_VERSION_MAJOR=6 -D LMDBAL_NAME=LMDBALQT6
|
||||
secrets:
|
||||
DEPLOY_TO_AUR_PRIVATE_KEY: ${{ secrets.DEPLOY_TO_AUR_PRIVATE_KEY }}
|
||||
DEPLOY_TO_AUR_USER_NAME: ${{ secrets.DEPLOY_TO_AUR_USER_NAME }}
|
||||
DEPLOY_TO_AUR_EMAIL: ${{ secrets.DEPLOY_TO_AUR_EMAIL }}
|
||||
|
||||
|
36
.gitea/workflows/test.yml
Normal file
36
.gitea/workflows/test.yml
Normal file
@ -0,0 +1,36 @@
|
||||
name: Build and run unit tests for LMDBAL
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
flags:
|
||||
required: true
|
||||
type: string
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
flags:
|
||||
description: "Flags for CMake configure stage"
|
||||
type: string
|
||||
default: "lmdbal"
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Building and rinning unit tests
|
||||
runs-on: archlinux
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Make a build directory
|
||||
run: mkdir build
|
||||
|
||||
- name: Configure
|
||||
working-directory: ./build
|
||||
run: cmake .. -D BUILD_TESTS=True -D QT_VERSION_MAJOR= ${{ inputs.flags }}
|
||||
|
||||
- name: Build
|
||||
working-directory: ./build
|
||||
run: cmake --build .
|
||||
|
||||
- name: Run tests
|
||||
working-directory: ./build/test
|
||||
run: ./runUnitTests
|
14
CHANGELOG.md
14
CHANGELOG.md
@ -1,5 +1,19 @@
|
||||
# Changelog
|
||||
|
||||
# LMDBAL 0.6.0 (UNRELEASED)
|
||||
### Improvements
|
||||
- Less dereferencing
|
||||
- Transactions are now closed with the database
|
||||
- Cursors are now closed if the public transaction that opened them got closed
|
||||
|
||||
### Bug fixes
|
||||
- SIGSEGV on closing database with opened transactions
|
||||
|
||||
# LMDBAL 0.5.4 (November 30, 2024)
|
||||
### Improvements
|
||||
- Technical release just to resolve build issues
|
||||
- Now different flavours of the package can coexist in the system
|
||||
|
||||
# LMDBAL 0.5.3 (November 14, 2023)
|
||||
### Improvements
|
||||
- Now you don't need to link agains lmdb, just linking against LMDBAL is enough
|
||||
|
@ -1,15 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(LMDBAL
|
||||
VERSION 0.5.4
|
||||
VERSION 0.6.0
|
||||
DESCRIPTION "LMDB (Lightning Memory-Mapped Database Manager) Abstraction Layer"
|
||||
LANGUAGES CXX
|
||||
)
|
||||
string(TOLOWER ${PROJECT_NAME} PROJECT_LOW)
|
||||
|
||||
cmake_policy(SET CMP0076 NEW)
|
||||
cmake_policy(SET CMP0079 NEW)
|
||||
|
||||
set(LMDBAL_NAME ${PROJECT_NAME} CACHE STRING "Override for library name and install path")
|
||||
|
||||
option(BUILD_STATIC "Builds library as static library" OFF)
|
||||
option(BUILD_TESTS "Builds tests" OFF)
|
||||
option(BUILD_DOC_MAN "Builds man page documentation" OFF)
|
||||
@ -25,6 +26,8 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
string(TOLOWER ${LMDBAL_NAME} LMDBAL_NAME_LOW)
|
||||
|
||||
if (NOT DEFINED QT_VERSION_MAJOR)
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
|
||||
endif()
|
||||
@ -38,9 +41,9 @@ if (NOT CMAKE_BUILD_TYPE)
|
||||
endif ()
|
||||
|
||||
if (BUILD_STATIC)
|
||||
add_library(${PROJECT_NAME} STATIC)
|
||||
add_library(${LMDBAL_NAME} STATIC)
|
||||
else ()
|
||||
add_library(${PROJECT_NAME} SHARED)
|
||||
add_library(${LMDBAL_NAME} SHARED)
|
||||
endif()
|
||||
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
@ -52,21 +55,21 @@ elseif (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
endif()
|
||||
|
||||
message("Compilation options: " ${COMPILE_OPTIONS})
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE ${COMPILE_OPTIONS})
|
||||
target_compile_options(${LMDBAL_NAME} PRIVATE ${COMPILE_OPTIONS})
|
||||
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY VERSION ${version})
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY SOVERSION 1)
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY EXPORT_NAME ${PROJECT_NAME})
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY INTERFACE_${PROJECT_NAME}_MAJOR_VERSION 1)
|
||||
set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY
|
||||
COMPATIBLE_INTERFACE_STRING ${PROJECT_NAME}_MAJOR_VERSION
|
||||
set_property(TARGET ${LMDBAL_NAME} PROPERTY VERSION ${version})
|
||||
set_property(TARGET ${LMDBAL_NAME} PROPERTY SOVERSION 1)
|
||||
set_property(TARGET ${LMDBAL_NAME} PROPERTY EXPORT_NAME ${LMDBAL_NAME})
|
||||
set_property(TARGET ${LMDBAL_NAME} PROPERTY INTERFACE_${LMDBAL_NAME}_MAJOR_VERSION 1)
|
||||
set_property(TARGET ${LMDBAL_NAME} APPEND PROPERTY
|
||||
COMPATIBLE_INTERFACE_STRING ${LMDBAL_NAME}_MAJOR_VERSION
|
||||
)
|
||||
|
||||
if (UNIX)
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${PROJECT_LOW}")
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY SKIP_BUILD_RPATH FALSE)
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY BUILD_WITH_INSTALL_RPATH FALSE)
|
||||
set_property(TARGET ${LMDBAL_NAME} PROPERTY INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${LMDBAL_NAME_LOW}")
|
||||
set_property(TARGET ${LMDBAL_NAME} PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
set_property(TARGET ${LMDBAL_NAME} PROPERTY SKIP_BUILD_RPATH FALSE)
|
||||
set_property(TARGET ${LMDBAL_NAME} PROPERTY BUILD_WITH_INSTALL_RPATH FALSE)
|
||||
endif()
|
||||
|
||||
add_subdirectory(src)
|
||||
@ -84,47 +87,47 @@ if (BUILD_TESTS)
|
||||
endif ()
|
||||
|
||||
target_include_directories(
|
||||
${PROJECT_NAME}
|
||||
${LMDBAL_NAME}
|
||||
PUBLIC
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_LOW}>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${LMDBAL_NAME_LOW}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/serializer>
|
||||
)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${Qt${QT_VERSION_MAJOR}_INCLUDE_DIRS})
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${Qt${QT_VERSION_MAJOR}Core_INCLUDE_DIRS})
|
||||
target_include_directories(${LMDBAL_NAME} PRIVATE ${Qt${QT_VERSION_MAJOR}_INCLUDE_DIRS})
|
||||
target_include_directories(${LMDBAL_NAME} PRIVATE ${Qt${QT_VERSION_MAJOR}Core_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(
|
||||
${PROJECT_NAME}
|
||||
${LMDBAL_NAME}
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
lmdb
|
||||
)
|
||||
|
||||
configure_package_config_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}Config.cmake"
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_LOW}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${LMDBAL_NAME_LOW}Config.cmake"
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LMDBAL_NAME_LOW}
|
||||
)
|
||||
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}ConfigVersion.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${LMDBAL_NAME_LOW}ConfigVersion.cmake"
|
||||
VERSION "${version}"
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
EXPORT ${PROJECT_LOW}Targets
|
||||
install(TARGETS ${LMDBAL_NAME}
|
||||
EXPORT ${LMDBAL_NAME_LOW}Targets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_LOW}
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${LMDBAL_NAME_LOW}
|
||||
)
|
||||
|
||||
install(EXPORT ${PROJECT_LOW}Targets
|
||||
FILE ${PROJECT_LOW}Targets.cmake
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_LOW}
|
||||
install(EXPORT ${LMDBAL_NAME_LOW}Targets
|
||||
FILE ${LMDBAL_NAME_LOW}Targets.cmake
|
||||
NAMESPACE ${LMDBAL_NAME}::
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LMDBAL_NAME_LOW}
|
||||
)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}Config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}ConfigVersion.cmake"
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_LOW}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${LMDBAL_NAME_LOW}Config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${LMDBAL_NAME_LOW}ConfigVersion.cmake"
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LMDBAL_NAME_LOW}
|
||||
)
|
||||
|
@ -1,14 +1,15 @@
|
||||
# LMDBAL - Lightning Memory Data Base Abstraction Level
|
||||
|
||||
[![AUR license](https://img.shields.io/aur/license/lmdbal?style=flat-square)](https://git.macaw.me/blue/lmdbal/raw/branch/master/LICENSE.md)
|
||||
[![AUR version](https://img.shields.io/aur/version/lmdbal?style=flat-square)](https://aur.archlinux.org/packages/lmdbal/)
|
||||
[![AUR qt5 version](https://img.shields.io/aur/version/lmdbal-qt5?style=flat-square)](https://aur.archlinux.org/packages/lmdbal-qt5/)
|
||||
[![AUR qt6 version](https://img.shields.io/aur/version/lmdbal-qt6?style=flat-square)](https://aur.archlinux.org/packages/lmdbal-qt6/)
|
||||
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me)
|
||||
[![Documentation](https://img.shields.io/badge/Documentation-HTML-green)](https://macaw.me/lmdbal/doc/html)
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- a compiler (c++ would do)
|
||||
- Qt 5 or higher (qt5-base would do)
|
||||
- a c++ compiler (g++ would do)
|
||||
- Qt 5 or 6 or higher (qt5-base or qt6-base would do)
|
||||
- lmdb
|
||||
- CMake 3.16 or higher
|
||||
- Doxygen (optional, for documentation)
|
||||
@ -84,6 +85,7 @@ Here is the list of keys you can pass to configuration phase of `cmake ..`:
|
||||
- `BUILD_DOC` - `True` build doxygen documentation, `False` does not (default is `False`);
|
||||
- `BUILD_DOXYGEN_AWESOME` - `True` build doxygen awesome theme if `BUILD_DOC` is also `True` (default is `False`);
|
||||
- `QT_VERSION_MAJOR` - `5` links against Qt5, `6` links agains Qt6, there is no default, so, if you didn't specify it the project will chose automatically;
|
||||
- `LMDBAL_NAME` - `LMDBAL` builds main target with this name, also install path witll be this name lowercase, usefull if you want to build `LMDBAL-QT6` not to conflict with `LMDBAL-QT5`;
|
||||
|
||||
#### Running tests
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/lmdbalTargets.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@LMDBAL_NAME_LOW@Targets.cmake")
|
||||
|
||||
check_required_components(lmdbal)
|
||||
check_required_components(@LMDBAL_NAME_LOW@)
|
||||
|
@ -1,23 +1,23 @@
|
||||
# Maintainer: Yury Gubich <blue@macaw.me>
|
||||
pkgname=lmdbal
|
||||
pkgver=0.5.4
|
||||
pkgrel=1
|
||||
pkgdesc="LMDB Abstraction Layer, qt5 version"
|
||||
pkgver=1.0.0
|
||||
pkgrel=4
|
||||
pkgdesc="LMDB Abstraction Layer"
|
||||
arch=('i686' 'x86_64')
|
||||
url="https://git.macaw.me/blue/lmdbal"
|
||||
license=('GPL3')
|
||||
depends=( 'lmdb' 'qt5-base')
|
||||
depends=( 'lmdb' )
|
||||
makedepends=('cmake>=3.16' 'gcc')
|
||||
optdepends=()
|
||||
|
||||
source=("$pkgname-$pkgver.tar.gz::https://git.macaw.me/blue/$pkgname/archive/$pkgver.tar.gz")
|
||||
source=("lmdbal-$pkgver-$pkgrel.tar.gz::https://git.macaw.me/blue/lmdbal/archive/$pkgver.tar.gz")
|
||||
sha256sums=('SKIP')
|
||||
build() {
|
||||
cd "$srcdir/$pkgname"
|
||||
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release -D QT_VERSION_MAJOR=5
|
||||
cd "$srcdir/lmdbal"
|
||||
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release
|
||||
cmake --build .
|
||||
}
|
||||
package() {
|
||||
cd "$srcdir/$pkgname"
|
||||
cd "$srcdir/lmdbal"
|
||||
DESTDIR="$pkgdir/" cmake --install .
|
||||
}
|
||||
|
@ -16,10 +16,11 @@ set(HEADERS
|
||||
cache.hpp
|
||||
operators.hpp
|
||||
transaction.h
|
||||
icursor.h
|
||||
)
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
|
||||
target_sources(${LMDBAL_NAME} PRIVATE ${SOURCES})
|
||||
|
||||
add_subdirectory(serializer)
|
||||
|
||||
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_LOW})
|
||||
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${LMDBAL_NAME_LOW})
|
||||
|
56
src/base.cpp
56
src/base.cpp
@ -43,7 +43,7 @@ LMDBAL::Base::Base(const QString& _name, uint16_t _mapSize):
|
||||
size(_mapSize),
|
||||
environment(),
|
||||
storages(),
|
||||
transactions(new Transactions())
|
||||
transactions()
|
||||
{}
|
||||
|
||||
/**
|
||||
@ -52,8 +52,6 @@ LMDBAL::Base::Base(const QString& _name, uint16_t _mapSize):
|
||||
LMDBAL::Base::~Base() {
|
||||
close();
|
||||
|
||||
delete transactions;
|
||||
|
||||
for (const std::pair<const std::string, iStorage*>& pair : storages)
|
||||
delete pair.second;
|
||||
}
|
||||
@ -68,14 +66,16 @@ LMDBAL::Base::~Base() {
|
||||
*/
|
||||
void LMDBAL::Base::close() {
|
||||
if (opened) {
|
||||
for (const LMDBAL::TransactionID id : *transactions)
|
||||
abortTransaction(id, emptyName);
|
||||
for (const std::pair<LMDBAL::TransactionID, LMDBAL::Transaction*> pair : transactions) {
|
||||
abortTransaction(pair.first, emptyName);
|
||||
pair.second->reset();
|
||||
}
|
||||
|
||||
for (const std::pair<const std::string, iStorage*>& pair : storages)
|
||||
pair.second->close();
|
||||
|
||||
mdb_env_close(environment);
|
||||
transactions->clear();
|
||||
transactions.clear();
|
||||
opened = false;
|
||||
}
|
||||
}
|
||||
@ -261,12 +261,13 @@ LMDBAL::WriteTransaction LMDBAL::Base::beginTransaction() {
|
||||
* \brief Aborts transaction
|
||||
*
|
||||
* Terminates transaction cancelling changes.
|
||||
* This is an optimal way to terminate read-only transactions
|
||||
* Every storage receives notification about this transaction being aborted.
|
||||
* This is an optimal way to abort public transactions
|
||||
*
|
||||
* \param[in] id - transaction ID you want to abort
|
||||
*
|
||||
* \exception LMDBAL::Closed - thrown if the database is closed
|
||||
* \exception LMDBAL::Unknown - thrown if transaction with given ID was not found or if something unexpected happened
|
||||
* \exception LMDBAL::Unknown - thrown if something unexpected happened
|
||||
*/
|
||||
void LMDBAL::Base::abortTransaction(LMDBAL::TransactionID id) const {
|
||||
return abortTransaction(id, emptyName);}
|
||||
@ -275,11 +276,13 @@ void LMDBAL::Base::abortTransaction(LMDBAL::TransactionID id) const {
|
||||
* \brief Commits transaction
|
||||
*
|
||||
* Terminates transaction applying changes.
|
||||
* Every storage receives notification about this transaction being committed.
|
||||
* This is an optimal way to commit public transactions
|
||||
*
|
||||
* \param[in] id - transaction ID you want to commit
|
||||
*
|
||||
* \exception LMDBAL::Closed - thrown if the database is closed
|
||||
* \exception LMDBAL::Unknown - thrown if transaction with given ID was not found or if something unexpected happened
|
||||
* \exception LMDBAL::Unknown - thrown if something unexpected happened
|
||||
*/
|
||||
void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id) {
|
||||
return commitTransaction(id, emptyName);}
|
||||
@ -287,7 +290,9 @@ void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id) {
|
||||
/**
|
||||
* \brief Begins read-only transaction
|
||||
*
|
||||
* This function is intended to be called from subordinate storage or cache
|
||||
* This function is intended to be called from subordinate storage, cache, transaction or cursor.
|
||||
* Every storage receives notification about this transaction being started.
|
||||
* This is an optimal way to begin read-only public transactions.
|
||||
*
|
||||
* \param[in] storageName - name of the storage/cache that you begin transaction from, needed just to inform if something went wrong
|
||||
*
|
||||
@ -301,7 +306,6 @@ LMDBAL::TransactionID LMDBAL::Base::beginReadOnlyTransaction(const std::string&
|
||||
throw Closed("beginReadOnlyTransaction", name, storageName);
|
||||
|
||||
TransactionID txn = beginPrivateReadOnlyTransaction(storageName);
|
||||
transactions->emplace(txn);
|
||||
for (const std::pair<const std::string, LMDBAL::iStorage*>& pair : storages)
|
||||
pair.second->transactionStarted(txn, true);
|
||||
|
||||
@ -311,7 +315,9 @@ LMDBAL::TransactionID LMDBAL::Base::beginReadOnlyTransaction(const std::string&
|
||||
/**
|
||||
* \brief Begins writable transaction
|
||||
*
|
||||
* This function is intended to be called from subordinate storage or cache
|
||||
* This function is intended to be called from subordinate storage, cache, transaction or cursor.
|
||||
* Every storage receives notification about this transaction being started.
|
||||
* This is an optimal way to begin public transactions.
|
||||
*
|
||||
* \param[in] storageName - name of the storage/cache that you begin transaction from, needed just to inform if something went wrong
|
||||
*
|
||||
@ -325,7 +331,6 @@ LMDBAL::TransactionID LMDBAL::Base::beginTransaction(const std::string& storageN
|
||||
throw Closed("beginTransaction", name, storageName);
|
||||
|
||||
TransactionID txn = beginPrivateTransaction(storageName);
|
||||
transactions->emplace(txn);
|
||||
for (const std::pair<const std::string, LMDBAL::iStorage*>& pair : storages)
|
||||
pair.second->transactionStarted(txn, false);
|
||||
|
||||
@ -336,55 +341,46 @@ LMDBAL::TransactionID LMDBAL::Base::beginTransaction(const std::string& storageN
|
||||
* \brief Aborts transaction
|
||||
*
|
||||
* Terminates transaction cancelling changes.
|
||||
* This is an optimal way to terminate read-only transactions.
|
||||
* This function is intended to be called from subordinate storage or cache
|
||||
* Every storage receives notification about this transaction being aborted.
|
||||
* This is an optimal way to abort public transactions.
|
||||
* This function is intended to be called from subordinate storage, cache, transaction or cursor
|
||||
*
|
||||
* \param[in] id - transaction ID you want to abort
|
||||
* \param[in] storageName - name of the storage/cache that you begin transaction from, needed just to inform if something went wrong
|
||||
*
|
||||
* \exception LMDBAL::Closed - thrown if the database is closed
|
||||
* \exception LMDBAL::Unknown - thrown if transaction with given ID was not found or if something unexpected happened
|
||||
* \exception LMDBAL::Unknown - thrown if something unexpected happened
|
||||
*/
|
||||
void LMDBAL::Base::abortTransaction(LMDBAL::TransactionID id, const std::string& storageName) const {
|
||||
if (!opened)
|
||||
throw Closed("abortTransaction", name, storageName);
|
||||
|
||||
Transactions::iterator itr = transactions->find(id);
|
||||
if (itr == transactions->end()) //TODO may be it's a good idea to make an exception class for this
|
||||
throw Unknown(name, "unable to abort transaction: transaction was not found", storageName);
|
||||
|
||||
abortPrivateTransaction(id, storageName);
|
||||
for (const std::pair<const std::string, LMDBAL::iStorage*>& pair : storages)
|
||||
pair.second->transactionAborted(id);
|
||||
|
||||
transactions->erase(itr);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Commits transaction
|
||||
*
|
||||
* Terminates transaction applying changes.
|
||||
* This function is intended to be called from subordinate storage or cache
|
||||
* Every storage receives notification about this transaction being committed.
|
||||
* This is an optimal way to commit public transactions
|
||||
* This function is intended to be called from subordinate storage, cache, transaction or cursor
|
||||
*
|
||||
* \param[in] id - transaction ID you want to commit
|
||||
* \param[in] storageName - name of the storage/cache that you begin transaction from, needed just to inform if something went wrong
|
||||
*
|
||||
* \exception LMDBAL::Closed - thrown if the database is closed
|
||||
* \exception LMDBAL::Unknown - thrown if transaction with given ID was not found or if something unexpected happened
|
||||
* \exception LMDBAL::Unknown - thrown if something unexpected happened
|
||||
*/
|
||||
void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id, const std::string& storageName) {
|
||||
if (!opened)
|
||||
throw Closed("abortTransaction", name, storageName);
|
||||
|
||||
Transactions::iterator itr = transactions->find(id);
|
||||
if (itr == transactions->end()) //TODO may be it's a good idea to make an exception class for this
|
||||
throw Unknown(name, "unable to commit transaction: transaction was not found", storageName);
|
||||
|
||||
commitPrivateTransaction(id, storageName);
|
||||
for (const std::pair<const std::string, LMDBAL::iStorage*>& pair : storages)
|
||||
pair.second->transactionCommited(id);
|
||||
|
||||
transactions->erase(itr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_BASE_H
|
||||
#define LMDBAL_BASE_H
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
@ -86,7 +85,7 @@ public:
|
||||
|
||||
private:
|
||||
typedef std::map<std::string, LMDBAL::iStorage*> Storages; /**<\brief Storage and Cache pointers are saved in the std::map*/
|
||||
typedef std::set<TransactionID> Transactions; /**<\brief Piblic transaction IDs are saved in the std::set*/
|
||||
typedef std::map<TransactionID, Transaction*> Transactions; /**<\brief Piblic transaction IDs are saved in the std::set*/
|
||||
|
||||
void commitTransaction(TransactionID id);
|
||||
void abortTransaction(TransactionID id) const;
|
||||
@ -106,7 +105,7 @@ private:
|
||||
uint16_t size; /**<\brief lmdb map size in MiB*/
|
||||
MDB_env* environment; /**<\brief lmdb environment handle*/
|
||||
Storages storages; /**<\brief Registered storages and caches*/
|
||||
Transactions* transactions; /**<\brief Active public transactions*/
|
||||
mutable Transactions transactions; /**<\brief Active public transactions*/
|
||||
|
||||
inline static const std::string emptyName = ""; /**<\brief Empty string for general fallback purposes*/
|
||||
};
|
||||
@ -215,5 +214,3 @@ template <class K, class V>
|
||||
LMDBAL::Cache<K, V>* LMDBAL::Base::getCache(const std::string& storageName) {
|
||||
return static_cast<Cache<K, V>*>(storages.at(storageName));
|
||||
}
|
||||
|
||||
#endif //LMDBAL_BASE_H
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_CACHE_H
|
||||
#define LMDBAL_CACHE_H
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
@ -134,5 +133,3 @@ protected:
|
||||
}
|
||||
|
||||
#include "cache.hpp"
|
||||
|
||||
#endif // LMDBAL_CACHE_H
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_CACHE_HPP
|
||||
#define LMDBAL_CACHE_HPP
|
||||
#pragma once
|
||||
|
||||
#include "cache.h"
|
||||
#include "exceptions.h"
|
||||
@ -898,5 +897,3 @@ void LMDBAL::Cache<K, V>::destroyTransactionEntry(const Entry& entry) const {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //LMDBAL_CACHE_HPP
|
||||
|
10
src/cursor.h
10
src/cursor.h
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_CURSOR_H
|
||||
#define LMDBAL_CURSOR_H
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
@ -25,11 +24,12 @@
|
||||
#include "base.h"
|
||||
#include "storage.h"
|
||||
#include "transaction.h"
|
||||
#include "icursor.h"
|
||||
|
||||
namespace LMDBAL {
|
||||
|
||||
template <class K, class V>
|
||||
class Cursor {
|
||||
class Cursor : public iCursor {
|
||||
friend class Storage<K, V>;
|
||||
private:
|
||||
enum State { /**<Cursor state:*/
|
||||
@ -72,9 +72,9 @@ public:
|
||||
void current(K& key, V& value) const;
|
||||
|
||||
private:
|
||||
virtual void terminated() override;
|
||||
void dropped();
|
||||
void freed();
|
||||
void terminated();
|
||||
void operateCursorRead(K& key, V& value, MDB_cursor_op operation, const std::string& methodName, const std::string& operationName) const;
|
||||
|
||||
private:
|
||||
@ -105,5 +105,3 @@ private:
|
||||
|
||||
|
||||
#include "cursor.hpp"
|
||||
|
||||
#endif //LMDBAL_CURSOR_H
|
||||
|
@ -16,10 +16,10 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_CURSOR_HPP
|
||||
#define LMDBAL_CURSOR_HPP
|
||||
#pragma once
|
||||
|
||||
#include "cursor.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
/**
|
||||
@ -85,6 +85,10 @@ LMDBAL::Cursor<K, V>::Cursor(Cursor&& other):
|
||||
if (id != 0)
|
||||
storage->cursors[id] = this;
|
||||
|
||||
if (state == openedPublic)
|
||||
storage->attachCursorToTransaction(id, cursor, this);
|
||||
|
||||
|
||||
other.freed();
|
||||
}
|
||||
|
||||
@ -111,6 +115,9 @@ LMDBAL::Cursor<K, V>& LMDBAL::Cursor<K, V>::operator = (Cursor&& other) {
|
||||
other.state = closed;
|
||||
|
||||
storage->cursors[id] = this;
|
||||
|
||||
if (state == openedPublic)
|
||||
storage->attachCursorToTransaction(id, cursor, this);
|
||||
}
|
||||
|
||||
return *this;
|
||||
@ -185,7 +192,18 @@ bool LMDBAL::Cursor<K, V>::empty () const {
|
||||
*/
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cursor<K, V>::terminated () {
|
||||
close(); //for now it's the same, but if I ever going to make writable cursor - here is where it's gonna be different
|
||||
switch (state) {
|
||||
case openedPublic:
|
||||
storage->_mdbCursorClose(cursor);
|
||||
state = closed;
|
||||
break;
|
||||
case openedPrivate:
|
||||
storage->closeCursorTransaction(cursor, true);
|
||||
state = closed;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -206,17 +224,11 @@ void LMDBAL::Cursor<K, V>::open () {
|
||||
if (empty())
|
||||
throw CursorEmpty(openCursorMethodName);
|
||||
|
||||
storage->ensureOpened(openCursorMethodName);
|
||||
switch (state) {
|
||||
case closed: {
|
||||
TransactionID txn = storage->beginReadOnlyTransaction();
|
||||
int result = storage->_mdbCursorOpen(txn, &cursor);
|
||||
if (result != MDB_SUCCESS)
|
||||
storage->throwUnknown(result, txn);
|
||||
|
||||
storage->transactionStarted(txn, true);
|
||||
case closed:
|
||||
storage->openCursorTransaction(&cursor, false);
|
||||
state = openedPrivate;
|
||||
} break;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -251,6 +263,7 @@ void LMDBAL::Cursor<K, V>::open (const Transaction& transaction) {
|
||||
if (result != MDB_SUCCESS)
|
||||
storage->throwUnknown(result);
|
||||
|
||||
storage->attachCursorToTransaction(id, cursor, this);
|
||||
state = openedPublic;
|
||||
} break;
|
||||
default:
|
||||
@ -282,21 +295,15 @@ void LMDBAL::Cursor<K, V>::renew () {
|
||||
|
||||
storage->ensureOpened(renewCursorMethodName);
|
||||
switch (state) {
|
||||
case openedPrivate: {
|
||||
TransactionID txn = storage->_mdbCursorTxn(cursor);
|
||||
storage->abortTransaction(txn);
|
||||
storage->transactionAborted(txn);
|
||||
[[fallthrough]];
|
||||
}
|
||||
case openedPublic: {
|
||||
TransactionID txn = storage->beginReadOnlyTransaction();
|
||||
int result = storage->_mdbCursorRenew(txn, cursor);
|
||||
if (result != MDB_SUCCESS)
|
||||
storage->throwUnknown(result, txn);
|
||||
|
||||
storage->transactionStarted(txn, true);
|
||||
case openedPrivate:
|
||||
storage->closeCursorTransaction(cursor, false);
|
||||
storage->openCursorTransaction(&cursor, true);
|
||||
break;
|
||||
case openedPublic:
|
||||
storage->disconnectCursorFromTransaction(id, cursor);
|
||||
storage->openCursorTransaction(&cursor, true);
|
||||
state = openedPrivate;
|
||||
} break;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -331,18 +338,22 @@ void LMDBAL::Cursor<K, V>::renew (const Transaction& transaction) {
|
||||
TransactionID txn = storage->extractTransactionId(transaction, renewCursorMethodName);
|
||||
switch (state) {
|
||||
case openedPrivate: {
|
||||
TransactionID txn = storage->_mdbCursorTxn(cursor);
|
||||
storage->abortTransaction(txn);
|
||||
storage->transactionAborted(txn);
|
||||
[[fallthrough]];
|
||||
}
|
||||
case openedPublic: {
|
||||
storage->closeCursorTransaction(cursor, false);
|
||||
int result = storage->_mdbCursorRenew(txn, cursor);
|
||||
if (result != MDB_SUCCESS)
|
||||
storage->throwUnknown(result);
|
||||
|
||||
storage->attachCursorToTransaction(id, cursor, this);
|
||||
state = openedPublic;
|
||||
} break;
|
||||
case openedPublic: {
|
||||
storage->disconnectCursorFromTransaction(id, cursor);
|
||||
int result = storage->_mdbCursorRenew(txn, cursor);
|
||||
if (result != MDB_SUCCESS)
|
||||
storage->throwUnknown(result);
|
||||
|
||||
storage->attachCursorToTransaction(id, cursor, this);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -361,19 +372,17 @@ void LMDBAL::Cursor<K, V>::renew (const Transaction& transaction) {
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cursor<K, V>::close () {
|
||||
switch (state) {
|
||||
case openedPublic: {
|
||||
case openedPublic:
|
||||
storage->disconnectCursorFromTransaction(id, cursor);
|
||||
storage->_mdbCursorClose(cursor);
|
||||
|
||||
state = closed;
|
||||
} break;
|
||||
case openedPrivate: {
|
||||
TransactionID txn = storage->_mdbCursorTxn(cursor);
|
||||
storage->_mdbCursorClose(cursor);
|
||||
storage->abortTransaction(txn);
|
||||
storage->transactionAborted(txn);
|
||||
break;
|
||||
case openedPrivate:
|
||||
storage->closeCursorTransaction(cursor, true);
|
||||
|
||||
state = closed;
|
||||
} break;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -482,7 +491,7 @@ void LMDBAL::Cursor<K, V>::prev (K& key, V& value) {
|
||||
* \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
|
||||
*/
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cursor<K, V>::current (K& key, V& value) const {
|
||||
void LMDBAL::Cursor<K, V>::current (K& key, V& value) const {
|
||||
operateCursorRead(key, value, MDB_GET_CURRENT, currentMethodName, currentOperationName);
|
||||
}
|
||||
|
||||
@ -657,5 +666,3 @@ void LMDBAL::Cursor<K, V>::operateCursorRead(
|
||||
else
|
||||
storage->discoveredRecord(key, value, storage->_mdbCursorTxn(cursor));
|
||||
}
|
||||
|
||||
#endif //LMDBAL_CURSOR_HPP
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_EXCEPTIONS_H
|
||||
#define LMDBAL_EXCEPTIONS_H
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
@ -41,7 +40,7 @@ public:
|
||||
/**
|
||||
* \brief Thrown if LMDBAL had issues creating or opening database directory
|
||||
*/
|
||||
class Directory: public Exception {
|
||||
class Directory : public Exception {
|
||||
public:
|
||||
/**
|
||||
* \brief Creates exception
|
||||
@ -237,5 +236,3 @@ private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LMDBAL_EXCEPTIONS_H
|
||||
|
32
src/icursor.h
Normal file
32
src/icursor.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* LMDB Abstraction Layer.
|
||||
* Copyright (C) 2023 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace LMDBAL {
|
||||
|
||||
class Transaction;
|
||||
|
||||
class iCursor {
|
||||
friend class Transaction;
|
||||
protected:
|
||||
virtual ~iCursor() {}
|
||||
virtual void terminated() = 0;
|
||||
};
|
||||
|
||||
}
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_OPERATORS_HPP
|
||||
#define LMDBAL_OPERATORS_HPP
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
@ -209,5 +208,3 @@ QDataStream& operator >> (QDataStream &in, std::list<K>& container) {
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
#endif //LMDBAL_OPERATORS_HPP
|
||||
|
@ -16,4 +16,4 @@ set(HEADERS
|
||||
serializer_qbytearray.hpp
|
||||
)
|
||||
|
||||
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_LOW})
|
||||
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${LMDBAL_NAME_LOW})
|
||||
|
@ -16,8 +16,8 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_SERIALIZER_H
|
||||
#define LMDBAL_SERIALIZER_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <cstring>
|
||||
|
||||
@ -68,5 +68,3 @@ private:
|
||||
#include "serializer_stdstring.hpp"
|
||||
#include "serializer_qstring.hpp"
|
||||
#include "serializer_qbytearray.hpp"
|
||||
|
||||
#endif // LMDBAL_SERIALIZER_H
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_SERIALIZER_HPP
|
||||
#define LMDBAL_SERIALIZER_HPP
|
||||
#pragma once
|
||||
|
||||
#include "serializer.h"
|
||||
|
||||
@ -159,5 +158,3 @@ MDB_val LMDBAL::Serializer<T>::getData() {
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
#endif //LMDBAL_SERIALIZER_HPP
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_SERIALIZER_DOUBLE_HPP
|
||||
#define LMDBAL_SERIALIZER_DOUBLE_HPP
|
||||
#pragma once
|
||||
|
||||
namespace LMDBAL {
|
||||
|
||||
@ -53,7 +52,5 @@ private:
|
||||
|
||||
}
|
||||
|
||||
#endif //LMDBAL_SERIALIZER_DOUBLE_HPP
|
||||
|
||||
|
||||
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_SERIALIZER_FLOAT_HPP
|
||||
#define LMDBAL_SERIALIZER_FLOAT_HPP
|
||||
#pragma once
|
||||
|
||||
namespace LMDBAL {
|
||||
|
||||
@ -52,8 +51,3 @@ private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LMDBAL_SERIALIZER_FLOAT_HPP
|
||||
|
||||
|
||||
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_SERIALIZER_INT16_HPP
|
||||
#define LMDBAL_SERIALIZER_INT16_HPP
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@ -54,5 +53,3 @@ private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LMDBAL_SERIALIZER_INT16_HPP
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_SERIALIZER_INT32_HPP
|
||||
#define LMDBAL_SERIALIZER_INT32_HPP
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@ -54,8 +53,3 @@ private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LMDBAL_SERIALIZER_INT32_HPP
|
||||
|
||||
|
||||
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_SERIALIZER_INT64_HPP
|
||||
#define LMDBAL_SERIALIZER_INT64_HPP
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@ -54,8 +53,3 @@ private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LMDBAL_SERIALIZER_INT64_HPP
|
||||
|
||||
|
||||
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_SERIALIZER_INT8_HPP
|
||||
#define LMDBAL_SERIALIZER_INT8_HPP
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@ -54,8 +53,3 @@ private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LMDBAL_SERIALIZER_INT8_HPP
|
||||
|
||||
|
||||
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_SERIALIZER_QBYTEARRAY_HPP
|
||||
#define LMDBAL_SERIALIZER_QBYTEARRAY_HPP
|
||||
#pragma once
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
@ -56,9 +55,3 @@ private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LMDBAL_SERIALIZER_QBYTEARRAY_HPP
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_SERIALIZER_QSTRING_HPP
|
||||
#define LMDBAL_SERIALIZER_QSTRING_HPP
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
@ -56,10 +55,3 @@ private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LMDBAL_SERIALIZER_QSTRING_HPP
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_SERIALIZER_STDSTRING_HPP
|
||||
#define LMDBAL_SERIALIZER_STDSTRING_HPP
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
@ -54,9 +53,3 @@ private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LMDBAL_SERIALIZER_STDSTRING_HPP
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_SERIALIZER_UINT16_HPP
|
||||
#define LMDBAL_SERIALIZER_UINT16_HPP
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@ -54,7 +53,3 @@ private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LMDBAL_SERIALIZER_UINT16_HPP
|
||||
|
||||
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_SERIALIZER_UINT32_HPP
|
||||
#define LMDBAL_SERIALIZER_UINT32_HPP
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@ -54,5 +53,3 @@ private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LMDBAL_SERIALIZER_UINT32_HPP
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_SERIALIZER_UINT64_HPP
|
||||
#define LMDBAL_SERIALIZER_UINT64_HPP
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@ -54,6 +53,3 @@ private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LMDBAL_SERIALIZER_UINT64_HPP
|
||||
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_SERIALIZER_UINT8_HPP
|
||||
#define LMDBAL_SERIALIZER_UINT8_HPP
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@ -54,8 +53,3 @@ private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LMDBAL_SERIALIZER_UINT8_HPP
|
||||
|
||||
|
||||
|
||||
|
@ -50,7 +50,7 @@ LMDBAL::iStorage::iStorage(Base* parent, const std::string& name, bool duplicate
|
||||
LMDBAL::iStorage::~iStorage() {}
|
||||
|
||||
/**
|
||||
* \brief A private virtual function I need to close each storage in the database
|
||||
* \brief A private virtual function to close each storage in the database
|
||||
*/
|
||||
void LMDBAL::iStorage::close() {
|
||||
mdb_dbi_close(db->environment, dbi);
|
||||
@ -159,6 +159,82 @@ LMDBAL::SizeType LMDBAL::iStorage::count() const {
|
||||
return amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Links cursor to the transaction it has been opened with
|
||||
*
|
||||
* This a service function is designed to be called by from LMDBAL::Cursor
|
||||
* Cursor must be opened by a public transaction
|
||||
*
|
||||
* \exception std::out_of_range thrown if LMDBAL::Transaction pointer wasn't found in the LMDBAL::Base
|
||||
*/
|
||||
|
||||
void LMDBAL::iStorage::attachCursorToTransaction (uint32_t cursorId, MDB_cursor* cursorHandle, iCursor* pointer) const {
|
||||
TransactionID txnID = _mdbCursorTxn(cursorHandle);
|
||||
Transaction* txn = db->transactions.at(txnID);
|
||||
txn->cursors[cursorId] = pointer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Opens a transaction that is ment to be private to LMDBAL::Cursor, but public to the LMDBAL::Storage
|
||||
*
|
||||
* This method is ment to be called from LMDBAL::Cursor
|
||||
*
|
||||
* \param[out] cursor - cursor handle
|
||||
* \param[in] renew - true if instead of opening cursor should be renewed
|
||||
*
|
||||
* \exception LMDBAL::Closed thrown if you try to open the cursor on a closed database
|
||||
* \exception LMDBAL::Unknown thrown if there was a problem opening the cursor by the lmdb, or to begin a transaction
|
||||
*/
|
||||
void LMDBAL::iStorage::openCursorTransaction (MDB_cursor** cursor, bool renew) const {
|
||||
ensureOpened(openCursorTransactionMethodName);
|
||||
|
||||
TransactionID txn = beginReadOnlyTransaction();
|
||||
int result;
|
||||
if (renew)
|
||||
result = _mdbCursorRenew(txn, *cursor);
|
||||
else
|
||||
result = _mdbCursorOpen(txn, cursor);
|
||||
|
||||
if (result != MDB_SUCCESS)
|
||||
throwUnknown(result, txn);
|
||||
|
||||
transactionStarted(txn, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Closes transaction that is private to LMDBAL::Cursor, but public to the LMDBAL::Storage
|
||||
*
|
||||
* This method is ment to be called from LMDBAL::Cursor
|
||||
*
|
||||
* \param[in] cursor - cursor handle
|
||||
* \param[in] closeCursor - true if the cursor should also get closed, false if you wish to leave it open
|
||||
*/
|
||||
void LMDBAL::iStorage::closeCursorTransaction (MDB_cursor* cursor, bool closeCursor) const {
|
||||
TransactionID txn = _mdbCursorTxn(cursor);
|
||||
if (closeCursor)
|
||||
_mdbCursorClose(cursor);
|
||||
|
||||
abortTransaction(txn);
|
||||
transactionAborted(txn);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disconnects cursor from the transaction it has been opened with
|
||||
*
|
||||
* This a service function is designed to be called by from LMDBAL::Cursor
|
||||
* Cursor must be still opened by a public transaction
|
||||
*
|
||||
* \exception std::out_of_range thrown if LMDBAL::Transaction pointer wasn't found in the LMDBAL::Base
|
||||
*/
|
||||
|
||||
void LMDBAL::iStorage::disconnectCursorFromTransaction (uint32_t cursorId, MDB_cursor* cursorHandle) const {
|
||||
TransactionID txnID = _mdbCursorTxn(cursorHandle);
|
||||
Transaction* txn = db->transactions.at(txnID);
|
||||
txn->cursors.erase(cursorId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Storage size (private transaction variant)
|
||||
*
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_STORAGE_H
|
||||
#define LMDBAL_STORAGE_H
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <cstring>
|
||||
@ -77,6 +76,11 @@ protected:
|
||||
virtual int drop(TransactionID transaction);
|
||||
virtual SizeType count(TransactionID txn) const;
|
||||
|
||||
void openCursorTransaction(MDB_cursor** cursor, bool renew = false) const;
|
||||
void closeCursorTransaction(MDB_cursor* cursor, bool closeCursor = false) const;
|
||||
void attachCursorToTransaction(uint32_t cursorId, MDB_cursor* cursorHandle, iCursor* pointer) const;
|
||||
void disconnectCursorFromTransaction(uint32_t cursorId, MDB_cursor* cursorHandle) const;
|
||||
|
||||
int _mdbOpen(MDB_txn* txn, unsigned int flags = 0);
|
||||
|
||||
int _mdbPut(MDB_txn* txn, MDB_val& key, MDB_val& data, unsigned int flags = 0);
|
||||
@ -104,24 +108,25 @@ public:
|
||||
virtual SizeType count(const Transaction& txn) const;
|
||||
|
||||
protected:
|
||||
MDB_dbi dbi; /**<\brief lmdb storage handle*/
|
||||
Base* db; /**<\brief parent database pointer (borrowed)*/
|
||||
const std::string name; /**<\brief this storage name*/
|
||||
const bool duplicates; /**<\brief true if storage supports duplicates*/
|
||||
MDB_dbi dbi; /**<\brief lmdb storage handle*/
|
||||
Base* db; /**<\brief parent database pointer (borrowed)*/
|
||||
const std::string name; /**<\brief this storage name*/
|
||||
const bool duplicates; /**<\brief true if storage supports duplicates*/
|
||||
|
||||
inline static const std::string dropMethodName = "drop"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string countMethodName = "count"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string flagsMethodName = "flags"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string dropMethodName = "drop"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string countMethodName = "count"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string flagsMethodName = "flags"; /**<\brief member function name, just for exceptions*/
|
||||
|
||||
inline static const std::string addRecordMethodName = "addRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string forceRecordMethodName = "forceRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string changeRecordMethodName = "changeRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string removeRecordMethodName = "removeRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string checkRecordMethodName = "checkRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string getRecordMethodName = "getRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string readAllMethodName = "readAllRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string replaceAllMethodName = "replaceAll"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string addRecordsMethodName = "addRecords"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string addRecordMethodName = "addRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string forceRecordMethodName = "forceRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string changeRecordMethodName = "changeRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string removeRecordMethodName = "removeRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string checkRecordMethodName = "checkRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string getRecordMethodName = "getRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string readAllMethodName = "readAllRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string replaceAllMethodName = "replaceAll"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string addRecordsMethodName = "addRecords"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string openCursorTransactionMethodName = "openCursorTransaction"; /**<\brief member function name, just for exceptions*/
|
||||
|
||||
protected:
|
||||
template <class K, class V>
|
||||
@ -199,5 +204,3 @@ protected:
|
||||
}
|
||||
|
||||
#include "storage.hpp"
|
||||
|
||||
#endif //LMDBAL_STORAGE_H
|
||||
|
@ -16,8 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMDBAL_STORAGE_HPP
|
||||
#define LMDBAL_STORAGE_HPP
|
||||
#pragma once
|
||||
|
||||
#include "storage.h"
|
||||
#include "exceptions.h"
|
||||
@ -1182,5 +1181,3 @@ template<>
|
||||
inline std::string LMDBAL::iStorage::toString(const std::string& value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
#endif //LMDBAL_STORAGE_HPP
|
||||
|
@ -1,3 +1,21 @@
|
||||
/*
|
||||
* LMDB Abstraction Layer.
|
||||
* Copyright (C) 2023 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "transaction.h"
|
||||
|
||||
/**
|
||||
@ -22,7 +40,8 @@
|
||||
LMDBAL::Transaction::Transaction():
|
||||
txn(nullptr),
|
||||
active(false),
|
||||
parent(nullptr)
|
||||
parent(nullptr),
|
||||
cursors()
|
||||
{}
|
||||
|
||||
/**
|
||||
@ -31,8 +50,11 @@ LMDBAL::Transaction::Transaction():
|
||||
LMDBAL::Transaction::Transaction(TransactionID txn, const Base* parent) :
|
||||
txn(txn),
|
||||
active(true),
|
||||
parent(parent)
|
||||
{}
|
||||
parent(parent),
|
||||
cursors()
|
||||
{
|
||||
parent->transactions[txn] = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Moves transaction to a new object
|
||||
@ -40,9 +62,14 @@ LMDBAL::Transaction::Transaction(TransactionID txn, const Base* parent) :
|
||||
LMDBAL::Transaction::Transaction(Transaction&& other):
|
||||
txn(other.txn),
|
||||
active(other.active),
|
||||
parent(other.parent)
|
||||
parent(other.parent),
|
||||
cursors(other.cursors)
|
||||
{
|
||||
other.active = false;
|
||||
if (active) {
|
||||
parent->transactions[txn] = this;
|
||||
|
||||
other.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,13 +83,21 @@ LMDBAL::Transaction::~Transaction() {
|
||||
* \brief Move-assigns transaction to the new object
|
||||
*/
|
||||
LMDBAL::Transaction& LMDBAL::Transaction::operator=(Transaction&& other) {
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
terminate();
|
||||
|
||||
txn = other.txn;
|
||||
active = other.active;
|
||||
parent = other.parent;
|
||||
cursors = other.cursors;
|
||||
|
||||
other.active = false;
|
||||
if (active) {
|
||||
parent->transactions[txn] = this;
|
||||
|
||||
other.reset();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -74,11 +109,32 @@ LMDBAL::Transaction& LMDBAL::Transaction::operator=(Transaction&& other) {
|
||||
*/
|
||||
void LMDBAL::Transaction::terminate() {
|
||||
if (active) {
|
||||
closeCursors();
|
||||
parent->abortTransaction(txn);
|
||||
active = false;
|
||||
parent->transactions.erase(txn);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Resets inner transaction properties to inactive state
|
||||
*/
|
||||
void LMDBAL::Transaction::reset() {
|
||||
active = false;
|
||||
txn = nullptr;
|
||||
parent = nullptr;
|
||||
cursors.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Closes attached curors;
|
||||
*/
|
||||
void LMDBAL::Transaction::closeCursors () {
|
||||
for (const std::pair<const uint32_t, iCursor*>& pair : cursors)
|
||||
pair.second->terminated();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns transaction states
|
||||
*
|
||||
@ -132,6 +188,12 @@ LMDBAL::WriteTransaction::WriteTransaction(WriteTransaction&& other):
|
||||
Transaction(std::move(other))
|
||||
{}
|
||||
|
||||
LMDBAL::WriteTransaction& LMDBAL::WriteTransaction::operator=(WriteTransaction&& other) {
|
||||
Transaction::operator=(std::move(other));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Aborts transaction cancelling all changes
|
||||
*
|
||||
@ -148,7 +210,9 @@ void LMDBAL::WriteTransaction::abort() {
|
||||
*/
|
||||
void LMDBAL::WriteTransaction::commit() {
|
||||
if (active) {
|
||||
closeCursors();
|
||||
const_cast<Base*>(parent)->commitTransaction(txn);
|
||||
active = false;
|
||||
parent->transactions.erase(txn);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,25 @@
|
||||
/*
|
||||
* LMDB Abstraction Layer.
|
||||
* Copyright (C) 2023 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include "icursor.h"
|
||||
|
||||
namespace LMDBAL {
|
||||
class iStorage;
|
||||
@ -21,11 +40,14 @@ public:
|
||||
|
||||
protected:
|
||||
Transaction(TransactionID txn, const Base* parent);
|
||||
void reset();
|
||||
void closeCursors();
|
||||
|
||||
protected:
|
||||
TransactionID txn; /**<\brief Transaction inner handler*/
|
||||
bool active; /**<\brief Transaction state*/
|
||||
const Base* parent; /**<\brief Pointer to the database this transaction belongs to*/
|
||||
TransactionID txn; /**<\brief Transaction inner handler*/
|
||||
bool active; /**<\brief Transaction state*/
|
||||
const Base* parent; /**<\brief Pointer to the database this transaction belongs to*/
|
||||
std::map<uint32_t, iCursor*> cursors; /**<\brief a collection of cursors curently opened under this transaction*/
|
||||
};
|
||||
|
||||
class WriteTransaction : public Transaction {
|
||||
@ -35,6 +57,7 @@ public:
|
||||
explicit WriteTransaction(WriteTransaction&& other);
|
||||
WriteTransaction(const WriteTransaction& other) = delete;
|
||||
WriteTransaction& operator = (const WriteTransaction& other) = delete;
|
||||
WriteTransaction& operator = (WriteTransaction&& other);
|
||||
|
||||
void commit();
|
||||
void abort();
|
||||
|
@ -20,7 +20,7 @@ target_include_directories(runUnitTests PRIVATE ${Qt${QT_VERSION_MAJOR}Core_INCL
|
||||
target_link_libraries(
|
||||
runUnitTests
|
||||
GTest::gtest_main
|
||||
${PROJECT_NAME}
|
||||
${LMDBAL_NAME}
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
)
|
||||
include(GoogleTest)
|
||||
|
@ -113,10 +113,9 @@ TEST_F(BaseTest, AddingKeysToCache) {
|
||||
EXPECT_EQ(db->ready(), true);
|
||||
c1->addRecord(2, "blah balah");
|
||||
c1->addRecord(-4, "testing goes brrr");
|
||||
c1->addRecord(140, "whatever");
|
||||
c1->addRecord(40, "whatever");
|
||||
c1->addRecord(-37, "aaaaa tss tsss tsss tsss aaaaaaa");
|
||||
EXPECT_EQ(c1->getRecord(140), "whatever");
|
||||
EXPECT_EQ(c1->getRecord(-116), "whatever");
|
||||
EXPECT_EQ(c1->getRecord(40), "whatever");
|
||||
}
|
||||
|
||||
TEST_F(BaseTest, AddingKeysToVariableCache) {
|
||||
@ -189,8 +188,8 @@ TEST_F(BaseTest, Persistence) {
|
||||
EXPECT_EQ(t2->count(), t2Size);
|
||||
|
||||
EXPECT_EQ(c1->count(), c1Size);
|
||||
EXPECT_EQ(c1->checkRecord(-116), true);
|
||||
EXPECT_EQ(c1->getRecord(-116), "whatever");
|
||||
EXPECT_EQ(c1->checkRecord(40), true);
|
||||
EXPECT_EQ(c1->getRecord(40), "whatever");
|
||||
EXPECT_EQ(c1->checkRecord(-4), true);
|
||||
EXPECT_EQ(c1->getRecord(-4), "testing goes brrr");
|
||||
EXPECT_EQ(c1->getRecord(-4), "testing goes brrr");
|
||||
|
@ -402,7 +402,7 @@ TEST_F(CacheCursorTest, CursorRAIIBehaviour) {
|
||||
|
||||
TEST_F(CacheCursorTest, CornerCases) {
|
||||
transaction.terminate();
|
||||
EXPECT_THROW(cursor.current(), LMDBAL::Unknown);
|
||||
EXPECT_THROW(cursor.current(), LMDBAL::CursorNotReady);
|
||||
cursor.close();
|
||||
|
||||
LMDBAL::Cursor<uint64_t, std::string> emptyCursor = emptyCache->createCursor();
|
||||
@ -436,5 +436,40 @@ TEST_F(CacheCursorTest, CornerCases) {
|
||||
EXPECT_EQ(element.first, reference->first);
|
||||
EXPECT_EQ(element.second, reference->second);
|
||||
EXPECT_THROW(cursor.prev(), LMDBAL::NotFound);
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
TEST_F(CacheCursorTest, TerminatedTransaction) {
|
||||
LMDBAL::Cursor<uint64_t, std::string> cr = cache->createCursor();
|
||||
|
||||
{
|
||||
LMDBAL::Transaction txn = db->beginReadOnlyTransaction();
|
||||
cr.open(txn);
|
||||
EXPECT_NO_THROW(cr.first());
|
||||
}
|
||||
|
||||
EXPECT_FALSE(cr.opened());
|
||||
|
||||
LMDBAL::Transaction txn2;
|
||||
{
|
||||
LMDBAL::Transaction txn = db->beginReadOnlyTransaction();
|
||||
EXPECT_TRUE(txn.isActive());
|
||||
EXPECT_FALSE(txn2.isActive());
|
||||
|
||||
cr.open(txn);
|
||||
EXPECT_TRUE(cr.opened());
|
||||
|
||||
txn2 = std::move(txn);
|
||||
EXPECT_FALSE(txn.isActive());
|
||||
EXPECT_TRUE(txn2.isActive());
|
||||
EXPECT_TRUE(cr.opened());
|
||||
}
|
||||
|
||||
EXPECT_TRUE(txn2.isActive());
|
||||
EXPECT_TRUE(cr.opened());
|
||||
|
||||
txn2.terminate();
|
||||
EXPECT_FALSE(txn2.isActive());
|
||||
EXPECT_FALSE(cr.opened());
|
||||
}
|
||||
|
@ -279,3 +279,26 @@ TEST_F(CacheTransactionsTest, RAIIResourceFree) {
|
||||
EXPECT_EQ(c1->getRecord(221), 14);
|
||||
}
|
||||
|
||||
TEST_F(CacheTransactionsTest, TransactionTerminationOnClose) {
|
||||
LMDBAL::WriteTransaction txn = db->beginTransaction();
|
||||
|
||||
c1->addRecord(578, 4552, txn);
|
||||
|
||||
EXPECT_EQ(c1->getRecord(578, txn), 4552);
|
||||
EXPECT_EQ(c1->checkRecord(578), false);
|
||||
|
||||
db->close();
|
||||
db->open();
|
||||
|
||||
EXPECT_EQ(txn.isActive(), false);
|
||||
EXPECT_THROW(c1->getRecord(578, txn), LMDBAL::TransactionTerminated);
|
||||
EXPECT_NO_THROW(txn.commit());
|
||||
|
||||
EXPECT_EQ(c1->checkRecord(578), false);
|
||||
|
||||
txn = db->beginTransaction();
|
||||
c1->addRecord(578, 4552, txn);
|
||||
txn.commit();
|
||||
|
||||
EXPECT_EQ(c1->getRecord(578), 4552);
|
||||
}
|
||||
|
@ -130,7 +130,10 @@ TEST_F(DuplicatesTest, Adding) {
|
||||
tu3->addRecord(7.20001, 4.00000001); //not sure how exactly, but it works
|
||||
|
||||
EXPECT_EQ(tu3->count(), 7);
|
||||
EXPECT_EQ(tu3->getRecord(7.2), -113);
|
||||
|
||||
std::set<float> res72({-113, -53.5478, 697, 4, 4.00000001});
|
||||
EXPECT_EQ(res72.count(tu3->getRecord(7.2)), 1);
|
||||
|
||||
float tu3dd = tu3->getRecord(5119);
|
||||
EXPECT_TRUE(tu3ds == tu3dd);
|
||||
EXPECT_EQ(tu3ds, tu3dd);
|
||||
@ -144,8 +147,11 @@ TEST_F(DuplicatesTest, Adding) {
|
||||
EXPECT_THROW(tu4->addRecord(327, 79.624923), LMDBAL::Exist);
|
||||
|
||||
EXPECT_EQ(tu4->count(), 4);
|
||||
EXPECT_EQ(tu4->getRecord(172), 0.00000001);
|
||||
EXPECT_EQ(tu4->getRecord(327), 463.28348); //since they are not int's they are compared sort of lexicographically
|
||||
|
||||
std::set<double> res327({463.28348, 79.624923});
|
||||
std::set<double> res172({0.00001, 0.00000001});
|
||||
EXPECT_EQ(res172.count(tu4->getRecord(172)), 1);
|
||||
EXPECT_EQ(res327.count(tu4->getRecord(327)), 1);
|
||||
|
||||
tu5->addRecord(-84.7, 45656753);
|
||||
EXPECT_THROW(tu5->addRecord(-84.7, 45656753), LMDBAL::Exist);
|
||||
@ -174,13 +180,19 @@ TEST_F(DuplicatesTest, Forcing) {
|
||||
tu1->addRecord(-56, 71);
|
||||
tu1->addRecord(-56, 274);
|
||||
tu1->addRecord(-56, 732);
|
||||
|
||||
std::set<uint16_t> res56({71, 274, 732});
|
||||
EXPECT_EQ(tu1->count(), tu1Size += 3);
|
||||
EXPECT_TRUE(tu1->forceRecord(-56, 322));
|
||||
res56.insert(322);
|
||||
EXPECT_EQ(tu1->count(), tu1Size += 1);
|
||||
EXPECT_EQ(tu1->getRecord(-56), 274); //like yeah, it's really counterintuitive, since it's compared byte by byte
|
||||
EXPECT_EQ(res56.count(tu1->getRecord(-56)), 1);
|
||||
|
||||
res56.insert(14);
|
||||
EXPECT_TRUE(tu1->forceRecord(-56, 14));
|
||||
EXPECT_EQ(tu1->count(), tu1Size += 1);
|
||||
EXPECT_EQ(tu1->getRecord(-56), 14);
|
||||
EXPECT_EQ(res56.count(tu1->getRecord(-56)), 1);
|
||||
|
||||
EXPECT_FALSE(tu1->forceRecord(-56, 274));
|
||||
EXPECT_EQ(tu1->count(), tu1Size);
|
||||
|
||||
@ -201,26 +213,38 @@ TEST_F(DuplicatesTest, Forcing) {
|
||||
tu3->addRecord(17.3, 93.21);
|
||||
tu3->addRecord(17.3, 6.6);
|
||||
tu3->addRecord(17.3, 105.1);
|
||||
std::set<float> res17({93.21, 6.6, 105.1});
|
||||
|
||||
EXPECT_EQ(tu3->count(), tu3Size += 3);
|
||||
EXPECT_TRUE(tu3->forceRecord(17.3, 74.9));
|
||||
res17.insert(74.9);
|
||||
EXPECT_EQ(tu3->count(), tu3Size += 1);
|
||||
EXPECT_EQ(tu3->getRecord(17.3), 105.1f); //here too, really one should not use this function with duplicates,
|
||||
EXPECT_TRUE(tu3->forceRecord(17.3, 5.1)); //unless he wishes for kinda randomish result
|
||||
EXPECT_EQ(res17.count(tu3->getRecord(17.3)), 1);
|
||||
|
||||
EXPECT_TRUE(tu3->forceRecord(17.3, 5.1));
|
||||
res17.insert(5.1);
|
||||
EXPECT_EQ(tu3->count(), tu3Size += 1);
|
||||
EXPECT_EQ(tu3->getRecord(17.3), 5.1f);
|
||||
EXPECT_EQ(res17.count(tu3->getRecord(17.3)), 1);
|
||||
|
||||
EXPECT_FALSE(tu3->forceRecord(17.3, 93.21));
|
||||
EXPECT_EQ(tu3->count(), tu3Size);
|
||||
|
||||
LMDBAL::SizeType tu4Size = tu4->count();
|
||||
tu4->addRecord(84, -359.109);
|
||||
tu4->addRecord(84, 2879.654);
|
||||
std::set<double> res84({-359.109, 2879.654});
|
||||
|
||||
EXPECT_EQ(tu4->count(), tu4Size += 2);
|
||||
EXPECT_TRUE(tu4->forceRecord(84, 72.9));
|
||||
res84.insert(72.9);
|
||||
EXPECT_EQ(tu4->count(), tu4Size += 1);
|
||||
EXPECT_EQ(tu4->getRecord(84), 2879.654);
|
||||
EXPECT_EQ(res84.count(tu4->getRecord(84)), 1);
|
||||
|
||||
EXPECT_TRUE(tu4->forceRecord(84, 2679.5));
|
||||
res84.insert(2679.5);
|
||||
EXPECT_EQ(tu4->count(), tu4Size += 1);
|
||||
EXPECT_EQ(tu4->getRecord(84), 2679.5);
|
||||
EXPECT_EQ(res84.count(tu4->getRecord(84)), 1);
|
||||
|
||||
EXPECT_FALSE(tu4->forceRecord(84, -359.109));
|
||||
EXPECT_EQ(tu4->count(), tu4Size);
|
||||
|
||||
@ -286,6 +310,7 @@ TEST_F(DuplicatesTest, Changing) {
|
||||
EXPECT_THROW(tu2->changeRecord("jeremy spins", -7), LMDBAL::Exist);
|
||||
|
||||
LMDBAL::SizeType tu3Size = tu3->count();
|
||||
std::set<float> res26;
|
||||
EXPECT_THROW(tu3->changeRecord(26.7, 68.22), LMDBAL::NotFound);
|
||||
EXPECT_EQ(tu3->count(), tu3Size);
|
||||
tu3->addRecord(26.7, 68.22);
|
||||
@ -294,11 +319,13 @@ TEST_F(DuplicatesTest, Changing) {
|
||||
tu3->changeRecord(26.7, 68.22); //should just do nothing usefull, but work normally
|
||||
EXPECT_EQ(tu3->getRecord(26.7), 68.22f);
|
||||
tu3->changeRecord(26.7, 23.18);
|
||||
res26.insert(23.18);
|
||||
EXPECT_EQ(tu3->count(), tu3Size);
|
||||
EXPECT_EQ(tu3->getRecord(26.7), 23.18f);
|
||||
tu3->addRecord(26.7, 22.16);
|
||||
res26.insert(22.16);
|
||||
EXPECT_EQ(tu3->count(), tu3Size += 1);
|
||||
EXPECT_EQ(tu3->getRecord(26.7), 23.18f);
|
||||
EXPECT_EQ(res26.count(tu3->getRecord(26.7)), 1);
|
||||
tu3->changeRecord(26.7, 21.7);
|
||||
EXPECT_EQ(tu3->count(), tu3Size);
|
||||
EXPECT_EQ(tu3->getRecord(26.7), 21.7f);
|
||||
@ -316,17 +343,21 @@ TEST_F(DuplicatesTest, Changing) {
|
||||
tu4->changeRecord(852, 6795.349); //should just do nothing usefull, but work normally
|
||||
EXPECT_EQ(tu4->getRecord(852), 6795.349);
|
||||
tu4->changeRecord(852, 13.54);
|
||||
std::set<double> res852({13.54});
|
||||
EXPECT_EQ(tu4->count(), tu4Size);
|
||||
EXPECT_EQ(tu4->getRecord(852), 13.54);
|
||||
tu4->addRecord(852, 213.85);
|
||||
res852.insert(213.85);
|
||||
EXPECT_EQ(tu4->count(), tu4Size += 1);
|
||||
EXPECT_EQ(tu4->getRecord(852), 13.54);
|
||||
EXPECT_EQ(res852.count(tu4->getRecord(852)), 1);
|
||||
tu4->changeRecord(852, 236.21);
|
||||
res852.insert(236.21);
|
||||
EXPECT_EQ(tu4->count(), tu4Size);
|
||||
EXPECT_EQ(tu4->getRecord(852), 236.21);
|
||||
tu4->changeRecord(852, 46324.1135);
|
||||
res852.insert(46324.1135);
|
||||
EXPECT_EQ(tu4->count(), tu4Size);
|
||||
EXPECT_EQ(tu4->getRecord(852), 213.85);
|
||||
EXPECT_EQ(res852.count(tu4->getRecord(852)), 1);
|
||||
EXPECT_THROW(tu4->changeRecord(852, 46324.1135), LMDBAL::Exist);
|
||||
}
|
||||
|
||||
|
@ -380,7 +380,7 @@ TEST_F(StorageCursorTest, CursorRAIIBehaviour) {
|
||||
TEST_F(StorageCursorTest, CornerCases) {
|
||||
EXPECT_EQ(getTableCursorsSize(), 1);
|
||||
transaction.terminate();
|
||||
EXPECT_THROW(cursor.current(), LMDBAL::Unknown);
|
||||
EXPECT_THROW(cursor.current(), LMDBAL::CursorNotReady);
|
||||
cursor.close();
|
||||
|
||||
LMDBAL::Cursor<uint64_t, std::string> emptyCursor = emptyTable->createCursor();
|
||||
@ -414,4 +414,40 @@ TEST_F(StorageCursorTest, CornerCases) {
|
||||
EXPECT_EQ(element.first, reference->first);
|
||||
EXPECT_EQ(element.second, reference->second);
|
||||
EXPECT_THROW(cursor.prev(), LMDBAL::NotFound);
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
TEST_F(StorageCursorTest, TerminatedTransaction) {
|
||||
LMDBAL::Cursor<uint64_t, std::string> cr = table->createCursor();
|
||||
|
||||
{
|
||||
LMDBAL::Transaction txn = db->beginReadOnlyTransaction();
|
||||
cr.open(txn);
|
||||
EXPECT_NO_THROW(cr.first());
|
||||
}
|
||||
|
||||
EXPECT_FALSE(cr.opened());
|
||||
|
||||
LMDBAL::Transaction txn2;
|
||||
{
|
||||
LMDBAL::Transaction txn = db->beginReadOnlyTransaction();
|
||||
EXPECT_TRUE(txn.isActive());
|
||||
EXPECT_FALSE(txn2.isActive());
|
||||
|
||||
cr.open(txn);
|
||||
EXPECT_TRUE(cr.opened());
|
||||
|
||||
txn2 = std::move(txn);
|
||||
EXPECT_FALSE(txn.isActive());
|
||||
EXPECT_TRUE(txn2.isActive());
|
||||
EXPECT_TRUE(cr.opened());
|
||||
}
|
||||
|
||||
EXPECT_TRUE(txn2.isActive());
|
||||
EXPECT_TRUE(cr.opened());
|
||||
|
||||
txn2.terminate();
|
||||
EXPECT_FALSE(txn2.isActive());
|
||||
EXPECT_FALSE(cr.opened());
|
||||
}
|
||||
|
@ -277,3 +277,27 @@ TEST_F(StorageTransactionsTest, RAIIResourceFree) {
|
||||
std::cout << "checking the final result" << std::endl;
|
||||
EXPECT_EQ(t1->getRecord(221), 14);
|
||||
}
|
||||
|
||||
TEST_F(StorageTransactionsTest, TransactionTerminationOnClose) {
|
||||
LMDBAL::WriteTransaction txn = db->beginTransaction();
|
||||
|
||||
t1->addRecord(543, 229, txn);
|
||||
|
||||
EXPECT_EQ(t1->getRecord(543, txn), 229);
|
||||
EXPECT_EQ(t1->checkRecord(543), false);
|
||||
|
||||
db->close();
|
||||
db->open();
|
||||
|
||||
EXPECT_EQ(txn.isActive(), false);
|
||||
EXPECT_THROW(t1->getRecord(543, txn), LMDBAL::TransactionTerminated);
|
||||
EXPECT_NO_THROW(txn.commit());
|
||||
|
||||
EXPECT_EQ(t1->checkRecord(543), false);
|
||||
|
||||
txn = db->beginTransaction();
|
||||
t1->addRecord(543, 229, txn);
|
||||
txn.commit();
|
||||
|
||||
EXPECT_EQ(t1->getRecord(543), 229);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user