forked from blue/lmdbal
Compare commits
34 commits
Author | SHA1 | Date | |
---|---|---|---|
22902a6489 | |||
f9902bc0b1 | |||
3701fb92a1 | |||
da9f935b14 | |||
ad1a13c323 | |||
3e27526517 | |||
a6ac974438 | |||
1585b8e4f5 | |||
3ae1fd15c0 | |||
bfb1d007ad | |||
ef86d0adf9 | |||
e88efb458f | |||
68ea7df6a9 | |||
56d35d4832 | |||
43d4900809 | |||
02946bbe98 | |||
7aaab5e807 | |||
649d967963 | |||
5670453314 | |||
2add0b1ae2 | |||
50a13d7290 | |||
75010c0bc6 | |||
4c5e4bb6ac | |||
f5d6fb9141 | |||
1083df36ee | |||
551495843d | |||
c3246fd2b5 | |||
8b7d548df6 | |||
7c26b09056 | |||
29b126c30e | |||
93ed15e1da | |||
75aafd2750 | |||
4fd3799c19 | |||
def2b8bc4d |
55 changed files with 2009 additions and 1143 deletions
80
.forgejo/workflows/aur.yml
Normal file
80
.forgejo/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
|
66
.forgejo/workflows/main.yml
Normal file
66
.forgejo/workflows/main.yml
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
name: Main LMDBAL workflow
|
||||||
|
run-name: ${{ gitea.actor }} is running LMDBAL main workflow
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-qt5:
|
||||||
|
name: Test LMDBAL with qt5
|
||||||
|
uses: ./.forgejo/workflows/test.yml
|
||||||
|
runs-on: archlinux
|
||||||
|
with:
|
||||||
|
flags: -D QT_VERSION_MAJOR=5 -D LMDBAL_NAME=LMDBAL-QT5
|
||||||
|
|
||||||
|
test-qt6:
|
||||||
|
name: Test LMDBAL with qt6
|
||||||
|
uses: ./.forgejo/workflows/test.yml
|
||||||
|
runs-on: archlinux
|
||||||
|
with:
|
||||||
|
flags: -D QT_VERSION_MAJOR=6 -D LMDBAL_NAME=LMDBAL-QT6
|
||||||
|
|
||||||
|
build-documentation:
|
||||||
|
name: Builds documentation
|
||||||
|
runs-on: archlinux
|
||||||
|
needs: [test-qt5, test-qt6]
|
||||||
|
steps:
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Make a build directory
|
||||||
|
run: mkdir build
|
||||||
|
|
||||||
|
- name: Configure
|
||||||
|
working-directory: ./build
|
||||||
|
run: cmake .. LMDBAL_BUILD_DOC_HTML=True -D LMDBAL_BUILD_DOC_XML=True -D LMDBAL_BUILD_DOC_MAN=True -D LMDBAL_BUILD_DOXYGEN_AWESOME=True
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
working-directory: ./build
|
||||||
|
run: cmake --build .
|
||||||
|
|
||||||
|
- name: Upload docs
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: lmdbal-doc
|
||||||
|
path: build/doc
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
deploy-doc:
|
||||||
|
name: Deploys documentation
|
||||||
|
runs-on: bm_site
|
||||||
|
needs: [build-documentation]
|
||||||
|
steps:
|
||||||
|
- name: Download docs
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: lmdbal-doc
|
||||||
|
path: build/doc
|
||||||
|
|
||||||
|
- name: Make sure deploy folder exists
|
||||||
|
run: mkdir -p /srv/http/doc/lmdbal
|
||||||
|
|
||||||
|
- name: Deploy docs
|
||||||
|
run: |
|
||||||
|
rm -rf /srv/http/doc/lmdbal/*
|
||||||
|
cp -r build/doc/html/* /srv/http/doc/lmdbal/
|
33
.forgejo/workflows/release.yml
Normal file
33
.forgejo/workflows/release.yml
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
name: LMDBAL Release workflow
|
||||||
|
run-name: ${{ gitea.actor }} is running LMDBAL Release workflow on release ${{ gitea.event.release.tag_name }}
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
qt5:
|
||||||
|
name: Release qt5 build to AUR
|
||||||
|
uses: ./.forgejo/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 }}
|
||||||
|
|
||||||
|
qt6:
|
||||||
|
name: Release qt6 build to AUR
|
||||||
|
uses: ./.forgejo/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 }}
|
||||||
|
|
39
.forgejo/workflows/test.yml
Normal file
39
.forgejo/workflows/test.yml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
name: Build and run unit tests for LMDBAL
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
flags:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
flags:
|
||||||
|
description: "Flags for CMake configure stage"
|
||||||
|
type: string
|
||||||
|
default: "lmdbal"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Building and running unit tests
|
||||||
|
runs-on: archlinux
|
||||||
|
steps:
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Make a build directory
|
||||||
|
run: mkdir build
|
||||||
|
|
||||||
|
- name: Configure
|
||||||
|
working-directory: ./build
|
||||||
|
run: cmake .. -D LMDBAL_STRICT=True -D LMDBAL_BUILD_TESTS=True ${{ inputs.flags }}
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
working-directory: ./build
|
||||||
|
run: cmake --build .
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
working-directory: ./build/test
|
||||||
|
run: ./runUnitTests
|
|
@ -1,39 +0,0 @@
|
||||||
name: Main LMDBAL workfow
|
|
||||||
run-name: ${{ gitea.actor }} is running LMDBAL main workflow
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
Archlinux:
|
|
||||||
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 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
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.DOMAIN_ROOT }}
|
|
||||||
username: ${{ secrets.DEPLOY_USER_NAME }}
|
|
||||||
key: ${{ secrets.DEPLOY_PRIVATE_KEY }}
|
|
||||||
source: "build/doc/html/*,build/doc/xml/*,build/doc/man/*"
|
|
||||||
target: "/srv/lmdbal/doc"
|
|
||||||
strip_components: 2
|
|
|
@ -1,46 +0,0 @@
|
||||||
name: LMDBAL Release workflow
|
|
||||||
run-name: ${{ gitea.actor }} is running LMDBAL Release workflow on release ${{ gitea.event.release.tag_name }}
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
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
|
|
||||||
|
|
||||||
- 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
|
|
||||||
|
|
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -1,5 +1,22 @@
|
||||||
# Changelog
|
# 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
|
||||||
|
- New primitive to manage database state - session
|
||||||
|
- Stricter warnings
|
||||||
|
- Sanitizer builds
|
||||||
|
|
||||||
|
### 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)
|
# LMDBAL 0.5.3 (November 14, 2023)
|
||||||
### Improvements
|
### Improvements
|
||||||
- Now you don't need to link agains lmdb, just linking against LMDBAL is enough
|
- Now you don't need to link agains lmdb, just linking against LMDBAL is enough
|
||||||
|
|
183
CMakeLists.txt
183
CMakeLists.txt
|
@ -1,21 +1,26 @@
|
||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
project(LMDBAL
|
project(LMDBAL
|
||||||
VERSION 0.5.4
|
VERSION 0.6.0
|
||||||
DESCRIPTION "LMDB (Lightning Memory-Mapped Database Manager) Abstraction Layer"
|
DESCRIPTION "LMDB (Lightning Memory-Mapped Database Manager) Abstraction Layer"
|
||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
)
|
)
|
||||||
string(TOLOWER ${PROJECT_NAME} PROJECT_LOW)
|
|
||||||
|
|
||||||
cmake_policy(SET CMP0076 NEW)
|
cmake_policy(SET CMP0076 NEW)
|
||||||
cmake_policy(SET CMP0079 NEW)
|
cmake_policy(SET CMP0079 NEW)
|
||||||
|
|
||||||
option(BUILD_STATIC "Builds library as static library" OFF)
|
set(LMDBAL_NAME ${PROJECT_NAME} CACHE STRING "Override for library name and install path")
|
||||||
option(BUILD_TESTS "Builds tests" OFF)
|
|
||||||
option(BUILD_DOC_MAN "Builds man page documentation" OFF)
|
option(LMDBAL_BUILD_STATIC "Builds library as static library" OFF)
|
||||||
option(BUILD_DOC_HTML "Builds html documentation" OFF)
|
option(LMDBAL_BUILD_TESTS "Builds tests" OFF)
|
||||||
option(BUILD_DOC_XML "Builds xml documentation" OFF)
|
option(LMDBAL_BUILD_DOC_MAN "Builds man page documentation" OFF)
|
||||||
option(BUILD_DOXYGEN_AWESOME "Builds documentation alternative style" OFF)
|
option(LMDBAL_BUILD_DOC_HTML "Builds html documentation" OFF)
|
||||||
|
option(LMDBAL_BUILD_DOC_XML "Builds xml documentation" OFF)
|
||||||
|
option(LMDBAL_BUILD_DOXYGEN_AWESOME "Builds documentation alternative style" OFF)
|
||||||
|
option(LMDBAL_STRICT "Builds with extra compiler warnings" OFF)
|
||||||
|
option(LMDBAL_ASAN "Enables Address Sanitizer" OFF)
|
||||||
|
option(LMDBAL_UBSAN "Enables Undefined Behavior Sanitizer" OFF)
|
||||||
|
option(LMDBAL_TSAN "Enables Thread Sanitizer" OFF)
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
include(CMakePackageConfigHelpers)
|
include(CMakePackageConfigHelpers)
|
||||||
|
@ -25,106 +30,148 @@ set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
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)
|
if (NOT DEFINED QT_VERSION_MAJOR)
|
||||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
|
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
|
||||||
endif()
|
endif ()
|
||||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
|
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
|
||||||
|
|
||||||
find_package(LMDB REQUIRED)
|
find_package(LMDB REQUIRED)
|
||||||
|
|
||||||
# Build type
|
# Build type
|
||||||
if (NOT CMAKE_BUILD_TYPE)
|
if (NOT CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (BUILD_STATIC)
|
if (LMDBAL_BUILD_STATIC)
|
||||||
add_library(${PROJECT_NAME} STATIC)
|
add_library(${LMDBAL_NAME} STATIC)
|
||||||
else ()
|
else ()
|
||||||
add_library(${PROJECT_NAME} SHARED)
|
add_library(${LMDBAL_NAME} SHARED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
|
||||||
list(APPEND COMPILE_OPTIONS -O3)
|
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||||
elseif (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
list(APPEND COMPILE_OPTIONS -Wall)
|
||||||
list(APPEND COMPILE_OPTIONS -g)
|
list(APPEND COMPILE_OPTIONS -Wextra)
|
||||||
list(APPEND COMPILE_OPTIONS -Wall)
|
list(APPEND COMPILE_OPTIONS -Wpedantic)
|
||||||
list(APPEND COMPILE_OPTIONS -Wextra)
|
|
||||||
endif()
|
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
list(APPEND COMPILE_OPTIONS -O3)
|
||||||
|
list(APPEND COMPILE_OPTIONS -DNDEBUG)
|
||||||
|
elseif (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
list(APPEND COMPILE_OPTIONS -O0)
|
||||||
|
list(APPEND COMPILE_OPTIONS -g3)
|
||||||
|
list(APPEND COMPILE_OPTIONS -ggdb)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (LMDBAL_STRICT)
|
||||||
|
list(APPEND COMPILE_OPTIONS -Wall)
|
||||||
|
list(APPEND COMPILE_OPTIONS -Werror)
|
||||||
|
list(APPEND COMPILE_OPTIONS -Wconversion)
|
||||||
|
list(APPEND COMPILE_OPTIONS -Wnon-virtual-dtor)
|
||||||
|
list(APPEND COMPILE_OPTIONS -Wold-style-cast)
|
||||||
|
list(APPEND COMPILE_OPTIONS -Wcast-align)
|
||||||
|
list(APPEND COMPILE_OPTIONS -Wunused)
|
||||||
|
list(APPEND COMPILE_OPTIONS -Woverloaded-virtual)
|
||||||
|
list(APPEND COMPILE_OPTIONS -Wsign-conversion)
|
||||||
|
list(APPEND COMPILE_OPTIONS -Wnull-dereference)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if(JAY_ENABLE_ASAN)
|
||||||
|
list(APPEND COMPILE_OPTIONS -fsanitize=address)
|
||||||
|
list(APPEND COMPILE_OPTIONS -fno-omit-frame-pointer)
|
||||||
|
list(APPEND LINK_OPTIONS -fsanitize=address)
|
||||||
|
add_link_options()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(JAY_ENABLE_UBSAN)
|
||||||
|
list(APPEND COMPILE_OPTIONS -fsanitize=undefined)
|
||||||
|
list(APPEND LINK_OPTIONS -fsanitize=undefined)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(JAY_ENABLE_TSAN)
|
||||||
|
list(APPEND COMPILE_OPTIONS -fsanitize=thread)
|
||||||
|
list(APPEND LINK_OPTIONS -fsanitize=thread)
|
||||||
|
endif()
|
||||||
|
endif ()
|
||||||
|
|
||||||
message("Compilation options: " ${COMPILE_OPTIONS})
|
message("Compilation options: " ${COMPILE_OPTIONS})
|
||||||
target_compile_options(${PROJECT_NAME} PRIVATE ${COMPILE_OPTIONS})
|
message("Linking options: " ${LINK_OPTIONS})
|
||||||
|
target_compile_options(${LMDBAL_NAME} PRIVATE ${COMPILE_OPTIONS})
|
||||||
|
target_link_options(${LMDBAL_NAME} PRIVATE ${LINK_OPTIONS})
|
||||||
|
|
||||||
set_property(TARGET ${PROJECT_NAME} PROPERTY VERSION ${version})
|
set_property(TARGET ${LMDBAL_NAME} PROPERTY VERSION ${version})
|
||||||
set_property(TARGET ${PROJECT_NAME} PROPERTY SOVERSION 1)
|
set_property(TARGET ${LMDBAL_NAME} PROPERTY SOVERSION 1)
|
||||||
set_property(TARGET ${PROJECT_NAME} PROPERTY EXPORT_NAME ${PROJECT_NAME})
|
set_property(TARGET ${LMDBAL_NAME} PROPERTY EXPORT_NAME ${LMDBAL_NAME})
|
||||||
set_property(TARGET ${PROJECT_NAME} PROPERTY INTERFACE_${PROJECT_NAME}_MAJOR_VERSION 1)
|
set_property(TARGET ${LMDBAL_NAME} PROPERTY INTERFACE_${LMDBAL_NAME}_MAJOR_VERSION 1)
|
||||||
set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY
|
set_property(TARGET ${LMDBAL_NAME} APPEND PROPERTY
|
||||||
COMPATIBLE_INTERFACE_STRING ${PROJECT_NAME}_MAJOR_VERSION
|
COMPATIBLE_INTERFACE_STRING ${LMDBAL_NAME}_MAJOR_VERSION
|
||||||
)
|
)
|
||||||
|
|
||||||
if (UNIX)
|
if (UNIX)
|
||||||
set_property(TARGET ${PROJECT_NAME} PROPERTY INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${PROJECT_LOW}")
|
set_property(TARGET ${LMDBAL_NAME} PROPERTY INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${LMDBAL_NAME_LOW}")
|
||||||
set_property(TARGET ${PROJECT_NAME} PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE)
|
set_property(TARGET ${LMDBAL_NAME} PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||||
set_property(TARGET ${PROJECT_NAME} PROPERTY SKIP_BUILD_RPATH FALSE)
|
set_property(TARGET ${LMDBAL_NAME} PROPERTY SKIP_BUILD_RPATH FALSE)
|
||||||
set_property(TARGET ${PROJECT_NAME} PROPERTY BUILD_WITH_INSTALL_RPATH FALSE)
|
set_property(TARGET ${LMDBAL_NAME} PROPERTY BUILD_WITH_INSTALL_RPATH FALSE)
|
||||||
endif()
|
endif ()
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
if (BUILD_DOC_MAN OR BUILD_DOC_HTML OR BUILD_DOC_XML)
|
if (LMDBAL_BUILD_DOC_MAN OR LMDBAL_BUILD_DOC_HTML OR LMDBAL_BUILD_DOC_XML)
|
||||||
find_package(Doxygen)
|
find_package(Doxygen)
|
||||||
if (DOXYGEN_FOUND)
|
if (DOXYGEN_FOUND)
|
||||||
add_subdirectory(doc)
|
add_subdirectory(doc)
|
||||||
else()
|
else ()
|
||||||
message("Was trying to build documentation, but Doxygen was not found, skipping documentation")
|
message("Was trying to build documentation, but Doxygen was not found, skipping documentation")
|
||||||
endif()
|
endif ()
|
||||||
endif()
|
endif ()
|
||||||
|
|
||||||
if (BUILD_TESTS)
|
if (LMDBAL_BUILD_TESTS)
|
||||||
add_subdirectory(test)
|
add_subdirectory(test)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
${PROJECT_NAME}
|
${LMDBAL_NAME}
|
||||||
PUBLIC
|
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>
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/serializer>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/serializer>
|
||||||
)
|
)
|
||||||
target_include_directories(${PROJECT_NAME} PRIVATE ${Qt${QT_VERSION_MAJOR}_INCLUDE_DIRS})
|
target_include_directories(${LMDBAL_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}Core_INCLUDE_DIRS})
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
${PROJECT_NAME}
|
${LMDBAL_NAME}
|
||||||
Qt${QT_VERSION_MAJOR}::Core
|
Qt${QT_VERSION_MAJOR}::Core
|
||||||
lmdb
|
lmdb
|
||||||
)
|
)
|
||||||
|
|
||||||
configure_package_config_file(
|
configure_package_config_file(
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in
|
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}Config.cmake"
|
"${CMAKE_CURRENT_BINARY_DIR}/${LMDBAL_NAME_LOW}Config.cmake"
|
||||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_LOW}
|
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LMDBAL_NAME_LOW}
|
||||||
)
|
)
|
||||||
|
|
||||||
write_basic_package_version_file(
|
write_basic_package_version_file(
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}ConfigVersion.cmake"
|
"${CMAKE_CURRENT_BINARY_DIR}/${LMDBAL_NAME_LOW}ConfigVersion.cmake"
|
||||||
VERSION "${version}"
|
VERSION "${version}"
|
||||||
COMPATIBILITY AnyNewerVersion
|
COMPATIBILITY AnyNewerVersion
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS ${PROJECT_NAME}
|
install(TARGETS ${LMDBAL_NAME}
|
||||||
EXPORT ${PROJECT_LOW}Targets
|
EXPORT ${LMDBAL_NAME_LOW}Targets
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
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
|
install(EXPORT ${LMDBAL_NAME_LOW}Targets
|
||||||
FILE ${PROJECT_LOW}Targets.cmake
|
FILE ${LMDBAL_NAME_LOW}Targets.cmake
|
||||||
NAMESPACE ${PROJECT_NAME}::
|
NAMESPACE ${LMDBAL_NAME}::
|
||||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_LOW}
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LMDBAL_NAME_LOW}
|
||||||
)
|
)
|
||||||
|
|
||||||
install(FILES
|
install(FILES
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}Config.cmake"
|
"${CMAKE_CURRENT_BINARY_DIR}/${LMDBAL_NAME_LOW}Config.cmake"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}ConfigVersion.cmake"
|
"${CMAKE_CURRENT_BINARY_DIR}/${LMDBAL_NAME_LOW}ConfigVersion.cmake"
|
||||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_LOW}
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LMDBAL_NAME_LOW}
|
||||||
)
|
)
|
||||||
|
|
41
README.md
41
README.md
|
@ -1,14 +1,15 @@
|
||||||
# LMDBAL - Lightning Memory Data Base Abstraction Level
|
# LMDBAL - Lightning Memory Data Base Abstraction Level
|
||||||
|
|
||||||
[](https://git.macaw.me/blue/lmdbal/raw/branch/master/LICENSE.md)
|
[](https://git.macaw.me/blue/lmdbal/raw/branch/master/LICENSE.md)
|
||||||
[](https://aur.archlinux.org/packages/lmdbal/)
|
[](https://aur.archlinux.org/packages/lmdbal-qt5/)
|
||||||
|
[](https://aur.archlinux.org/packages/lmdbal-qt6/)
|
||||||
[](https://liberapay.com/macaw.me)
|
[](https://liberapay.com/macaw.me)
|
||||||
[](https://macaw.me/lmdbal/doc/html)
|
[](https://macaw.me/lmdbal/doc/html)
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- a compiler (c++ would do)
|
- a c++ compiler (g++ would do)
|
||||||
- Qt 5 or higher (qt5-base would do)
|
- Qt 5 or 6 or higher (qt5-base or qt6-base would do)
|
||||||
- lmdb
|
- lmdb
|
||||||
- CMake 3.16 or higher
|
- CMake 3.16 or higher
|
||||||
- Doxygen (optional, for documentation)
|
- Doxygen (optional, for documentation)
|
||||||
|
@ -18,7 +19,8 @@
|
||||||
|
|
||||||
#### As a system library
|
#### As a system library
|
||||||
|
|
||||||
If you're using LMDBAL as a system library you probably have no control over it's build options. The easiest way to include the project is to add following
|
If you're using LMDBAL as a system library, you probably have no control over its build options.
|
||||||
|
The easiest way to include the project is to add the following
|
||||||
|
|
||||||
```
|
```
|
||||||
find_package(lmdbal)
|
find_package(lmdbal)
|
||||||
|
@ -28,14 +30,14 @@ if (LMDBAL_FOUND)
|
||||||
endif()
|
endif()
|
||||||
```
|
```
|
||||||
|
|
||||||
#### As an embeded subproject
|
#### As an embedded subproject
|
||||||
|
|
||||||
If you're using LMDBAL as a embeded library you might want to control it's build options, for example you can run
|
If you're using LMDBAL as a embedded library, you might want to control its build options, for example, you can run
|
||||||
```
|
```
|
||||||
set(BUILD_STATIC ON)
|
set(LMDBAL_BUILD_STATIC ON)
|
||||||
```
|
```
|
||||||
|
|
||||||
before including the library in your project. This will set the library to be build in a static mode.
|
... before including the library in your project. This will set the library to be build in a static mode.
|
||||||
|
|
||||||
Then you want to run something like this
|
Then you want to run something like this
|
||||||
```
|
```
|
||||||
|
@ -66,9 +68,11 @@ $ cmake --build .
|
||||||
$ cmake --install . --prefix install
|
$ cmake --install . --prefix install
|
||||||
```
|
```
|
||||||
|
|
||||||
This way will create you a `lmdbal/build` directory with temporary files, and `lmdbal/build/install` with all the export files for installation to the system.
|
This way will create you a `lmdbal/build` directory with temporary files, and `lmdbal/build/install`
|
||||||
|
with all the export files for installation to the system.
|
||||||
|
|
||||||
After `cmake ..` you can specify keys to alter the building process. In this context building keys are transfered like so
|
After `cmake ..` you can specify keys to alter the building process.
|
||||||
|
In this context building keys are transferred like so
|
||||||
|
|
||||||
```
|
```
|
||||||
cmake .. -D KEY1=VALUE1 -D KEY2=VALUE2 ...
|
cmake .. -D KEY1=VALUE1 -D KEY2=VALUE2 ...
|
||||||
|
@ -79,21 +83,26 @@ cmake .. -D KEY1=VALUE1 -D KEY2=VALUE2 ...
|
||||||
Here is the list of keys you can pass to configuration phase of `cmake ..`:
|
Here is the list of keys you can pass to configuration phase of `cmake ..`:
|
||||||
|
|
||||||
- `CMAKE_BUILD_TYPE` - `Debug` just builds showing all warnings, `Release` builds with no warnings and applies optimizations (default is `Debug`);
|
- `CMAKE_BUILD_TYPE` - `Debug` just builds showing all warnings, `Release` builds with no warnings and applies optimizations (default is `Debug`);
|
||||||
- `BUILD_STATIC` - `True` builds project as a static library, `False` builds as dynamic (default is `False`);
|
- `LMDBAL_BUILD_STATIC` - `True` builds project as a static library, `False` builds as dynamic (default is `False`);
|
||||||
- `BUILD_TESTS` - `True` build unit tests, `False` does not (default is `False`);
|
- `LMDBAL_BUILD_TESTS` - `True` build unit tests, `False` does not (default is `False`);
|
||||||
- `BUILD_DOC` - `True` build doxygen documentation, `False` does not (default is `False`);
|
- `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`);
|
- `LMDBAL_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;
|
- `QT_VERSION_MAJOR` - `5` links against Qt5, `6` links against Qt6, there is no default, so, if you didn't specify it, the project will choose automatically;
|
||||||
|
- `LMDBAL_NAME` - `LMDBAL` builds the main target with this name, also the installation path will be this name lowercase, useful if you want to build `LMDBAL-QT6` not to conflict with `LMDBAL-QT5`;
|
||||||
|
- `LMDBAL_STRICT` - `True` builds with extra warnings and considers them errors (default is `False`);
|
||||||
|
- `LMDBAL_ASAN` - `True` builds with address sanitizer (default is `False`);
|
||||||
|
- `LMDBAL_TSAN` - `True` builds with thread sanitizer (default is `False`);
|
||||||
|
- `LMDBAL_UBSAN` - `True` builds with undefined behavior sanitizer (default is `False`);
|
||||||
|
|
||||||
#### Running tests
|
#### Running tests
|
||||||
|
|
||||||
If you built the library with `-D BUILD_TESTS=True`, then there will be `lmdbal/build/tests/runUnitTests` executable file. You can simply run it as
|
If you built the library with `-D LMDBAL_BUILD_TESTS=True`, then there will be `lmdbal/build/tests/runUnitTests` executable file. You can run it as
|
||||||
|
|
||||||
```
|
```
|
||||||
./runUnitTests
|
./runUnitTests
|
||||||
```
|
```
|
||||||
|
|
||||||
if you're in the same directory with it
|
... if you're in the same directory with it
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
@PACKAGE_INIT@
|
@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,14 +1,14 @@
|
||||||
if (BUILD_DOC_HTML)
|
if (LMDBAL_BUILD_DOC_HTML)
|
||||||
set(DOXYGEN_GENERATE_HTML YES)
|
set(DOXYGEN_GENERATE_HTML YES)
|
||||||
endif()
|
endif()
|
||||||
if (BUILD_DOC_MAN)
|
if (LMDBAL_BUILD_DOC_MAN)
|
||||||
set(DOXYGEN_GENERATE_MAN YES)
|
set(DOXYGEN_GENERATE_MAN YES)
|
||||||
endif()
|
endif()
|
||||||
if (BUILD_DOC_XML)
|
if (LMDBAL_BUILD_DOC_XML)
|
||||||
set(DOXYGEN_GENERATE_XML YES)
|
set(DOXYGEN_GENERATE_XML YES)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BUILD_DOXYGEN_AWESOME)
|
if (LMDBAL_BUILD_DOXYGEN_AWESOME)
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
ExternalProject_Add(doxygen-awesome-css
|
ExternalProject_Add(doxygen-awesome-css
|
||||||
GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css.git
|
GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css.git
|
||||||
|
@ -42,25 +42,25 @@ doxygen_add_docs(
|
||||||
ALL
|
ALL
|
||||||
COMMENT "Generate man and html pages"
|
COMMENT "Generate man and html pages"
|
||||||
)
|
)
|
||||||
if (BUILD_DOC_MAN)
|
if (LMDBAL_BUILD_DOC_MAN)
|
||||||
install(DIRECTORY
|
install(DIRECTORY
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/man
|
${CMAKE_CURRENT_BINARY_DIR}/man
|
||||||
TYPE DOC
|
TYPE DOC
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
if (BUILD_DOC_HTML)
|
if (LMDBAL_BUILD_DOC_HTML)
|
||||||
install(DIRECTORY
|
install(DIRECTORY
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/html
|
${CMAKE_CURRENT_BINARY_DIR}/html
|
||||||
TYPE DOC
|
TYPE DOC
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
if (BUILD_DOC_XML)
|
if (LMDBAL_BUILD_DOC_XML)
|
||||||
install(DIRECTORY
|
install(DIRECTORY
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/xml
|
${CMAKE_CURRENT_BINARY_DIR}/xml
|
||||||
TYPE DOC
|
TYPE DOC
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BUILD_DOXYGEN_AWESOME)
|
if (LMDBAL_BUILD_DOXYGEN_AWESOME)
|
||||||
add_dependencies(documentation doxygen-awesome-css)
|
add_dependencies(documentation doxygen-awesome-css)
|
||||||
endif()
|
endif()
|
|
@ -1,23 +1,23 @@
|
||||||
# Maintainer: Yury Gubich <blue@macaw.me>
|
# Maintainer: Yury Gubich <blue@macaw.me>
|
||||||
pkgname=lmdbal
|
pkgname=lmdbal
|
||||||
pkgver=0.5.4
|
pkgver=1.0.0
|
||||||
pkgrel=1
|
pkgrel=4
|
||||||
pkgdesc="LMDB Abstraction Layer, qt5 version"
|
pkgdesc="LMDB Abstraction Layer"
|
||||||
arch=('i686' 'x86_64')
|
arch=('i686' 'x86_64')
|
||||||
url="https://git.macaw.me/blue/lmdbal"
|
url="https://git.macaw.me/blue/lmdbal"
|
||||||
license=('GPL3')
|
license=('GPL3')
|
||||||
depends=( 'lmdb' 'qt5-base')
|
depends=( 'lmdb' )
|
||||||
makedepends=('cmake>=3.16' 'gcc')
|
makedepends=('cmake>=3.16' 'gcc')
|
||||||
optdepends=()
|
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')
|
sha256sums=('SKIP')
|
||||||
build() {
|
build() {
|
||||||
cd "$srcdir/$pkgname"
|
cd "$srcdir/lmdbal"
|
||||||
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release -D QT_VERSION_MAJOR=5
|
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release
|
||||||
cmake --build .
|
cmake --build .
|
||||||
}
|
}
|
||||||
package() {
|
package() {
|
||||||
cd "$srcdir/$pkgname"
|
cd "$srcdir/lmdbal"
|
||||||
DESTDIR="$pkgdir/" cmake --install .
|
DESTDIR="$pkgdir/" cmake --install .
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
exceptions.cpp
|
exceptions.cpp
|
||||||
storage.cpp
|
storagecommon.cpp
|
||||||
base.cpp
|
base.cpp
|
||||||
transaction.cpp
|
transaction.cpp
|
||||||
|
cursorcommon.cpp
|
||||||
|
session.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
|
@ -10,16 +12,20 @@ set(HEADERS
|
||||||
exceptions.h
|
exceptions.h
|
||||||
storage.h
|
storage.h
|
||||||
storage.hpp
|
storage.hpp
|
||||||
|
storagecommon.h
|
||||||
|
storagecommon.hpp
|
||||||
cursor.h
|
cursor.h
|
||||||
cursor.hpp
|
cursor.hpp
|
||||||
|
cursorcommon.h
|
||||||
cache.h
|
cache.h
|
||||||
cache.hpp
|
cache.hpp
|
||||||
operators.hpp
|
operators.hpp
|
||||||
transaction.h
|
transaction.h
|
||||||
|
session.h
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
|
target_sources(${LMDBAL_NAME} PRIVATE ${SOURCES})
|
||||||
|
|
||||||
add_subdirectory(serializer)
|
add_subdirectory(serializer)
|
||||||
|
|
||||||
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_LOW})
|
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${LMDBAL_NAME_LOW})
|
||||||
|
|
260
src/base.cpp
260
src/base.cpp
|
@ -18,8 +18,10 @@
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "exceptions.h"
|
#include "exceptions.h"
|
||||||
|
#include "session.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "transaction.h"
|
#include "transaction.h"
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
#define UNUSED(x) (void)(x)
|
#define UNUSED(x) (void)(x)
|
||||||
|
|
||||||
|
@ -28,97 +30,55 @@
|
||||||
* \brief Database abstraction
|
* \brief Database abstraction
|
||||||
*
|
*
|
||||||
* This is a basic class that represents the database as a collection of storages.
|
* This is a basic class that represents the database as a collection of storages.
|
||||||
* Storages is something key-value database has instead of tables in classic SQL databases.
|
* Storages are something that key-value databases have instead of tables in classic SQL databases.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Creates the database
|
* \brief Creates the database
|
||||||
*
|
*
|
||||||
* \param[in] _name - name of the database, it is going to affect folder name that is created to store data
|
* \param[in] _name - name of the database, it is going to affect the folder name that is created to store data
|
||||||
* \param[in] _mapSize - LMDB map size (MiB), multiplied by 1024^2 and passed to <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#gaa2506ec8dab3d969b0e609cd82e619e5">mdb_env_set_mapsize</a> during the call of LMDBAL::Base::open()
|
* \param[in] _mapSize - LMDB map size (MiB), multiplied by 1024^2 and passed to <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#gaa2506ec8dab3d969b0e609cd82e619e5">mdb_env_set_mapsize</a> during the call of LMDBAL::Base::open()
|
||||||
*/
|
*/
|
||||||
LMDBAL::Base::Base(const QString& _name, uint16_t _mapSize):
|
LMDBAL::Base::Base(const QString& _name, uint16_t _mapSize):
|
||||||
name(_name.toStdString()),
|
name(_name.toStdString()),
|
||||||
opened(false),
|
sessions(),
|
||||||
size(_mapSize),
|
size(_mapSize),
|
||||||
environment(),
|
environment(),
|
||||||
storages(),
|
storages(),
|
||||||
transactions(new Transactions())
|
transactions(),
|
||||||
|
mutex()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Destroys the database
|
* \brief Destroys the database
|
||||||
*/
|
*/
|
||||||
LMDBAL::Base::~Base() {
|
LMDBAL::Base::~Base() {
|
||||||
close();
|
std::lock_guard lock(mutex);
|
||||||
|
|
||||||
delete transactions;
|
for (Session* session : sessions)
|
||||||
|
session->terminate();
|
||||||
|
|
||||||
for (const std::pair<const std::string, iStorage*>& pair : storages)
|
if (opened())
|
||||||
|
deactivate();
|
||||||
|
|
||||||
|
for (const std::pair<const std::string, StorageCommon*>& pair : storages)
|
||||||
delete pair.second;
|
delete pair.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
LMDBAL::Session LMDBAL::Base::open() {
|
||||||
* \brief Closes the database
|
return {this};
|
||||||
*
|
|
||||||
* Closes all lmdb handles, aborts all public transactions.
|
|
||||||
* This function will do nothing on closed database
|
|
||||||
*
|
|
||||||
* \exception LMDBAL::Unknown - thrown if something went wrong aborting transactions
|
|
||||||
*/
|
|
||||||
void LMDBAL::Base::close() {
|
|
||||||
if (opened) {
|
|
||||||
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();
|
|
||||||
opened = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Opens the database
|
|
||||||
*
|
|
||||||
* Almost every LMDBAL::Base require it to be opened, this function does it.
|
|
||||||
* It laso creates the directory for the database if it was an initial launch.
|
|
||||||
* This function will do nothing on opened database
|
|
||||||
*
|
|
||||||
* \exception LMDBAL::Unknown - thrown if something went wrong opening storages and caches
|
|
||||||
*/
|
|
||||||
void LMDBAL::Base::open() {
|
|
||||||
if (!opened) {
|
|
||||||
mdb_env_create(&environment);
|
|
||||||
QString path = createDirectory();
|
|
||||||
|
|
||||||
mdb_env_set_maxdbs(environment, storages.size());
|
|
||||||
mdb_env_set_mapsize(environment, size * 1024UL * 1024UL);
|
|
||||||
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
|
||||||
|
|
||||||
TransactionID txn = beginPrivateTransaction(emptyName);
|
|
||||||
for (const std::pair<const std::string, iStorage*>& pair : storages) {
|
|
||||||
iStorage* storage = pair.second;
|
|
||||||
int rc = storage->open(txn);
|
|
||||||
if (rc)
|
|
||||||
throw Unknown(name, mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
commitPrivateTransaction(txn, emptyName);
|
|
||||||
opened = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Removes database directory
|
* \brief Removes database directory
|
||||||
*
|
*
|
||||||
* \returns true if removal was successfull of if no directory was created where it's expected to be, false otherwise
|
* \returns true if removal was successful of if no directory was created where it's expected to be, false otherwise
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::Opened - thrown if this function was called on opened database
|
* \exception LMDBAL::Opened - thrown if this function was called on an opened database
|
||||||
*/
|
*/
|
||||||
bool LMDBAL::Base::removeDirectory() {
|
bool LMDBAL::Base::removeDirectory() {
|
||||||
if (opened)
|
if (opened())
|
||||||
throw Opened(name, "remove database directory");
|
throw Opened(name, "remove database directory");
|
||||||
|
|
||||||
QString path = getPath();
|
QString path = getPath();
|
||||||
|
@ -141,11 +101,11 @@ bool LMDBAL::Base::removeDirectory() {
|
||||||
*
|
*
|
||||||
* \returns the path of the created directory
|
* \returns the path of the created directory
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::Opened - thrown if called on opened database
|
* \exception LMDBAL::Opened - thrown if called on an opened database
|
||||||
* \exception LMDBAL::Directory - if the database couldn't create the folder
|
* \exception LMDBAL::Directory - if the database couldn't create the folder
|
||||||
*/
|
*/
|
||||||
QString LMDBAL::Base::createDirectory() {
|
QString LMDBAL::Base::createDirectory() {
|
||||||
if (opened)
|
if (opened())
|
||||||
throw Opened(name, "create database directory");
|
throw Opened(name, "create database directory");
|
||||||
|
|
||||||
QString path = getPath();
|
QString path = getPath();
|
||||||
|
@ -184,8 +144,9 @@ QString LMDBAL::Base::getPath() const {
|
||||||
*
|
*
|
||||||
* \returns true if the database is opened and ready for work, false otherwise
|
* \returns true if the database is opened and ready for work, false otherwise
|
||||||
*/
|
*/
|
||||||
bool LMDBAL::Base::ready() const {
|
bool LMDBAL::Base::opened() const {
|
||||||
return opened;}
|
return !sessions.empty();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Drops the database
|
* \brief Drops the database
|
||||||
|
@ -196,11 +157,11 @@ bool LMDBAL::Base::ready() const {
|
||||||
* \exception LMDBAL::Unknown - thrown if something unexpected happend
|
* \exception LMDBAL::Unknown - thrown if something unexpected happend
|
||||||
*/
|
*/
|
||||||
void LMDBAL::Base::drop() {
|
void LMDBAL::Base::drop() {
|
||||||
if (!opened)
|
if (!opened())
|
||||||
throw Closed("drop", name);
|
throw Closed("drop", name);
|
||||||
|
|
||||||
TransactionID txn = beginPrivateTransaction(emptyName);
|
TransactionID txn = beginPrivateTransaction(emptyName);
|
||||||
for (const std::pair<const std::string, iStorage*>& pair : storages) {
|
for (const std::pair<const std::string, StorageCommon*>& pair : storages) {
|
||||||
int rc = pair.second->drop(txn);
|
int rc = pair.second->drop(txn);
|
||||||
if (rc != MDB_SUCCESS) {
|
if (rc != MDB_SUCCESS) {
|
||||||
abortPrivateTransaction(txn, emptyName);
|
abortPrivateTransaction(txn, emptyName);
|
||||||
|
@ -209,7 +170,7 @@ void LMDBAL::Base::drop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
commitPrivateTransaction(txn, emptyName);
|
commitPrivateTransaction(txn, emptyName);
|
||||||
for (const std::pair<const std::string, iStorage*>& pair : storages)
|
for (const std::pair<const std::string, StorageCommon*>& pair : storages)
|
||||||
pair.second->handleDrop();
|
pair.second->handleDrop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,12 +222,13 @@ LMDBAL::WriteTransaction LMDBAL::Base::beginTransaction() {
|
||||||
* \brief Aborts transaction
|
* \brief Aborts transaction
|
||||||
*
|
*
|
||||||
* Terminates transaction cancelling changes.
|
* 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
|
* \param[in] id - transaction ID you want to abort
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::Closed - thrown if the database is closed
|
* \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 {
|
void LMDBAL::Base::abortTransaction(LMDBAL::TransactionID id) const {
|
||||||
return abortTransaction(id, emptyName);}
|
return abortTransaction(id, emptyName);}
|
||||||
|
@ -275,11 +237,13 @@ void LMDBAL::Base::abortTransaction(LMDBAL::TransactionID id) const {
|
||||||
* \brief Commits transaction
|
* \brief Commits transaction
|
||||||
*
|
*
|
||||||
* Terminates transaction applying changes.
|
* 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
|
* \param[in] id - transaction ID you want to commit
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::Closed - thrown if the database is closed
|
* \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) {
|
void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id) {
|
||||||
return commitTransaction(id, emptyName);}
|
return commitTransaction(id, emptyName);}
|
||||||
|
@ -287,7 +251,9 @@ void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id) {
|
||||||
/**
|
/**
|
||||||
* \brief Begins read-only transaction
|
* \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
|
* \param[in] storageName - name of the storage/cache that you begin transaction from, needed just to inform if something went wrong
|
||||||
*
|
*
|
||||||
|
@ -297,12 +263,11 @@ void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id) {
|
||||||
* \exception LMDBAL::Unknown - thrown if something unexpected happened
|
* \exception LMDBAL::Unknown - thrown if something unexpected happened
|
||||||
*/
|
*/
|
||||||
LMDBAL::TransactionID LMDBAL::Base::beginReadOnlyTransaction(const std::string& storageName) const {
|
LMDBAL::TransactionID LMDBAL::Base::beginReadOnlyTransaction(const std::string& storageName) const {
|
||||||
if (!opened)
|
if (!opened())
|
||||||
throw Closed("beginReadOnlyTransaction", name, storageName);
|
throw Closed("beginReadOnlyTransaction", name, storageName);
|
||||||
|
|
||||||
TransactionID txn = beginPrivateReadOnlyTransaction(storageName);
|
TransactionID txn = beginPrivateReadOnlyTransaction(storageName);
|
||||||
transactions->emplace(txn);
|
for (const std::pair<const std::string, LMDBAL::StorageCommon*>& pair : storages)
|
||||||
for (const std::pair<const std::string, LMDBAL::iStorage*>& pair : storages)
|
|
||||||
pair.second->transactionStarted(txn, true);
|
pair.second->transactionStarted(txn, true);
|
||||||
|
|
||||||
return txn;
|
return txn;
|
||||||
|
@ -311,7 +276,9 @@ LMDBAL::TransactionID LMDBAL::Base::beginReadOnlyTransaction(const std::string&
|
||||||
/**
|
/**
|
||||||
* \brief Begins writable transaction
|
* \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
|
* \param[in] storageName - name of the storage/cache that you begin transaction from, needed just to inform if something went wrong
|
||||||
*
|
*
|
||||||
|
@ -321,12 +288,11 @@ LMDBAL::TransactionID LMDBAL::Base::beginReadOnlyTransaction(const std::string&
|
||||||
* \exception LMDBAL::Unknown - thrown if something unexpected happened
|
* \exception LMDBAL::Unknown - thrown if something unexpected happened
|
||||||
*/
|
*/
|
||||||
LMDBAL::TransactionID LMDBAL::Base::beginTransaction(const std::string& storageName) const {
|
LMDBAL::TransactionID LMDBAL::Base::beginTransaction(const std::string& storageName) const {
|
||||||
if (!opened)
|
if (!opened())
|
||||||
throw Closed("beginTransaction", name, storageName);
|
throw Closed("beginTransaction", name, storageName);
|
||||||
|
|
||||||
TransactionID txn = beginPrivateTransaction(storageName);
|
TransactionID txn = beginPrivateTransaction(storageName);
|
||||||
transactions->emplace(txn);
|
for (const std::pair<const std::string, LMDBAL::StorageCommon*>& pair : storages)
|
||||||
for (const std::pair<const std::string, LMDBAL::iStorage*>& pair : storages)
|
|
||||||
pair.second->transactionStarted(txn, false);
|
pair.second->transactionStarted(txn, false);
|
||||||
|
|
||||||
return txn;
|
return txn;
|
||||||
|
@ -336,55 +302,46 @@ LMDBAL::TransactionID LMDBAL::Base::beginTransaction(const std::string& storageN
|
||||||
* \brief Aborts transaction
|
* \brief Aborts transaction
|
||||||
*
|
*
|
||||||
* Terminates transaction cancelling changes.
|
* Terminates transaction cancelling changes.
|
||||||
* This is an optimal way to terminate read-only transactions.
|
* Every storage receives notification about this transaction being aborted.
|
||||||
* This function is intended to be called from subordinate storage or cache
|
* 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] 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
|
* \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::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 {
|
void LMDBAL::Base::abortTransaction(LMDBAL::TransactionID id, const std::string& storageName) const {
|
||||||
if (!opened)
|
if (!opened())
|
||||||
throw Closed("abortTransaction", name, storageName);
|
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);
|
abortPrivateTransaction(id, storageName);
|
||||||
for (const std::pair<const std::string, LMDBAL::iStorage*>& pair : storages)
|
for (const std::pair<const std::string, LMDBAL::StorageCommon*>& pair : storages)
|
||||||
pair.second->transactionAborted(id);
|
pair.second->transactionAborted(id);
|
||||||
|
|
||||||
transactions->erase(itr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Commits transaction
|
* \brief Commits transaction
|
||||||
*
|
*
|
||||||
* Terminates transaction applying changes.
|
* 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] 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
|
* \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::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) {
|
void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id, const std::string& storageName) {
|
||||||
if (!opened)
|
if (!opened())
|
||||||
throw Closed("abortTransaction", name, storageName);
|
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);
|
commitPrivateTransaction(id, storageName);
|
||||||
for (const std::pair<const std::string, LMDBAL::iStorage*>& pair : storages)
|
for (const std::pair<const std::string, LMDBAL::StorageCommon*>& pair : storages)
|
||||||
pair.second->transactionCommited(id);
|
pair.second->transactionCommited(id);
|
||||||
|
|
||||||
transactions->erase(itr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -412,7 +369,7 @@ LMDBAL::TransactionID LMDBAL::Base::beginPrivateReadOnlyTransaction(const std::s
|
||||||
* \brief Begins writable transaction
|
* \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 or cache,
|
||||||
* it's not accounted in transaction collection, other storages and caches are not notified about this kind of transaction
|
* it's not accounted in the transaction collection, other storages and caches are not notified about this kind of transaction
|
||||||
*
|
*
|
||||||
* \param[in] storageName - name of the storage/cache that you begin transaction from, needed just to inform if something went wrong
|
* \param[in] storageName - name of the storage/cache that you begin transaction from, needed just to inform if something went wrong
|
||||||
*
|
*
|
||||||
|
@ -433,10 +390,10 @@ LMDBAL::TransactionID LMDBAL::Base::beginPrivateTransaction(const std::string& s
|
||||||
/**
|
/**
|
||||||
* \brief Aborts transaction
|
* \brief Aborts transaction
|
||||||
*
|
*
|
||||||
* Terminates transaction cancelling changes.
|
* Terminates transaction, cancelling changes.
|
||||||
* This is an optimal way to terminate read-only transactions.
|
* This is an optimal way to terminate read-only transactions.
|
||||||
* This function is intended to be called from subordinate storage or cache,
|
* This function is intended to be called from subordinate storage or cache,
|
||||||
* it's not accounted in transaction collection, other storages and caches are not notified about this kind of transaction
|
* it's not accounted in the transaction collection, other storages and caches are not notified about this kind of transaction
|
||||||
*
|
*
|
||||||
* \param[in] id - transaction ID you want to abort
|
* \param[in] id - transaction ID you want to abort
|
||||||
* \param[in] storageName - name of the storage/cache that you begin transaction from, unused here
|
* \param[in] storageName - name of the storage/cache that you begin transaction from, unused here
|
||||||
|
@ -463,3 +420,104 @@ void LMDBAL::Base::commitPrivateTransaction(LMDBAL::TransactionID id, const std:
|
||||||
if (rc != MDB_SUCCESS)
|
if (rc != MDB_SUCCESS)
|
||||||
throw Unknown(name, mdb_strerror(rc), storageName);
|
throw Unknown(name, mdb_strerror(rc), storageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Registers session
|
||||||
|
*
|
||||||
|
* Registers the session in the session collection.
|
||||||
|
* If it was the first accounted session, it activates the database
|
||||||
|
*
|
||||||
|
* \exception LMDBAL::Unknown - thrown if this session was already registered
|
||||||
|
*/
|
||||||
|
void LMDBAL::Base::registerSession(Session* session) {
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
|
||||||
|
if (sessions.empty())
|
||||||
|
activate();
|
||||||
|
|
||||||
|
if (!sessions.insert(session).second)
|
||||||
|
throw Unknown(name, "session already registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Unregisters session
|
||||||
|
*
|
||||||
|
* Unregisters the session from the session collection.
|
||||||
|
* If it was the last accounted session, it deactivates the database
|
||||||
|
*
|
||||||
|
* \exception LMDBAL::Unknown - thrown if this session was not registered
|
||||||
|
*/
|
||||||
|
void LMDBAL::Base::unregisterSession(Session* session) {
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
|
||||||
|
if (sessions.size() == 1)
|
||||||
|
deactivate();
|
||||||
|
|
||||||
|
if (sessions.erase(session) != 1)
|
||||||
|
throw Unknown(name, "session was not registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Swaps sessions
|
||||||
|
*
|
||||||
|
* Replaces one session by another in the session collection.
|
||||||
|
*
|
||||||
|
* \exception LMDBAL::Unknown - thrown if there is some unexpected state with sessions, it means the database is in the wrong state
|
||||||
|
*/
|
||||||
|
void LMDBAL::Base::replaceSession(Session* closing, Session* opening) {
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
|
||||||
|
if (sessions.erase(closing) != 1)
|
||||||
|
throw Unknown(name, "session was not registered");
|
||||||
|
|
||||||
|
if (!sessions.insert(opening).second)
|
||||||
|
throw Unknown(name, "session already registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Deactivates the database,
|
||||||
|
*
|
||||||
|
* Closes all lmdb handles, aborts all public transactions.
|
||||||
|
* This function will emit SIGSEGV on a deactivated database
|
||||||
|
*
|
||||||
|
* \exception LMDBAL::Unknown - thrown if something went wrong aborting transactions
|
||||||
|
*/
|
||||||
|
void LMDBAL::Base::deactivate() {
|
||||||
|
for (const std::pair<TransactionID, Transaction*> pair : transactions) {
|
||||||
|
abortTransaction(pair.first, emptyName);
|
||||||
|
pair.second->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const std::pair<const std::string, StorageCommon*>& pair : storages)
|
||||||
|
pair.second->close();
|
||||||
|
|
||||||
|
mdb_env_close(environment);
|
||||||
|
transactions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Activates the database
|
||||||
|
*
|
||||||
|
* Almost every LMDBAL::Base require it to be opened, this function does it.
|
||||||
|
* It also creates the directory for the database if it was an initial launch.
|
||||||
|
* This function will behave unpredictably on an activated database, possible data corruption.
|
||||||
|
*
|
||||||
|
* \exception LMDBAL::Unknown - thrown if something went wrong opening storages and caches
|
||||||
|
*/
|
||||||
|
void LMDBAL::Base::activate() {
|
||||||
|
mdb_env_create(&environment);
|
||||||
|
QString path = createDirectory();
|
||||||
|
|
||||||
|
mdb_env_set_maxdbs(environment, static_cast<MDB_dbi>(storages.size()));
|
||||||
|
mdb_env_set_mapsize(environment, size * 1024UL * 1024UL);
|
||||||
|
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
||||||
|
|
||||||
|
TransactionID txn = beginPrivateTransaction(emptyName);
|
||||||
|
for (const std::pair<const std::string, StorageCommon*>& pair : storages) {
|
||||||
|
StorageCommon* storage = pair.second;
|
||||||
|
if (const int rc = storage->open(txn))
|
||||||
|
throw Unknown(name, mdb_strerror(rc));
|
||||||
|
}
|
||||||
|
|
||||||
|
commitPrivateTransaction(txn, emptyName);
|
||||||
|
}
|
51
src/base.h
51
src/base.h
|
@ -16,14 +16,15 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_BASE_H
|
#pragma once
|
||||||
#define LMDBAL_BASE_H
|
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
@ -35,9 +36,10 @@
|
||||||
|
|
||||||
namespace LMDBAL {
|
namespace LMDBAL {
|
||||||
|
|
||||||
class iStorage;
|
class StorageCommon;
|
||||||
class Transaction;
|
class Transaction;
|
||||||
class WriteTransaction;
|
class WriteTransaction;
|
||||||
|
class Session;
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
class Serializer;
|
class Serializer;
|
||||||
|
@ -49,20 +51,20 @@ template <class K, class V>
|
||||||
class Cache;
|
class Cache;
|
||||||
|
|
||||||
typedef MDB_txn* TransactionID; /**<\brief I'm going to use transaction pointers as transaction IDs*/
|
typedef MDB_txn* TransactionID; /**<\brief I'm going to use transaction pointers as transaction IDs*/
|
||||||
typedef uint32_t SizeType; /**<\brief All LMDBAL sizes are uint32_t*/
|
typedef uint32_t SizeType; /**<\brief All LMDBAL sizes are uint32*/
|
||||||
|
|
||||||
class Base {
|
class Base {
|
||||||
friend class iStorage;
|
friend class StorageCommon;
|
||||||
friend class Transaction;
|
friend class Transaction;
|
||||||
friend class WriteTransaction;
|
friend class WriteTransaction;
|
||||||
public:
|
friend class Session;
|
||||||
|
|
||||||
|
public:
|
||||||
Base(const QString& name, uint16_t mapSize = 10);
|
Base(const QString& name, uint16_t mapSize = 10);
|
||||||
~Base();
|
~Base();
|
||||||
|
|
||||||
void open();
|
Session open();
|
||||||
void close();
|
bool opened() const;
|
||||||
bool ready() const;
|
|
||||||
bool removeDirectory();
|
bool removeDirectory();
|
||||||
QString createDirectory();
|
QString createDirectory();
|
||||||
QString getName() const;
|
QString getName() const;
|
||||||
|
@ -85,8 +87,9 @@ public:
|
||||||
LMDBAL::Cache<K, V>* getCache(const std::string& storageName);
|
LMDBAL::Cache<K, V>* getCache(const std::string& storageName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::map<std::string, LMDBAL::iStorage*> Storages; /**<\brief Storage and Cache pointers are saved in the std::map*/
|
typedef std::map<std::string, LMDBAL::StorageCommon *> 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 Public transaction IDs are saved in the std::map*/
|
||||||
|
typedef std::set<Session*> Sessions; /**<\brief Sessions are saved in the std::set*/
|
||||||
|
|
||||||
void commitTransaction(TransactionID id);
|
void commitTransaction(TransactionID id);
|
||||||
void abortTransaction(TransactionID id) const;
|
void abortTransaction(TransactionID id) const;
|
||||||
|
@ -100,13 +103,21 @@ private:
|
||||||
void commitPrivateTransaction(TransactionID id, const std::string& storageName);
|
void commitPrivateTransaction(TransactionID id, const std::string& storageName);
|
||||||
void abortPrivateTransaction(TransactionID id, const std::string& storageName) const;
|
void abortPrivateTransaction(TransactionID id, const std::string& storageName) const;
|
||||||
|
|
||||||
|
void registerSession(Session* session);
|
||||||
|
void unregisterSession(Session* session);
|
||||||
|
void replaceSession(Session* closing, Session* opening);
|
||||||
|
|
||||||
|
void activate();
|
||||||
|
void deactivate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string name; /**<\brief Name of this database*/
|
std::string name; /**<\brief Name of this database*/
|
||||||
bool opened; /**<\brief State of this database*/
|
Sessions sessions; /**<\brief Opened session pointers*/
|
||||||
uint16_t size; /**<\brief lmdb map size in MiB*/
|
uint16_t size; /**<\brief lmdb map size in MiB*/
|
||||||
MDB_env* environment; /**<\brief lmdb environment handle*/
|
MDB_env* environment; /**<\brief lmdb environment handle*/
|
||||||
Storages storages; /**<\brief Registered storages and caches*/
|
Storages storages; /**<\brief Registered storages and caches*/
|
||||||
Transactions* transactions; /**<\brief Active public transactions*/
|
mutable Transactions transactions; /**<\brief Active public transactions*/
|
||||||
|
std::mutex mutex; /**<\brief Mutex for thread safety*/
|
||||||
|
|
||||||
inline static const std::string emptyName = ""; /**<\brief Empty string for general fallback purposes*/
|
inline static const std::string emptyName = ""; /**<\brief Empty string for general fallback purposes*/
|
||||||
};
|
};
|
||||||
|
@ -133,11 +144,11 @@ private:
|
||||||
*/
|
*/
|
||||||
template <class K, class V>
|
template <class K, class V>
|
||||||
LMDBAL::Storage<K, V>* LMDBAL::Base::addStorage(const std::string& storageName, bool duplicates) {
|
LMDBAL::Storage<K, V>* LMDBAL::Base::addStorage(const std::string& storageName, bool duplicates) {
|
||||||
if (opened)
|
if (opened())
|
||||||
throw Opened(name, "add storage " + storageName);
|
throw Opened(name, "add storage " + storageName);
|
||||||
|
|
||||||
Storage<K, V>* storage = new Storage<K, V>(this, storageName, duplicates);
|
auto storage = new Storage<K, V>(this, storageName, duplicates);
|
||||||
std::pair<Storages::const_iterator, bool> pair = storages.insert(std::make_pair(storageName, (iStorage*)storage));
|
std::pair<Storages::const_iterator, bool> pair = storages.emplace(storageName, static_cast<StorageCommon *>(storage));
|
||||||
if (!pair.second)
|
if (!pair.second)
|
||||||
throw StorageDuplicate(name, storageName);
|
throw StorageDuplicate(name, storageName);
|
||||||
|
|
||||||
|
@ -161,11 +172,11 @@ LMDBAL::Storage<K, V>* LMDBAL::Base::addStorage(const std::string& storageName,
|
||||||
*/
|
*/
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
LMDBAL::Cache<K, V> * LMDBAL::Base::addCache(const std::string& storageName) {
|
LMDBAL::Cache<K, V> * LMDBAL::Base::addCache(const std::string& storageName) {
|
||||||
if (opened)
|
if (opened())
|
||||||
throw Opened(name, "add cache " + storageName);
|
throw Opened(name, "add cache " + storageName);
|
||||||
|
|
||||||
Cache<K, V>* cache = new Cache<K, V>(this, storageName, false);
|
auto cache = new Cache<K, V>(this, storageName, false);
|
||||||
std::pair<Storages::const_iterator, bool> pair = storages.insert(std::make_pair(storageName, (iStorage*)cache));
|
std::pair<Storages::const_iterator, bool> pair = storages.emplace(storageName, static_cast<StorageCommon *>(cache));
|
||||||
if (!pair.second)
|
if (!pair.second)
|
||||||
throw StorageDuplicate(name, storageName);
|
throw StorageDuplicate(name, storageName);
|
||||||
|
|
||||||
|
@ -215,5 +226,3 @@ template <class K, class V>
|
||||||
LMDBAL::Cache<K, V>* LMDBAL::Base::getCache(const std::string& storageName) {
|
LMDBAL::Cache<K, V>* LMDBAL::Base::getCache(const std::string& storageName) {
|
||||||
return static_cast<Cache<K, V>*>(storages.at(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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_CACHE_H
|
#pragma once
|
||||||
#define LMDBAL_CACHE_H
|
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
@ -134,5 +133,3 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "cache.hpp"
|
#include "cache.hpp"
|
||||||
|
|
||||||
#endif // LMDBAL_CACHE_H
|
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_CACHE_HPP
|
#pragma once
|
||||||
#define LMDBAL_CACHE_HPP
|
|
||||||
|
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "exceptions.h"
|
#include "exceptions.h"
|
||||||
|
@ -64,10 +63,10 @@ LMDBAL::Cache<K, V>::~Cache() {
|
||||||
|
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
void LMDBAL::Cache<K, V>::addRecord(const K& key, const V& value) {
|
void LMDBAL::Cache<K, V>::addRecord(const K& key, const V& value) {
|
||||||
iStorage::ensureOpened(iStorage::addRecordMethodName);
|
StorageCommon::ensureOpened(StorageCommon::addRecordMethodName);
|
||||||
|
|
||||||
if (cache->count(key) > 0)
|
if (cache->count(key) > 0)
|
||||||
iStorage::throwDuplicate(iStorage::toString(key));
|
StorageCommon::throwDuplicate(StorageCommon::toString(key));
|
||||||
|
|
||||||
Storage<K, V>::addRecord(key, value);
|
Storage<K, V>::addRecord(key, value);
|
||||||
handleAddRecord(key, value);
|
handleAddRecord(key, value);
|
||||||
|
@ -76,7 +75,7 @@ void LMDBAL::Cache<K, V>::addRecord(const K& key, const V& value) {
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
void LMDBAL::Cache<K, V>::addRecord(const K& key, const V& value, TransactionID txn) {
|
void LMDBAL::Cache<K, V>::addRecord(const K& key, const V& value, TransactionID txn) {
|
||||||
if (cache->count(key) > 0)
|
if (cache->count(key) > 0)
|
||||||
iStorage::throwDuplicate(iStorage::toString(key));
|
StorageCommon::throwDuplicate(StorageCommon::toString(key));
|
||||||
|
|
||||||
Storage<K, V>::addRecord(key, value, txn);
|
Storage<K, V>::addRecord(key, value, txn);
|
||||||
|
|
||||||
|
@ -97,7 +96,7 @@ void LMDBAL::Cache<K, V>::handleAddRecord(const K& key, const V& value) {
|
||||||
|
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
bool LMDBAL::Cache<K, V>::forceRecord(const K& key, const V& value) {
|
bool LMDBAL::Cache<K, V>::forceRecord(const K& key, const V& value) {
|
||||||
iStorage::ensureOpened(iStorage::forceRecordMethodName);
|
StorageCommon::ensureOpened(StorageCommon::forceRecordMethodName);
|
||||||
|
|
||||||
bool added = Storage<K, V>::forceRecord(key, value);
|
bool added = Storage<K, V>::forceRecord(key, value);
|
||||||
handleForceRecord(key, value, added);
|
handleForceRecord(key, value, added);
|
||||||
|
@ -137,18 +136,18 @@ void LMDBAL::Cache<K, V>::handleForceRecord(const K& key, const V& value, bool a
|
||||||
|
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
void LMDBAL::Cache<K, V>::changeRecord(const K& key, const V& value) {
|
void LMDBAL::Cache<K, V>::changeRecord(const K& key, const V& value) {
|
||||||
iStorage::ensureOpened(iStorage::changeRecordMethodName);
|
StorageCommon::ensureOpened(StorageCommon::changeRecordMethodName);
|
||||||
|
|
||||||
if (mode == Mode::full) {
|
if (mode == Mode::full) {
|
||||||
typename std::map<K, V>::iterator itr = cache->find(key);
|
typename std::map<K, V>::iterator itr = cache->find(key);
|
||||||
if (itr == cache->end())
|
if (itr == cache->end())
|
||||||
iStorage::throwNotFound(iStorage::toString(key));
|
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||||
|
|
||||||
Storage<K, V>::changeRecord(key, value);
|
Storage<K, V>::changeRecord(key, value);
|
||||||
itr->second = value;
|
itr->second = value;
|
||||||
} else {
|
} else {
|
||||||
if (abscent->count(key) > 0)
|
if (abscent->count(key) > 0)
|
||||||
iStorage::throwNotFound(iStorage::toString(key));
|
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Storage<K, V>::changeRecord(key, value);
|
Storage<K, V>::changeRecord(key, value);
|
||||||
|
@ -170,12 +169,12 @@ void LMDBAL::Cache<K, V>::changeRecord(const K& key, const V& value, Transaction
|
||||||
if (mode == Mode::full) {
|
if (mode == Mode::full) {
|
||||||
typename std::map<K, V>::iterator itr = cache->find(key);
|
typename std::map<K, V>::iterator itr = cache->find(key);
|
||||||
if (itr == cache->end())
|
if (itr == cache->end())
|
||||||
iStorage::throwNotFound(iStorage::toString(key));
|
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||||
|
|
||||||
Storage<K, V>::changeRecord(key, value, txn);
|
Storage<K, V>::changeRecord(key, value, txn);
|
||||||
} else {
|
} else {
|
||||||
if (abscent->count(key) > 0)
|
if (abscent->count(key) > 0)
|
||||||
iStorage::throwNotFound(iStorage::toString(key));
|
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Storage<K, V>::changeRecord(key, value, txn);
|
Storage<K, V>::changeRecord(key, value, txn);
|
||||||
|
@ -208,7 +207,7 @@ void LMDBAL::Cache<K, V>::handleChangeRecord(const K& key, const V& value) {
|
||||||
|
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
V LMDBAL::Cache<K, V>::getRecord(const K& key) const {
|
V LMDBAL::Cache<K, V>::getRecord(const K& key) const {
|
||||||
iStorage::ensureOpened(iStorage::getRecordMethodName);
|
StorageCommon::ensureOpened(StorageCommon::getRecordMethodName);
|
||||||
|
|
||||||
V value;
|
V value;
|
||||||
Cache<K, V>::getRecord(key, value);
|
Cache<K, V>::getRecord(key, value);
|
||||||
|
@ -217,7 +216,7 @@ V LMDBAL::Cache<K, V>::getRecord(const K& key) const {
|
||||||
|
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out) const {
|
void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out) const {
|
||||||
iStorage::ensureOpened(iStorage::getRecordMethodName);
|
StorageCommon::ensureOpened(StorageCommon::getRecordMethodName);
|
||||||
|
|
||||||
typename std::map<K, V>::const_iterator itr = cache->find(key);
|
typename std::map<K, V>::const_iterator itr = cache->find(key);
|
||||||
if (itr != cache->end()) {
|
if (itr != cache->end()) {
|
||||||
|
@ -226,7 +225,7 @@ void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode == Mode::full || abscent->count(key) != 0)
|
if (mode == Mode::full || abscent->count(key) != 0)
|
||||||
iStorage::throwNotFound(iStorage::toString(key));
|
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Storage<K, V>::getRecord(key, out);
|
Storage<K, V>::getRecord(key, out);
|
||||||
|
@ -270,7 +269,7 @@ void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out, TransactionID txn) con
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Operation::remove:
|
case Operation::remove:
|
||||||
iStorage::throwNotFound(iStorage::toString(key));
|
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||||
break;
|
break;
|
||||||
case Operation::change:
|
case Operation::change:
|
||||||
if (static_cast<std::pair<K, V>*>(entry.second)->first == key) {
|
if (static_cast<std::pair<K, V>*>(entry.second)->first == key) {
|
||||||
|
@ -286,7 +285,7 @@ void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out, TransactionID txn) con
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Operation::drop:
|
case Operation::drop:
|
||||||
iStorage::throwNotFound(iStorage::toString(key));
|
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||||
break;
|
break;
|
||||||
case Operation::replace: {
|
case Operation::replace: {
|
||||||
std::map<K, V>* newMap = static_cast<std::map<K, V>*>(entry.second);
|
std::map<K, V>* newMap = static_cast<std::map<K, V>*>(entry.second);
|
||||||
|
@ -295,7 +294,7 @@ void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out, TransactionID txn) con
|
||||||
out = vitr->second;
|
out = vitr->second;
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
iStorage::throwNotFound(iStorage::toString(key));
|
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -323,7 +322,7 @@ void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out, TransactionID txn) con
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode == Mode::full || abscent->count(key) != 0)
|
if (mode == Mode::full || abscent->count(key) != 0)
|
||||||
iStorage::throwNotFound(iStorage::toString(key));
|
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Storage<K, V>::getRecord(key, out, txn);
|
Storage<K, V>::getRecord(key, out, txn);
|
||||||
|
@ -353,7 +352,7 @@ void LMDBAL::Cache<K, V>::discoveredRecord(const K& key, const V& value, Transac
|
||||||
|
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
bool LMDBAL::Cache<K, V>::checkRecord(const K& key) const {
|
bool LMDBAL::Cache<K, V>::checkRecord(const K& key) const {
|
||||||
iStorage::ensureOpened(iStorage::checkRecordMethodName);
|
StorageCommon::ensureOpened(StorageCommon::checkRecordMethodName);
|
||||||
|
|
||||||
typename std::map<K, V>::const_iterator itr = cache->find(key);
|
typename std::map<K, V>::const_iterator itr = cache->find(key);
|
||||||
if (itr != cache->end())
|
if (itr != cache->end())
|
||||||
|
@ -458,7 +457,7 @@ void LMDBAL::Cache<K, V>::appendToCache(const K& key, const V& value) const {
|
||||||
|
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
std::map<K, V> LMDBAL::Cache<K, V>::readAll() const {
|
std::map<K, V> LMDBAL::Cache<K, V>::readAll() const {
|
||||||
iStorage::ensureOpened(iStorage::readAllMethodName);
|
StorageCommon::ensureOpened(StorageCommon::readAllMethodName);
|
||||||
|
|
||||||
if (mode != Mode::full) { //there is a room for optimization
|
if (mode != Mode::full) { //there is a room for optimization
|
||||||
mode = Mode::full; //I can read and deserialize only those values
|
mode = Mode::full; //I can read and deserialize only those values
|
||||||
|
@ -472,7 +471,7 @@ std::map<K, V> LMDBAL::Cache<K, V>::readAll() const {
|
||||||
|
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
void LMDBAL::Cache<K, V>::readAll(std::map<K, V>& out) const {
|
void LMDBAL::Cache<K, V>::readAll(std::map<K, V>& out) const {
|
||||||
iStorage::ensureOpened(iStorage::readAllMethodName);
|
StorageCommon::ensureOpened(StorageCommon::readAllMethodName);
|
||||||
|
|
||||||
if (mode != Mode::full) { //there is a room for optimization
|
if (mode != Mode::full) { //there is a room for optimization
|
||||||
mode = Mode::full; //I can read and deserialize only those values
|
mode = Mode::full; //I can read and deserialize only those values
|
||||||
|
@ -643,7 +642,7 @@ void LMDBAL::Cache<K, V>::handleAddRecords(const std::map<K, V>& data, bool over
|
||||||
|
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
void LMDBAL::Cache<K, V>::removeRecord(const K& key) {
|
void LMDBAL::Cache<K, V>::removeRecord(const K& key) {
|
||||||
iStorage::ensureOpened(iStorage::removeRecordMethodName);
|
StorageCommon::ensureOpened(StorageCommon::removeRecordMethodName);
|
||||||
|
|
||||||
bool noKey = false;
|
bool noKey = false;
|
||||||
if (mode != Mode::full)
|
if (mode != Mode::full)
|
||||||
|
@ -652,7 +651,7 @@ void LMDBAL::Cache<K, V>::removeRecord(const K& key) {
|
||||||
noKey = abscent->count(key) > 0;
|
noKey = abscent->count(key) > 0;
|
||||||
|
|
||||||
if (noKey)
|
if (noKey)
|
||||||
iStorage::throwNotFound(iStorage::toString(key));
|
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||||
|
|
||||||
Storage<K, V>::removeRecord(key);
|
Storage<K, V>::removeRecord(key);
|
||||||
handleRemoveRecord(key);
|
handleRemoveRecord(key);
|
||||||
|
@ -667,7 +666,7 @@ void LMDBAL::Cache<K, V>::removeRecord(const K& key, TransactionID txn) {
|
||||||
noKey = abscent->count(key) > 0;
|
noKey = abscent->count(key) > 0;
|
||||||
|
|
||||||
if (noKey)
|
if (noKey)
|
||||||
iStorage::throwNotFound(iStorage::toString(key));
|
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||||
|
|
||||||
Storage<K, V>::removeRecord(key, txn);
|
Storage<K, V>::removeRecord(key, txn);
|
||||||
|
|
||||||
|
@ -782,8 +781,8 @@ void LMDBAL::Cache<K, V>::handleMode() const {
|
||||||
|
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
int LMDBAL::Cache<K, V>::drop(const WriteTransaction& transaction) {
|
int LMDBAL::Cache<K, V>::drop(const WriteTransaction& transaction) {
|
||||||
iStorage::ensureOpened(iStorage::dropMethodName);
|
StorageCommon::ensureOpened(StorageCommon::dropMethodName);
|
||||||
TransactionID txn = iStorage::extractTransactionId(transaction, iStorage::dropMethodName);
|
TransactionID txn = StorageCommon::extractTransactionId(transaction, StorageCommon::dropMethodName);
|
||||||
int res = Storage<K, V>::drop(txn);
|
int res = Storage<K, V>::drop(txn);
|
||||||
|
|
||||||
if (res != MDB_SUCCESS)
|
if (res != MDB_SUCCESS)
|
||||||
|
@ -898,5 +897,3 @@ void LMDBAL::Cache<K, V>::destroyTransactionEntry(const Entry& entry) const {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //LMDBAL_CACHE_HPP
|
|
||||||
|
|
49
src/cursor.h
49
src/cursor.h
|
@ -16,8 +16,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_CURSOR_H
|
#pragma once
|
||||||
#define LMDBAL_CURSOR_H
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -25,19 +24,13 @@
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "transaction.h"
|
#include "transaction.h"
|
||||||
|
#include "cursorcommon.h"
|
||||||
|
|
||||||
namespace LMDBAL {
|
namespace LMDBAL {
|
||||||
|
|
||||||
template <class K, class V>
|
template <class K, class V>
|
||||||
class Cursor {
|
class Cursor : public CursorCommon {
|
||||||
friend class Storage<K, V>;
|
friend class Storage<K, V>;
|
||||||
private:
|
|
||||||
enum State { /**<Cursor state:*/
|
|
||||||
closed, /**< - closed*/
|
|
||||||
openedPublic, /**< - opened with public transaction, all storages will be notified about it after it's done*/
|
|
||||||
openedPrivate /**< - opened with private transaction, only current storage will be notified when cursor is closed*/
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Cursor();
|
Cursor();
|
||||||
Cursor(Storage<K, V>* parent);
|
Cursor(Storage<K, V>* parent);
|
||||||
|
@ -48,14 +41,6 @@ public:
|
||||||
Cursor& operator = (const Cursor& other) = delete;
|
Cursor& operator = (const Cursor& other) = delete;
|
||||||
Cursor& operator = (Cursor&& other);
|
Cursor& operator = (Cursor&& other);
|
||||||
|
|
||||||
void open();
|
|
||||||
void open(const Transaction& transaction);
|
|
||||||
void renew();
|
|
||||||
void renew(const Transaction& transaction);
|
|
||||||
void close();
|
|
||||||
bool opened() const;
|
|
||||||
bool empty() const;
|
|
||||||
|
|
||||||
void drop();
|
void drop();
|
||||||
|
|
||||||
std::pair<K, V> first();
|
std::pair<K, V> first();
|
||||||
|
@ -72,38 +57,10 @@ public:
|
||||||
void current(K& key, V& value) const;
|
void current(K& key, V& value) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
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;
|
void operateCursorRead(K& key, V& value, MDB_cursor_op operation, const std::string& methodName, const std::string& operationName) const;
|
||||||
|
|
||||||
private:
|
|
||||||
Storage<K, V>* storage;
|
|
||||||
MDB_cursor* cursor;
|
|
||||||
State state;
|
|
||||||
uint32_t id;
|
|
||||||
|
|
||||||
inline static const std::string openCursorMethodName = "Cursor::open"; /**<\brief member function name, just for exceptions*/
|
|
||||||
inline static const std::string closeCursorMethodName = "Cursor::close"; /**<\brief member function name, just for exceptions*/
|
|
||||||
inline static const std::string renewCursorMethodName = "Cursor::renew"; /**<\brief member function name, just for exceptions*/
|
|
||||||
|
|
||||||
inline static const std::string firstMethodName = "first"; /**<\brief member function name, just for exceptions*/
|
|
||||||
inline static const std::string lastMethodName = "last"; /**<\brief member function name, just for exceptions*/
|
|
||||||
inline static const std::string nextMethodName = "next"; /**<\brief member function name, just for exceptions*/
|
|
||||||
inline static const std::string prevMethodName = "prev"; /**<\brief member function name, just for exceptions*/
|
|
||||||
inline static const std::string currentMethodName = "current"; /**<\brief member function name, just for exceptions*/
|
|
||||||
inline static const std::string setMethodName = "set"; /**<\brief member function name, just for exceptions*/
|
|
||||||
|
|
||||||
inline static const std::string firstOperationName = "Cursor::first"; /**<\brief member function name, just for exceptions*/
|
|
||||||
inline static const std::string lastOperationName = "Cursor::last"; /**<\brief member function name, just for exceptions*/
|
|
||||||
inline static const std::string nextOperationName = "Cursor::next"; /**<\brief member function name, just for exceptions*/
|
|
||||||
inline static const std::string prevOperationName = "Cursor::prev"; /**<\brief member function name, just for exceptions*/
|
|
||||||
inline static const std::string currentOperationName = "Cursor::current"; /**<\brief member function name, just for exceptions*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#include "cursor.hpp"
|
#include "cursor.hpp"
|
||||||
|
|
||||||
#endif //LMDBAL_CURSOR_H
|
|
||||||
|
|
332
src/cursor.hpp
332
src/cursor.hpp
|
@ -16,10 +16,10 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_CURSOR_HPP
|
#pragma once
|
||||||
#define LMDBAL_CURSOR_HPP
|
|
||||||
|
|
||||||
#include "cursor.h"
|
#include "cursor.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,8 +41,6 @@
|
||||||
* You are not supposed to instantiate or destory instances of this class yourself!
|
* You are not supposed to instantiate or destory instances of this class yourself!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static uint32_t idCounter = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Creates a cursor
|
* \brief Creates a cursor
|
||||||
*
|
*
|
||||||
|
@ -50,12 +48,9 @@ static uint32_t idCounter = 0;
|
||||||
*/
|
*/
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
LMDBAL::Cursor<K, V>::Cursor(Storage<K, V>* parent):
|
LMDBAL::Cursor<K, V>::Cursor(Storage<K, V>* parent):
|
||||||
storage(parent),
|
CursorCommon(parent)
|
||||||
cursor(nullptr),
|
|
||||||
state(closed),
|
|
||||||
id(++idCounter)
|
|
||||||
{
|
{
|
||||||
storage->cursors[id] = this;
|
parent->cursors[id] = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,10 +60,7 @@ LMDBAL::Cursor<K, V>::Cursor(Storage<K, V>* parent):
|
||||||
*/
|
*/
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
LMDBAL::Cursor<K, V>::Cursor():
|
LMDBAL::Cursor<K, V>::Cursor():
|
||||||
storage(nullptr),
|
CursorCommon()
|
||||||
cursor(nullptr),
|
|
||||||
state(closed),
|
|
||||||
id(0)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,57 +68,39 @@ LMDBAL::Cursor<K, V>::Cursor():
|
||||||
*/
|
*/
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
LMDBAL::Cursor<K, V>::Cursor(Cursor&& other):
|
LMDBAL::Cursor<K, V>::Cursor(Cursor&& other):
|
||||||
storage(other.storage),
|
CursorCommon(std::move(other))
|
||||||
cursor(other.cursor),
|
|
||||||
state(other.state),
|
|
||||||
id(other.id)
|
|
||||||
{
|
{
|
||||||
other.terminated();
|
if (!empty())
|
||||||
if (id != 0)
|
static_cast<Storage<K, V>*>(storage)->cursors[id] = this;
|
||||||
storage->cursors[id] = this;
|
|
||||||
|
|
||||||
other.freed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief A private function that turns cursor into an empty one
|
* \brief Move assignment operator
|
||||||
*
|
*
|
||||||
* This function is called from LMDBAL::Storage, when it gets destroyed, but still has some valid.
|
* Transfers other cursor into this one
|
||||||
* Those cursors will become empty, and can't be used anymore
|
|
||||||
*/
|
*/
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
LMDBAL::Cursor<K, V>& LMDBAL::Cursor<K, V>::operator = (Cursor&& other) {
|
LMDBAL::Cursor<K, V>& LMDBAL::Cursor<K, V>::operator = (Cursor&& other) {
|
||||||
terminated();
|
if (!empty() && other.empty())
|
||||||
|
static_cast<Storage<K, V>*>(storage)->cursors.erase(id);
|
||||||
|
|
||||||
if (id != 0)
|
CursorCommon::operator=(std::move(other));
|
||||||
storage->cursors.erase(id);
|
|
||||||
|
|
||||||
storage = other.storage;
|
if (!empty())
|
||||||
cursor = other.cursor;
|
static_cast<Storage<K, V>*>(storage)->cursors[id] = this;
|
||||||
state = other.state;
|
|
||||||
id = other.id;
|
|
||||||
|
|
||||||
if (id != 0) {
|
|
||||||
other.freed();
|
|
||||||
other.state = closed;
|
|
||||||
|
|
||||||
storage->cursors[id] = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Destroys a cursor
|
* \brief Destroys this cursor
|
||||||
*
|
*
|
||||||
* If the cursor wasn't properly closed - it's going to be upon destruction
|
* If the cursor wasn't properly closed - it's going to be upon destruction
|
||||||
*/
|
*/
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
LMDBAL::Cursor<K, V>::~Cursor () {
|
LMDBAL::Cursor<K, V>::~Cursor () {
|
||||||
close();
|
|
||||||
|
|
||||||
if (id != 0)
|
if (id != 0)
|
||||||
storage->cursors.erase(id);
|
static_cast<Storage<K, V>*>(storage)->cursors.erase(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -140,251 +114,9 @@ void LMDBAL::Cursor<K, V>::drop () {
|
||||||
close();
|
close();
|
||||||
|
|
||||||
if (id != 0)
|
if (id != 0)
|
||||||
storage->cursors.erase(id);
|
static_cast<Storage<K, V>*>(storage)->cursors.erase(id);
|
||||||
|
|
||||||
freed();
|
reset();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A private method that turns cursor into an empty one
|
|
||||||
*
|
|
||||||
* This function is called from LMDBAL::Storage, when it gets destroyed, but still has some valid cursors.
|
|
||||||
* Those cursors will become empty, and can't be used anymore
|
|
||||||
*/
|
|
||||||
template<class K, class V>
|
|
||||||
void LMDBAL::Cursor<K, V>::dropped () {
|
|
||||||
terminated();
|
|
||||||
freed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A private method that turns cursor into an empty one (submethod)
|
|
||||||
*
|
|
||||||
* This function is called from LMDBAL::Storage, when the cursor is getting destoryed.
|
|
||||||
* Those cursors will become empty, and can't be used anymore
|
|
||||||
*/
|
|
||||||
template<class K, class V>
|
|
||||||
void LMDBAL::Cursor<K, V>::freed () {
|
|
||||||
cursor = nullptr;
|
|
||||||
storage = nullptr;
|
|
||||||
id = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Returns true if the cursor is empty
|
|
||||||
*
|
|
||||||
* Empty cursors can't be used, they can be only targets of move operations
|
|
||||||
*/
|
|
||||||
template<class K, class V>
|
|
||||||
bool LMDBAL::Cursor<K, V>::empty () const {
|
|
||||||
return id == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A private function the storage owning this cursor will call to inform this cursor that the thansaction needs to be aborted
|
|
||||||
*/
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Opens the cursor for operations.
|
|
||||||
*
|
|
||||||
* This is a normal way to start the sequence of operations with the cursor.
|
|
||||||
* This variant of the function creates a read only transaction just for this cursor
|
|
||||||
*
|
|
||||||
* This function should be called when the LMDBAL::Storage is already opened and before any query with this cursor!
|
|
||||||
* It will do nothing to a cursor that was already opened (no matter what way).
|
|
||||||
*
|
|
||||||
* \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
|
|
||||||
* \exception LMDBAL::CursorEmpty thrown if the cursor was empty
|
|
||||||
*/
|
|
||||||
template<class K, class V>
|
|
||||||
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);
|
|
||||||
state = openedPrivate;
|
|
||||||
} break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Opens the cursor for operations.
|
|
||||||
*
|
|
||||||
* This is a normal way to start the sequence of operations with the cursor.
|
|
||||||
* This variant of the function uses for queries a transaction you have obtained somewhere else.
|
|
||||||
*
|
|
||||||
* This function should be called when the LMDBAL::Storage is already opened and before any query with this cursor!
|
|
||||||
* It will do nothing to a cursor that was already opened (no matter what way).
|
|
||||||
*
|
|
||||||
* \param[in] transaction - a transaction, can be read only
|
|
||||||
*
|
|
||||||
* \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
|
|
||||||
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
|
|
||||||
* \exception LMDBAL::CursorEmpty thrown if the cursor was empty
|
|
||||||
*/
|
|
||||||
template<class K, class V>
|
|
||||||
void LMDBAL::Cursor<K, V>::open (const Transaction& transaction) {
|
|
||||||
if (empty())
|
|
||||||
throw CursorEmpty(openCursorMethodName);
|
|
||||||
|
|
||||||
storage->ensureOpened(openCursorMethodName);
|
|
||||||
TransactionID txn = storage->extractTransactionId(transaction, openCursorMethodName);
|
|
||||||
switch (state) {
|
|
||||||
case closed: {
|
|
||||||
int result = storage->_mdbCursorOpen(txn, &cursor);
|
|
||||||
if (result != MDB_SUCCESS)
|
|
||||||
storage->throwUnknown(result);
|
|
||||||
|
|
||||||
state = openedPublic;
|
|
||||||
} break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Renews a cursor
|
|
||||||
*
|
|
||||||
* This function aborts current transaction if the cursor was opened with it's own transaction
|
|
||||||
* (does not mess up if the transaction was public),
|
|
||||||
* creates new private transaction and rebinds this cursor to it.
|
|
||||||
*
|
|
||||||
* Theoretically you could call this method if your public transaction was aborted (or commited)
|
|
||||||
* but you wish to continue to keep working with your cursor.
|
|
||||||
* Or if you just want to rebind your cursor to a new private transaction.
|
|
||||||
*
|
|
||||||
* This function does nothing if the cursor is closed
|
|
||||||
*
|
|
||||||
* \exception LMDBAL::Closed thrown if you try to renew the cursor on a closed database
|
|
||||||
* \exception LMDBAL::Unknown thrown if there was a problem beginning new transaction or if there was a problem renewing the cursor by lmdb
|
|
||||||
* \exception LMDBAL::CursorEmpty thrown if the cursor was empty
|
|
||||||
*/
|
|
||||||
template<class K, class V>
|
|
||||||
void LMDBAL::Cursor<K, V>::renew () {
|
|
||||||
if (empty())
|
|
||||||
throw CursorEmpty(openCursorMethodName);
|
|
||||||
|
|
||||||
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);
|
|
||||||
state = openedPrivate;
|
|
||||||
} break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Renews a cursor
|
|
||||||
*
|
|
||||||
* This function aborts current transaction if the cursor was opened with it's own transaction
|
|
||||||
* (does not mess up if the transaction was public),
|
|
||||||
* and rebinds this cursor to a passed new transaction.
|
|
||||||
*
|
|
||||||
* Theoretically you could call this method if your previous public transaction was aborted (or commited)
|
|
||||||
* but you wish to continue to keep working with your cursor.
|
|
||||||
* Or if you just want to rebind your cursor to another public transaction.
|
|
||||||
*
|
|
||||||
* This function does nothing if the cursor is closed
|
|
||||||
*
|
|
||||||
* \param[in] transaction - a transaction you wish this cursor to be bound to
|
|
||||||
*
|
|
||||||
* \exception LMDBAL::Closed thrown if you try to renew the cursor on a closed database
|
|
||||||
* \exception LMDBAL::Unknown thrown if there was a problem renewing the cursor by lmdb
|
|
||||||
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
|
|
||||||
* \exception LMDBAL::CursorEmpty thrown if the cursor was empty
|
|
||||||
*/
|
|
||||||
template<class K, class V>
|
|
||||||
void LMDBAL::Cursor<K, V>::renew (const Transaction& transaction) {
|
|
||||||
if (empty())
|
|
||||||
throw CursorEmpty(openCursorMethodName);
|
|
||||||
|
|
||||||
storage->ensureOpened(renewCursorMethodName);
|
|
||||||
TransactionID txn = storage->extractTransactionId(transaction, renewCursorMethodName);
|
|
||||||
switch (state) {
|
|
||||||
case openedPrivate: {
|
|
||||||
TransactionID txn = storage->_mdbCursorTxn(cursor);
|
|
||||||
storage->abortTransaction(txn);
|
|
||||||
storage->transactionAborted(txn);
|
|
||||||
[[fallthrough]];
|
|
||||||
}
|
|
||||||
case openedPublic: {
|
|
||||||
int result = storage->_mdbCursorRenew(txn, cursor);
|
|
||||||
if (result != MDB_SUCCESS)
|
|
||||||
storage->throwUnknown(result);
|
|
||||||
|
|
||||||
state = openedPublic;
|
|
||||||
} break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Termiates a sequence of operations with the cursor
|
|
||||||
*
|
|
||||||
* This is a normal way to tell that you're done with the cursor and don't want to continue the sequence of queries.
|
|
||||||
* The state of the cursor is lost after calling this method, some inner resorce is freed.
|
|
||||||
*
|
|
||||||
* If the cursor was opened with the private transaction - the owner storage will be notified of the aborted transaction.
|
|
||||||
*
|
|
||||||
* This function does nothing on a closed cursor.
|
|
||||||
*/
|
|
||||||
template<class K, class V>
|
|
||||||
void LMDBAL::Cursor<K, V>::close () {
|
|
||||||
switch (state) {
|
|
||||||
case openedPublic: {
|
|
||||||
storage->_mdbCursorClose(cursor);
|
|
||||||
|
|
||||||
state = closed;
|
|
||||||
} break;
|
|
||||||
case openedPrivate: {
|
|
||||||
TransactionID txn = storage->_mdbCursorTxn(cursor);
|
|
||||||
storage->_mdbCursorClose(cursor);
|
|
||||||
storage->abortTransaction(txn);
|
|
||||||
storage->transactionAborted(txn);
|
|
||||||
|
|
||||||
state = closed;
|
|
||||||
} break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Tells if the cursor is open
|
|
||||||
*/
|
|
||||||
template<class K, class V>
|
|
||||||
bool LMDBAL::Cursor<K, V>::opened () const {
|
|
||||||
return state != closed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -482,7 +214,7 @@ void LMDBAL::Cursor<K, V>::prev (K& key, V& value) {
|
||||||
* \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
|
* \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
|
||||||
*/
|
*/
|
||||||
template<class K, class V>
|
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);
|
operateCursorRead(key, value, MDB_GET_CURRENT, currentMethodName, currentOperationName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,16 +337,16 @@ std::pair<K, V> LMDBAL::Cursor<K, V>::current () const {
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
bool LMDBAL::Cursor<K, V>::set (const K& key) {
|
bool LMDBAL::Cursor<K, V>::set (const K& key) {
|
||||||
if (state == closed)
|
if (state == closed)
|
||||||
storage->throwCursorNotReady(setMethodName);
|
static_cast<Storage<K, V>*>(storage)->throwCursorNotReady(setMethodName);
|
||||||
|
|
||||||
MDB_val mdbKey = storage->keySerializer.setData(key);
|
MDB_val mdbKey = static_cast<Storage<K, V>*>(storage)->keySerializer.setData(key);
|
||||||
int result = storage->_mdbCursorSet(cursor, mdbKey);
|
int result = static_cast<Storage<K, V>*>(storage)->_mdbCursorSet(handle, mdbKey);
|
||||||
if (result == MDB_SUCCESS)
|
if (result == MDB_SUCCESS)
|
||||||
return true;
|
return true;
|
||||||
else if (result == MDB_NOTFOUND)
|
else if (result == MDB_NOTFOUND)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
storage->throwUnknown(result);
|
static_cast<Storage<K, V>*>(storage)->throwUnknown(result);
|
||||||
return false; //unreachable, just to suppress the warning
|
return false; //unreachable, just to suppress the warning
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -642,20 +374,18 @@ void LMDBAL::Cursor<K, V>::operateCursorRead(
|
||||||
const std::string& operationName
|
const std::string& operationName
|
||||||
) const {
|
) const {
|
||||||
if (state == closed)
|
if (state == closed)
|
||||||
storage->throwCursorNotReady(methodName);
|
static_cast<Storage<K, V>*>(storage)->throwCursorNotReady(methodName);
|
||||||
|
|
||||||
MDB_val mdbKey, mdbValue;
|
MDB_val mdbKey, mdbValue;
|
||||||
int result = storage->_mdbCursorGet(cursor, mdbKey, mdbValue, operation);
|
int result = static_cast<Storage<K, V>*>(storage)->_mdbCursorGet(handle, mdbKey, mdbValue, operation);
|
||||||
if (result != MDB_SUCCESS)
|
if (result != MDB_SUCCESS)
|
||||||
storage->throwNotFoundOrUnknown(result, operationName);
|
static_cast<Storage<K, V>*>(storage)->throwNotFoundOrUnknown(result, operationName);
|
||||||
|
|
||||||
storage->keySerializer.deserialize(mdbKey, key);
|
static_cast<Storage<K, V>*>(storage)->keySerializer.deserialize(mdbKey, key);
|
||||||
storage->valueSerializer.deserialize(mdbValue, value);
|
static_cast<Storage<K, V>*>(storage)->valueSerializer.deserialize(mdbValue, value);
|
||||||
|
|
||||||
if (state == openedPrivate)
|
if (state == openedPrivate)
|
||||||
storage->discoveredRecord(key, value);
|
static_cast<Storage<K, V>*>(storage)->discoveredRecord(key, value);
|
||||||
else
|
else
|
||||||
storage->discoveredRecord(key, value, storage->_mdbCursorTxn(cursor));
|
static_cast<Storage<K, V>*>(storage)->discoveredRecord(key, value, static_cast<Storage<K, V>*>(storage)->_mdbCursorTxn(handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //LMDBAL_CURSOR_HPP
|
|
||||||
|
|
366
src/cursorcommon.cpp
Normal file
366
src/cursorcommon.cpp
Normal file
|
@ -0,0 +1,366 @@
|
||||||
|
/*
|
||||||
|
* 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 "cursorcommon.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class LMDBAL::CursorCommon
|
||||||
|
* \brief An object to manage cursor internals and state.
|
||||||
|
*
|
||||||
|
* Cursors are owned by the storage, they die with the storage.
|
||||||
|
* They also get closed if the storage is closed (if you close by the database for example)
|
||||||
|
*
|
||||||
|
* You can obtain an instance of this class calling LMDBAL::Storage::createCursor()
|
||||||
|
* and destory it calling LMDBAL::Storage::destoryCursor() at any time, LMDBAL::Base doesn't necessarily need to be opened.
|
||||||
|
*
|
||||||
|
* You are not supposed to instantiate or destory instances of this class yourself!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "storagecommon.h"
|
||||||
|
|
||||||
|
static uint32_t idCounter = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Creates a empty class
|
||||||
|
*/
|
||||||
|
LMDBAL::CursorCommon::CursorCommon ():
|
||||||
|
id(0),
|
||||||
|
state(closed),
|
||||||
|
handle(nullptr),
|
||||||
|
storage(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Creates a cursor
|
||||||
|
*
|
||||||
|
* \param[in] _storage a storage that created this cursor
|
||||||
|
*/
|
||||||
|
LMDBAL::CursorCommon::CursorCommon (StorageCommon* _storage):
|
||||||
|
id(++idCounter),
|
||||||
|
state(closed),
|
||||||
|
handle(nullptr),
|
||||||
|
storage(_storage)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Moves other cursor into this class
|
||||||
|
*
|
||||||
|
* \param[in] other other instance that is being moved
|
||||||
|
*/
|
||||||
|
LMDBAL::CursorCommon::CursorCommon (CursorCommon&& other):
|
||||||
|
id(other.id),
|
||||||
|
state(other.state),
|
||||||
|
handle(other.handle),
|
||||||
|
storage(other.storage)
|
||||||
|
{
|
||||||
|
other.dropped();
|
||||||
|
|
||||||
|
if (state == openedPublic)
|
||||||
|
attachToTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Destroys this cursor
|
||||||
|
*
|
||||||
|
* If the cursor wasn't properly closed - it's going to be upon destruction
|
||||||
|
*/
|
||||||
|
LMDBAL::CursorCommon::~CursorCommon () noexcept {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Move assignment operator
|
||||||
|
*
|
||||||
|
* Transfers other cursor into this one
|
||||||
|
*/
|
||||||
|
LMDBAL::CursorCommon& LMDBAL::CursorCommon::operator = (CursorCommon&& other) {
|
||||||
|
terminated();
|
||||||
|
|
||||||
|
id = other.id;
|
||||||
|
state = other.state;
|
||||||
|
handle = other.handle;
|
||||||
|
storage = other.storage;
|
||||||
|
|
||||||
|
other.reset();
|
||||||
|
|
||||||
|
if (state == openedPublic)
|
||||||
|
attachToTransaction();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A private method that turns cursor into an empty one
|
||||||
|
*
|
||||||
|
* This method is called from LMDBAL::Storage, when the cursor is getting destoryed.
|
||||||
|
* After this method cursors will become empty, and can't be used anymore
|
||||||
|
*/
|
||||||
|
void LMDBAL::CursorCommon::reset () {
|
||||||
|
id = 0;
|
||||||
|
state = closed;
|
||||||
|
handle = nullptr;
|
||||||
|
storage = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A private method that turns cursor into an empty one
|
||||||
|
*
|
||||||
|
* This function is called from LMDBAL::Storage, when it gets destroyed, but still has some valid cursors.
|
||||||
|
* Those cursors will become empty, and can't be used anymore
|
||||||
|
*/
|
||||||
|
void LMDBAL::CursorCommon::dropped () {
|
||||||
|
terminated();
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A private function called to inform the cursor he has been terminated
|
||||||
|
*
|
||||||
|
* Is expected to be called from transaction, database, storage or move constructor
|
||||||
|
*/
|
||||||
|
void LMDBAL::CursorCommon::terminated () {
|
||||||
|
switch (state) {
|
||||||
|
case openedPublic:
|
||||||
|
storage->_mdbCursorClose(handle);
|
||||||
|
state = closed;
|
||||||
|
break;
|
||||||
|
case openedPrivate:
|
||||||
|
storage->closeCursorTransaction(handle, true);
|
||||||
|
state = closed;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Termiates a sequence of operations with the cursor
|
||||||
|
*
|
||||||
|
* This is a normal way to tell that you're done with the cursor and don't want to continue the sequence of queries.
|
||||||
|
* The state of the cursor is lost after calling this method, some inner resorce is freed.
|
||||||
|
*
|
||||||
|
* If the cursor was opened with the private transaction - the owner storage will be notified of the aborted transaction.
|
||||||
|
*
|
||||||
|
* This function does nothing on a closed cursor.
|
||||||
|
*/
|
||||||
|
void LMDBAL::CursorCommon::close () {
|
||||||
|
switch (state) {
|
||||||
|
case openedPublic:
|
||||||
|
disconnectFromTransaction();
|
||||||
|
storage->_mdbCursorClose(handle);
|
||||||
|
|
||||||
|
state = closed;
|
||||||
|
break;
|
||||||
|
case openedPrivate:
|
||||||
|
storage->closeCursorTransaction(handle, true);
|
||||||
|
|
||||||
|
state = closed;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Opens the cursor for operations.
|
||||||
|
*
|
||||||
|
* This is a normal way to start the sequence of operations with the cursor.
|
||||||
|
* This variant of the function creates a read only transaction just for this cursor
|
||||||
|
*
|
||||||
|
* This function should be called when the LMDBAL::Storage is already opened and before any query with this cursor!
|
||||||
|
* It will do nothing to a cursor that was already opened (no matter what way).
|
||||||
|
*
|
||||||
|
* \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
|
||||||
|
* \exception LMDBAL::CursorEmpty thrown if the cursor was empty
|
||||||
|
*/
|
||||||
|
void LMDBAL::CursorCommon::open () {
|
||||||
|
if (empty())
|
||||||
|
throw CursorEmpty(openCursorMethodName);
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case closed:
|
||||||
|
storage->openCursorTransaction(&handle, false);
|
||||||
|
state = openedPrivate;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Opens the cursor for operations.
|
||||||
|
*
|
||||||
|
* This is a normal way to start the sequence of operations with the cursor.
|
||||||
|
* This variant of the function uses for queries a transaction you have obtained somewhere else.
|
||||||
|
*
|
||||||
|
* This function should be called when the LMDBAL::Storage is already opened and before any query with this cursor!
|
||||||
|
* It will do nothing to a cursor that was already opened (no matter what way).
|
||||||
|
*
|
||||||
|
* \param[in] transaction - a transaction, can be read only
|
||||||
|
*
|
||||||
|
* \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
|
||||||
|
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
|
||||||
|
* \exception LMDBAL::CursorEmpty thrown if the cursor was empty
|
||||||
|
*/
|
||||||
|
void LMDBAL::CursorCommon::open (const Transaction& transaction) {
|
||||||
|
if (empty())
|
||||||
|
throw CursorEmpty(openCursorMethodName);
|
||||||
|
|
||||||
|
storage->ensureOpened(openCursorMethodName);
|
||||||
|
TransactionID txn = storage->extractTransactionId(transaction, openCursorMethodName);
|
||||||
|
switch (state) {
|
||||||
|
case closed: {
|
||||||
|
int result = storage->_mdbCursorOpen(txn, &handle);
|
||||||
|
if (result != MDB_SUCCESS)
|
||||||
|
storage->throwUnknown(result);
|
||||||
|
|
||||||
|
transaction.cursors[id] = this;
|
||||||
|
state = openedPublic;
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Renews a cursor
|
||||||
|
*
|
||||||
|
* This function aborts current transaction if the cursor was opened with it's own transaction
|
||||||
|
* (does not mess up if the transaction was public),
|
||||||
|
* creates new private transaction and rebinds this cursor to it.
|
||||||
|
*
|
||||||
|
* Theoretically you could call this method if your public transaction was aborted (or commited)
|
||||||
|
* but you wish to continue to keep working with your cursor.
|
||||||
|
* Or if you just want to rebind your cursor to a new private transaction.
|
||||||
|
*
|
||||||
|
* This function does nothing if the cursor is closed
|
||||||
|
*
|
||||||
|
* \exception LMDBAL::Closed thrown if you try to renew the cursor on a closed database
|
||||||
|
* \exception LMDBAL::Unknown thrown if there was a problem beginning new transaction or if there was a problem renewing the cursor by lmdb
|
||||||
|
* \exception LMDBAL::CursorEmpty thrown if the cursor was empty
|
||||||
|
*/
|
||||||
|
void LMDBAL::CursorCommon::renew () {
|
||||||
|
if (empty())
|
||||||
|
throw CursorEmpty(openCursorMethodName);
|
||||||
|
|
||||||
|
storage->ensureOpened(renewCursorMethodName);
|
||||||
|
switch (state) {
|
||||||
|
case openedPrivate:
|
||||||
|
storage->closeCursorTransaction(handle, false);
|
||||||
|
storage->openCursorTransaction(&handle, true);
|
||||||
|
break;
|
||||||
|
case openedPublic:
|
||||||
|
disconnectFromTransaction();
|
||||||
|
storage->openCursorTransaction(&handle, true);
|
||||||
|
state = openedPrivate;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Renews a cursor
|
||||||
|
*
|
||||||
|
* This function aborts current transaction if the cursor was opened with it's own transaction
|
||||||
|
* (does not mess up if the transaction was public),
|
||||||
|
* and rebinds this cursor to a passed new transaction.
|
||||||
|
*
|
||||||
|
* Theoretically you could call this method if your previous public transaction was aborted (or commited)
|
||||||
|
* but you wish to continue to keep working with your cursor.
|
||||||
|
* Or if you just want to rebind your cursor to another public transaction.
|
||||||
|
*
|
||||||
|
* This function does nothing if the cursor is closed
|
||||||
|
*
|
||||||
|
* \param[in] transaction - a transaction you wish this cursor to be bound to
|
||||||
|
*
|
||||||
|
* \exception LMDBAL::Closed thrown if you try to renew the cursor on a closed database
|
||||||
|
* \exception LMDBAL::Unknown thrown if there was a problem renewing the cursor by lmdb
|
||||||
|
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
|
||||||
|
* \exception LMDBAL::CursorEmpty thrown if the cursor was empty
|
||||||
|
*/
|
||||||
|
void LMDBAL::CursorCommon::renew (const Transaction& transaction) {
|
||||||
|
if (empty())
|
||||||
|
throw CursorEmpty(openCursorMethodName);
|
||||||
|
|
||||||
|
storage->ensureOpened(renewCursorMethodName);
|
||||||
|
TransactionID txn = storage->extractTransactionId(transaction, renewCursorMethodName);
|
||||||
|
switch (state) {
|
||||||
|
case openedPrivate: {
|
||||||
|
storage->closeCursorTransaction(handle, false);
|
||||||
|
int result = storage->_mdbCursorRenew(txn, handle);
|
||||||
|
if (result != MDB_SUCCESS)
|
||||||
|
storage->throwUnknown(result);
|
||||||
|
|
||||||
|
transaction.cursors[id] = this;
|
||||||
|
state = openedPublic;
|
||||||
|
} break;
|
||||||
|
case openedPublic: {
|
||||||
|
disconnectFromTransaction();
|
||||||
|
int result = storage->_mdbCursorRenew(txn, handle);
|
||||||
|
if (result != MDB_SUCCESS)
|
||||||
|
storage->throwUnknown(result);
|
||||||
|
|
||||||
|
transaction.cursors[id] = this;
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Returns true if the cursor is empty
|
||||||
|
*
|
||||||
|
* Empty cursors can't be used, they can be only targets of move operations
|
||||||
|
*/
|
||||||
|
bool LMDBAL::CursorCommon::empty () const {
|
||||||
|
return id == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Tells if the cursor is open
|
||||||
|
*/
|
||||||
|
bool LMDBAL::CursorCommon::opened () const {
|
||||||
|
return state != closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Links cursor to the transaction it has been opened with
|
||||||
|
*
|
||||||
|
* 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::CursorCommon::attachToTransaction () {
|
||||||
|
Transaction* txn = storage->getTransactionForCursor(handle);
|
||||||
|
txn->cursors[id] = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Disconnects cursor from the transaction it has been opened with
|
||||||
|
*
|
||||||
|
* 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::CursorCommon::disconnectFromTransaction () {
|
||||||
|
Transaction* txn = storage->getTransactionForCursor(handle);
|
||||||
|
txn->cursors.erase(id);
|
||||||
|
}
|
94
src/cursorcommon.h
Normal file
94
src/cursorcommon.h
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <lmdb.h>
|
||||||
|
|
||||||
|
namespace LMDBAL {
|
||||||
|
|
||||||
|
class Transaction;
|
||||||
|
class StorageCommon;
|
||||||
|
|
||||||
|
class CursorCommon {
|
||||||
|
friend class Transaction;
|
||||||
|
protected:
|
||||||
|
enum State { /**<Cursor state:*/
|
||||||
|
closed, /**< - closed*/
|
||||||
|
openedPublic, /**< - opened with public transaction, all storages will be notified about it after it's done*/
|
||||||
|
openedPrivate /**< - opened with private transaction, only current storage will be notified when cursor is closed*/
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CursorCommon();
|
||||||
|
CursorCommon(StorageCommon* storage);
|
||||||
|
CursorCommon(const CursorCommon& other) = delete;
|
||||||
|
CursorCommon(CursorCommon&& other);
|
||||||
|
virtual ~CursorCommon() noexcept;
|
||||||
|
|
||||||
|
CursorCommon& operator = (const CursorCommon& other) = delete;
|
||||||
|
CursorCommon& operator = (CursorCommon&& other);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void open();
|
||||||
|
void open(const Transaction& transaction);
|
||||||
|
void renew();
|
||||||
|
void renew(const Transaction& transaction);
|
||||||
|
bool opened() const;
|
||||||
|
bool empty() const;
|
||||||
|
void close();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void terminated();
|
||||||
|
void dropped();
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void attachToTransaction();
|
||||||
|
void disconnectFromTransaction();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint32_t id;
|
||||||
|
State state;
|
||||||
|
MDB_cursor* handle;
|
||||||
|
StorageCommon* storage;
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline static const std::string openCursorMethodName = "Cursor::open"; /**<\brief member function name, just for exceptions*/
|
||||||
|
inline static const std::string closeCursorMethodName = "Cursor::close"; /**<\brief member function name, just for exceptions*/
|
||||||
|
inline static const std::string renewCursorMethodName = "Cursor::renew"; /**<\brief member function name, just for exceptions*/
|
||||||
|
|
||||||
|
protected:
|
||||||
|
inline static const std::string firstMethodName = "first"; /**<\brief member function name, just for exceptions in heir*/
|
||||||
|
inline static const std::string lastMethodName = "last"; /**<\brief member function name, just for exceptions in heir*/
|
||||||
|
inline static const std::string nextMethodName = "next"; /**<\brief member function name, just for exceptions in heir*/
|
||||||
|
inline static const std::string prevMethodName = "prev"; /**<\brief member function name, just for exceptions in heir*/
|
||||||
|
inline static const std::string currentMethodName = "current"; /**<\brief member function name, just for exceptions in heir*/
|
||||||
|
inline static const std::string setMethodName = "set"; /**<\brief member function name, just for exceptions in heir*/
|
||||||
|
|
||||||
|
inline static const std::string firstOperationName = "Cursor::first"; /**<\brief member function name, just for exceptions in heir*/
|
||||||
|
inline static const std::string lastOperationName = "Cursor::last"; /**<\brief member function name, just for exceptions in heir*/
|
||||||
|
inline static const std::string nextOperationName = "Cursor::next"; /**<\brief member function name, just for exceptions in heir*/
|
||||||
|
inline static const std::string prevOperationName = "Cursor::prev"; /**<\brief member function name, just for exceptions in heir*/
|
||||||
|
inline static const std::string currentOperationName = "Cursor::current"; /**<\brief member function name, just for exceptions in heir*/
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -16,8 +16,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_EXCEPTIONS_H
|
#pragma once
|
||||||
#define LMDBAL_EXCEPTIONS_H
|
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -41,7 +40,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* \brief Thrown if LMDBAL had issues creating or opening database directory
|
* \brief Thrown if LMDBAL had issues creating or opening database directory
|
||||||
*/
|
*/
|
||||||
class Directory: public Exception {
|
class Directory : public Exception {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* \brief Creates exception
|
* \brief Creates exception
|
||||||
|
@ -237,5 +236,3 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //LMDBAL_EXCEPTIONS_H
|
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_OPERATORS_HPP
|
#pragma once
|
||||||
#define LMDBAL_OPERATORS_HPP
|
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
@ -209,5 +208,3 @@ QDataStream& operator >> (QDataStream &in, std::list<K>& container) {
|
||||||
|
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //LMDBAL_OPERATORS_HPP
|
|
||||||
|
|
|
@ -16,4 +16,4 @@ set(HEADERS
|
||||||
serializer_qbytearray.hpp
|
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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_SERIALIZER_H
|
#pragma once
|
||||||
#define LMDBAL_SERIALIZER_H
|
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
@ -68,5 +68,3 @@ private:
|
||||||
#include "serializer_stdstring.hpp"
|
#include "serializer_stdstring.hpp"
|
||||||
#include "serializer_qstring.hpp"
|
#include "serializer_qstring.hpp"
|
||||||
#include "serializer_qbytearray.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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_SERIALIZER_HPP
|
#pragma once
|
||||||
#define LMDBAL_SERIALIZER_HPP
|
|
||||||
|
|
||||||
#include "serializer.h"
|
#include "serializer.h"
|
||||||
|
|
||||||
|
@ -115,7 +114,7 @@ T LMDBAL::Serializer<T>::deserialize(const MDB_val& value) {
|
||||||
template<class T>
|
template<class T>
|
||||||
void LMDBAL::Serializer<T>::deserialize(const MDB_val& value, T& result) {
|
void LMDBAL::Serializer<T>::deserialize(const MDB_val& value, T& result) {
|
||||||
clear();
|
clear();
|
||||||
bytes.setRawData((char*)value.mv_data, value.mv_size);
|
bytes.setRawData(static_cast<char *>(value.mv_data), value.mv_size);
|
||||||
stream >> result;
|
stream >> result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,9 +154,7 @@ MDB_val LMDBAL::Serializer<T>::getData() {
|
||||||
MDB_val val;
|
MDB_val val;
|
||||||
|
|
||||||
val.mv_size = buffer.pos();
|
val.mv_size = buffer.pos();
|
||||||
val.mv_data = (char*)bytes.data();
|
val.mv_data = bytes.data();
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //LMDBAL_SERIALIZER_HPP
|
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_SERIALIZER_DOUBLE_HPP
|
#pragma once
|
||||||
#define LMDBAL_SERIALIZER_DOUBLE_HPP
|
|
||||||
|
|
||||||
namespace LMDBAL {
|
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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_SERIALIZER_FLOAT_HPP
|
#pragma once
|
||||||
#define LMDBAL_SERIALIZER_FLOAT_HPP
|
|
||||||
|
|
||||||
namespace LMDBAL {
|
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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_SERIALIZER_INT16_HPP
|
#pragma once
|
||||||
#define LMDBAL_SERIALIZER_INT16_HPP
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_SERIALIZER_INT32_HPP
|
#pragma once
|
||||||
#define LMDBAL_SERIALIZER_INT32_HPP
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_SERIALIZER_INT64_HPP
|
#pragma once
|
||||||
#define LMDBAL_SERIALIZER_INT64_HPP
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_SERIALIZER_INT8_HPP
|
#pragma once
|
||||||
#define LMDBAL_SERIALIZER_INT8_HPP
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_SERIALIZER_QBYTEARRAY_HPP
|
#pragma once
|
||||||
#define LMDBAL_SERIALIZER_QBYTEARRAY_HPP
|
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
|
||||||
|
@ -35,7 +34,17 @@ public:
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
void deserialize(const MDB_val& data, QByteArray& result) {
|
void deserialize(const MDB_val& data, QByteArray& result) {
|
||||||
result.setRawData((char*)data.mv_data, data.mv_size);
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||||
|
if (data.mv_size > static_cast<size_t>(std::numeric_limits<qsizetype>::max()))
|
||||||
|
throw std::runtime_error("Data size exceeds QByteArray capacity");
|
||||||
|
|
||||||
|
result.setRawData(static_cast<char *>(data.mv_data), static_cast<qsizetype>(data.mv_size));
|
||||||
|
#else
|
||||||
|
if (data.mv_size > static_cast<size_t>(std::numeric_limits<uint>::max()))
|
||||||
|
throw std::runtime_error("Data size exceeds QByteArray capacity");
|
||||||
|
|
||||||
|
result.setRawData(static_cast<char *>(data.mv_data), static_cast<uint>(data.mv_size));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
MDB_val setData(const QByteArray& data) {
|
MDB_val setData(const QByteArray& data) {
|
||||||
value = data;
|
value = data;
|
||||||
|
@ -44,7 +53,7 @@ public:
|
||||||
MDB_val getData() {
|
MDB_val getData() {
|
||||||
MDB_val result;
|
MDB_val result;
|
||||||
result.mv_data = value.data();
|
result.mv_data = value.data();
|
||||||
result.mv_size = value.size();
|
result.mv_size = static_cast<size_t>(value.size());
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
void clear() {
|
void clear() {
|
||||||
|
@ -56,9 +65,3 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //LMDBAL_SERIALIZER_QBYTEARRAY_HPP
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_SERIALIZER_QSTRING_HPP
|
#pragma once
|
||||||
#define LMDBAL_SERIALIZER_QSTRING_HPP
|
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
@ -32,11 +31,33 @@ public:
|
||||||
~Serializer() {};
|
~Serializer() {};
|
||||||
|
|
||||||
QString deserialize(const MDB_val& data) {
|
QString deserialize(const MDB_val& data) {
|
||||||
value = QByteArray((char*)data.mv_data, data.mv_size);
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||||
|
if (data.mv_size > static_cast<size_t>(std::numeric_limits<qsizetype>::max()))
|
||||||
|
throw std::runtime_error("Data size exceeds QByteArray capacity");
|
||||||
|
|
||||||
|
value = QByteArray(static_cast<char *>(data.mv_data), static_cast<qsizetype>(data.mv_size));
|
||||||
|
#else
|
||||||
|
if (data.mv_size > static_cast<size_t>(std::numeric_limits<int>::max()))
|
||||||
|
throw std::runtime_error("Data size exceeds QByteArray capacity");
|
||||||
|
|
||||||
|
value = QByteArray(static_cast<char *>(data.mv_data), static_cast<int>(data.mv_size));
|
||||||
|
#endif
|
||||||
|
|
||||||
return QString::fromUtf8(value);
|
return QString::fromUtf8(value);
|
||||||
};
|
};
|
||||||
void deserialize(const MDB_val& data, QString& result) {
|
void deserialize(const MDB_val& data, QString& result) {
|
||||||
value = QByteArray((char*)data.mv_data, data.mv_size);
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||||
|
if (data.mv_size > static_cast<size_t>(std::numeric_limits<qsizetype>::max()))
|
||||||
|
throw std::runtime_error("Data size exceeds QByteArray capacity");
|
||||||
|
|
||||||
|
value = QByteArray(static_cast<char *>(data.mv_data), static_cast<qsizetype>(data.mv_size));
|
||||||
|
#else
|
||||||
|
if (data.mv_size > static_cast<size_t>(std::numeric_limits<int>::max()))
|
||||||
|
throw std::runtime_error("Data size exceeds QByteArray capacity");
|
||||||
|
|
||||||
|
value = QByteArray(static_cast<char *>(data.mv_data), static_cast<int>(data.mv_size));
|
||||||
|
#endif
|
||||||
|
|
||||||
result = QString::fromUtf8(value);
|
result = QString::fromUtf8(value);
|
||||||
}
|
}
|
||||||
MDB_val setData(const QString& data) {
|
MDB_val setData(const QString& data) {
|
||||||
|
@ -46,7 +67,7 @@ public:
|
||||||
MDB_val getData() {
|
MDB_val getData() {
|
||||||
MDB_val result;
|
MDB_val result;
|
||||||
result.mv_data = value.data();
|
result.mv_data = value.data();
|
||||||
result.mv_size = value.size();
|
result.mv_size = static_cast<size_t>(value.size());
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
void clear() {}; //not possible;
|
void clear() {}; //not possible;
|
||||||
|
@ -55,11 +76,4 @@ private:
|
||||||
QByteArray value;
|
QByteArray value;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //LMDBAL_SERIALIZER_QSTRING_HPP
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_SERIALIZER_STDSTRING_HPP
|
#pragma once
|
||||||
#define LMDBAL_SERIALIZER_STDSTRING_HPP
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -35,7 +34,7 @@ public:
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
void deserialize(const MDB_val& data, std::string& result) {
|
void deserialize(const MDB_val& data, std::string& result) {
|
||||||
result.assign((char*)data.mv_data, data.mv_size);
|
result.assign(static_cast<char *>(data.mv_data), data.mv_size);
|
||||||
}
|
}
|
||||||
MDB_val setData(const std::string& data) {
|
MDB_val setData(const std::string& data) {
|
||||||
value = data;
|
value = data;
|
||||||
|
@ -43,7 +42,7 @@ public:
|
||||||
};
|
};
|
||||||
MDB_val getData() {
|
MDB_val getData() {
|
||||||
MDB_val result;
|
MDB_val result;
|
||||||
result.mv_data = (char*)value.c_str();
|
result.mv_data = const_cast<char *>(value.c_str());
|
||||||
result.mv_size = value.size();
|
result.mv_size = value.size();
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
@ -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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_SERIALIZER_UINT16_HPP
|
#pragma once
|
||||||
#define LMDBAL_SERIALIZER_UINT16_HPP
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_SERIALIZER_UINT32_HPP
|
#pragma once
|
||||||
#define LMDBAL_SERIALIZER_UINT32_HPP
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_SERIALIZER_UINT64_HPP
|
#pragma once
|
||||||
#define LMDBAL_SERIALIZER_UINT64_HPP
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_SERIALIZER_UINT8_HPP
|
#pragma once
|
||||||
#define LMDBAL_SERIALIZER_UINT8_HPP
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
@ -54,8 +53,3 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //LMDBAL_SERIALIZER_UINT8_HPP
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
73
src/session.cpp
Normal file
73
src/session.cpp
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* 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 "session.h"
|
||||||
|
|
||||||
|
LMDBAL::Session::Session():
|
||||||
|
parent(nullptr) {}
|
||||||
|
|
||||||
|
LMDBAL::Session::Session(Base* parent):
|
||||||
|
parent(parent)
|
||||||
|
{
|
||||||
|
parent->registerSession(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
LMDBAL::Session::Session(Session&& other):
|
||||||
|
parent(other.parent)
|
||||||
|
{
|
||||||
|
if (parent)
|
||||||
|
parent->replaceSession(&other, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
LMDBAL::Session::~Session() {
|
||||||
|
if (parent)
|
||||||
|
parent->unregisterSession(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
LMDBAL::Session& LMDBAL::Session::operator = (Session&& other) {
|
||||||
|
if (parent)
|
||||||
|
if (other.parent)
|
||||||
|
parent->unregisterSession(&other);
|
||||||
|
else
|
||||||
|
parent->unregisterSession(this);
|
||||||
|
else
|
||||||
|
if (other.parent)
|
||||||
|
other.parent->replaceSession(&other, this);
|
||||||
|
|
||||||
|
parent = other.parent;
|
||||||
|
other.terminate();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LMDBAL::Session::close() {
|
||||||
|
if (!parent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
parent->unregisterSession(this);
|
||||||
|
terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LMDBAL::Session::opened() const {
|
||||||
|
return parent != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LMDBAL::Session::terminate() {
|
||||||
|
parent = nullptr;
|
||||||
|
}
|
||||||
|
|
47
src/session.h
Normal file
47
src/session.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
namespace LMDBAL {
|
||||||
|
|
||||||
|
class Session {
|
||||||
|
friend class Base;
|
||||||
|
public:
|
||||||
|
explicit Session();
|
||||||
|
~Session();
|
||||||
|
Session(const Session&) = delete;
|
||||||
|
Session(Session&&);
|
||||||
|
|
||||||
|
Session& operator = (const Session&) = delete;
|
||||||
|
Session& operator = (Session&&);
|
||||||
|
|
||||||
|
void close();
|
||||||
|
bool opened() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Base* parent;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Session(Base* parent);
|
||||||
|
void terminate();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
107
src/storage.h
107
src/storage.h
|
@ -16,8 +16,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_STORAGE_H
|
#pragma once
|
||||||
#define LMDBAL_STORAGE_H
|
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -26,6 +25,7 @@
|
||||||
#include "serializer.h"
|
#include "serializer.h"
|
||||||
#include "cursor.h"
|
#include "cursor.h"
|
||||||
#include "transaction.h"
|
#include "transaction.h"
|
||||||
|
#include "storagecommon.h"
|
||||||
|
|
||||||
class BaseTest;
|
class BaseTest;
|
||||||
class DuplicatesTest;
|
class DuplicatesTest;
|
||||||
|
@ -34,105 +34,8 @@ class StorageCursorTest;
|
||||||
|
|
||||||
namespace LMDBAL {
|
namespace LMDBAL {
|
||||||
|
|
||||||
class iStorage {
|
|
||||||
friend class Base;
|
|
||||||
public:
|
|
||||||
protected:
|
|
||||||
iStorage(Base* parent, const std::string& name, bool duplicates = false);
|
|
||||||
virtual ~iStorage();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A private virtual function I need to open each storage in the database
|
|
||||||
*
|
|
||||||
* \param[in] transaction - lmdb transaction to call <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a">mdb_dbi_open</a>
|
|
||||||
* \returns MDB_SUCCESS if everything went smooth or MDB_<error> -like error code
|
|
||||||
*/
|
|
||||||
virtual int open(MDB_txn * transaction) = 0;
|
|
||||||
virtual void close();
|
|
||||||
virtual void handleDrop();
|
|
||||||
|
|
||||||
bool isDBOpened() const;
|
|
||||||
const std::string& dbName() const;
|
|
||||||
|
|
||||||
void ensureOpened(const std::string& methodName) const;
|
|
||||||
void throwDuplicateOrUnknown(int rc, const std::string& key) const;
|
|
||||||
void throwDuplicateOrUnknown(int rc, TransactionID txn, const std::string& key) const;
|
|
||||||
void throwNotFoundOrUnknown(int rc, const std::string& key) const;
|
|
||||||
void throwNotFoundOrUnknown(int rc, TransactionID txn, const std::string& key) const;
|
|
||||||
void throwUnknown(int rc, TransactionID txn) const;
|
|
||||||
void throwUnknown(int rc) const;
|
|
||||||
void throwUnknown(const std::string& message) const;
|
|
||||||
void throwDuplicate(const std::string& key) const;
|
|
||||||
void throwNotFound(const std::string& key) const;
|
|
||||||
void throwCursorNotReady(const std::string& method) const;
|
|
||||||
|
|
||||||
TransactionID extractTransactionId(const Transaction& txn, const std::string& action = "") const;
|
|
||||||
TransactionID beginReadOnlyTransaction() const;
|
|
||||||
TransactionID beginTransaction() const;
|
|
||||||
void commitTransaction(TransactionID id);
|
|
||||||
void abortTransaction(TransactionID id) const;
|
|
||||||
virtual void transactionStarted(TransactionID txn, bool readOnly) const;
|
|
||||||
virtual void transactionCommited(TransactionID txn);
|
|
||||||
virtual void transactionAborted(TransactionID txn) const;
|
|
||||||
virtual int drop(TransactionID transaction);
|
|
||||||
virtual SizeType count(TransactionID txn) 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);
|
|
||||||
int _mdbGet(MDB_txn* txn, MDB_val& key, MDB_val& data) const;
|
|
||||||
int _mdbDel(MDB_txn* txn, MDB_val& key);
|
|
||||||
int _mdbDel(MDB_txn* txn, MDB_val& key, MDB_val& data);
|
|
||||||
|
|
||||||
int _mdbStat(MDB_txn* txn, MDB_stat& stat) const;
|
|
||||||
int _mdbFlags(MDB_txn* txn, uint32_t& flags) const;
|
|
||||||
|
|
||||||
int _mdbCursorOpen(MDB_txn* txn, MDB_cursor** cursor) const;
|
|
||||||
void _mdbCursorClose(MDB_cursor* cursor) const;
|
|
||||||
int _mdbCursorGet(MDB_cursor* cursor, MDB_val& key, MDB_val& data, MDB_cursor_op operation) const;
|
|
||||||
int _mdbCursorSet(MDB_cursor* cursor, MDB_val& key) const;
|
|
||||||
int _mdbCursorDel(MDB_cursor* cursor, unsigned int flags = 0);
|
|
||||||
int _mdbCursorPut(MDB_cursor* cursor, MDB_val& key, MDB_val& data, unsigned int flags = 0);
|
|
||||||
|
|
||||||
int _mdbCursorRenew(MDB_txn* txn, MDB_cursor* cursor) const;
|
|
||||||
MDB_txn* _mdbCursorTxn(MDB_cursor* cursor) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual void drop();
|
|
||||||
virtual int drop(const WriteTransaction& txn);
|
|
||||||
virtual SizeType count() const;
|
|
||||||
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*/
|
|
||||||
|
|
||||||
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*/
|
|
||||||
|
|
||||||
protected:
|
|
||||||
template <class K, class V>
|
|
||||||
int makeStorage(MDB_txn* transaction, bool duplicates = false);
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
static std::string toString(const T& value);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class K, class V>
|
template <class K, class V>
|
||||||
class Storage : public iStorage {
|
class Storage : public StorageCommon {
|
||||||
friend class ::BaseTest;
|
friend class ::BaseTest;
|
||||||
friend class ::DuplicatesTest;
|
friend class ::DuplicatesTest;
|
||||||
friend class ::CacheCursorTest;
|
friend class ::CacheCursorTest;
|
||||||
|
@ -160,7 +63,7 @@ protected:
|
||||||
virtual uint32_t addRecords(const std::map<K, V>& data, TransactionID txn, bool overwrite = false);
|
virtual uint32_t addRecords(const std::map<K, V>& data, TransactionID txn, bool overwrite = false);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using iStorage::drop;
|
using StorageCommon::drop;
|
||||||
virtual void addRecord(const K& key, const V& value);
|
virtual void addRecord(const K& key, const V& value);
|
||||||
virtual void addRecord(const K& key, const V& value, const WriteTransaction& txn);
|
virtual void addRecord(const K& key, const V& value, const WriteTransaction& txn);
|
||||||
virtual bool forceRecord(const K& key, const V& value); //returns true if there was addition, false if change
|
virtual bool forceRecord(const K& key, const V& value); //returns true if there was addition, false if change
|
||||||
|
@ -199,5 +102,3 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "storage.hpp"
|
#include "storage.hpp"
|
||||||
|
|
||||||
#endif //LMDBAL_STORAGE_H
|
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMDBAL_STORAGE_HPP
|
#pragma once
|
||||||
#define LMDBAL_STORAGE_HPP
|
|
||||||
|
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "exceptions.h"
|
#include "exceptions.h"
|
||||||
|
@ -47,7 +46,7 @@
|
||||||
*/
|
*/
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
LMDBAL::Storage<K, V>::Storage(Base* parent, const std::string& name, bool duplicates):
|
LMDBAL::Storage<K, V>::Storage(Base* parent, const std::string& name, bool duplicates):
|
||||||
iStorage(parent, name, duplicates),
|
StorageCommon(parent, name, duplicates),
|
||||||
keySerializer(),
|
keySerializer(),
|
||||||
valueSerializer(),
|
valueSerializer(),
|
||||||
cursors()
|
cursors()
|
||||||
|
@ -1016,7 +1015,7 @@ void LMDBAL::Storage<K, V>::close() {
|
||||||
for (const std::pair<const uint32_t, Cursor<K, V>*>& pair : cursors)
|
for (const std::pair<const uint32_t, Cursor<K, V>*>& pair : cursors)
|
||||||
pair.second->terminated();
|
pair.second->terminated();
|
||||||
|
|
||||||
iStorage::close();
|
StorageCommon::close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1052,7 +1051,7 @@ void LMDBAL::Storage<K, V>::destroyCursor(LMDBAL::Cursor<K, V>& cursor) {
|
||||||
|
|
||||||
cursor.close();
|
cursor.close();
|
||||||
cursors.erase(itr);
|
cursors.erase(itr);
|
||||||
cursor.freed();
|
cursor.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1104,83 +1103,3 @@ void LMDBAL::Storage<K, V>::discoveredRecord(const K& key, const V& value, Trans
|
||||||
UNUSED(value);
|
UNUSED(value);
|
||||||
UNUSED(txn);
|
UNUSED(txn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A functiion to actually open <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#gadbe68a06c448dfb62da16443d251a78b">MDB_dbi</a> storage
|
|
||||||
*
|
|
||||||
* \tparam K type of keys in opening storage
|
|
||||||
*
|
|
||||||
* \param[in] transaction - lmdb transaction to call <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a">mdb_dbi_open</a>, must be a writable transaction!
|
|
||||||
* \param[in] duplicates - true if key duplicates are allowed (false by default)
|
|
||||||
*
|
|
||||||
* \returns MDB_SUCCESS if everything went smooth or MDB_<error> -like error code
|
|
||||||
*
|
|
||||||
* This is a way to optimise database using MDB_INTEGERKEY flag,
|
|
||||||
* when the key is actually kind of an integer
|
|
||||||
* This infrastructure also allowes us to customize mdb_dbi_open call in the future
|
|
||||||
*/
|
|
||||||
template<class K, class V>
|
|
||||||
inline int LMDBAL::iStorage::makeStorage(MDB_txn* transaction, bool duplicates) {
|
|
||||||
unsigned int flags = MDB_CREATE;
|
|
||||||
if constexpr (std::is_integral<K>::value)
|
|
||||||
flags |= MDB_INTEGERKEY;
|
|
||||||
|
|
||||||
if (duplicates) {
|
|
||||||
flags |= MDB_DUPSORT;
|
|
||||||
|
|
||||||
if constexpr (std::is_scalar<V>::value)
|
|
||||||
flags |= MDB_DUPFIXED;
|
|
||||||
|
|
||||||
if constexpr (
|
|
||||||
std::is_same<V, uint32_t>::value ||
|
|
||||||
std::is_same<V, int32_t>::value ||
|
|
||||||
std::is_same<V, uint64_t>::value ||
|
|
||||||
std::is_same<V, int64_t>::value
|
|
||||||
) //for some reason lmdb breaks if it's not one of these types in MDB_DUPFIXED mode
|
|
||||||
flags |= MDB_INTEGERDUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _mdbOpen(transaction, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A method to cast a value (which can be a value or a key) to string.
|
|
||||||
*
|
|
||||||
* This function is mainly used in exceptions, to report which key was duplicated or not found.
|
|
||||||
* You can define your own specializations to this function in case std::to_string doesn't cover your case
|
|
||||||
*
|
|
||||||
* \param[in] value a value that should be converted to string
|
|
||||||
* \returns a string presentation of value
|
|
||||||
*/
|
|
||||||
template<class T>
|
|
||||||
inline std::string LMDBAL::iStorage::toString(const T& value) {
|
|
||||||
return std::to_string(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A method to cast a value (which can be a value or a key) to string.
|
|
||||||
*
|
|
||||||
* QString spectialization
|
|
||||||
*
|
|
||||||
* \param[in] value a value that should be converted to string
|
|
||||||
* \returns a string presentation of value
|
|
||||||
*/
|
|
||||||
template<>
|
|
||||||
inline std::string LMDBAL::iStorage::toString(const QString& value) {
|
|
||||||
return value.toStdString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A method to cast a value (which can be a value or a key) to string.
|
|
||||||
*
|
|
||||||
* std::string spectialization
|
|
||||||
*
|
|
||||||
* \param[in] value a value that should be converted to string
|
|
||||||
* \returns a string presentation of value
|
|
||||||
*/
|
|
||||||
template<>
|
|
||||||
inline std::string LMDBAL::iStorage::toString(const std::string& value) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //LMDBAL_STORAGE_HPP
|
|
||||||
|
|
|
@ -16,18 +16,20 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "storage.h"
|
#include "storagecommon.h"
|
||||||
|
|
||||||
|
#include "cursorcommon.h"
|
||||||
|
|
||||||
#define UNUSED(x) (void)(x)
|
#define UNUSED(x) (void)(x)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class LMDBAL::iStorage
|
* \class LMDBAL::StorageCommon
|
||||||
*
|
*
|
||||||
* \brief Storage interface
|
* \brief Storage interface
|
||||||
*
|
*
|
||||||
* This is a interface-like class, it's designed to be an inner database interface to
|
* This is an interface-like class, it's designed to be an inner database interface to
|
||||||
* be used as a polymorphic entity, and provide protected interaction with the database
|
* be used as a polymorphic entity and provide protected interaction with the database
|
||||||
* from the heirs code
|
* from the heir code
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,7 +39,7 @@
|
||||||
* \param[in] name - the name of the storage
|
* \param[in] name - the name of the storage
|
||||||
* \param[in] duplicates - true if key duplicates are allowed (false by default)
|
* \param[in] duplicates - true if key duplicates are allowed (false by default)
|
||||||
*/
|
*/
|
||||||
LMDBAL::iStorage::iStorage(Base* parent, const std::string& name, bool duplicates):
|
LMDBAL::StorageCommon::StorageCommon(Base* parent, const std::string& name, bool duplicates):
|
||||||
dbi(),
|
dbi(),
|
||||||
db(parent),
|
db(parent),
|
||||||
name(name),
|
name(name),
|
||||||
|
@ -47,12 +49,12 @@ LMDBAL::iStorage::iStorage(Base* parent, const std::string& name, bool duplicate
|
||||||
/**
|
/**
|
||||||
* \brief Destroys a storage interface
|
* \brief Destroys a storage interface
|
||||||
*/
|
*/
|
||||||
LMDBAL::iStorage::~iStorage() {}
|
LMDBAL::StorageCommon::~StorageCommon () = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \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() {
|
void LMDBAL::StorageCommon::close() {
|
||||||
mdb_dbi_close(db->environment, dbi);
|
mdb_dbi_close(db->environment, dbi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +69,7 @@ void LMDBAL::iStorage::close() {
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
|
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
|
||||||
*/
|
*/
|
||||||
LMDBAL::TransactionID LMDBAL::iStorage::extractTransactionId(const Transaction& txn, const std::string& action) const {
|
LMDBAL::TransactionID LMDBAL::StorageCommon::extractTransactionId(const Transaction& txn, const std::string& action) const {
|
||||||
if (!txn.isActive())
|
if (!txn.isActive())
|
||||||
throw TransactionTerminated(db->name, name, action);
|
throw TransactionTerminated(db->name, name, action);
|
||||||
|
|
||||||
|
@ -82,11 +84,11 @@ LMDBAL::TransactionID LMDBAL::iStorage::extractTransactionId(const Transaction&
|
||||||
* \exception LMDBAL::Closed thrown if the database was closed
|
* \exception LMDBAL::Closed thrown if the database was closed
|
||||||
* \exception LMDBAL::Unknown thrown if something unexpected happened
|
* \exception LMDBAL::Unknown thrown if something unexpected happened
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::drop() {
|
void LMDBAL::StorageCommon::drop() {
|
||||||
ensureOpened(dropMethodName);
|
ensureOpened(dropMethodName);
|
||||||
|
|
||||||
TransactionID txn = beginTransaction();
|
TransactionID txn = beginTransaction();
|
||||||
int rc = iStorage::drop(txn);
|
int rc = StorageCommon::drop(txn);
|
||||||
if (rc != MDB_SUCCESS) {
|
if (rc != MDB_SUCCESS) {
|
||||||
abortTransaction(txn);
|
abortTransaction(txn);
|
||||||
throw Unknown(db->name, mdb_strerror(rc), name);
|
throw Unknown(db->name, mdb_strerror(rc), name);
|
||||||
|
@ -101,10 +103,10 @@ void LMDBAL::iStorage::drop() {
|
||||||
*
|
*
|
||||||
* Just performs content drop
|
* Just performs content drop
|
||||||
*
|
*
|
||||||
* \param[in] transaction - transaction ID, must be writable transaction!
|
* \param[in] transaction - transaction ID; must be writable transaction!
|
||||||
* \returns MDB_SUCCESS if everything went fine, MDB_<error> code otherwise
|
* \returns MDB_SUCCESS if everything went fine, MDB_<error> code otherwise
|
||||||
*/
|
*/
|
||||||
int LMDBAL::iStorage::drop(TransactionID transaction) {
|
int LMDBAL::StorageCommon::drop(TransactionID transaction) {
|
||||||
return mdb_drop(transaction, dbi, 0);
|
return mdb_drop(transaction, dbi, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,24 +115,24 @@ int LMDBAL::iStorage::drop(TransactionID transaction) {
|
||||||
*
|
*
|
||||||
* Just performs content drop
|
* Just performs content drop
|
||||||
*
|
*
|
||||||
* \param[in] txn - transaction ID, must be writable transaction!
|
* \param[in] txn - transaction ID; must be writable transaction!
|
||||||
* \returns MDB_SUCCESS if everything went fine, MDB_<error> code otherwise
|
* \returns MDB_SUCCESS if everything went fine, MDB_<error> code otherwise
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::TransactionTerminated thrown if the transaction was not active
|
* \exception LMDBAL::TransactionTerminated thrown if the transaction was not active
|
||||||
*/
|
*/
|
||||||
int LMDBAL::iStorage::drop(const WriteTransaction& txn) {
|
int LMDBAL::StorageCommon::drop(const WriteTransaction& txn) {
|
||||||
ensureOpened(dropMethodName);
|
ensureOpened(dropMethodName);
|
||||||
return drop(extractTransactionId(txn, dropMethodName));
|
return drop(extractTransactionId(txn, dropMethodName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Helper function, thows exception if the database is not opened
|
* \brief Helper function; throws an exception if the database is not opened
|
||||||
*
|
*
|
||||||
* \param[in] methodName - name of the method this function is called from, just for display in std::exception::what() message
|
* \param[in] methodName - name of the method this function is called from, just for display in std::exception::what() message
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::Closed thrown if the database was closed
|
* \exception LMDBAL::Closed thrown if the database was closed
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::ensureOpened(const std::string& methodName) const {
|
void LMDBAL::StorageCommon::ensureOpened(const std::string& methodName) const {
|
||||||
if (!isDBOpened())
|
if (!isDBOpened())
|
||||||
throw Closed(methodName, db->name, name);
|
throw Closed(methodName, db->name, name);
|
||||||
}
|
}
|
||||||
|
@ -138,12 +140,12 @@ void LMDBAL::iStorage::ensureOpened(const std::string& methodName) const {
|
||||||
/**
|
/**
|
||||||
* \brief Storage size
|
* \brief Storage size
|
||||||
*
|
*
|
||||||
* \returns amount of records in the storage
|
* \returns number of records in the storage
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::Closed thrown if the database was closed
|
* \exception LMDBAL::Closed thrown if the database was closed
|
||||||
* \exception LMDBAL::Unknown thrown if something unexpected happened
|
* \exception LMDBAL::Unknown thrown if something unexpected happened
|
||||||
*/
|
*/
|
||||||
LMDBAL::SizeType LMDBAL::iStorage::count() const {
|
LMDBAL::SizeType LMDBAL::StorageCommon::count() const {
|
||||||
ensureOpened(countMethodName);
|
ensureOpened(countMethodName);
|
||||||
|
|
||||||
TransactionID txn = beginReadOnlyTransaction();
|
TransactionID txn = beginReadOnlyTransaction();
|
||||||
|
@ -159,34 +161,94 @@ LMDBAL::SizeType LMDBAL::iStorage::count() const {
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Retrieves a public transaction object for a cursor handle
|
||||||
|
*
|
||||||
|
* Cursor must be still opened by a public transaction
|
||||||
|
*
|
||||||
|
* \param[out] cursor - cursor handle
|
||||||
|
*
|
||||||
|
* \exception std::out_of_range thrown if LMDBAL::Transaction pointer wasn't found in the LMDBAL::Base
|
||||||
|
*/
|
||||||
|
LMDBAL::Transaction* LMDBAL::StorageCommon::getTransactionForCursor (MDB_cursor* cursor) const {
|
||||||
|
TransactionID txnID = _mdbCursorTxn(cursor);
|
||||||
|
return db->transactions.at(txnID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \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::StorageCommon::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::StorageCommon::closeCursorTransaction (MDB_cursor* cursor, bool closeCursor) const {
|
||||||
|
TransactionID txn = _mdbCursorTxn(cursor);
|
||||||
|
if (closeCursor)
|
||||||
|
_mdbCursorClose(cursor);
|
||||||
|
|
||||||
|
abortTransaction(txn);
|
||||||
|
transactionAborted(txn);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Storage size (private transaction variant)
|
* \brief Storage size (private transaction variant)
|
||||||
*
|
*
|
||||||
* \param[in] txn - transaction ID, can be read-only transaction
|
* \param[in] txn - transaction ID; can be read-only transaction
|
||||||
* \returns amount of records in the storage
|
* \returns number of records in the storage
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::Unknown thrown if something unexpected happened
|
* \exception LMDBAL::Unknown thrown if something unexpected happened
|
||||||
*/
|
*/
|
||||||
LMDBAL::SizeType LMDBAL::iStorage::count(TransactionID txn) const {
|
LMDBAL::SizeType LMDBAL::StorageCommon::count(TransactionID txn) const {
|
||||||
MDB_stat stat;
|
MDB_stat stat;
|
||||||
int rc = mdb_stat(txn, dbi, &stat);
|
if (const int rc = mdb_stat(txn, dbi, &stat); rc != MDB_SUCCESS)
|
||||||
if (rc != MDB_SUCCESS)
|
|
||||||
throw Unknown(db->name, mdb_strerror(rc), name);
|
throw Unknown(db->name, mdb_strerror(rc), name);
|
||||||
|
|
||||||
return stat.ms_entries;
|
if (stat.ms_entries > std::numeric_limits<SizeType>::max())
|
||||||
|
throw Unknown(db->name, "Storage size exceeds it's limits", name);
|
||||||
|
|
||||||
|
return static_cast<SizeType>(stat.ms_entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Storage size (public transaction variant)
|
* \brief Storage size (public transaction variant)
|
||||||
*
|
*
|
||||||
* \param[in] txn - transaction, can be read-only transaction
|
* \param[in] txn - transaction; can be read-only transaction
|
||||||
* \returns amount of records in the storage
|
* \returns number of records in the storage
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::Closed thrown if the database was closed
|
* \exception LMDBAL::Closed thrown if the database was closed
|
||||||
* \exception LMDBAL::Unknown thrown if something unexpected happened
|
* \exception LMDBAL::Unknown thrown if something unexpected happened
|
||||||
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
|
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
|
||||||
*/
|
*/
|
||||||
LMDBAL::SizeType LMDBAL::iStorage::count(const Transaction& txn) const {
|
LMDBAL::SizeType LMDBAL::StorageCommon::count(const Transaction& txn) const {
|
||||||
ensureOpened(countMethodName);
|
ensureOpened(countMethodName);
|
||||||
return count(extractTransactionId(txn, countMethodName));
|
return count(extractTransactionId(txn, countMethodName));
|
||||||
}
|
}
|
||||||
|
@ -203,7 +265,7 @@ LMDBAL::SizeType LMDBAL::iStorage::count(const Transaction& txn) const {
|
||||||
* \exception LMDBAL::Exist thrown if rc == MDB_KEYEXIST
|
* \exception LMDBAL::Exist thrown if rc == MDB_KEYEXIST
|
||||||
* \exception LMDBAL::Unknown thrown if rc != MDB_KEYEXIST
|
* \exception LMDBAL::Unknown thrown if rc != MDB_KEYEXIST
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::throwDuplicateOrUnknown(int rc, TransactionID txn, const std::string& key) const {
|
void LMDBAL::StorageCommon::throwDuplicateOrUnknown(int rc, TransactionID txn, const std::string& key) const {
|
||||||
abortTransaction(txn);
|
abortTransaction(txn);
|
||||||
throwDuplicateOrUnknown(rc, key);
|
throwDuplicateOrUnknown(rc, key);
|
||||||
}
|
}
|
||||||
|
@ -220,7 +282,7 @@ void LMDBAL::iStorage::throwDuplicateOrUnknown(int rc, TransactionID txn, const
|
||||||
* \exception LMDBAL::NotFound thrown if rc == MDB_NOTFOUND
|
* \exception LMDBAL::NotFound thrown if rc == MDB_NOTFOUND
|
||||||
* \exception LMDBAL::Unknown thrown if rc != MDB_NOTFOUND
|
* \exception LMDBAL::Unknown thrown if rc != MDB_NOTFOUND
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::throwNotFoundOrUnknown(int rc, LMDBAL::TransactionID txn, const std::string& key) const {
|
void LMDBAL::StorageCommon::throwNotFoundOrUnknown(int rc, LMDBAL::TransactionID txn, const std::string& key) const {
|
||||||
abortTransaction(txn);
|
abortTransaction(txn);
|
||||||
throwNotFoundOrUnknown(rc, key);
|
throwNotFoundOrUnknown(rc, key);
|
||||||
}
|
}
|
||||||
|
@ -236,7 +298,7 @@ void LMDBAL::iStorage::throwNotFoundOrUnknown(int rc, LMDBAL::TransactionID txn,
|
||||||
* \exception LMDBAL::Exist thrown if rc == MDB_KEYEXIST
|
* \exception LMDBAL::Exist thrown if rc == MDB_KEYEXIST
|
||||||
* \exception LMDBAL::Unknown thrown if rc != MDB_KEYEXIST
|
* \exception LMDBAL::Unknown thrown if rc != MDB_KEYEXIST
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::throwDuplicateOrUnknown(int rc, const std::string& key) const {
|
void LMDBAL::StorageCommon::throwDuplicateOrUnknown(int rc, const std::string& key) const {
|
||||||
if (rc == MDB_KEYEXIST)
|
if (rc == MDB_KEYEXIST)
|
||||||
throwDuplicate(key);
|
throwDuplicate(key);
|
||||||
else
|
else
|
||||||
|
@ -254,7 +316,7 @@ void LMDBAL::iStorage::throwDuplicateOrUnknown(int rc, const std::string& key) c
|
||||||
* \exception LMDBAL::NotFound thrown if rc == MDB_NOTFOUND
|
* \exception LMDBAL::NotFound thrown if rc == MDB_NOTFOUND
|
||||||
* \exception LMDBAL::Unknown thrown if rc != MDB_NOTFOUND
|
* \exception LMDBAL::Unknown thrown if rc != MDB_NOTFOUND
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::throwNotFoundOrUnknown(int rc, const std::string& key) const {
|
void LMDBAL::StorageCommon::throwNotFoundOrUnknown(int rc, const std::string& key) const {
|
||||||
if (rc == MDB_NOTFOUND)
|
if (rc == MDB_NOTFOUND)
|
||||||
throwNotFound(key);
|
throwNotFound(key);
|
||||||
else
|
else
|
||||||
|
@ -271,7 +333,7 @@ void LMDBAL::iStorage::throwNotFoundOrUnknown(int rc, const std::string& key) co
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::Unknown thrown everytime
|
* \exception LMDBAL::Unknown thrown everytime
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::throwUnknown(int rc, LMDBAL::TransactionID txn) const {
|
void LMDBAL::StorageCommon::throwUnknown(int rc, LMDBAL::TransactionID txn) const {
|
||||||
abortTransaction(txn);
|
abortTransaction(txn);
|
||||||
throwUnknown(rc);
|
throwUnknown(rc);
|
||||||
}
|
}
|
||||||
|
@ -283,7 +345,7 @@ void LMDBAL::iStorage::throwUnknown(int rc, LMDBAL::TransactionID txn) const {
|
||||||
*
|
*
|
||||||
* \returns database name
|
* \returns database name
|
||||||
*/
|
*/
|
||||||
const std::string & LMDBAL::iStorage::dbName() const {
|
const std::string & LMDBAL::StorageCommon::dbName() const {
|
||||||
return db->name;}
|
return db->name;}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -293,8 +355,8 @@ const std::string & LMDBAL::iStorage::dbName() const {
|
||||||
*
|
*
|
||||||
* \returns true if database is ipened, false otherwise
|
* \returns true if database is ipened, false otherwise
|
||||||
*/
|
*/
|
||||||
bool LMDBAL::iStorage::isDBOpened() const {
|
bool LMDBAL::StorageCommon::isDBOpened() const {
|
||||||
return db->opened;}
|
return db->opened();}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Throws LMDBAL::Unknown
|
* \brief Throws LMDBAL::Unknown
|
||||||
|
@ -305,7 +367,7 @@ bool LMDBAL::iStorage::isDBOpened() const {
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::Unknown thrown everytime
|
* \exception LMDBAL::Unknown thrown everytime
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::throwUnknown(int rc) const {
|
void LMDBAL::StorageCommon::throwUnknown(int rc) const {
|
||||||
throw Unknown(db->name, mdb_strerror(rc), name);}
|
throw Unknown(db->name, mdb_strerror(rc), name);}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -317,7 +379,7 @@ void LMDBAL::iStorage::throwUnknown(int rc) const {
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::Unknown thrown everytime
|
* \exception LMDBAL::Unknown thrown everytime
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::throwUnknown(const std::string& message) const {
|
void LMDBAL::StorageCommon::throwUnknown(const std::string& message) const {
|
||||||
throw Unknown(db->name, message, name);}
|
throw Unknown(db->name, message, name);}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -329,7 +391,7 @@ void LMDBAL::iStorage::throwUnknown(const std::string& message) const {
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::Exist thrown everytime
|
* \exception LMDBAL::Exist thrown everytime
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::throwDuplicate(const std::string& key) const {
|
void LMDBAL::StorageCommon::throwDuplicate(const std::string& key) const {
|
||||||
throw Exist(key, db->name, name);}
|
throw Exist(key, db->name, name);}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -341,7 +403,7 @@ void LMDBAL::iStorage::throwDuplicate(const std::string& key) const {
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::NotFound thrown everytime
|
* \exception LMDBAL::NotFound thrown everytime
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::throwNotFound(const std::string& key) const {
|
void LMDBAL::StorageCommon::throwNotFound(const std::string& key) const {
|
||||||
throw NotFound(key, db->name, name);}
|
throw NotFound(key, db->name, name);}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -353,7 +415,7 @@ void LMDBAL::iStorage::throwNotFound(const std::string& key) const {
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::CursorNotReady thrown everytime
|
* \exception LMDBAL::CursorNotReady thrown everytime
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::throwCursorNotReady(const std::string& method) const {
|
void LMDBAL::StorageCommon::throwCursorNotReady(const std::string& method) const {
|
||||||
throw CursorNotReady(method, db->name, name);}
|
throw CursorNotReady(method, db->name, name);}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -363,7 +425,7 @@ void LMDBAL::iStorage::throwCursorNotReady(const std::string& method) const {
|
||||||
*
|
*
|
||||||
* \returns read only transaction
|
* \returns read only transaction
|
||||||
*/
|
*/
|
||||||
LMDBAL::TransactionID LMDBAL::iStorage::beginReadOnlyTransaction() const {
|
LMDBAL::TransactionID LMDBAL::StorageCommon::beginReadOnlyTransaction() const {
|
||||||
return db->beginPrivateReadOnlyTransaction(name);}
|
return db->beginPrivateReadOnlyTransaction(name);}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -373,7 +435,7 @@ LMDBAL::TransactionID LMDBAL::iStorage::beginReadOnlyTransaction() const {
|
||||||
*
|
*
|
||||||
* \returns read only transaction
|
* \returns read only transaction
|
||||||
*/
|
*/
|
||||||
LMDBAL::TransactionID LMDBAL::iStorage::beginTransaction() const {
|
LMDBAL::TransactionID LMDBAL::StorageCommon::beginTransaction() const {
|
||||||
return db->beginPrivateTransaction(name);}
|
return db->beginPrivateTransaction(name);}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -381,7 +443,7 @@ LMDBAL::TransactionID LMDBAL::iStorage::beginTransaction() const {
|
||||||
*
|
*
|
||||||
* Ment to be called from heirs, name is reported to the database just to be displayed in std::exception::what() message
|
* Ment to be called from heirs, name is reported to the database just to be displayed in std::exception::what() message
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::abortTransaction(LMDBAL::TransactionID id) const {
|
void LMDBAL::StorageCommon::abortTransaction(LMDBAL::TransactionID id) const {
|
||||||
db->abortPrivateTransaction(id, name);}
|
db->abortPrivateTransaction(id, name);}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -391,7 +453,7 @@ void LMDBAL::iStorage::abortTransaction(LMDBAL::TransactionID id) const {
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::Unknown thrown if something unexpected happened
|
* \exception LMDBAL::Unknown thrown if something unexpected happened
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::commitTransaction(LMDBAL::TransactionID id) {
|
void LMDBAL::StorageCommon::commitTransaction(LMDBAL::TransactionID id) {
|
||||||
db->commitPrivateTransaction(id, name);}
|
db->commitPrivateTransaction(id, name);}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -406,7 +468,7 @@ void LMDBAL::iStorage::commitTransaction(LMDBAL::TransactionID id) {
|
||||||
* \param[in] txn - ID of started transaction
|
* \param[in] txn - ID of started transaction
|
||||||
* \param[in] readOnly - true if transaction is read-only, false otherwise
|
* \param[in] readOnly - true if transaction is read-only, false otherwise
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::transactionStarted(LMDBAL::TransactionID txn, bool readOnly) const {
|
void LMDBAL::StorageCommon::transactionStarted(LMDBAL::TransactionID txn, bool readOnly) const {
|
||||||
UNUSED(txn);
|
UNUSED(txn);
|
||||||
UNUSED(readOnly);
|
UNUSED(readOnly);
|
||||||
}
|
}
|
||||||
|
@ -422,7 +484,7 @@ void LMDBAL::iStorage::transactionStarted(LMDBAL::TransactionID txn, bool readOn
|
||||||
*
|
*
|
||||||
* \param[in] txn - ID of started transaction
|
* \param[in] txn - ID of started transaction
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::transactionCommited(LMDBAL::TransactionID txn) {
|
void LMDBAL::StorageCommon::transactionCommited(LMDBAL::TransactionID txn) {
|
||||||
UNUSED(txn);}
|
UNUSED(txn);}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -436,7 +498,7 @@ void LMDBAL::iStorage::transactionCommited(LMDBAL::TransactionID txn) {
|
||||||
*
|
*
|
||||||
* \param[in] txn - ID of started transaction
|
* \param[in] txn - ID of started transaction
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::transactionAborted(LMDBAL::TransactionID txn) const {
|
void LMDBAL::StorageCommon::transactionAborted(LMDBAL::TransactionID txn) const {
|
||||||
UNUSED(txn);}
|
UNUSED(txn);}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -445,65 +507,64 @@ void LMDBAL::iStorage::transactionAborted(LMDBAL::TransactionID txn) const {
|
||||||
* It's a protected method that is called to optimise drop process
|
* It's a protected method that is called to optimise drop process
|
||||||
* after the transaction is commited. Used just for optimisations.
|
* after the transaction is commited. Used just for optimisations.
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::handleDrop() {}
|
void LMDBAL::StorageCommon::handleDrop() {}
|
||||||
|
|
||||||
int LMDBAL::iStorage::_mdbOpen(MDB_txn *txn, unsigned int flags) {
|
int LMDBAL::StorageCommon::_mdbOpen(MDB_txn *txn, unsigned int flags) {
|
||||||
return mdb_dbi_open(txn, name.c_str(), flags, &dbi);
|
return mdb_dbi_open(txn, name.c_str(), flags, &dbi);
|
||||||
}
|
}
|
||||||
|
|
||||||
int LMDBAL::iStorage::_mdbPut(MDB_txn* txn, MDB_val& key, MDB_val& data, unsigned int flags) {
|
int LMDBAL::StorageCommon::_mdbPut(MDB_txn* txn, MDB_val& key, MDB_val& data, unsigned int flags) {
|
||||||
return mdb_put(txn, dbi, &key, &data, flags);
|
return mdb_put(txn, dbi, &key, &data, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
int LMDBAL::iStorage::_mdbGet(MDB_txn* txn, MDB_val& key, MDB_val& data) const {
|
int LMDBAL::StorageCommon::_mdbGet(MDB_txn* txn, MDB_val& key, MDB_val& data) const {
|
||||||
return mdb_get(txn, dbi, &key, &data);
|
return mdb_get(txn, dbi, &key, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
int LMDBAL::iStorage::_mdbDel(MDB_txn* txn, MDB_val& key) {
|
int LMDBAL::StorageCommon::_mdbDel(MDB_txn* txn, MDB_val& key) {
|
||||||
return mdb_del(txn, dbi, &key, NULL);
|
return mdb_del(txn, dbi, &key, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int LMDBAL::iStorage::_mdbDel(MDB_txn* txn, MDB_val& key, MDB_val& data) {
|
int LMDBAL::StorageCommon::_mdbDel(MDB_txn* txn, MDB_val& key, MDB_val& data) {
|
||||||
return mdb_del(txn, dbi, &key, &data);
|
return mdb_del(txn, dbi, &key, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
int LMDBAL::iStorage::_mdbStat(MDB_txn* txn, MDB_stat& stat) const {
|
int LMDBAL::StorageCommon::_mdbStat(MDB_txn* txn, MDB_stat& stat) const {
|
||||||
return mdb_stat(txn, dbi, &stat);
|
return mdb_stat(txn, dbi, &stat);
|
||||||
}
|
}
|
||||||
|
|
||||||
int LMDBAL::iStorage::_mdbFlags(MDB_txn* txn, uint32_t& flags) const {
|
int LMDBAL::StorageCommon::_mdbFlags(MDB_txn* txn, uint32_t& flags) const {
|
||||||
return mdb_dbi_flags(txn, dbi, &flags);
|
return mdb_dbi_flags(txn, dbi, &flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int LMDBAL::StorageCommon::_mdbCursorOpen(MDB_txn* txn, MDB_cursor** cursor) const {
|
||||||
int LMDBAL::iStorage::_mdbCursorOpen(MDB_txn* txn, MDB_cursor** cursor) const {
|
|
||||||
return mdb_cursor_open(txn, dbi, cursor);
|
return mdb_cursor_open(txn, dbi, cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LMDBAL::iStorage::_mdbCursorClose(MDB_cursor *cursor) const {
|
void LMDBAL::StorageCommon::_mdbCursorClose(MDB_cursor *cursor) const {
|
||||||
mdb_cursor_close(cursor);
|
mdb_cursor_close(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
int LMDBAL::iStorage::_mdbCursorGet(MDB_cursor* cursor, MDB_val& key, MDB_val& data, MDB_cursor_op operation) const {
|
int LMDBAL::StorageCommon::_mdbCursorGet(MDB_cursor* cursor, MDB_val& key, MDB_val& data, MDB_cursor_op operation) const {
|
||||||
return mdb_cursor_get(cursor, &key, &data, operation);
|
return mdb_cursor_get(cursor, &key, &data, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
int LMDBAL::iStorage::_mdbCursorSet(MDB_cursor* cursor, MDB_val& key) const {
|
int LMDBAL::StorageCommon::_mdbCursorSet(MDB_cursor* cursor, MDB_val& key) const {
|
||||||
return mdb_cursor_get(cursor, &key, NULL, MDB_SET);
|
return mdb_cursor_get(cursor, &key, NULL, MDB_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
int LMDBAL::iStorage::_mdbCursorDel(MDB_cursor* cursor, unsigned int flags) {
|
int LMDBAL::StorageCommon::_mdbCursorDel(MDB_cursor* cursor, unsigned int flags) {
|
||||||
return mdb_cursor_del(cursor, flags);
|
return mdb_cursor_del(cursor, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
int LMDBAL::iStorage::_mdbCursorPut(MDB_cursor* cursor, MDB_val& key, MDB_val& data, unsigned int flags) {
|
int LMDBAL::StorageCommon::_mdbCursorPut(MDB_cursor* cursor, MDB_val& key, MDB_val& data, unsigned int flags) {
|
||||||
return mdb_cursor_put(cursor, &key, &data, flags);
|
return mdb_cursor_put(cursor, &key, &data, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
int LMDBAL::iStorage::_mdbCursorRenew(MDB_txn* txn, MDB_cursor* cursor) const {
|
int LMDBAL::StorageCommon::_mdbCursorRenew(MDB_txn* txn, MDB_cursor* cursor) const {
|
||||||
return mdb_cursor_renew(txn, cursor);
|
return mdb_cursor_renew(txn, cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
MDB_txn* LMDBAL::iStorage::_mdbCursorTxn(MDB_cursor* cursor) const {
|
MDB_txn* LMDBAL::StorageCommon::_mdbCursorTxn(MDB_cursor* cursor) const {
|
||||||
return mdb_cursor_txn(cursor);
|
return mdb_cursor_txn(cursor);
|
||||||
}
|
}
|
139
src/storagecommon.h
Normal file
139
src/storagecommon.h
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
* 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 <string>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <lmdb.h>
|
||||||
|
|
||||||
|
#include "base.h"
|
||||||
|
#include "transaction.h"
|
||||||
|
|
||||||
|
namespace LMDBAL {
|
||||||
|
|
||||||
|
class CursorCommon;
|
||||||
|
|
||||||
|
class StorageCommon {
|
||||||
|
friend class Base;
|
||||||
|
friend class CursorCommon;
|
||||||
|
public:
|
||||||
|
protected:
|
||||||
|
StorageCommon(Base* parent, const std::string& name, bool duplicates = false);
|
||||||
|
virtual ~ StorageCommon();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A private virtual function I need to open each storage in the database
|
||||||
|
*
|
||||||
|
* \param[in] transaction - lmdb transaction to call <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a">mdb_dbi_open</a>
|
||||||
|
* \returns MDB_SUCCESS if everything went smooth or MDB_<error> -like error code
|
||||||
|
*/
|
||||||
|
virtual int open(MDB_txn * transaction) = 0;
|
||||||
|
virtual void close();
|
||||||
|
virtual void handleDrop();
|
||||||
|
|
||||||
|
bool isDBOpened() const;
|
||||||
|
const std::string& dbName() const;
|
||||||
|
|
||||||
|
void ensureOpened(const std::string& methodName) const;
|
||||||
|
void throwDuplicateOrUnknown(int rc, const std::string& key) const;
|
||||||
|
void throwDuplicateOrUnknown(int rc, TransactionID txn, const std::string& key) const;
|
||||||
|
void throwNotFoundOrUnknown(int rc, const std::string& key) const;
|
||||||
|
void throwNotFoundOrUnknown(int rc, TransactionID txn, const std::string& key) const;
|
||||||
|
void throwUnknown(int rc, TransactionID txn) const;
|
||||||
|
void throwUnknown(int rc) const;
|
||||||
|
void throwUnknown(const std::string& message) const;
|
||||||
|
void throwDuplicate(const std::string& key) const;
|
||||||
|
void throwNotFound(const std::string& key) const;
|
||||||
|
void throwCursorNotReady(const std::string& method) const;
|
||||||
|
|
||||||
|
TransactionID extractTransactionId(const Transaction& txn, const std::string& action = "") const;
|
||||||
|
TransactionID beginReadOnlyTransaction() const;
|
||||||
|
TransactionID beginTransaction() const;
|
||||||
|
void commitTransaction(TransactionID id);
|
||||||
|
void abortTransaction(TransactionID id) const;
|
||||||
|
virtual void transactionStarted(TransactionID txn, bool readOnly) const;
|
||||||
|
virtual void transactionCommited(TransactionID txn);
|
||||||
|
virtual void transactionAborted(TransactionID txn) const;
|
||||||
|
virtual int drop(TransactionID transaction);
|
||||||
|
virtual SizeType count(TransactionID txn) const;
|
||||||
|
|
||||||
|
Transaction* getTransactionForCursor(MDB_cursor* cursor) const;
|
||||||
|
|
||||||
|
void openCursorTransaction(MDB_cursor** cursor, bool renew = false) const;
|
||||||
|
void closeCursorTransaction(MDB_cursor* cursor, bool closeCursor = false) 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);
|
||||||
|
int _mdbGet(MDB_txn* txn, MDB_val& key, MDB_val& data) const;
|
||||||
|
int _mdbDel(MDB_txn* txn, MDB_val& key);
|
||||||
|
int _mdbDel(MDB_txn* txn, MDB_val& key, MDB_val& data);
|
||||||
|
|
||||||
|
int _mdbStat(MDB_txn* txn, MDB_stat& stat) const;
|
||||||
|
int _mdbFlags(MDB_txn* txn, uint32_t& flags) const;
|
||||||
|
|
||||||
|
int _mdbCursorOpen(MDB_txn* txn, MDB_cursor** cursor) const;
|
||||||
|
void _mdbCursorClose(MDB_cursor* cursor) const;
|
||||||
|
int _mdbCursorGet(MDB_cursor* cursor, MDB_val& key, MDB_val& data, MDB_cursor_op operation) const;
|
||||||
|
int _mdbCursorSet(MDB_cursor* cursor, MDB_val& key) const;
|
||||||
|
int _mdbCursorDel(MDB_cursor* cursor, unsigned int flags = 0);
|
||||||
|
int _mdbCursorPut(MDB_cursor* cursor, MDB_val& key, MDB_val& data, unsigned int flags = 0);
|
||||||
|
|
||||||
|
int _mdbCursorRenew(MDB_txn* txn, MDB_cursor* cursor) const;
|
||||||
|
MDB_txn* _mdbCursorTxn(MDB_cursor* cursor) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual void drop();
|
||||||
|
virtual int drop(const WriteTransaction& txn);
|
||||||
|
virtual SizeType count() const;
|
||||||
|
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*/
|
||||||
|
|
||||||
|
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*/
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template <class K, class V>
|
||||||
|
int makeStorage(MDB_txn* transaction, bool duplicates = false);
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
static std::string toString(const T& value);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "storagecommon.hpp"
|
99
src/storagecommon.hpp
Normal file
99
src/storagecommon.hpp
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* 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 "storagecommon.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A functiion to actually open <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#gadbe68a06c448dfb62da16443d251a78b">MDB_dbi</a> storage
|
||||||
|
*
|
||||||
|
* \tparam K type of keys in opening storage
|
||||||
|
*
|
||||||
|
* \param[in] transaction - lmdb transaction to call <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a">mdb_dbi_open</a>, must be a writable transaction!
|
||||||
|
* \param[in] duplicates - true if key duplicates are allowed (false by default)
|
||||||
|
*
|
||||||
|
* \returns MDB_SUCCESS if everything went smooth or MDB_<error> -like error code
|
||||||
|
*
|
||||||
|
* This is a way to optimise database using MDB_INTEGERKEY flag,
|
||||||
|
* when the key is actually kind of an integer
|
||||||
|
* This infrastructure also allowes us to customize mdb_dbi_open call in the future
|
||||||
|
*/
|
||||||
|
template<class K, class V>
|
||||||
|
inline int LMDBAL::StorageCommon::makeStorage(MDB_txn* transaction, bool duplicates) {
|
||||||
|
unsigned int flags = MDB_CREATE;
|
||||||
|
if constexpr (std::is_integral<K>::value)
|
||||||
|
flags |= MDB_INTEGERKEY;
|
||||||
|
|
||||||
|
if (duplicates) {
|
||||||
|
flags |= MDB_DUPSORT;
|
||||||
|
|
||||||
|
if constexpr (std::is_scalar<V>::value)
|
||||||
|
flags |= MDB_DUPFIXED;
|
||||||
|
|
||||||
|
if constexpr (
|
||||||
|
std::is_same<V, uint32_t>::value ||
|
||||||
|
std::is_same<V, int32_t>::value ||
|
||||||
|
std::is_same<V, uint64_t>::value ||
|
||||||
|
std::is_same<V, int64_t>::value
|
||||||
|
) //for some reason lmdb breaks if it's not one of these types in MDB_DUPFIXED mode
|
||||||
|
flags |= MDB_INTEGERDUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _mdbOpen(transaction, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A method to cast a value (which can be a value or a key) to string.
|
||||||
|
*
|
||||||
|
* This function is mainly used in exceptions, to report which key was duplicated or not found.
|
||||||
|
* You can define your own specializations to this function in case std::to_string doesn't cover your case
|
||||||
|
*
|
||||||
|
* \param[in] value a value that should be converted to string
|
||||||
|
* \returns a string presentation of value
|
||||||
|
*/
|
||||||
|
template<class T>
|
||||||
|
inline std::string LMDBAL::StorageCommon::toString(const T& value) {
|
||||||
|
return std::to_string(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A method to cast a value (which can be a value or a key) to string.
|
||||||
|
*
|
||||||
|
* QString spectialization
|
||||||
|
*
|
||||||
|
* \param[in] value a value that should be converted to string
|
||||||
|
* \returns a string presentation of value
|
||||||
|
*/
|
||||||
|
template<>
|
||||||
|
inline std::string LMDBAL::StorageCommon::toString(const QString& value) {
|
||||||
|
return value.toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A method to cast a value (which can be a value or a key) to string.
|
||||||
|
*
|
||||||
|
* std::string spectialization
|
||||||
|
*
|
||||||
|
* \param[in] value a value that should be converted to string
|
||||||
|
* \returns a string presentation of value
|
||||||
|
*/
|
||||||
|
template<>
|
||||||
|
inline std::string LMDBAL::StorageCommon::toString(const std::string& value) {
|
||||||
|
return value;
|
||||||
|
}
|
|
@ -1,5 +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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "transaction.h"
|
#include "transaction.h"
|
||||||
|
|
||||||
|
#include "cursorcommon.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class LMDBAL::Transaction
|
* \class LMDBAL::Transaction
|
||||||
* \brief Public read only transaction
|
* \brief Public read only transaction
|
||||||
|
@ -22,7 +42,8 @@
|
||||||
LMDBAL::Transaction::Transaction():
|
LMDBAL::Transaction::Transaction():
|
||||||
txn(nullptr),
|
txn(nullptr),
|
||||||
active(false),
|
active(false),
|
||||||
parent(nullptr)
|
parent(nullptr),
|
||||||
|
cursors()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,8 +52,11 @@ LMDBAL::Transaction::Transaction():
|
||||||
LMDBAL::Transaction::Transaction(TransactionID txn, const Base* parent) :
|
LMDBAL::Transaction::Transaction(TransactionID txn, const Base* parent) :
|
||||||
txn(txn),
|
txn(txn),
|
||||||
active(true),
|
active(true),
|
||||||
parent(parent)
|
parent(parent),
|
||||||
{}
|
cursors()
|
||||||
|
{
|
||||||
|
parent->transactions[txn] = this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Moves transaction to a new object
|
* \brief Moves transaction to a new object
|
||||||
|
@ -40,9 +64,14 @@ LMDBAL::Transaction::Transaction(TransactionID txn, const Base* parent) :
|
||||||
LMDBAL::Transaction::Transaction(Transaction&& other):
|
LMDBAL::Transaction::Transaction(Transaction&& other):
|
||||||
txn(other.txn),
|
txn(other.txn),
|
||||||
active(other.active),
|
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 +85,21 @@ LMDBAL::Transaction::~Transaction() {
|
||||||
* \brief Move-assigns transaction to the new object
|
* \brief Move-assigns transaction to the new object
|
||||||
*/
|
*/
|
||||||
LMDBAL::Transaction& LMDBAL::Transaction::operator=(Transaction&& other) {
|
LMDBAL::Transaction& LMDBAL::Transaction::operator=(Transaction&& other) {
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
|
||||||
terminate();
|
terminate();
|
||||||
|
|
||||||
txn = other.txn;
|
txn = other.txn;
|
||||||
active = other.active;
|
active = other.active;
|
||||||
parent = other.parent;
|
parent = other.parent;
|
||||||
|
cursors = other.cursors;
|
||||||
|
|
||||||
other.active = false;
|
if (active) {
|
||||||
|
parent->transactions[txn] = this;
|
||||||
|
|
||||||
|
other.reset();
|
||||||
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -74,11 +111,32 @@ LMDBAL::Transaction& LMDBAL::Transaction::operator=(Transaction&& other) {
|
||||||
*/
|
*/
|
||||||
void LMDBAL::Transaction::terminate() {
|
void LMDBAL::Transaction::terminate() {
|
||||||
if (active) {
|
if (active) {
|
||||||
|
closeCursors();
|
||||||
parent->abortTransaction(txn);
|
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, CursorCommon*>& pair : cursors)
|
||||||
|
pair.second->terminated();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Returns transaction states
|
* \brief Returns transaction states
|
||||||
*
|
*
|
||||||
|
@ -132,6 +190,12 @@ LMDBAL::WriteTransaction::WriteTransaction(WriteTransaction&& other):
|
||||||
Transaction(std::move(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
|
* \brief Aborts transaction cancelling all changes
|
||||||
*
|
*
|
||||||
|
@ -148,7 +212,9 @@ void LMDBAL::WriteTransaction::abort() {
|
||||||
*/
|
*/
|
||||||
void LMDBAL::WriteTransaction::commit() {
|
void LMDBAL::WriteTransaction::commit() {
|
||||||
if (active) {
|
if (active) {
|
||||||
|
closeCursors();
|
||||||
const_cast<Base*>(parent)->commitTransaction(txn);
|
const_cast<Base*>(parent)->commitTransaction(txn);
|
||||||
active = false;
|
parent->transactions.erase(txn);
|
||||||
|
reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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
|
#pragma once
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
|
|
||||||
namespace LMDBAL {
|
namespace LMDBAL {
|
||||||
class iStorage;
|
class StorageCommon;
|
||||||
|
class CursorCommon;
|
||||||
|
|
||||||
class Transaction {
|
class Transaction {
|
||||||
friend class Base;
|
friend class Base;
|
||||||
friend class iStorage;
|
friend class StorageCommon;
|
||||||
|
friend class CursorCommon;
|
||||||
public:
|
public:
|
||||||
explicit Transaction();
|
explicit Transaction();
|
||||||
explicit Transaction(Transaction&& other);
|
explicit Transaction(Transaction&& other);
|
||||||
|
@ -21,11 +41,14 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Transaction(TransactionID txn, const Base* parent);
|
Transaction(TransactionID txn, const Base* parent);
|
||||||
|
void reset();
|
||||||
|
void closeCursors();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TransactionID txn; /**<\brief Transaction inner handler*/
|
TransactionID txn; /**<\brief Transaction inner handler*/
|
||||||
bool active; /**<\brief Transaction state*/
|
bool active; /**<\brief Transaction state*/
|
||||||
const Base* parent; /**<\brief Pointer to the database this transaction belongs to*/
|
const Base* parent; /**<\brief Pointer to the database this transaction belongs to*/
|
||||||
|
mutable std::map<uint32_t, CursorCommon*> cursors; /**<\brief a collection of cursors curently opened under this transaction*/
|
||||||
};
|
};
|
||||||
|
|
||||||
class WriteTransaction : public Transaction {
|
class WriteTransaction : public Transaction {
|
||||||
|
@ -35,6 +58,7 @@ public:
|
||||||
explicit WriteTransaction(WriteTransaction&& other);
|
explicit WriteTransaction(WriteTransaction&& other);
|
||||||
WriteTransaction(const WriteTransaction& other) = delete;
|
WriteTransaction(const WriteTransaction& other) = delete;
|
||||||
WriteTransaction& operator = (const WriteTransaction& other) = delete;
|
WriteTransaction& operator = (const WriteTransaction& other) = delete;
|
||||||
|
WriteTransaction& operator = (WriteTransaction&& other);
|
||||||
|
|
||||||
void commit();
|
void commit();
|
||||||
void abort();
|
void abort();
|
||||||
|
|
|
@ -20,7 +20,7 @@ target_include_directories(runUnitTests PRIVATE ${Qt${QT_VERSION_MAJOR}Core_INCL
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
runUnitTests
|
runUnitTests
|
||||||
GTest::gtest_main
|
GTest::gtest_main
|
||||||
${PROJECT_NAME}
|
${LMDBAL_NAME}
|
||||||
Qt${QT_VERSION_MAJOR}::Core
|
Qt${QT_VERSION_MAJOR}::Core
|
||||||
)
|
)
|
||||||
include(GoogleTest)
|
include(GoogleTest)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
@ -34,13 +35,14 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TearDownTestSuite() {
|
static void TearDownTestSuite() {
|
||||||
db->close();
|
session.close();
|
||||||
db->removeDirectory();
|
db->removeDirectory();
|
||||||
delete db;
|
delete db;
|
||||||
db = nullptr;
|
db = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static LMDBAL::Base* db;
|
static LMDBAL::Base* db;
|
||||||
|
static LMDBAL::Session session;
|
||||||
|
|
||||||
LMDBAL::Storage<uint32_t, uint32_t>* t1;
|
LMDBAL::Storage<uint32_t, uint32_t>* t1;
|
||||||
LMDBAL::Storage<QString, QString>* t2;
|
LMDBAL::Storage<QString, QString>* t2;
|
||||||
|
@ -50,19 +52,20 @@ protected:
|
||||||
|
|
||||||
|
|
||||||
LMDBAL::Base* BaseTest::db = nullptr;
|
LMDBAL::Base* BaseTest::db = nullptr;
|
||||||
|
LMDBAL::Session BaseTest::session;
|
||||||
|
|
||||||
TEST_F(BaseTest, RemovingDirectory) {
|
TEST_F(BaseTest, RemovingDirectory) {
|
||||||
EXPECT_EQ(db->removeDirectory(), true);
|
EXPECT_EQ(db->removeDirectory(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BaseTest, OpeningClosingDatabase) {
|
TEST_F(BaseTest, OpeningClosingDatabase) {
|
||||||
EXPECT_EQ(db->ready(), false);
|
EXPECT_EQ(db->opened(), false);
|
||||||
db->open();
|
session = db->open();
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
db->close();
|
session.close();
|
||||||
EXPECT_EQ(db->ready(), false);
|
EXPECT_EQ(db->opened(), false);
|
||||||
db->open();
|
session = db->open();
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BaseTest, Flags) {
|
TEST_F(BaseTest, Flags) {
|
||||||
|
@ -93,7 +96,7 @@ TEST_F(BaseTest, Flags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BaseTest, AddingIntegerKey) {
|
TEST_F(BaseTest, AddingIntegerKey) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
t1->addRecord(1, 2);
|
t1->addRecord(1, 2);
|
||||||
t1->addRecord(2, 2);
|
t1->addRecord(2, 2);
|
||||||
t1->addRecord(3, 15);
|
t1->addRecord(3, 15);
|
||||||
|
@ -101,7 +104,7 @@ TEST_F(BaseTest, AddingIntegerKey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BaseTest, AddingQStringKey) {
|
TEST_F(BaseTest, AddingQStringKey) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
t2->addRecord("hello", "world");
|
t2->addRecord("hello", "world");
|
||||||
t2->addRecord("aaa", "gagdfsdf");
|
t2->addRecord("aaa", "gagdfsdf");
|
||||||
t2->addRecord("sdfhga", "DSFFDG");
|
t2->addRecord("sdfhga", "DSFFDG");
|
||||||
|
@ -110,17 +113,16 @@ TEST_F(BaseTest, AddingQStringKey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BaseTest, AddingKeysToCache) {
|
TEST_F(BaseTest, AddingKeysToCache) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
c1->addRecord(2, "blah balah");
|
c1->addRecord(2, "blah balah");
|
||||||
c1->addRecord(-4, "testing goes brrr");
|
c1->addRecord(-4, "testing goes brrr");
|
||||||
c1->addRecord(140, "whatever");
|
c1->addRecord(40, "whatever");
|
||||||
c1->addRecord(-37, "aaaaa tss tsss tsss tsss aaaaaaa");
|
c1->addRecord(-37, "aaaaa tss tsss tsss tsss aaaaaaa");
|
||||||
EXPECT_EQ(c1->getRecord(140), "whatever");
|
EXPECT_EQ(c1->getRecord(40), "whatever");
|
||||||
EXPECT_EQ(c1->getRecord(-116), "whatever");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BaseTest, AddingKeysToVariableCache) {
|
TEST_F(BaseTest, AddingKeysToVariableCache) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
c2->addRecord("regrets", "blah balah");
|
c2->addRecord("regrets", "blah balah");
|
||||||
c2->addRecord("fossil fingers", 842);
|
c2->addRecord("fossil fingers", 842);
|
||||||
c2->addRecord("preloaded cut", 539.75);
|
c2->addRecord("preloaded cut", 539.75);
|
||||||
|
@ -133,7 +135,7 @@ TEST_F(BaseTest, AddingKeysToVariableCache) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BaseTest, AddingRepeatingKey) {
|
TEST_F(BaseTest, AddingRepeatingKey) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
EXPECT_THROW(t1->addRecord(3, 24), LMDBAL::Exist);
|
EXPECT_THROW(t1->addRecord(3, 24), LMDBAL::Exist);
|
||||||
EXPECT_EQ(t1->getRecord(3), 15);
|
EXPECT_EQ(t1->getRecord(3), 15);
|
||||||
|
@ -149,7 +151,7 @@ TEST_F(BaseTest, AddingRepeatingKey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BaseTest, GettingNotExistingKeys) {
|
TEST_F(BaseTest, GettingNotExistingKeys) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
EXPECT_THROW(t2->getRecord("almonds"), LMDBAL::NotFound);
|
EXPECT_THROW(t2->getRecord("almonds"), LMDBAL::NotFound);
|
||||||
EXPECT_THROW(t1->getRecord(64), LMDBAL::NotFound);
|
EXPECT_THROW(t1->getRecord(64), LMDBAL::NotFound);
|
||||||
|
@ -158,21 +160,21 @@ TEST_F(BaseTest, GettingNotExistingKeys) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BaseTest, Persistence) {
|
TEST_F(BaseTest, Persistence) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
uint32_t t1Size = t1->count();
|
uint32_t t1Size = t1->count();
|
||||||
uint32_t t2Size = t2->count();
|
uint32_t t2Size = t2->count();
|
||||||
uint32_t c1Size = c1->count();
|
uint32_t c1Size = c1->count();
|
||||||
uint32_t c2Size = c2->count();
|
uint32_t c2Size = c2->count();
|
||||||
|
|
||||||
db->close();
|
|
||||||
delete db;
|
delete db;
|
||||||
|
EXPECT_EQ(session.opened(), false);
|
||||||
|
|
||||||
db = new LMDBAL::Base("testBase");
|
db = new LMDBAL::Base("testBase");
|
||||||
t1 = db->addStorage<uint32_t, uint32_t>("table1");
|
t1 = db->addStorage<uint32_t, uint32_t>("table1");
|
||||||
t2 = db->addStorage<QString, QString>("table2");
|
t2 = db->addStorage<QString, QString>("table2");
|
||||||
c1 = db->addCache<int8_t, std::string>("cache1");
|
c1 = db->addCache<int8_t, std::string>("cache1");
|
||||||
c2 = db->addCache<std::string, QVariant>("cache2");
|
c2 = db->addCache<std::string, QVariant>("cache2");
|
||||||
db->open();
|
session = db->open();
|
||||||
|
|
||||||
EXPECT_EQ(t1->count(), t1Size);
|
EXPECT_EQ(t1->count(), t1Size);
|
||||||
EXPECT_EQ(t1->getRecord(3), 15);
|
EXPECT_EQ(t1->getRecord(3), 15);
|
||||||
|
@ -189,8 +191,8 @@ TEST_F(BaseTest, Persistence) {
|
||||||
EXPECT_EQ(t2->count(), t2Size);
|
EXPECT_EQ(t2->count(), t2Size);
|
||||||
|
|
||||||
EXPECT_EQ(c1->count(), c1Size);
|
EXPECT_EQ(c1->count(), c1Size);
|
||||||
EXPECT_EQ(c1->checkRecord(-116), true);
|
EXPECT_EQ(c1->checkRecord(40), true);
|
||||||
EXPECT_EQ(c1->getRecord(-116), "whatever");
|
EXPECT_EQ(c1->getRecord(40), "whatever");
|
||||||
EXPECT_EQ(c1->checkRecord(-4), true);
|
EXPECT_EQ(c1->checkRecord(-4), true);
|
||||||
EXPECT_EQ(c1->getRecord(-4), "testing goes brrr");
|
EXPECT_EQ(c1->getRecord(-4), "testing goes brrr");
|
||||||
EXPECT_EQ(c1->getRecord(-4), "testing goes brrr");
|
EXPECT_EQ(c1->getRecord(-4), "testing goes brrr");
|
||||||
|
@ -213,7 +215,7 @@ TEST_F(BaseTest, Persistence) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BaseTest, CountAndDrop) {
|
TEST_F(BaseTest, CountAndDrop) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
EXPECT_EQ(t1->count(), 3);
|
EXPECT_EQ(t1->count(), 3);
|
||||||
EXPECT_EQ(t2->count(), 4);
|
EXPECT_EQ(t2->count(), 4);
|
||||||
EXPECT_EQ(c1->count(), 4);
|
EXPECT_EQ(c1->count(), 4);
|
||||||
|
@ -238,7 +240,7 @@ TEST_F(BaseTest, CountAndDrop) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BaseTest, Change) {
|
TEST_F(BaseTest, Change) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
EXPECT_EQ(t1->count(), 1);
|
EXPECT_EQ(t1->count(), 1);
|
||||||
EXPECT_EQ(t2->count(), 1);
|
EXPECT_EQ(t2->count(), 1);
|
||||||
EXPECT_EQ(c1->count(), 2);
|
EXPECT_EQ(c1->count(), 2);
|
||||||
|
@ -284,7 +286,7 @@ TEST_F(BaseTest, Change) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BaseTest, Force) {
|
TEST_F(BaseTest, Force) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
EXPECT_EQ(t1->forceRecord(58, 35), false); //changing
|
EXPECT_EQ(t1->forceRecord(58, 35), false); //changing
|
||||||
EXPECT_EQ(t1->forceRecord(68, 36), true); //adding
|
EXPECT_EQ(t1->forceRecord(68, 36), true); //adding
|
||||||
|
@ -319,7 +321,7 @@ TEST_F(BaseTest, Force) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BaseTest, ReadAll) {
|
TEST_F(BaseTest, ReadAll) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
std::map<uint32_t, uint32_t> m1 = t1->readAll();
|
std::map<uint32_t, uint32_t> m1 = t1->readAll();
|
||||||
std::map<QString, QString> m2 = t2->readAll();
|
std::map<QString, QString> m2 = t2->readAll();
|
||||||
|
@ -350,7 +352,7 @@ TEST_F(BaseTest, ReadAll) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BaseTest, ReplaceAll) {
|
TEST_F(BaseTest, ReplaceAll) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
t1->replaceAll({
|
t1->replaceAll({
|
||||||
{7, 48},
|
{7, 48},
|
||||||
|
@ -418,7 +420,7 @@ TEST_F(BaseTest, ReplaceAll) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BaseTest, AddRecords) {
|
TEST_F(BaseTest, AddRecords) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
LMDBAL::SizeType s1 = t1->addRecords({
|
LMDBAL::SizeType s1 = t1->addRecords({
|
||||||
{5, 3},
|
{5, 3},
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "cursor.h"
|
#include "cursor.h"
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
class CacheCursorTest : public ::testing::Test {
|
class CacheCursorTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
|
@ -19,7 +20,7 @@ protected:
|
||||||
db = new LMDBAL::Base("testBase");
|
db = new LMDBAL::Base("testBase");
|
||||||
db->addCache<uint64_t, std::string>("table1");
|
db->addCache<uint64_t, std::string>("table1");
|
||||||
db->addCache<uint64_t, std::string>("empty");
|
db->addCache<uint64_t, std::string>("empty");
|
||||||
db->open();
|
session = db->open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +31,7 @@ protected:
|
||||||
static void TearDownTestSuite() {
|
static void TearDownTestSuite() {
|
||||||
cursor.drop();
|
cursor.drop();
|
||||||
transaction.terminate();
|
transaction.terminate();
|
||||||
db->close();
|
session.close();
|
||||||
db->removeDirectory();
|
db->removeDirectory();
|
||||||
delete db;
|
delete db;
|
||||||
db = nullptr;
|
db = nullptr;
|
||||||
|
@ -39,6 +40,7 @@ protected:
|
||||||
static LMDBAL::Base* db;
|
static LMDBAL::Base* db;
|
||||||
static LMDBAL::Cursor<uint64_t, std::string> cursor;
|
static LMDBAL::Cursor<uint64_t, std::string> cursor;
|
||||||
static LMDBAL::Transaction transaction;
|
static LMDBAL::Transaction transaction;
|
||||||
|
static LMDBAL::Session session;
|
||||||
|
|
||||||
LMDBAL::Cache<uint64_t, std::string>* cache;
|
LMDBAL::Cache<uint64_t, std::string>* cache;
|
||||||
LMDBAL::Cache<uint64_t, std::string>* emptyCache;
|
LMDBAL::Cache<uint64_t, std::string>* emptyCache;
|
||||||
|
@ -47,6 +49,7 @@ protected:
|
||||||
LMDBAL::Base* CacheCursorTest::db = nullptr;
|
LMDBAL::Base* CacheCursorTest::db = nullptr;
|
||||||
LMDBAL::Cursor<uint64_t, std::string> CacheCursorTest::cursor;
|
LMDBAL::Cursor<uint64_t, std::string> CacheCursorTest::cursor;
|
||||||
LMDBAL::Transaction CacheCursorTest::transaction;
|
LMDBAL::Transaction CacheCursorTest::transaction;
|
||||||
|
LMDBAL::Session CacheCursorTest::session;
|
||||||
|
|
||||||
static const std::map<uint64_t, std::string> data({
|
static const std::map<uint64_t, std::string> data({
|
||||||
{245665783, "bothering nerds"},
|
{245665783, "bothering nerds"},
|
||||||
|
@ -402,7 +405,7 @@ TEST_F(CacheCursorTest, CursorRAIIBehaviour) {
|
||||||
|
|
||||||
TEST_F(CacheCursorTest, CornerCases) {
|
TEST_F(CacheCursorTest, CornerCases) {
|
||||||
transaction.terminate();
|
transaction.terminate();
|
||||||
EXPECT_THROW(cursor.current(), LMDBAL::Unknown);
|
EXPECT_THROW(cursor.current(), LMDBAL::CursorNotReady);
|
||||||
cursor.close();
|
cursor.close();
|
||||||
|
|
||||||
LMDBAL::Cursor<uint64_t, std::string> emptyCursor = emptyCache->createCursor();
|
LMDBAL::Cursor<uint64_t, std::string> emptyCursor = emptyCache->createCursor();
|
||||||
|
@ -436,5 +439,40 @@ TEST_F(CacheCursorTest, CornerCases) {
|
||||||
EXPECT_EQ(element.first, reference->first);
|
EXPECT_EQ(element.first, reference->first);
|
||||||
EXPECT_EQ(element.second, reference->second);
|
EXPECT_EQ(element.second, reference->second);
|
||||||
EXPECT_THROW(cursor.prev(), LMDBAL::NotFound);
|
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());
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
class CacheTransactionsTest : public testing::Test {
|
class CacheTransactionsTest : public testing::Test {
|
||||||
protected:
|
protected:
|
||||||
|
@ -39,18 +40,19 @@ protected:
|
||||||
db->addStorage<std::string, float>("cache2");
|
db->addStorage<std::string, float>("cache2");
|
||||||
}
|
}
|
||||||
|
|
||||||
db->open();
|
session = db->open();
|
||||||
db->drop();
|
db->drop();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TearDownTestSuite() {
|
static void TearDownTestSuite() {
|
||||||
db->close();
|
session.close();
|
||||||
db->removeDirectory();
|
db->removeDirectory();
|
||||||
delete db;
|
delete db;
|
||||||
db = nullptr;
|
db = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static LMDBAL::Base* db;
|
static LMDBAL::Base* db;
|
||||||
|
static LMDBAL::Session session;
|
||||||
|
|
||||||
LMDBAL::Cache<int16_t, int64_t>* c1;
|
LMDBAL::Cache<int16_t, int64_t>* c1;
|
||||||
LMDBAL::Cache<std::string, float>* c2;
|
LMDBAL::Cache<std::string, float>* c2;
|
||||||
|
@ -58,9 +60,10 @@ protected:
|
||||||
|
|
||||||
|
|
||||||
LMDBAL::Base* CacheTransactionsTest::db = nullptr;
|
LMDBAL::Base* CacheTransactionsTest::db = nullptr;
|
||||||
|
LMDBAL::Session CacheTransactionsTest::session;
|
||||||
|
|
||||||
TEST_F(CacheTransactionsTest, Adding) {
|
TEST_F(CacheTransactionsTest, Adding) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
EXPECT_EQ(c1->count(), 0);
|
EXPECT_EQ(c1->count(), 0);
|
||||||
EXPECT_EQ(c2->count(), 0);
|
EXPECT_EQ(c2->count(), 0);
|
||||||
|
|
||||||
|
@ -90,7 +93,7 @@ TEST_F(CacheTransactionsTest, Adding) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CacheTransactionsTest, Aborting) {
|
TEST_F(CacheTransactionsTest, Aborting) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
LMDBAL::SizeType s1 = c1->count();
|
LMDBAL::SizeType s1 = c1->count();
|
||||||
LMDBAL::SizeType s2 = c2->count();
|
LMDBAL::SizeType s2 = c2->count();
|
||||||
|
@ -114,7 +117,7 @@ TEST_F(CacheTransactionsTest, Aborting) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CacheTransactionsTest, Reading) {
|
TEST_F(CacheTransactionsTest, Reading) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
LMDBAL::Transaction txn = db->beginReadOnlyTransaction();
|
LMDBAL::Transaction txn = db->beginReadOnlyTransaction();
|
||||||
|
|
||||||
|
@ -132,7 +135,7 @@ TEST_F(CacheTransactionsTest, Reading) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CacheTransactionsTest, ConcurentReading) {
|
TEST_F(CacheTransactionsTest, ConcurentReading) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
LMDBAL::SizeType size = c1->count();
|
LMDBAL::SizeType size = c1->count();
|
||||||
LMDBAL::WriteTransaction txn = db->beginTransaction();
|
LMDBAL::WriteTransaction txn = db->beginTransaction();
|
||||||
|
@ -168,7 +171,7 @@ TEST_F(CacheTransactionsTest, ConcurentReading) {
|
||||||
|
|
||||||
|
|
||||||
TEST_F(CacheTransactionsTest, ConcurentModification) {
|
TEST_F(CacheTransactionsTest, ConcurentModification) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
//if you start one writable transaction after another
|
//if you start one writable transaction after another
|
||||||
//in a single thread like so:
|
//in a single thread like so:
|
||||||
|
@ -182,7 +185,7 @@ TEST_F(CacheTransactionsTest, ConcurentModification) {
|
||||||
|
|
||||||
int pid = fork();
|
int pid = fork();
|
||||||
if (pid == 0) { // I am the child
|
if (pid == 0) { // I am the child
|
||||||
usleep(1);
|
usleep(5);
|
||||||
std::cout << "beggining second transaction" << std::endl;
|
std::cout << "beggining second transaction" << std::endl;
|
||||||
LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause
|
LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause
|
||||||
//and wait for the first transaction to get finished
|
//and wait for the first transaction to get finished
|
||||||
|
@ -208,7 +211,7 @@ TEST_F(CacheTransactionsTest, ConcurentModification) {
|
||||||
LMDBAL::WriteTransaction txn1 = db->beginTransaction();
|
LMDBAL::WriteTransaction txn1 = db->beginTransaction();
|
||||||
|
|
||||||
std::cout << "putting parent thread to sleep for 5 ms" << std::endl;
|
std::cout << "putting parent thread to sleep for 5 ms" << std::endl;
|
||||||
usleep(5);
|
usleep(10);
|
||||||
|
|
||||||
std::cout << "adding first transaction value" << std::endl;
|
std::cout << "adding first transaction value" << std::endl;
|
||||||
c1->addRecord(5, 812, txn1);
|
c1->addRecord(5, 812, txn1);
|
||||||
|
@ -231,11 +234,11 @@ TEST_F(CacheTransactionsTest, ConcurentModification) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CacheTransactionsTest, RAIIResourceFree) {
|
TEST_F(CacheTransactionsTest, RAIIResourceFree) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
int pid = fork();
|
int pid = fork();
|
||||||
if (pid == 0) { // I am the child
|
if (pid == 0) { // I am the child
|
||||||
usleep(1);
|
usleep(5);
|
||||||
std::cout << "beggining child transaction" << std::endl;
|
std::cout << "beggining child transaction" << std::endl;
|
||||||
LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause
|
LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause
|
||||||
//and wait for the first transaction to get finished
|
//and wait for the first transaction to get finished
|
||||||
|
@ -256,7 +259,7 @@ TEST_F(CacheTransactionsTest, RAIIResourceFree) {
|
||||||
LMDBAL::WriteTransaction txn1 = db->beginTransaction();
|
LMDBAL::WriteTransaction txn1 = db->beginTransaction();
|
||||||
|
|
||||||
std::cout << "putting parent thread to sleep for 5 ms" << std::endl;
|
std::cout << "putting parent thread to sleep for 5 ms" << std::endl;
|
||||||
usleep(5);
|
usleep(10);
|
||||||
std::cout << "parent thread woke up" << std::endl;
|
std::cout << "parent thread woke up" << std::endl;
|
||||||
|
|
||||||
std::cout << "adding value from parent thread" << std::endl;
|
std::cout << "adding value from parent thread" << std::endl;
|
||||||
|
@ -279,3 +282,26 @@ TEST_F(CacheTransactionsTest, RAIIResourceFree) {
|
||||||
EXPECT_EQ(c1->getRecord(221), 14);
|
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);
|
||||||
|
|
||||||
|
session.close();
|
||||||
|
session = 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);
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "cursor.h"
|
#include "cursor.h"
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
class DuplicatesTest : public ::testing::Test {
|
class DuplicatesTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
|
@ -35,18 +36,19 @@ protected:
|
||||||
db->addStorage<uint16_t, double>("intDouble", true);
|
db->addStorage<uint16_t, double>("intDouble", true);
|
||||||
db->addStorage<float, int64_t>("floatLong", true);
|
db->addStorage<float, int64_t>("floatLong", true);
|
||||||
|
|
||||||
db->open();
|
session = db->open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TearDownTestSuite() {
|
static void TearDownTestSuite() {
|
||||||
db->close();
|
session.close();
|
||||||
db->removeDirectory();
|
db->removeDirectory();
|
||||||
delete db;
|
delete db;
|
||||||
db = nullptr;
|
db = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static LMDBAL::Base* db;
|
static LMDBAL::Base* db;
|
||||||
|
static LMDBAL::Session session;
|
||||||
|
|
||||||
LMDBAL::Storage<int16_t, uint16_t>* tu1;
|
LMDBAL::Storage<int16_t, uint16_t>* tu1;
|
||||||
LMDBAL::Storage<std::string, int8_t>* tu2;
|
LMDBAL::Storage<std::string, int8_t>* tu2;
|
||||||
|
@ -56,6 +58,7 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
LMDBAL::Base* DuplicatesTest::db = nullptr;
|
LMDBAL::Base* DuplicatesTest::db = nullptr;
|
||||||
|
LMDBAL::Session DuplicatesTest::session;
|
||||||
|
|
||||||
TEST_F(DuplicatesTest, Flags) {
|
TEST_F(DuplicatesTest, Flags) {
|
||||||
uint32_t tu1Flags = getTU1Flags();
|
uint32_t tu1Flags = getTU1Flags();
|
||||||
|
@ -130,7 +133,10 @@ TEST_F(DuplicatesTest, Adding) {
|
||||||
tu3->addRecord(7.20001, 4.00000001); //not sure how exactly, but it works
|
tu3->addRecord(7.20001, 4.00000001); //not sure how exactly, but it works
|
||||||
|
|
||||||
EXPECT_EQ(tu3->count(), 7);
|
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);
|
float tu3dd = tu3->getRecord(5119);
|
||||||
EXPECT_TRUE(tu3ds == tu3dd);
|
EXPECT_TRUE(tu3ds == tu3dd);
|
||||||
EXPECT_EQ(tu3ds, tu3dd);
|
EXPECT_EQ(tu3ds, tu3dd);
|
||||||
|
@ -144,8 +150,11 @@ TEST_F(DuplicatesTest, Adding) {
|
||||||
EXPECT_THROW(tu4->addRecord(327, 79.624923), LMDBAL::Exist);
|
EXPECT_THROW(tu4->addRecord(327, 79.624923), LMDBAL::Exist);
|
||||||
|
|
||||||
EXPECT_EQ(tu4->count(), 4);
|
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);
|
tu5->addRecord(-84.7, 45656753);
|
||||||
EXPECT_THROW(tu5->addRecord(-84.7, 45656753), LMDBAL::Exist);
|
EXPECT_THROW(tu5->addRecord(-84.7, 45656753), LMDBAL::Exist);
|
||||||
|
@ -174,13 +183,19 @@ TEST_F(DuplicatesTest, Forcing) {
|
||||||
tu1->addRecord(-56, 71);
|
tu1->addRecord(-56, 71);
|
||||||
tu1->addRecord(-56, 274);
|
tu1->addRecord(-56, 274);
|
||||||
tu1->addRecord(-56, 732);
|
tu1->addRecord(-56, 732);
|
||||||
|
|
||||||
|
std::set<uint16_t> res56({71, 274, 732});
|
||||||
EXPECT_EQ(tu1->count(), tu1Size += 3);
|
EXPECT_EQ(tu1->count(), tu1Size += 3);
|
||||||
EXPECT_TRUE(tu1->forceRecord(-56, 322));
|
EXPECT_TRUE(tu1->forceRecord(-56, 322));
|
||||||
|
res56.insert(322);
|
||||||
EXPECT_EQ(tu1->count(), tu1Size += 1);
|
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_TRUE(tu1->forceRecord(-56, 14));
|
||||||
EXPECT_EQ(tu1->count(), tu1Size += 1);
|
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_FALSE(tu1->forceRecord(-56, 274));
|
||||||
EXPECT_EQ(tu1->count(), tu1Size);
|
EXPECT_EQ(tu1->count(), tu1Size);
|
||||||
|
|
||||||
|
@ -201,26 +216,38 @@ TEST_F(DuplicatesTest, Forcing) {
|
||||||
tu3->addRecord(17.3, 93.21);
|
tu3->addRecord(17.3, 93.21);
|
||||||
tu3->addRecord(17.3, 6.6);
|
tu3->addRecord(17.3, 6.6);
|
||||||
tu3->addRecord(17.3, 105.1);
|
tu3->addRecord(17.3, 105.1);
|
||||||
|
std::set<float> res17({93.21, 6.6, 105.1});
|
||||||
|
|
||||||
EXPECT_EQ(tu3->count(), tu3Size += 3);
|
EXPECT_EQ(tu3->count(), tu3Size += 3);
|
||||||
EXPECT_TRUE(tu3->forceRecord(17.3, 74.9));
|
EXPECT_TRUE(tu3->forceRecord(17.3, 74.9));
|
||||||
|
res17.insert(74.9);
|
||||||
EXPECT_EQ(tu3->count(), tu3Size += 1);
|
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_EQ(res17.count(tu3->getRecord(17.3)), 1);
|
||||||
EXPECT_TRUE(tu3->forceRecord(17.3, 5.1)); //unless he wishes for kinda randomish result
|
|
||||||
|
EXPECT_TRUE(tu3->forceRecord(17.3, 5.1));
|
||||||
|
res17.insert(5.1);
|
||||||
EXPECT_EQ(tu3->count(), tu3Size += 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_FALSE(tu3->forceRecord(17.3, 93.21));
|
||||||
EXPECT_EQ(tu3->count(), tu3Size);
|
EXPECT_EQ(tu3->count(), tu3Size);
|
||||||
|
|
||||||
LMDBAL::SizeType tu4Size = tu4->count();
|
LMDBAL::SizeType tu4Size = tu4->count();
|
||||||
tu4->addRecord(84, -359.109);
|
tu4->addRecord(84, -359.109);
|
||||||
tu4->addRecord(84, 2879.654);
|
tu4->addRecord(84, 2879.654);
|
||||||
|
std::set<double> res84({-359.109, 2879.654});
|
||||||
|
|
||||||
EXPECT_EQ(tu4->count(), tu4Size += 2);
|
EXPECT_EQ(tu4->count(), tu4Size += 2);
|
||||||
EXPECT_TRUE(tu4->forceRecord(84, 72.9));
|
EXPECT_TRUE(tu4->forceRecord(84, 72.9));
|
||||||
|
res84.insert(72.9);
|
||||||
EXPECT_EQ(tu4->count(), tu4Size += 1);
|
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));
|
EXPECT_TRUE(tu4->forceRecord(84, 2679.5));
|
||||||
|
res84.insert(2679.5);
|
||||||
EXPECT_EQ(tu4->count(), tu4Size += 1);
|
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_FALSE(tu4->forceRecord(84, -359.109));
|
||||||
EXPECT_EQ(tu4->count(), tu4Size);
|
EXPECT_EQ(tu4->count(), tu4Size);
|
||||||
|
|
||||||
|
@ -286,6 +313,7 @@ TEST_F(DuplicatesTest, Changing) {
|
||||||
EXPECT_THROW(tu2->changeRecord("jeremy spins", -7), LMDBAL::Exist);
|
EXPECT_THROW(tu2->changeRecord("jeremy spins", -7), LMDBAL::Exist);
|
||||||
|
|
||||||
LMDBAL::SizeType tu3Size = tu3->count();
|
LMDBAL::SizeType tu3Size = tu3->count();
|
||||||
|
std::set<float> res26;
|
||||||
EXPECT_THROW(tu3->changeRecord(26.7, 68.22), LMDBAL::NotFound);
|
EXPECT_THROW(tu3->changeRecord(26.7, 68.22), LMDBAL::NotFound);
|
||||||
EXPECT_EQ(tu3->count(), tu3Size);
|
EXPECT_EQ(tu3->count(), tu3Size);
|
||||||
tu3->addRecord(26.7, 68.22);
|
tu3->addRecord(26.7, 68.22);
|
||||||
|
@ -294,11 +322,13 @@ TEST_F(DuplicatesTest, Changing) {
|
||||||
tu3->changeRecord(26.7, 68.22); //should just do nothing usefull, but work normally
|
tu3->changeRecord(26.7, 68.22); //should just do nothing usefull, but work normally
|
||||||
EXPECT_EQ(tu3->getRecord(26.7), 68.22f);
|
EXPECT_EQ(tu3->getRecord(26.7), 68.22f);
|
||||||
tu3->changeRecord(26.7, 23.18);
|
tu3->changeRecord(26.7, 23.18);
|
||||||
|
res26.insert(23.18);
|
||||||
EXPECT_EQ(tu3->count(), tu3Size);
|
EXPECT_EQ(tu3->count(), tu3Size);
|
||||||
EXPECT_EQ(tu3->getRecord(26.7), 23.18f);
|
EXPECT_EQ(tu3->getRecord(26.7), 23.18f);
|
||||||
tu3->addRecord(26.7, 22.16);
|
tu3->addRecord(26.7, 22.16);
|
||||||
|
res26.insert(22.16);
|
||||||
EXPECT_EQ(tu3->count(), tu3Size += 1);
|
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);
|
tu3->changeRecord(26.7, 21.7);
|
||||||
EXPECT_EQ(tu3->count(), tu3Size);
|
EXPECT_EQ(tu3->count(), tu3Size);
|
||||||
EXPECT_EQ(tu3->getRecord(26.7), 21.7f);
|
EXPECT_EQ(tu3->getRecord(26.7), 21.7f);
|
||||||
|
@ -316,17 +346,21 @@ TEST_F(DuplicatesTest, Changing) {
|
||||||
tu4->changeRecord(852, 6795.349); //should just do nothing usefull, but work normally
|
tu4->changeRecord(852, 6795.349); //should just do nothing usefull, but work normally
|
||||||
EXPECT_EQ(tu4->getRecord(852), 6795.349);
|
EXPECT_EQ(tu4->getRecord(852), 6795.349);
|
||||||
tu4->changeRecord(852, 13.54);
|
tu4->changeRecord(852, 13.54);
|
||||||
|
std::set<double> res852({13.54});
|
||||||
EXPECT_EQ(tu4->count(), tu4Size);
|
EXPECT_EQ(tu4->count(), tu4Size);
|
||||||
EXPECT_EQ(tu4->getRecord(852), 13.54);
|
EXPECT_EQ(tu4->getRecord(852), 13.54);
|
||||||
tu4->addRecord(852, 213.85);
|
tu4->addRecord(852, 213.85);
|
||||||
|
res852.insert(213.85);
|
||||||
EXPECT_EQ(tu4->count(), tu4Size += 1);
|
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);
|
tu4->changeRecord(852, 236.21);
|
||||||
|
res852.insert(236.21);
|
||||||
EXPECT_EQ(tu4->count(), tu4Size);
|
EXPECT_EQ(tu4->count(), tu4Size);
|
||||||
EXPECT_EQ(tu4->getRecord(852), 236.21);
|
EXPECT_EQ(tu4->getRecord(852), 236.21);
|
||||||
tu4->changeRecord(852, 46324.1135);
|
tu4->changeRecord(852, 46324.1135);
|
||||||
|
res852.insert(46324.1135);
|
||||||
EXPECT_EQ(tu4->count(), tu4Size);
|
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);
|
EXPECT_THROW(tu4->changeRecord(852, 46324.1135), LMDBAL::Exist);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "cursor.h"
|
#include "cursor.h"
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
class StorageCursorTest : public ::testing::Test {
|
class StorageCursorTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
|
@ -18,7 +19,7 @@ protected:
|
||||||
db = new LMDBAL::Base("testBase");
|
db = new LMDBAL::Base("testBase");
|
||||||
db->addStorage<uint64_t, std::string>("table1");
|
db->addStorage<uint64_t, std::string>("table1");
|
||||||
db->addStorage<uint64_t, std::string>("empty");
|
db->addStorage<uint64_t, std::string>("empty");
|
||||||
db->open();
|
session = db->open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +30,7 @@ protected:
|
||||||
static void TearDownTestSuite() {
|
static void TearDownTestSuite() {
|
||||||
cursor.drop();
|
cursor.drop();
|
||||||
transaction.terminate();
|
transaction.terminate();
|
||||||
db->close();
|
session.close();
|
||||||
db->removeDirectory();
|
db->removeDirectory();
|
||||||
delete db;
|
delete db;
|
||||||
db = nullptr;
|
db = nullptr;
|
||||||
|
@ -38,6 +39,7 @@ protected:
|
||||||
static LMDBAL::Base* db;
|
static LMDBAL::Base* db;
|
||||||
static LMDBAL::Cursor<uint64_t, std::string> cursor;
|
static LMDBAL::Cursor<uint64_t, std::string> cursor;
|
||||||
static LMDBAL::Transaction transaction;
|
static LMDBAL::Transaction transaction;
|
||||||
|
static LMDBAL::Session session;
|
||||||
|
|
||||||
LMDBAL::Storage<uint64_t, std::string>* table;
|
LMDBAL::Storage<uint64_t, std::string>* table;
|
||||||
LMDBAL::Storage<uint64_t, std::string>* emptyTable;
|
LMDBAL::Storage<uint64_t, std::string>* emptyTable;
|
||||||
|
@ -46,6 +48,7 @@ protected:
|
||||||
LMDBAL::Base* StorageCursorTest::db = nullptr;
|
LMDBAL::Base* StorageCursorTest::db = nullptr;
|
||||||
LMDBAL::Cursor<uint64_t, std::string> StorageCursorTest::cursor;
|
LMDBAL::Cursor<uint64_t, std::string> StorageCursorTest::cursor;
|
||||||
LMDBAL::Transaction StorageCursorTest::transaction = LMDBAL::Transaction();
|
LMDBAL::Transaction StorageCursorTest::transaction = LMDBAL::Transaction();
|
||||||
|
LMDBAL::Session StorageCursorTest::session = LMDBAL::Session();
|
||||||
|
|
||||||
static const std::map<uint64_t, std::string> data({
|
static const std::map<uint64_t, std::string> data({
|
||||||
{245665783, "bothering nerds"},
|
{245665783, "bothering nerds"},
|
||||||
|
@ -380,7 +383,7 @@ TEST_F(StorageCursorTest, CursorRAIIBehaviour) {
|
||||||
TEST_F(StorageCursorTest, CornerCases) {
|
TEST_F(StorageCursorTest, CornerCases) {
|
||||||
EXPECT_EQ(getTableCursorsSize(), 1);
|
EXPECT_EQ(getTableCursorsSize(), 1);
|
||||||
transaction.terminate();
|
transaction.terminate();
|
||||||
EXPECT_THROW(cursor.current(), LMDBAL::Unknown);
|
EXPECT_THROW(cursor.current(), LMDBAL::CursorNotReady);
|
||||||
cursor.close();
|
cursor.close();
|
||||||
|
|
||||||
LMDBAL::Cursor<uint64_t, std::string> emptyCursor = emptyTable->createCursor();
|
LMDBAL::Cursor<uint64_t, std::string> emptyCursor = emptyTable->createCursor();
|
||||||
|
@ -414,4 +417,40 @@ TEST_F(StorageCursorTest, CornerCases) {
|
||||||
EXPECT_EQ(element.first, reference->first);
|
EXPECT_EQ(element.first, reference->first);
|
||||||
EXPECT_EQ(element.second, reference->second);
|
EXPECT_EQ(element.second, reference->second);
|
||||||
EXPECT_THROW(cursor.prev(), LMDBAL::NotFound);
|
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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
class StorageTransactionsTest : public testing::Test {
|
class StorageTransactionsTest : public testing::Test {
|
||||||
protected:
|
protected:
|
||||||
|
@ -39,18 +40,19 @@ protected:
|
||||||
db->addStorage<std::string, float>("table2");
|
db->addStorage<std::string, float>("table2");
|
||||||
}
|
}
|
||||||
|
|
||||||
db->open();
|
session = db->open();
|
||||||
db->drop();
|
db->drop();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TearDownTestSuite() {
|
static void TearDownTestSuite() {
|
||||||
db->close();
|
session.close();
|
||||||
db->removeDirectory();
|
db->removeDirectory();
|
||||||
delete db;
|
delete db;
|
||||||
db = nullptr;
|
db = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static LMDBAL::Base* db;
|
static LMDBAL::Base* db;
|
||||||
|
static LMDBAL::Session session;
|
||||||
|
|
||||||
LMDBAL::Storage<int16_t, int64_t>* t1;
|
LMDBAL::Storage<int16_t, int64_t>* t1;
|
||||||
LMDBAL::Storage<std::string, float>* t2;
|
LMDBAL::Storage<std::string, float>* t2;
|
||||||
|
@ -58,9 +60,10 @@ protected:
|
||||||
|
|
||||||
|
|
||||||
LMDBAL::Base* StorageTransactionsTest::db = nullptr;
|
LMDBAL::Base* StorageTransactionsTest::db = nullptr;
|
||||||
|
LMDBAL::Session StorageTransactionsTest::session;
|
||||||
|
|
||||||
TEST_F(StorageTransactionsTest, Adding) {
|
TEST_F(StorageTransactionsTest, Adding) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
EXPECT_EQ(t1->count(), 0);
|
EXPECT_EQ(t1->count(), 0);
|
||||||
EXPECT_EQ(t2->count(), 0);
|
EXPECT_EQ(t2->count(), 0);
|
||||||
|
|
||||||
|
@ -90,7 +93,7 @@ TEST_F(StorageTransactionsTest, Adding) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(StorageTransactionsTest, Aborting) {
|
TEST_F(StorageTransactionsTest, Aborting) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
LMDBAL::SizeType s1 = t1->count();
|
LMDBAL::SizeType s1 = t1->count();
|
||||||
LMDBAL::SizeType s2 = t2->count();
|
LMDBAL::SizeType s2 = t2->count();
|
||||||
|
@ -114,7 +117,7 @@ TEST_F(StorageTransactionsTest, Aborting) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(StorageTransactionsTest, Reading) {
|
TEST_F(StorageTransactionsTest, Reading) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
LMDBAL::Transaction txn = db->beginReadOnlyTransaction();
|
LMDBAL::Transaction txn = db->beginReadOnlyTransaction();
|
||||||
|
|
||||||
|
@ -132,7 +135,7 @@ TEST_F(StorageTransactionsTest, Reading) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(StorageTransactionsTest, ConcurentReading) {
|
TEST_F(StorageTransactionsTest, ConcurentReading) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
LMDBAL::SizeType size = t1->count();
|
LMDBAL::SizeType size = t1->count();
|
||||||
LMDBAL::WriteTransaction txn = db->beginTransaction();
|
LMDBAL::WriteTransaction txn = db->beginTransaction();
|
||||||
|
@ -167,7 +170,7 @@ TEST_F(StorageTransactionsTest, ConcurentReading) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(StorageTransactionsTest, ConcurentModification) {
|
TEST_F(StorageTransactionsTest, ConcurentModification) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
//if you start one writable transaction after another
|
//if you start one writable transaction after another
|
||||||
//in a single thread like so:
|
//in a single thread like so:
|
||||||
|
@ -181,7 +184,7 @@ TEST_F(StorageTransactionsTest, ConcurentModification) {
|
||||||
|
|
||||||
int pid = fork();
|
int pid = fork();
|
||||||
if (pid == 0) { // I am the child
|
if (pid == 0) { // I am the child
|
||||||
usleep(1);
|
usleep(5);
|
||||||
std::cout << "beggining second transaction" << std::endl;
|
std::cout << "beggining second transaction" << std::endl;
|
||||||
LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause
|
LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause
|
||||||
//and wait for the first transaction to get finished
|
//and wait for the first transaction to get finished
|
||||||
|
@ -207,7 +210,7 @@ TEST_F(StorageTransactionsTest, ConcurentModification) {
|
||||||
LMDBAL::WriteTransaction txn1 = db->beginTransaction();
|
LMDBAL::WriteTransaction txn1 = db->beginTransaction();
|
||||||
|
|
||||||
std::cout << "putting parent thread to sleep for 5 ms" << std::endl;
|
std::cout << "putting parent thread to sleep for 5 ms" << std::endl;
|
||||||
usleep(5);
|
usleep(10);
|
||||||
|
|
||||||
std::cout << "adding first transaction value" << std::endl;
|
std::cout << "adding first transaction value" << std::endl;
|
||||||
t1->addRecord(5, 812, txn1);
|
t1->addRecord(5, 812, txn1);
|
||||||
|
@ -230,11 +233,11 @@ TEST_F(StorageTransactionsTest, ConcurentModification) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(StorageTransactionsTest, RAIIResourceFree) {
|
TEST_F(StorageTransactionsTest, RAIIResourceFree) {
|
||||||
EXPECT_EQ(db->ready(), true);
|
EXPECT_EQ(db->opened(), true);
|
||||||
|
|
||||||
int pid = fork();
|
int pid = fork();
|
||||||
if (pid == 0) { // I am the child
|
if (pid == 0) { // I am the child
|
||||||
usleep(1);
|
usleep(5);
|
||||||
std::cout << "beggining child transaction" << std::endl;
|
std::cout << "beggining child transaction" << std::endl;
|
||||||
LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause
|
LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause
|
||||||
//and wait for the first transaction to get finished
|
//and wait for the first transaction to get finished
|
||||||
|
@ -255,7 +258,7 @@ TEST_F(StorageTransactionsTest, RAIIResourceFree) {
|
||||||
LMDBAL::WriteTransaction txn1 = db->beginTransaction();
|
LMDBAL::WriteTransaction txn1 = db->beginTransaction();
|
||||||
|
|
||||||
std::cout << "putting parent thread to sleep for 5 ms" << std::endl;
|
std::cout << "putting parent thread to sleep for 5 ms" << std::endl;
|
||||||
usleep(5);
|
usleep(10);
|
||||||
std::cout << "parent thread woke up" << std::endl;
|
std::cout << "parent thread woke up" << std::endl;
|
||||||
|
|
||||||
std::cout << "adding value from parent thread" << std::endl;
|
std::cout << "adding value from parent thread" << std::endl;
|
||||||
|
@ -277,3 +280,27 @@ TEST_F(StorageTransactionsTest, RAIIResourceFree) {
|
||||||
std::cout << "checking the final result" << std::endl;
|
std::cout << "checking the final result" << std::endl;
|
||||||
EXPECT_EQ(t1->getRecord(221), 14);
|
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);
|
||||||
|
|
||||||
|
session.close();
|
||||||
|
session = 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…
Add table
Add a link
Reference in a new issue