Compare commits

..

1 Commits

47 changed files with 342 additions and 729 deletions

View File

@ -1,80 +0,0 @@
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

View File

@ -6,22 +6,8 @@ on:
- master
jobs:
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
Archlinux:
runs-on: archlinux
needs: [test-qt5, test-qt6]
steps:
- name: Check out repository code
uses: actions/checkout@v3
@ -31,12 +17,16 @@ jobs:
- name: Configure
working-directory: ./build
run: cmake .. BUILD_DOC_HTML=True -D BUILD_DOC_XML=True -D BUILD_DOC_MAN=True -D BUILD_DOXYGEN_AWESOME=True
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
- 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

View File

@ -5,29 +5,42 @@ on:
types: [published]
jobs:
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 }}
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
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 }}
- 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

View File

@ -1,36 +0,0 @@
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

View File

@ -1,19 +1,5 @@
# 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

View File

@ -1,16 +1,15 @@
cmake_minimum_required(VERSION 3.16)
project(LMDBAL
VERSION 0.6.0
VERSION 0.5.4
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)
@ -26,8 +25,6 @@ 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()
@ -41,9 +38,9 @@ if (NOT CMAKE_BUILD_TYPE)
endif ()
if (BUILD_STATIC)
add_library(${LMDBAL_NAME} STATIC)
add_library(${PROJECT_NAME} STATIC)
else ()
add_library(${LMDBAL_NAME} SHARED)
add_library(${PROJECT_NAME} SHARED)
endif()
if (CMAKE_BUILD_TYPE STREQUAL "Release")
@ -55,21 +52,21 @@ elseif (CMAKE_BUILD_TYPE STREQUAL "Debug")
endif()
message("Compilation options: " ${COMPILE_OPTIONS})
target_compile_options(${LMDBAL_NAME} PRIVATE ${COMPILE_OPTIONS})
target_compile_options(${PROJECT_NAME} PRIVATE ${COMPILE_OPTIONS})
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
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
)
if (UNIX)
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)
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)
endif()
add_subdirectory(src)
@ -87,47 +84,47 @@ if (BUILD_TESTS)
endif ()
target_include_directories(
${LMDBAL_NAME}
${PROJECT_NAME}
PUBLIC
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${LMDBAL_NAME_LOW}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_LOW}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/serializer>
)
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_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_link_libraries(
${LMDBAL_NAME}
${PROJECT_NAME}
Qt${QT_VERSION_MAJOR}::Core
lmdb
)
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/${LMDBAL_NAME_LOW}Config.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LMDBAL_NAME_LOW}
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}Config.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_LOW}
)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/${LMDBAL_NAME_LOW}ConfigVersion.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}ConfigVersion.cmake"
VERSION "${version}"
COMPATIBILITY AnyNewerVersion
)
install(TARGETS ${LMDBAL_NAME}
EXPORT ${LMDBAL_NAME_LOW}Targets
install(TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_LOW}Targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${LMDBAL_NAME_LOW}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${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(EXPORT ${PROJECT_LOW}Targets
FILE ${PROJECT_LOW}Targets.cmake
NAMESPACE ${PROJECT_NAME}::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_LOW}
)
install(FILES
"${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}
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}Config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}ConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_LOW}
)

View File

@ -1,15 +1,14 @@
# 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 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/)
[![AUR version](https://img.shields.io/aur/version/lmdbal?style=flat-square)](https://aur.archlinux.org/packages/lmdbal/)
[![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 c++ compiler (g++ would do)
- Qt 5 or 6 or higher (qt5-base or qt6-base would do)
- a compiler (c++ would do)
- Qt 5 or higher (qt5-base would do)
- lmdb
- CMake 3.16 or higher
- Doxygen (optional, for documentation)
@ -85,7 +84,6 @@ 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

View File

@ -1,5 +1,5 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@LMDBAL_NAME_LOW@Targets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/lmdbalTargets.cmake")
check_required_components(@LMDBAL_NAME_LOW@)
check_required_components(lmdbal)

View File

@ -1,23 +1,23 @@
# Maintainer: Yury Gubich <blue@macaw.me>
pkgname=lmdbal
pkgver=1.0.0
pkgrel=4
pkgdesc="LMDB Abstraction Layer"
pkgver=0.5.4
pkgrel=1
pkgdesc="LMDB Abstraction Layer, qt5 version"
arch=('i686' 'x86_64')
url="https://git.macaw.me/blue/lmdbal"
license=('GPL3')
depends=( 'lmdb' )
depends=( 'lmdb' 'qt5-base')
makedepends=('cmake>=3.16' 'gcc')
optdepends=()
source=("lmdbal-$pkgver-$pkgrel.tar.gz::https://git.macaw.me/blue/lmdbal/archive/$pkgver.tar.gz")
source=("$pkgname-$pkgver.tar.gz::https://git.macaw.me/blue/$pkgname/archive/$pkgver.tar.gz")
sha256sums=('SKIP')
build() {
cd "$srcdir/lmdbal"
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release
cd "$srcdir/$pkgname"
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release -D QT_VERSION_MAJOR=5
cmake --build .
}
package() {
cd "$srcdir/lmdbal"
cd "$srcdir/$pkgname"
DESTDIR="$pkgdir/" cmake --install .
}

View File

@ -16,11 +16,10 @@ set(HEADERS
cache.hpp
operators.hpp
transaction.h
icursor.h
)
target_sources(${LMDBAL_NAME} PRIVATE ${SOURCES})
target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
add_subdirectory(serializer)
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${LMDBAL_NAME_LOW})
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_LOW})

View File

@ -43,7 +43,7 @@ LMDBAL::Base::Base(const QString& _name, uint16_t _mapSize):
size(_mapSize),
environment(),
storages(),
transactions()
transactions(new Transactions())
{}
/**
@ -52,6 +52,8 @@ 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;
}
@ -66,16 +68,14 @@ LMDBAL::Base::~Base() {
*/
void LMDBAL::Base::close() {
if (opened) {
for (const std::pair<LMDBAL::TransactionID, LMDBAL::Transaction*> pair : transactions) {
abortTransaction(pair.first, emptyName);
pair.second->reset();
}
for (const LMDBAL::TransactionID id : *transactions)
abortTransaction(id, emptyName);
for (const std::pair<const std::string, iStorage*>& pair : storages)
pair.second->close();
mdb_env_close(environment);
transactions.clear();
transactions->clear();
opened = false;
}
}
@ -261,13 +261,12 @@ LMDBAL::WriteTransaction LMDBAL::Base::beginTransaction() {
* \brief Aborts transaction
*
* Terminates transaction cancelling changes.
* Every storage receives notification about this transaction being aborted.
* This is an optimal way to abort public transactions
* This is an optimal way to terminate read-only transactions
*
* \param[in] id - transaction ID you want to abort
*
* \exception LMDBAL::Closed - thrown if the database is closed
* \exception LMDBAL::Unknown - thrown if something unexpected happened
* \exception LMDBAL::Unknown - thrown if transaction with given ID was not found or if something unexpected happened
*/
void LMDBAL::Base::abortTransaction(LMDBAL::TransactionID id) const {
return abortTransaction(id, emptyName);}
@ -276,13 +275,11 @@ 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 something unexpected happened
* \exception LMDBAL::Unknown - thrown if transaction with given ID was not found or if something unexpected happened
*/
void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id) {
return commitTransaction(id, emptyName);}
@ -290,9 +287,7 @@ void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id) {
/**
* \brief Begins read-only transaction
*
* 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.
* This function is intended to be called from subordinate storage or cache
*
* \param[in] storageName - name of the storage/cache that you begin transaction from, needed just to inform if something went wrong
*
@ -306,6 +301,7 @@ 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);
@ -315,9 +311,7 @@ LMDBAL::TransactionID LMDBAL::Base::beginReadOnlyTransaction(const std::string&
/**
* \brief Begins writable transaction
*
* 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.
* This function is intended to be called from subordinate storage or cache
*
* \param[in] storageName - name of the storage/cache that you begin transaction from, needed just to inform if something went wrong
*
@ -331,6 +325,7 @@ 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);
@ -341,46 +336,55 @@ LMDBAL::TransactionID LMDBAL::Base::beginTransaction(const std::string& storageN
* \brief Aborts transaction
*
* Terminates transaction cancelling changes.
* 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
* This is an optimal way to terminate read-only transactions.
* This function is intended to be called from subordinate storage or cache
*
* \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 something unexpected happened
* \exception LMDBAL::Unknown - thrown if transaction with given ID was not found or 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.
* 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
* This function is intended to be called from subordinate storage or cache
*
* \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 something unexpected happened
* \exception LMDBAL::Unknown - thrown if transaction with given ID was not found or 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);
}
/**

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_BASE_H
#define LMDBAL_BASE_H
#include <map>
#include <set>
@ -85,7 +86,7 @@ public:
private:
typedef std::map<std::string, LMDBAL::iStorage*> Storages; /**<\brief Storage and Cache pointers are saved in the std::map*/
typedef std::map<TransactionID, Transaction*> Transactions; /**<\brief Piblic transaction IDs are saved in the std::set*/
typedef std::set<TransactionID> Transactions; /**<\brief Piblic transaction IDs are saved in the std::set*/
void commitTransaction(TransactionID id);
void abortTransaction(TransactionID id) const;
@ -105,7 +106,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*/
mutable Transactions transactions; /**<\brief Active public transactions*/
Transactions* transactions; /**<\brief Active public transactions*/
inline static const std::string emptyName = ""; /**<\brief Empty string for general fallback purposes*/
};
@ -214,3 +215,5 @@ 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

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_CACHE_H
#define LMDBAL_CACHE_H
#include <map>
#include <set>
@ -133,3 +134,5 @@ protected:
}
#include "cache.hpp"
#endif // LMDBAL_CACHE_H

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_CACHE_HPP
#define LMDBAL_CACHE_HPP
#include "cache.h"
#include "exceptions.h"
@ -897,3 +898,5 @@ void LMDBAL::Cache<K, V>::destroyTransactionEntry(const Entry& entry) const {
break;
}
}
#endif //LMDBAL_CACHE_HPP

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_CURSOR_H
#define LMDBAL_CURSOR_H
#include <string>
@ -24,12 +25,11 @@
#include "base.h"
#include "storage.h"
#include "transaction.h"
#include "icursor.h"
namespace LMDBAL {
template <class K, class V>
class Cursor : public iCursor {
class Cursor {
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,3 +105,5 @@ private:
#include "cursor.hpp"
#endif //LMDBAL_CURSOR_H

View File

@ -16,10 +16,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_CURSOR_HPP
#define LMDBAL_CURSOR_HPP
#include "cursor.h"
#include <iostream>
/**
@ -85,10 +85,6 @@ LMDBAL::Cursor<K, V>::Cursor(Cursor&& other):
if (id != 0)
storage->cursors[id] = this;
if (state == openedPublic)
storage->attachCursorToTransaction(id, cursor, this);
other.freed();
}
@ -115,9 +111,6 @@ 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;
@ -192,18 +185,7 @@ bool LMDBAL::Cursor<K, V>::empty () const {
*/
template<class K, class V>
void LMDBAL::Cursor<K, V>::terminated () {
switch (state) {
case openedPublic:
storage->_mdbCursorClose(cursor);
state = closed;
break;
case openedPrivate:
storage->closeCursorTransaction(cursor, true);
state = closed;
break;
default:
break;
}
close(); //for now it's the same, but if I ever going to make writable cursor - here is where it's gonna be different
}
/**
@ -224,11 +206,17 @@ void LMDBAL::Cursor<K, V>::open () {
if (empty())
throw CursorEmpty(openCursorMethodName);
storage->ensureOpened(openCursorMethodName);
switch (state) {
case closed:
storage->openCursorTransaction(&cursor, false);
case closed: {
TransactionID txn = storage->beginReadOnlyTransaction();
int result = storage->_mdbCursorOpen(txn, &cursor);
if (result != MDB_SUCCESS)
storage->throwUnknown(result, txn);
storage->transactionStarted(txn, true);
state = openedPrivate;
break;
} break;
default:
break;
}
@ -263,7 +251,6 @@ 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:
@ -295,15 +282,21 @@ void LMDBAL::Cursor<K, V>::renew () {
storage->ensureOpened(renewCursorMethodName);
switch (state) {
case openedPrivate:
storage->closeCursorTransaction(cursor, false);
storage->openCursorTransaction(&cursor, true);
break;
case openedPublic:
storage->disconnectCursorFromTransaction(id, cursor);
storage->openCursorTransaction(&cursor, true);
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);
state = openedPrivate;
break;
} break;
default:
break;
}
@ -338,21 +331,17 @@ void LMDBAL::Cursor<K, V>::renew (const Transaction& transaction) {
TransactionID txn = storage->extractTransactionId(transaction, renewCursorMethodName);
switch (state) {
case openedPrivate: {
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;
TransactionID txn = storage->_mdbCursorTxn(cursor);
storage->abortTransaction(txn);
storage->transactionAborted(txn);
[[fallthrough]];
}
case openedPublic: {
storage->disconnectCursorFromTransaction(id, cursor);
int result = storage->_mdbCursorRenew(txn, cursor);
if (result != MDB_SUCCESS)
storage->throwUnknown(result);
storage->attachCursorToTransaction(id, cursor, this);
state = openedPublic;
} break;
default:
break;
@ -372,17 +361,19 @@ void LMDBAL::Cursor<K, V>::renew (const Transaction& transaction) {
template<class K, class V>
void LMDBAL::Cursor<K, V>::close () {
switch (state) {
case openedPublic:
storage->disconnectCursorFromTransaction(id, cursor);
case openedPublic: {
storage->_mdbCursorClose(cursor);
state = closed;
break;
case openedPrivate:
storage->closeCursorTransaction(cursor, true);
} break;
case openedPrivate: {
TransactionID txn = storage->_mdbCursorTxn(cursor);
storage->_mdbCursorClose(cursor);
storage->abortTransaction(txn);
storage->transactionAborted(txn);
state = closed;
break;
} break;
default:
break;
}
@ -491,7 +482,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);
}
@ -666,3 +657,5 @@ void LMDBAL::Cursor<K, V>::operateCursorRead(
else
storage->discoveredRecord(key, value, storage->_mdbCursorTxn(cursor));
}
#endif //LMDBAL_CURSOR_HPP

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_EXCEPTIONS_H
#define LMDBAL_EXCEPTIONS_H
#include <stdexcept>
#include <string>
@ -40,7 +41,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
@ -236,3 +237,5 @@ private:
};
}
#endif //LMDBAL_EXCEPTIONS_H

View File

@ -1,32 +0,0 @@
/*
* 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;
};
}

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_OPERATORS_HPP
#define LMDBAL_OPERATORS_HPP
#include <map>
#include <set>
@ -208,3 +209,5 @@ QDataStream& operator >> (QDataStream &in, std::list<K>& container) {
return in;
}
#endif //LMDBAL_OPERATORS_HPP

View File

@ -16,4 +16,4 @@ set(HEADERS
serializer_qbytearray.hpp
)
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${LMDBAL_NAME_LOW})
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_LOW})

View File

@ -16,8 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_SERIALIZER_H
#define LMDBAL_SERIALIZER_H
#include <cstring>
@ -68,3 +68,5 @@ private:
#include "serializer_stdstring.hpp"
#include "serializer_qstring.hpp"
#include "serializer_qbytearray.hpp"
#endif // LMDBAL_SERIALIZER_H

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_SERIALIZER_HPP
#define LMDBAL_SERIALIZER_HPP
#include "serializer.h"
@ -158,3 +159,5 @@ MDB_val LMDBAL::Serializer<T>::getData() {
return val;
}
#endif //LMDBAL_SERIALIZER_HPP

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_SERIALIZER_DOUBLE_HPP
#define LMDBAL_SERIALIZER_DOUBLE_HPP
namespace LMDBAL {
@ -52,5 +53,7 @@ private:
}
#endif //LMDBAL_SERIALIZER_DOUBLE_HPP

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_SERIALIZER_FLOAT_HPP
#define LMDBAL_SERIALIZER_FLOAT_HPP
namespace LMDBAL {
@ -51,3 +52,8 @@ private:
};
}
#endif //LMDBAL_SERIALIZER_FLOAT_HPP

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_SERIALIZER_INT16_HPP
#define LMDBAL_SERIALIZER_INT16_HPP
#include <stdint.h>
@ -53,3 +54,5 @@ private:
};
}
#endif //LMDBAL_SERIALIZER_INT16_HPP

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_SERIALIZER_INT32_HPP
#define LMDBAL_SERIALIZER_INT32_HPP
#include <stdint.h>
@ -53,3 +54,8 @@ private:
};
}
#endif //LMDBAL_SERIALIZER_INT32_HPP

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_SERIALIZER_INT64_HPP
#define LMDBAL_SERIALIZER_INT64_HPP
#include <stdint.h>
@ -53,3 +54,8 @@ private:
};
}
#endif //LMDBAL_SERIALIZER_INT64_HPP

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_SERIALIZER_INT8_HPP
#define LMDBAL_SERIALIZER_INT8_HPP
#include <stdint.h>
@ -53,3 +54,8 @@ private:
};
}
#endif //LMDBAL_SERIALIZER_INT8_HPP

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_SERIALIZER_QBYTEARRAY_HPP
#define LMDBAL_SERIALIZER_QBYTEARRAY_HPP
#include <QByteArray>
@ -55,3 +56,9 @@ private:
};
}
#endif //LMDBAL_SERIALIZER_QBYTEARRAY_HPP

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_SERIALIZER_QSTRING_HPP
#define LMDBAL_SERIALIZER_QSTRING_HPP
#include <QString>
#include <QByteArray>
@ -55,3 +56,10 @@ private:
};
}
#endif //LMDBAL_SERIALIZER_QSTRING_HPP

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_SERIALIZER_STDSTRING_HPP
#define LMDBAL_SERIALIZER_STDSTRING_HPP
#include <string>
@ -53,3 +54,9 @@ private:
};
}
#endif //LMDBAL_SERIALIZER_STDSTRING_HPP

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_SERIALIZER_UINT16_HPP
#define LMDBAL_SERIALIZER_UINT16_HPP
#include <stdint.h>
@ -53,3 +54,7 @@ private:
};
}
#endif //LMDBAL_SERIALIZER_UINT16_HPP

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_SERIALIZER_UINT32_HPP
#define LMDBAL_SERIALIZER_UINT32_HPP
#include <stdint.h>
@ -53,3 +54,5 @@ private:
};
}
#endif //LMDBAL_SERIALIZER_UINT32_HPP

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_SERIALIZER_UINT64_HPP
#define LMDBAL_SERIALIZER_UINT64_HPP
#include <stdint.h>
@ -53,3 +54,6 @@ private:
};
}
#endif //LMDBAL_SERIALIZER_UINT64_HPP

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_SERIALIZER_UINT8_HPP
#define LMDBAL_SERIALIZER_UINT8_HPP
#include <stdint.h>
@ -53,3 +54,8 @@ private:
};
}
#endif //LMDBAL_SERIALIZER_UINT8_HPP

View File

@ -50,7 +50,7 @@ LMDBAL::iStorage::iStorage(Base* parent, const std::string& name, bool duplicate
LMDBAL::iStorage::~iStorage() {}
/**
* \brief A private virtual function to close each storage in the database
* \brief A private virtual function I need to close each storage in the database
*/
void LMDBAL::iStorage::close() {
mdb_dbi_close(db->environment, dbi);
@ -159,82 +159,6 @@ 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)
*

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_STORAGE_H
#define LMDBAL_STORAGE_H
#include <type_traits>
#include <cstring>
@ -76,11 +77,6 @@ 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);
@ -108,25 +104,24 @@ 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 openCursorTransactionMethodName = "openCursorTransaction"; /**<\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*/
protected:
template <class K, class V>
@ -204,3 +199,5 @@ protected:
}
#include "storage.hpp"
#endif //LMDBAL_STORAGE_H

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LMDBAL_STORAGE_HPP
#define LMDBAL_STORAGE_HPP
#include "storage.h"
#include "exceptions.h"
@ -1181,3 +1182,5 @@ template<>
inline std::string LMDBAL::iStorage::toString(const std::string& value) {
return value;
}
#endif //LMDBAL_STORAGE_HPP

View File

@ -1,21 +1,3 @@
/*
* 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"
/**
@ -40,8 +22,7 @@
LMDBAL::Transaction::Transaction():
txn(nullptr),
active(false),
parent(nullptr),
cursors()
parent(nullptr)
{}
/**
@ -50,11 +31,8 @@ LMDBAL::Transaction::Transaction():
LMDBAL::Transaction::Transaction(TransactionID txn, const Base* parent) :
txn(txn),
active(true),
parent(parent),
cursors()
{
parent->transactions[txn] = this;
}
parent(parent)
{}
/**
* \brief Moves transaction to a new object
@ -62,14 +40,9 @@ LMDBAL::Transaction::Transaction(TransactionID txn, const Base* parent) :
LMDBAL::Transaction::Transaction(Transaction&& other):
txn(other.txn),
active(other.active),
parent(other.parent),
cursors(other.cursors)
parent(other.parent)
{
if (active) {
parent->transactions[txn] = this;
other.reset();
}
other.active = false;
}
/**
@ -83,21 +56,13 @@ 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;
if (active) {
parent->transactions[txn] = this;
other.reset();
}
other.active = false;
return *this;
}
@ -109,32 +74,11 @@ LMDBAL::Transaction& LMDBAL::Transaction::operator=(Transaction&& other) {
*/
void LMDBAL::Transaction::terminate() {
if (active) {
closeCursors();
parent->abortTransaction(txn);
parent->transactions.erase(txn);
reset();
active = false;
}
}
/**
* \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
*
@ -188,12 +132,6 @@ 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
*
@ -210,9 +148,7 @@ void LMDBAL::WriteTransaction::abort() {
*/
void LMDBAL::WriteTransaction::commit() {
if (active) {
closeCursors();
const_cast<Base*>(parent)->commitTransaction(txn);
parent->transactions.erase(txn);
reset();
active = false;
}
}

View File

@ -1,25 +1,6 @@
/*
* 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;
@ -40,14 +21,11 @@ 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*/
std::map<uint32_t, iCursor*> cursors; /**<\brief a collection of cursors curently opened under this transaction*/
TransactionID txn; /**<\brief Transaction inner handler*/
bool active; /**<\brief Transaction state*/
const Base* parent; /**<\brief Pointer to the database this transaction belongs to*/
};
class WriteTransaction : public Transaction {
@ -57,7 +35,6 @@ public:
explicit WriteTransaction(WriteTransaction&& other);
WriteTransaction(const WriteTransaction& other) = delete;
WriteTransaction& operator = (const WriteTransaction& other) = delete;
WriteTransaction& operator = (WriteTransaction&& other);
void commit();
void abort();

View File

@ -20,7 +20,7 @@ target_include_directories(runUnitTests PRIVATE ${Qt${QT_VERSION_MAJOR}Core_INCL
target_link_libraries(
runUnitTests
GTest::gtest_main
${LMDBAL_NAME}
${PROJECT_NAME}
Qt${QT_VERSION_MAJOR}::Core
)
include(GoogleTest)

View File

@ -113,9 +113,10 @@ TEST_F(BaseTest, AddingKeysToCache) {
EXPECT_EQ(db->ready(), true);
c1->addRecord(2, "blah balah");
c1->addRecord(-4, "testing goes brrr");
c1->addRecord(40, "whatever");
c1->addRecord(140, "whatever");
c1->addRecord(-37, "aaaaa tss tsss tsss tsss aaaaaaa");
EXPECT_EQ(c1->getRecord(40), "whatever");
EXPECT_EQ(c1->getRecord(140), "whatever");
EXPECT_EQ(c1->getRecord(-116), "whatever");
}
TEST_F(BaseTest, AddingKeysToVariableCache) {
@ -188,8 +189,8 @@ TEST_F(BaseTest, Persistence) {
EXPECT_EQ(t2->count(), t2Size);
EXPECT_EQ(c1->count(), c1Size);
EXPECT_EQ(c1->checkRecord(40), true);
EXPECT_EQ(c1->getRecord(40), "whatever");
EXPECT_EQ(c1->checkRecord(-116), true);
EXPECT_EQ(c1->getRecord(-116), "whatever");
EXPECT_EQ(c1->checkRecord(-4), true);
EXPECT_EQ(c1->getRecord(-4), "testing goes brrr");
EXPECT_EQ(c1->getRecord(-4), "testing goes brrr");

View File

@ -402,7 +402,7 @@ TEST_F(CacheCursorTest, CursorRAIIBehaviour) {
TEST_F(CacheCursorTest, CornerCases) {
transaction.terminate();
EXPECT_THROW(cursor.current(), LMDBAL::CursorNotReady);
EXPECT_THROW(cursor.current(), LMDBAL::Unknown);
cursor.close();
LMDBAL::Cursor<uint64_t, std::string> emptyCursor = emptyCache->createCursor();
@ -436,40 +436,5 @@ 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());
}

View File

@ -279,26 +279,3 @@ 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);
}

View File

@ -130,10 +130,7 @@ TEST_F(DuplicatesTest, Adding) {
tu3->addRecord(7.20001, 4.00000001); //not sure how exactly, but it works
EXPECT_EQ(tu3->count(), 7);
std::set<float> res72({-113, -53.5478, 697, 4, 4.00000001});
EXPECT_EQ(res72.count(tu3->getRecord(7.2)), 1);
EXPECT_EQ(tu3->getRecord(7.2), -113);
float tu3dd = tu3->getRecord(5119);
EXPECT_TRUE(tu3ds == tu3dd);
EXPECT_EQ(tu3ds, tu3dd);
@ -147,11 +144,8 @@ TEST_F(DuplicatesTest, Adding) {
EXPECT_THROW(tu4->addRecord(327, 79.624923), LMDBAL::Exist);
EXPECT_EQ(tu4->count(), 4);
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);
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
tu5->addRecord(-84.7, 45656753);
EXPECT_THROW(tu5->addRecord(-84.7, 45656753), LMDBAL::Exist);
@ -180,19 +174,13 @@ 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(res56.count(tu1->getRecord(-56)), 1);
res56.insert(14);
EXPECT_EQ(tu1->getRecord(-56), 274); //like yeah, it's really counterintuitive, since it's compared byte by byte
EXPECT_TRUE(tu1->forceRecord(-56, 14));
EXPECT_EQ(tu1->count(), tu1Size += 1);
EXPECT_EQ(res56.count(tu1->getRecord(-56)), 1);
EXPECT_EQ(tu1->getRecord(-56), 14);
EXPECT_FALSE(tu1->forceRecord(-56, 274));
EXPECT_EQ(tu1->count(), tu1Size);
@ -213,38 +201,26 @@ 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(res17.count(tu3->getRecord(17.3)), 1);
EXPECT_TRUE(tu3->forceRecord(17.3, 5.1));
res17.insert(5.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(tu3->count(), tu3Size += 1);
EXPECT_EQ(res17.count(tu3->getRecord(17.3)), 1);
EXPECT_EQ(tu3->getRecord(17.3), 5.1f);
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(res84.count(tu4->getRecord(84)), 1);
EXPECT_EQ(tu4->getRecord(84), 2879.654);
EXPECT_TRUE(tu4->forceRecord(84, 2679.5));
res84.insert(2679.5);
EXPECT_EQ(tu4->count(), tu4Size += 1);
EXPECT_EQ(res84.count(tu4->getRecord(84)), 1);
EXPECT_EQ(tu4->getRecord(84), 2679.5);
EXPECT_FALSE(tu4->forceRecord(84, -359.109));
EXPECT_EQ(tu4->count(), tu4Size);
@ -310,7 +286,6 @@ 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);
@ -319,13 +294,11 @@ 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(res26.count(tu3->getRecord(26.7)), 1);
EXPECT_EQ(tu3->getRecord(26.7), 23.18f);
tu3->changeRecord(26.7, 21.7);
EXPECT_EQ(tu3->count(), tu3Size);
EXPECT_EQ(tu3->getRecord(26.7), 21.7f);
@ -343,21 +316,17 @@ 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(res852.count(tu4->getRecord(852)), 1);
EXPECT_EQ(tu4->getRecord(852), 13.54);
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(res852.count(tu4->getRecord(852)), 1);
EXPECT_EQ(tu4->getRecord(852), 213.85);
EXPECT_THROW(tu4->changeRecord(852, 46324.1135), LMDBAL::Exist);
}

View File

@ -380,7 +380,7 @@ TEST_F(StorageCursorTest, CursorRAIIBehaviour) {
TEST_F(StorageCursorTest, CornerCases) {
EXPECT_EQ(getTableCursorsSize(), 1);
transaction.terminate();
EXPECT_THROW(cursor.current(), LMDBAL::CursorNotReady);
EXPECT_THROW(cursor.current(), LMDBAL::Unknown);
cursor.close();
LMDBAL::Cursor<uint64_t, std::string> emptyCursor = emptyTable->createCursor();
@ -414,40 +414,4 @@ 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());
}

View File

@ -277,27 +277,3 @@ 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);
}