diff --git a/.forgejo/workflows/aur.yml b/.forgejo/workflows/aur.yml deleted file mode 100644 index ef1df58..0000000 --- a/.forgejo/workflows/aur.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Release to AUR -on: - workflow_call: - inputs: - package-name: - required: true - type: string - description: - required: true - type: string - depends: - required: true - type: string - flags: - required: true - type: string - workflow_dispatch: - inputs: - package-name: - description: "The name under which the package is going to be packaged to AUR" - type: string - default: "lmdbal" - description: - description: "The description of the package" - type: string - default: "LMDB Abstraction Layer" - depends: - description: "Additional dependencies" - type: string - default: "" - flags: - description: "Additional CMake flags" - type: string - default: "" - -jobs: - aur: - name: Releasing ${{ inputs.package-name }} to AUR - runs-on: archlinux - steps: - - name: Download the release tarball - run: curl -sL ${{ gitea.server_url }}/${{ gitea.repository }}/archive/${{ gitea.event.release.tag_name }}.tar.gz --output tarball.tar.gz - - - name: Calculate SHA256 for the tarball - run: echo "tbSum=$(sha256sum tarball.tar.gz | cut -d ' ' -f 1)" >> $GITHUB_ENV - - - name: Unarchive tarball - run: tar -xvzf tarball.tar.gz - - - name: Clone the AUR repository - run: | - echo "${{ secrets.DEPLOY_TO_AUR_PRIVATE_KEY }}" > key - chmod 600 key - GIT_SSH_COMMAND="ssh -i key -o 'IdentitiesOnly yes' -o 'StrictHostKeyChecking no'" git clone ssh://aur@aur.archlinux.org/${{ inputs.package-name }}.git aur - chmod 777 -R aur - cd aur - git config user.name ${{ secrets.DEPLOY_TO_AUR_USER_NAME }} - git config user.email ${{ secrets.DEPLOY_TO_AUR_EMAIL }} - git checkout -b master || git checkout master - - - name: Copy PKGBUILD to the directory - run: cp lmdbal/packaging/Archlinux/PKGBUILD aur/ - - - name: Patch PKGBUILD file, and generate .SRCINFO - working-directory: aur - run: | - sed -i "/sha256sums=/c\sha256sums=('${{ env.tbSum }}')" PKGBUILD - sed -i "/pkgname=lmdbal/c\pkgname=${{ inputs.package-name }}" PKGBUILD - sed -i "/pkgver=1.0.0/c\pkgver=${{ gitea.event.release.tag_name }}" PKGBUILD - sed -i "/pkgdesc=\"LMDB Abstraction Layer\"/c\pkgdesc=\"${{ inputs.description }}\"" PKGBUILD - sed -i "/depends=( 'lmdb' )/c\depends=( 'lmdb' ${{ inputs.depends }} )" PKGBUILD - sed -i '/cmake . -D/s/$/ ${{ inputs.flags }}/' PKGBUILD - sudo -u build makepkg --printsrcinfo > .SRCINFO - - - name: Commit package to aur - working-directory: aur - run: | - git add PKGBUILD .SRCINFO - git commit -m "${{ gitea.event.release.body }}" - GIT_SSH_COMMAND="ssh -i ../key -o 'IdentitiesOnly yes' -o 'StrictHostKeyChecking no'" git push origin master diff --git a/.forgejo/workflows/main.yml b/.forgejo/workflows/main.yml deleted file mode 100644 index f3f2fcf..0000000 --- a/.forgejo/workflows/main.yml +++ /dev/null @@ -1,66 +0,0 @@ -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/ diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml deleted file mode 100644 index 1af4d7c..0000000 --- a/.forgejo/workflows/release.yml +++ /dev/null @@ -1,33 +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: - 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 }} - diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml deleted file mode 100644 index b8b3e07..0000000 --- a/.forgejo/workflows/test.yml +++ /dev/null @@ -1,39 +0,0 @@ -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 diff --git a/.gitea/workflows/main.yml b/.gitea/workflows/main.yml new file mode 100644 index 0000000..8c29f65 --- /dev/null +++ b/.gitea/workflows/main.yml @@ -0,0 +1,39 @@ +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 diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..e3772ff --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,46 @@ +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 + diff --git a/CHANGELOG.md b/CHANGELOG.md index 7987afe..a70560d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,22 +1,5 @@ # Changelog -# LMDBAL 0.6.0 (UNRELEASED) -### Improvements -- Less dereferencing -- Transactions are now closed with the database -- Cursors are now closed if the public transaction that opened them got closed -- 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) ### Improvements - Now you don't need to link agains lmdb, just linking against LMDBAL is enough diff --git a/CMakeLists.txt b/CMakeLists.txt index 325901b..dcafaa1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,26 +1,21 @@ cmake_minimum_required(VERSION 3.16) project(LMDBAL - VERSION 0.6.0 - DESCRIPTION "LMDB (Lightning Memory-Mapped Database Manager) Abstraction Layer" - LANGUAGES CXX + VERSION 0.5.4 + DESCRIPTION "LMDB (Lightning Memory-Mapped Database Manager) Abstraction Layer" + LANGUAGES CXX ) +string(TOLOWER ${PROJECT_NAME} PROJECT_LOW) cmake_policy(SET CMP0076 NEW) cmake_policy(SET CMP0079 NEW) -set(LMDBAL_NAME ${PROJECT_NAME} CACHE STRING "Override for library name and install path") - -option(LMDBAL_BUILD_STATIC "Builds library as static library" OFF) -option(LMDBAL_BUILD_TESTS "Builds tests" OFF) -option(LMDBAL_BUILD_DOC_MAN "Builds man page documentation" 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) +option(BUILD_STATIC "Builds library as static library" OFF) +option(BUILD_TESTS "Builds tests" OFF) +option(BUILD_DOC_MAN "Builds man page documentation" OFF) +option(BUILD_DOC_HTML "Builds html documentation" OFF) +option(BUILD_DOC_XML "Builds xml documentation" OFF) +option(BUILD_DOXYGEN_AWESOME "Builds documentation alternative style" OFF) include(GNUInstallDirs) include(CMakePackageConfigHelpers) @@ -30,148 +25,106 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") -string(TOLOWER ${LMDBAL_NAME} LMDBAL_NAME_LOW) - if (NOT DEFINED QT_VERSION_MAJOR) - find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core) -endif () + find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core) +endif() find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) find_package(LMDB REQUIRED) # Build type if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Debug) + set(CMAKE_BUILD_TYPE Debug) endif () -if (LMDBAL_BUILD_STATIC) - add_library(${LMDBAL_NAME} STATIC) +if (BUILD_STATIC) + add_library(${PROJECT_NAME} STATIC) else () - add_library(${LMDBAL_NAME} SHARED) + add_library(${PROJECT_NAME} SHARED) endif() - -if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - list(APPEND COMPILE_OPTIONS -Wall) - list(APPEND COMPILE_OPTIONS -Wextra) - list(APPEND COMPILE_OPTIONS -Wpedantic) - - 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 () +if (CMAKE_BUILD_TYPE STREQUAL "Release") + list(APPEND COMPILE_OPTIONS -O3) +elseif (CMAKE_BUILD_TYPE STREQUAL "Debug") + list(APPEND COMPILE_OPTIONS -g) + list(APPEND COMPILE_OPTIONS -Wall) + list(APPEND COMPILE_OPTIONS -Wextra) +endif() message("Compilation options: " ${COMPILE_OPTIONS}) -message("Linking options: " ${LINK_OPTIONS}) -target_compile_options(${LMDBAL_NAME} PRIVATE ${COMPILE_OPTIONS}) -target_link_options(${LMDBAL_NAME} PRIVATE ${LINK_OPTIONS}) +target_compile_options(${PROJECT_NAME} PRIVATE ${COMPILE_OPTIONS}) -set_property(TARGET ${LMDBAL_NAME} PROPERTY VERSION ${version}) -set_property(TARGET ${LMDBAL_NAME} PROPERTY SOVERSION 1) -set_property(TARGET ${LMDBAL_NAME} PROPERTY EXPORT_NAME ${LMDBAL_NAME}) -set_property(TARGET ${LMDBAL_NAME} PROPERTY INTERFACE_${LMDBAL_NAME}_MAJOR_VERSION 1) -set_property(TARGET ${LMDBAL_NAME} APPEND PROPERTY - COMPATIBLE_INTERFACE_STRING ${LMDBAL_NAME}_MAJOR_VERSION +set_property(TARGET ${PROJECT_NAME} PROPERTY VERSION ${version}) +set_property(TARGET ${PROJECT_NAME} PROPERTY SOVERSION 1) +set_property(TARGET ${PROJECT_NAME} PROPERTY EXPORT_NAME ${PROJECT_NAME}) +set_property(TARGET ${PROJECT_NAME} PROPERTY INTERFACE_${PROJECT_NAME}_MAJOR_VERSION 1) +set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY + COMPATIBLE_INTERFACE_STRING ${PROJECT_NAME}_MAJOR_VERSION ) if (UNIX) - set_property(TARGET ${LMDBAL_NAME} PROPERTY INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${LMDBAL_NAME_LOW}") - set_property(TARGET ${LMDBAL_NAME} PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE) - set_property(TARGET ${LMDBAL_NAME} PROPERTY SKIP_BUILD_RPATH FALSE) - set_property(TARGET ${LMDBAL_NAME} PROPERTY BUILD_WITH_INSTALL_RPATH FALSE) -endif () + set_property(TARGET ${PROJECT_NAME} PROPERTY INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${PROJECT_LOW}") + set_property(TARGET ${PROJECT_NAME} PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE) + set_property(TARGET ${PROJECT_NAME} PROPERTY SKIP_BUILD_RPATH FALSE) + set_property(TARGET ${PROJECT_NAME} PROPERTY BUILD_WITH_INSTALL_RPATH FALSE) +endif() add_subdirectory(src) -if (LMDBAL_BUILD_DOC_MAN OR LMDBAL_BUILD_DOC_HTML OR LMDBAL_BUILD_DOC_XML) - find_package(Doxygen) - if (DOXYGEN_FOUND) - add_subdirectory(doc) - else () - message("Was trying to build documentation, but Doxygen was not found, skipping documentation") - endif () -endif () +if (BUILD_DOC_MAN OR BUILD_DOC_HTML OR BUILD_DOC_XML) + find_package(Doxygen) + if (DOXYGEN_FOUND) + add_subdirectory(doc) + else() + message("Was trying to build documentation, but Doxygen was not found, skipping documentation") + endif() +endif() -if (LMDBAL_BUILD_TESTS) - add_subdirectory(test) +if (BUILD_TESTS) + add_subdirectory(test) endif () target_include_directories( - ${LMDBAL_NAME} - PUBLIC - $ - $ - $ + ${PROJECT_NAME} + PUBLIC + $ + $ + $ ) -target_include_directories(${LMDBAL_NAME} PRIVATE ${Qt${QT_VERSION_MAJOR}_INCLUDE_DIRS}) -target_include_directories(${LMDBAL_NAME} PRIVATE ${Qt${QT_VERSION_MAJOR}Core_INCLUDE_DIRS}) +target_include_directories(${PROJECT_NAME} PRIVATE ${Qt${QT_VERSION_MAJOR}_INCLUDE_DIRS}) +target_include_directories(${PROJECT_NAME} PRIVATE ${Qt${QT_VERSION_MAJOR}Core_INCLUDE_DIRS}) target_link_libraries( - ${LMDBAL_NAME} - Qt${QT_VERSION_MAJOR}::Core - lmdb + ${PROJECT_NAME} + Qt${QT_VERSION_MAJOR}::Core + lmdb ) configure_package_config_file( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in - "${CMAKE_CURRENT_BINARY_DIR}/${LMDBAL_NAME_LOW}Config.cmake" - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LMDBAL_NAME_LOW} + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}Config.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_LOW} ) write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/${LMDBAL_NAME_LOW}ConfigVersion.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}ConfigVersion.cmake" VERSION "${version}" COMPATIBILITY AnyNewerVersion ) -install(TARGETS ${LMDBAL_NAME} - EXPORT ${LMDBAL_NAME_LOW}Targets +install(TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_LOW}Targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${LMDBAL_NAME_LOW} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_LOW} ) -install(EXPORT ${LMDBAL_NAME_LOW}Targets - FILE ${LMDBAL_NAME_LOW}Targets.cmake - NAMESPACE ${LMDBAL_NAME}:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LMDBAL_NAME_LOW} +install(EXPORT ${PROJECT_LOW}Targets + FILE ${PROJECT_LOW}Targets.cmake + NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_LOW} ) install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/${LMDBAL_NAME_LOW}Config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/${LMDBAL_NAME_LOW}ConfigVersion.cmake" - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LMDBAL_NAME_LOW} + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LOW}ConfigVersion.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_LOW} ) diff --git a/README.md b/README.md index 3c5d671..73302ef 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,14 @@ # LMDBAL - Lightning Memory Data Base Abstraction Level [![AUR license](https://img.shields.io/aur/license/lmdbal?style=flat-square)](https://git.macaw.me/blue/lmdbal/raw/branch/master/LICENSE.md) -[![AUR qt5 version](https://img.shields.io/aur/version/lmdbal-qt5?style=flat-square&label=lmdbal-qt5)](https://aur.archlinux.org/packages/lmdbal-qt5/) -[![AUR qt6 version](https://img.shields.io/aur/version/lmdbal-qt6?style=flat-square&label=lmdbal-qt6)](https://aur.archlinux.org/packages/lmdbal-qt6/) +[![AUR version](https://img.shields.io/aur/version/lmdbal?style=flat-square)](https://aur.archlinux.org/packages/lmdbal/) [![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me) [![Documentation](https://img.shields.io/badge/Documentation-HTML-green)](https://macaw.me/lmdbal/doc/html) ### Prerequisites -- a c++ compiler (g++ would do) -- Qt 5 or 6 or higher (qt5-base or qt6-base would do) +- a compiler (c++ would do) +- Qt 5 or higher (qt5-base would do) - lmdb - CMake 3.16 or higher - Doxygen (optional, for documentation) @@ -19,8 +18,7 @@ #### As a system library -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 +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 ``` find_package(lmdbal) @@ -30,14 +28,14 @@ if (LMDBAL_FOUND) endif() ``` -#### As an embedded subproject +#### As an embeded subproject -If you're using LMDBAL as a embedded library, you might want to control its build options, for example, you can run +If you're using LMDBAL as a embeded library you might want to control it's build options, for example you can run ``` -set(LMDBAL_BUILD_STATIC ON) +set(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 ``` @@ -68,11 +66,9 @@ $ cmake --build . $ 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 transferred like so +After `cmake ..` you can specify keys to alter the building process. In this context building keys are transfered like so ``` cmake .. -D KEY1=VALUE1 -D KEY2=VALUE2 ... @@ -83,26 +79,21 @@ cmake .. -D KEY1=VALUE1 -D KEY2=VALUE2 ... 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`); -- `LMDBAL_BUILD_STATIC` - `True` builds project as a static library, `False` builds as dynamic (default is `False`); -- `LMDBAL_BUILD_TESTS` - `True` build unit tests, `False` does not (default is `False`); +- `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`); - `BUILD_DOC` - `True` build doxygen documentation, `False` does not (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 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`); +- `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; #### Running tests -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 +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 ``` ./runUnitTests ``` -... if you're in the same directory with it +if you're in the same directory with it ## License diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in index f470d3d..8fea032 100644 --- a/cmake/Config.cmake.in +++ b/cmake/Config.cmake.in @@ -1,5 +1,5 @@ @PACKAGE_INIT@ -include("${CMAKE_CURRENT_LIST_DIR}/@LMDBAL_NAME_LOW@Targets.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/lmdbalTargets.cmake") -check_required_components(@LMDBAL_NAME_LOW@) +check_required_components(lmdbal) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 66f3b76..0757340 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1,14 +1,14 @@ -if (LMDBAL_BUILD_DOC_HTML) +if (BUILD_DOC_HTML) set(DOXYGEN_GENERATE_HTML YES) endif() -if (LMDBAL_BUILD_DOC_MAN) +if (BUILD_DOC_MAN) set(DOXYGEN_GENERATE_MAN YES) endif() -if (LMDBAL_BUILD_DOC_XML) +if (BUILD_DOC_XML) set(DOXYGEN_GENERATE_XML YES) endif() -if (LMDBAL_BUILD_DOXYGEN_AWESOME) +if (BUILD_DOXYGEN_AWESOME) include(ExternalProject) ExternalProject_Add(doxygen-awesome-css GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css.git @@ -42,25 +42,25 @@ doxygen_add_docs( ALL COMMENT "Generate man and html pages" ) -if (LMDBAL_BUILD_DOC_MAN) +if (BUILD_DOC_MAN) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/man TYPE DOC ) endif() -if (LMDBAL_BUILD_DOC_HTML) +if (BUILD_DOC_HTML) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html TYPE DOC ) endif() -if (LMDBAL_BUILD_DOC_XML) +if (BUILD_DOC_XML) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/xml TYPE DOC ) endif() -if (LMDBAL_BUILD_DOXYGEN_AWESOME) +if (BUILD_DOXYGEN_AWESOME) add_dependencies(documentation doxygen-awesome-css) endif() \ No newline at end of file diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index c83a648..0f4b0da 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -1,23 +1,23 @@ # Maintainer: Yury Gubich pkgname=lmdbal -pkgver=1.0.0 -pkgrel=4 -pkgdesc="LMDB Abstraction Layer" +pkgver=0.5.4 +pkgrel=1 +pkgdesc="LMDB Abstraction Layer, qt5 version" arch=('i686' 'x86_64') url="https://git.macaw.me/blue/lmdbal" license=('GPL3') -depends=( 'lmdb' ) +depends=( 'lmdb' 'qt5-base') makedepends=('cmake>=3.16' 'gcc') optdepends=() -source=("lmdbal-$pkgver-$pkgrel.tar.gz::https://git.macaw.me/blue/lmdbal/archive/$pkgver.tar.gz") +source=("$pkgname-$pkgver.tar.gz::https://git.macaw.me/blue/$pkgname/archive/$pkgver.tar.gz") sha256sums=('SKIP') build() { - cd "$srcdir/lmdbal" - cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release + cd "$srcdir/$pkgname" + cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release -D QT_VERSION_MAJOR=5 cmake --build . } package() { - cd "$srcdir/lmdbal" + cd "$srcdir/$pkgname" DESTDIR="$pkgdir/" cmake --install . } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9f9469e..956e6b0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,10 +1,8 @@ set(SOURCES exceptions.cpp - storagecommon.cpp + storage.cpp base.cpp transaction.cpp - cursorcommon.cpp - session.cpp ) set(HEADERS @@ -12,20 +10,16 @@ set(HEADERS exceptions.h storage.h storage.hpp - storagecommon.h - storagecommon.hpp cursor.h cursor.hpp - cursorcommon.h cache.h cache.hpp operators.hpp transaction.h - session.h ) -target_sources(${LMDBAL_NAME} PRIVATE ${SOURCES}) +target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) add_subdirectory(serializer) -install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${LMDBAL_NAME_LOW}) +install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_LOW}) diff --git a/src/base.cpp b/src/base.cpp index ec4524c..149343b 100644 --- a/src/base.cpp +++ b/src/base.cpp @@ -18,10 +18,8 @@ #include "base.h" #include "exceptions.h" -#include "session.h" #include "storage.h" #include "transaction.h" -#include "session.h" #define UNUSED(x) (void)(x) @@ -30,55 +28,97 @@ * \brief Database abstraction * * This is a basic class that represents the database as a collection of storages. - * Storages are something that key-value databases have instead of tables in classic SQL databases. + * Storages is something key-value database has instead of tables in classic SQL databases. */ /** * \brief Creates the database * - * \param[in] _name - name of the database, it is going to affect the folder name that is created to store data + * \param[in] _name - name of the database, it is going to affect folder name that is created to store data * \param[in] _mapSize - LMDB map size (MiB), multiplied by 1024^2 and passed to mdb_env_set_mapsize during the call of LMDBAL::Base::open() */ LMDBAL::Base::Base(const QString& _name, uint16_t _mapSize): name(_name.toStdString()), - sessions(), + opened(false), size(_mapSize), environment(), storages(), - transactions(), - mutex() + transactions(new Transactions()) {} /** * \brief Destroys the database */ LMDBAL::Base::~Base() { - std::lock_guard lock(mutex); + close(); - for (Session* session : sessions) - session->terminate(); + delete transactions; - if (opened()) - deactivate(); - - for (const std::pair& pair : storages) + for (const std::pair& pair : storages) delete pair.second; } -LMDBAL::Session LMDBAL::Base::open() { - return {this}; +/** + * \brief Closes the database + * + * 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& 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& 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 * - * \returns true if removal was successful of if no directory was created where it's expected to be, false otherwise + * \returns true if removal was successfull of if no directory was created where it's expected to be, false otherwise * - * \exception LMDBAL::Opened - thrown if this function was called on an opened database + * \exception LMDBAL::Opened - thrown if this function was called on opened database */ bool LMDBAL::Base::removeDirectory() { - if (opened()) + if (opened) throw Opened(name, "remove database directory"); QString path = getPath(); @@ -101,11 +141,11 @@ bool LMDBAL::Base::removeDirectory() { * * \returns the path of the created directory * - * \exception LMDBAL::Opened - thrown if called on an opened database + * \exception LMDBAL::Opened - thrown if called on opened database * \exception LMDBAL::Directory - if the database couldn't create the folder */ QString LMDBAL::Base::createDirectory() { - if (opened()) + if (opened) throw Opened(name, "create database directory"); QString path = getPath(); @@ -144,9 +184,8 @@ QString LMDBAL::Base::getPath() const { * * \returns true if the database is opened and ready for work, false otherwise */ -bool LMDBAL::Base::opened() const { - return !sessions.empty(); -} +bool LMDBAL::Base::ready() const { + return opened;} /** * \brief Drops the database @@ -157,11 +196,11 @@ bool LMDBAL::Base::opened() const { * \exception LMDBAL::Unknown - thrown if something unexpected happend */ void LMDBAL::Base::drop() { - if (!opened()) + if (!opened) throw Closed("drop", name); TransactionID txn = beginPrivateTransaction(emptyName); - for (const std::pair& pair : storages) { + for (const std::pair& pair : storages) { int rc = pair.second->drop(txn); if (rc != MDB_SUCCESS) { abortPrivateTransaction(txn, emptyName); @@ -170,7 +209,7 @@ void LMDBAL::Base::drop() { } commitPrivateTransaction(txn, emptyName); - for (const std::pair& pair : storages) + for (const std::pair& pair : storages) pair.second->handleDrop(); } @@ -222,13 +261,12 @@ LMDBAL::WriteTransaction LMDBAL::Base::beginTransaction() { * \brief Aborts transaction * * Terminates transaction cancelling changes. - * Every storage receives notification about this transaction being aborted. - * This is an optimal way to abort public transactions + * This is an optimal way to terminate read-only transactions * * \param[in] id - transaction ID you want to abort * * \exception LMDBAL::Closed - thrown if the database is closed - * \exception LMDBAL::Unknown - thrown if something unexpected happened + * \exception LMDBAL::Unknown - thrown if transaction with given ID was not found or if something unexpected happened */ void LMDBAL::Base::abortTransaction(LMDBAL::TransactionID id) const { return abortTransaction(id, emptyName);} @@ -237,13 +275,11 @@ void LMDBAL::Base::abortTransaction(LMDBAL::TransactionID id) const { * \brief Commits transaction * * Terminates transaction applying changes. - * Every storage receives notification about this transaction being committed. - * This is an optimal way to commit public transactions * * \param[in] id - transaction ID you want to commit * * \exception LMDBAL::Closed - thrown if the database is closed - * \exception LMDBAL::Unknown - thrown if something unexpected happened + * \exception LMDBAL::Unknown - thrown if transaction with given ID was not found or if something unexpected happened */ void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id) { return commitTransaction(id, emptyName);} @@ -251,9 +287,7 @@ void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id) { /** * \brief Begins read-only transaction * - * This function is intended to be called from subordinate storage, cache, transaction or cursor. - * Every storage receives notification about this transaction being started. - * This is an optimal way to begin read-only public transactions. + * This function is intended to be called from subordinate storage or cache * * \param[in] storageName - name of the storage/cache that you begin transaction from, needed just to inform if something went wrong * @@ -263,11 +297,12 @@ void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id) { * \exception LMDBAL::Unknown - thrown if something unexpected happened */ LMDBAL::TransactionID LMDBAL::Base::beginReadOnlyTransaction(const std::string& storageName) const { - if (!opened()) + if (!opened) throw Closed("beginReadOnlyTransaction", name, storageName); TransactionID txn = beginPrivateReadOnlyTransaction(storageName); - for (const std::pair& pair : storages) + transactions->emplace(txn); + for (const std::pair& pair : storages) pair.second->transactionStarted(txn, true); return txn; @@ -276,9 +311,7 @@ LMDBAL::TransactionID LMDBAL::Base::beginReadOnlyTransaction(const std::string& /** * \brief Begins writable transaction * - * This function is intended to be called from subordinate storage, cache, transaction or cursor. - * Every storage receives notification about this transaction being started. - * This is an optimal way to begin public transactions. + * This function is intended to be called from subordinate storage or cache * * \param[in] storageName - name of the storage/cache that you begin transaction from, needed just to inform if something went wrong * @@ -288,11 +321,12 @@ LMDBAL::TransactionID LMDBAL::Base::beginReadOnlyTransaction(const std::string& * \exception LMDBAL::Unknown - thrown if something unexpected happened */ LMDBAL::TransactionID LMDBAL::Base::beginTransaction(const std::string& storageName) const { - if (!opened()) + if (!opened) throw Closed("beginTransaction", name, storageName); TransactionID txn = beginPrivateTransaction(storageName); - for (const std::pair& pair : storages) + transactions->emplace(txn); + for (const std::pair& pair : storages) pair.second->transactionStarted(txn, false); return txn; @@ -302,46 +336,55 @@ LMDBAL::TransactionID LMDBAL::Base::beginTransaction(const std::string& storageN * \brief Aborts transaction * * Terminates transaction cancelling changes. - * Every storage receives notification about this transaction being aborted. - * This is an optimal way to abort public transactions. - * This function is intended to be called from subordinate storage, cache, transaction or cursor + * This is an optimal way to terminate read-only transactions. + * This function is intended to be called from subordinate storage or cache * * \param[in] id - transaction ID you want to abort * \param[in] storageName - name of the storage/cache that you begin transaction from, needed just to inform if something went wrong * * \exception LMDBAL::Closed - thrown if the database is closed - * \exception LMDBAL::Unknown - thrown if something unexpected happened + * \exception LMDBAL::Unknown - thrown if transaction with given ID was not found or if something unexpected happened */ void LMDBAL::Base::abortTransaction(LMDBAL::TransactionID id, const std::string& storageName) const { - if (!opened()) + if (!opened) throw Closed("abortTransaction", name, storageName); + Transactions::iterator itr = transactions->find(id); + if (itr == transactions->end()) //TODO may be it's a good idea to make an exception class for this + throw Unknown(name, "unable to abort transaction: transaction was not found", storageName); + abortPrivateTransaction(id, storageName); - for (const std::pair& pair : storages) + for (const std::pair& pair : storages) pair.second->transactionAborted(id); + + transactions->erase(itr); } /** * \brief Commits transaction * * Terminates transaction applying changes. - * Every storage receives notification about this transaction being committed. - * This is an optimal way to commit public transactions - * This function is intended to be called from subordinate storage, cache, transaction or cursor + * This function is intended to be called from subordinate storage or cache * * \param[in] id - transaction ID you want to commit * \param[in] storageName - name of the storage/cache that you begin transaction from, needed just to inform if something went wrong * * \exception LMDBAL::Closed - thrown if the database is closed - * \exception LMDBAL::Unknown - thrown if something unexpected happened + * \exception LMDBAL::Unknown - thrown if transaction with given ID was not found or if something unexpected happened */ void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id, const std::string& storageName) { - if (!opened()) + if (!opened) throw Closed("abortTransaction", name, storageName); + Transactions::iterator itr = transactions->find(id); + if (itr == transactions->end()) //TODO may be it's a good idea to make an exception class for this + throw Unknown(name, "unable to commit transaction: transaction was not found", storageName); + commitPrivateTransaction(id, storageName); - for (const std::pair& pair : storages) + for (const std::pair& pair : storages) pair.second->transactionCommited(id); + + transactions->erase(itr); } /** @@ -369,7 +412,7 @@ LMDBAL::TransactionID LMDBAL::Base::beginPrivateReadOnlyTransaction(const std::s * \brief Begins writable transaction * * This function is intended to be called from subordinate storage or cache, - * it's not accounted in the transaction collection, other storages and caches are not notified about this kind of transaction + * it's not accounted in 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 * @@ -390,10 +433,10 @@ LMDBAL::TransactionID LMDBAL::Base::beginPrivateTransaction(const std::string& s /** * \brief Aborts transaction * - * Terminates transaction, cancelling changes. + * Terminates transaction cancelling changes. * This is an optimal way to terminate read-only transactions. * This function is intended to be called from subordinate storage or cache, - * it's not accounted in the transaction collection, other storages and caches are not notified about this kind of transaction + * it's not accounted in 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] storageName - name of the storage/cache that you begin transaction from, unused here @@ -420,104 +463,3 @@ void LMDBAL::Base::commitPrivateTransaction(LMDBAL::TransactionID id, const std: if (rc != MDB_SUCCESS) 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 pair : transactions) { - abortTransaction(pair.first, emptyName); - pair.second->reset(); - } - - for (const std::pair& 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(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& pair : storages) { - StorageCommon* storage = pair.second; - if (const int rc = storage->open(txn)) - throw Unknown(name, mdb_strerror(rc)); - } - - commitPrivateTransaction(txn, emptyName); -} \ No newline at end of file diff --git a/src/base.h b/src/base.h index 230cf14..c99bf21 100644 --- a/src/base.h +++ b/src/base.h @@ -16,15 +16,14 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_BASE_H +#define LMDBAL_BASE_H #include #include #include #include #include -#include -#include #include #include @@ -36,10 +35,9 @@ namespace LMDBAL { -class StorageCommon; +class iStorage; class Transaction; class WriteTransaction; -class Session; template class Serializer; @@ -51,20 +49,20 @@ template class Cache; 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*/ +typedef uint32_t SizeType; /**<\brief All LMDBAL sizes are uint32_t*/ class Base { - friend class StorageCommon; + friend class iStorage; friend class Transaction; friend class WriteTransaction; - friend class Session; - public: + Base(const QString& name, uint16_t mapSize = 10); ~Base(); - Session open(); - bool opened() const; + void open(); + void close(); + bool ready() const; bool removeDirectory(); QString createDirectory(); QString getName() const; @@ -87,9 +85,8 @@ public: LMDBAL::Cache* getCache(const std::string& storageName); private: - typedef std::map Storages; /**<\brief Storage and Cache pointers are saved in the std::map*/ - typedef std::map Transactions; /**<\brief Public transaction IDs are saved in the std::map*/ - typedef std::set Sessions; /**<\brief Sessions are saved in the std::set*/ + typedef std::map Storages; /**<\brief Storage and Cache pointers are saved in the std::map*/ + typedef std::set Transactions; /**<\brief Piblic transaction IDs are saved in the std::set*/ void commitTransaction(TransactionID id); void abortTransaction(TransactionID id) const; @@ -103,21 +100,13 @@ private: void commitPrivateTransaction(TransactionID id, const std::string& storageName); 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: std::string name; /**<\brief Name of this database*/ - Sessions sessions; /**<\brief Opened session pointers*/ + bool opened; /**<\brief State of this database*/ uint16_t size; /**<\brief lmdb map size in MiB*/ MDB_env* environment; /**<\brief lmdb environment handle*/ Storages storages; /**<\brief Registered storages and caches*/ - mutable Transactions transactions; /**<\brief Active public transactions*/ - std::mutex mutex; /**<\brief Mutex for thread safety*/ + Transactions* transactions; /**<\brief Active public transactions*/ inline static const std::string emptyName = ""; /**<\brief Empty string for general fallback purposes*/ }; @@ -144,11 +133,11 @@ private: */ template LMDBAL::Storage* LMDBAL::Base::addStorage(const std::string& storageName, bool duplicates) { - if (opened()) + if (opened) throw Opened(name, "add storage " + storageName); - auto storage = new Storage(this, storageName, duplicates); - std::pair pair = storages.emplace(storageName, static_cast(storage)); + Storage* storage = new Storage(this, storageName, duplicates); + std::pair pair = storages.insert(std::make_pair(storageName, (iStorage*)storage)); if (!pair.second) throw StorageDuplicate(name, storageName); @@ -172,11 +161,11 @@ LMDBAL::Storage* LMDBAL::Base::addStorage(const std::string& storageName, */ template LMDBAL::Cache * LMDBAL::Base::addCache(const std::string& storageName) { - if (opened()) + if (opened) throw Opened(name, "add cache " + storageName); - auto cache = new Cache(this, storageName, false); - std::pair pair = storages.emplace(storageName, static_cast(cache)); + Cache* cache = new Cache(this, storageName, false); + std::pair pair = storages.insert(std::make_pair(storageName, (iStorage*)cache)); if (!pair.second) throw StorageDuplicate(name, storageName); @@ -226,3 +215,5 @@ template LMDBAL::Cache* LMDBAL::Base::getCache(const std::string& storageName) { return static_cast*>(storages.at(storageName)); } + +#endif //LMDBAL_BASE_H diff --git a/src/cache.h b/src/cache.h index 0084984..0883e65 100644 --- a/src/cache.h +++ b/src/cache.h @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_CACHE_H +#define LMDBAL_CACHE_H #include #include @@ -133,3 +134,5 @@ protected: } #include "cache.hpp" + +#endif // LMDBAL_CACHE_H diff --git a/src/cache.hpp b/src/cache.hpp index 6dd8c54..e3c6d60 100644 --- a/src/cache.hpp +++ b/src/cache.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_CACHE_HPP +#define LMDBAL_CACHE_HPP #include "cache.h" #include "exceptions.h" @@ -63,10 +64,10 @@ LMDBAL::Cache::~Cache() { template void LMDBAL::Cache::addRecord(const K& key, const V& value) { - StorageCommon::ensureOpened(StorageCommon::addRecordMethodName); + iStorage::ensureOpened(iStorage::addRecordMethodName); if (cache->count(key) > 0) - StorageCommon::throwDuplicate(StorageCommon::toString(key)); + iStorage::throwDuplicate(iStorage::toString(key)); Storage::addRecord(key, value); handleAddRecord(key, value); @@ -75,7 +76,7 @@ void LMDBAL::Cache::addRecord(const K& key, const V& value) { template void LMDBAL::Cache::addRecord(const K& key, const V& value, TransactionID txn) { if (cache->count(key) > 0) - StorageCommon::throwDuplicate(StorageCommon::toString(key)); + iStorage::throwDuplicate(iStorage::toString(key)); Storage::addRecord(key, value, txn); @@ -96,7 +97,7 @@ void LMDBAL::Cache::handleAddRecord(const K& key, const V& value) { template bool LMDBAL::Cache::forceRecord(const K& key, const V& value) { - StorageCommon::ensureOpened(StorageCommon::forceRecordMethodName); + iStorage::ensureOpened(iStorage::forceRecordMethodName); bool added = Storage::forceRecord(key, value); handleForceRecord(key, value, added); @@ -136,18 +137,18 @@ void LMDBAL::Cache::handleForceRecord(const K& key, const V& value, bool a template void LMDBAL::Cache::changeRecord(const K& key, const V& value) { - StorageCommon::ensureOpened(StorageCommon::changeRecordMethodName); + iStorage::ensureOpened(iStorage::changeRecordMethodName); if (mode == Mode::full) { typename std::map::iterator itr = cache->find(key); if (itr == cache->end()) - StorageCommon::throwNotFound(StorageCommon::toString(key)); + iStorage::throwNotFound(iStorage::toString(key)); Storage::changeRecord(key, value); itr->second = value; } else { if (abscent->count(key) > 0) - StorageCommon::throwNotFound(StorageCommon::toString(key)); + iStorage::throwNotFound(iStorage::toString(key)); try { Storage::changeRecord(key, value); @@ -169,12 +170,12 @@ void LMDBAL::Cache::changeRecord(const K& key, const V& value, Transaction if (mode == Mode::full) { typename std::map::iterator itr = cache->find(key); if (itr == cache->end()) - StorageCommon::throwNotFound(StorageCommon::toString(key)); + iStorage::throwNotFound(iStorage::toString(key)); Storage::changeRecord(key, value, txn); } else { if (abscent->count(key) > 0) - StorageCommon::throwNotFound(StorageCommon::toString(key)); + iStorage::throwNotFound(iStorage::toString(key)); try { Storage::changeRecord(key, value, txn); @@ -207,7 +208,7 @@ void LMDBAL::Cache::handleChangeRecord(const K& key, const V& value) { template V LMDBAL::Cache::getRecord(const K& key) const { - StorageCommon::ensureOpened(StorageCommon::getRecordMethodName); + iStorage::ensureOpened(iStorage::getRecordMethodName); V value; Cache::getRecord(key, value); @@ -216,7 +217,7 @@ V LMDBAL::Cache::getRecord(const K& key) const { template void LMDBAL::Cache::getRecord(const K& key, V& out) const { - StorageCommon::ensureOpened(StorageCommon::getRecordMethodName); + iStorage::ensureOpened(iStorage::getRecordMethodName); typename std::map::const_iterator itr = cache->find(key); if (itr != cache->end()) { @@ -225,7 +226,7 @@ void LMDBAL::Cache::getRecord(const K& key, V& out) const { } if (mode == Mode::full || abscent->count(key) != 0) - StorageCommon::throwNotFound(StorageCommon::toString(key)); + iStorage::throwNotFound(iStorage::toString(key)); try { Storage::getRecord(key, out); @@ -269,7 +270,7 @@ void LMDBAL::Cache::getRecord(const K& key, V& out, TransactionID txn) con } break; case Operation::remove: - StorageCommon::throwNotFound(StorageCommon::toString(key)); + iStorage::throwNotFound(iStorage::toString(key)); break; case Operation::change: if (static_cast*>(entry.second)->first == key) { @@ -285,7 +286,7 @@ void LMDBAL::Cache::getRecord(const K& key, V& out, TransactionID txn) con } break; case Operation::drop: - StorageCommon::throwNotFound(StorageCommon::toString(key)); + iStorage::throwNotFound(iStorage::toString(key)); break; case Operation::replace: { std::map* newMap = static_cast*>(entry.second); @@ -294,7 +295,7 @@ void LMDBAL::Cache::getRecord(const K& key, V& out, TransactionID txn) con out = vitr->second; return; } else { - StorageCommon::throwNotFound(StorageCommon::toString(key)); + iStorage::throwNotFound(iStorage::toString(key)); } } break; @@ -322,7 +323,7 @@ void LMDBAL::Cache::getRecord(const K& key, V& out, TransactionID txn) con } if (mode == Mode::full || abscent->count(key) != 0) - StorageCommon::throwNotFound(StorageCommon::toString(key)); + iStorage::throwNotFound(iStorage::toString(key)); try { Storage::getRecord(key, out, txn); @@ -352,7 +353,7 @@ void LMDBAL::Cache::discoveredRecord(const K& key, const V& value, Transac template bool LMDBAL::Cache::checkRecord(const K& key) const { - StorageCommon::ensureOpened(StorageCommon::checkRecordMethodName); + iStorage::ensureOpened(iStorage::checkRecordMethodName); typename std::map::const_iterator itr = cache->find(key); if (itr != cache->end()) @@ -457,7 +458,7 @@ void LMDBAL::Cache::appendToCache(const K& key, const V& value) const { template std::map LMDBAL::Cache::readAll() const { - StorageCommon::ensureOpened(StorageCommon::readAllMethodName); + iStorage::ensureOpened(iStorage::readAllMethodName); if (mode != Mode::full) { //there is a room for optimization mode = Mode::full; //I can read and deserialize only those values @@ -471,7 +472,7 @@ std::map LMDBAL::Cache::readAll() const { template void LMDBAL::Cache::readAll(std::map& out) const { - StorageCommon::ensureOpened(StorageCommon::readAllMethodName); + iStorage::ensureOpened(iStorage::readAllMethodName); if (mode != Mode::full) { //there is a room for optimization mode = Mode::full; //I can read and deserialize only those values @@ -642,7 +643,7 @@ void LMDBAL::Cache::handleAddRecords(const std::map& data, bool over template void LMDBAL::Cache::removeRecord(const K& key) { - StorageCommon::ensureOpened(StorageCommon::removeRecordMethodName); + iStorage::ensureOpened(iStorage::removeRecordMethodName); bool noKey = false; if (mode != Mode::full) @@ -651,7 +652,7 @@ void LMDBAL::Cache::removeRecord(const K& key) { noKey = abscent->count(key) > 0; if (noKey) - StorageCommon::throwNotFound(StorageCommon::toString(key)); + iStorage::throwNotFound(iStorage::toString(key)); Storage::removeRecord(key); handleRemoveRecord(key); @@ -666,7 +667,7 @@ void LMDBAL::Cache::removeRecord(const K& key, TransactionID txn) { noKey = abscent->count(key) > 0; if (noKey) - StorageCommon::throwNotFound(StorageCommon::toString(key)); + iStorage::throwNotFound(iStorage::toString(key)); Storage::removeRecord(key, txn); @@ -781,8 +782,8 @@ void LMDBAL::Cache::handleMode() const { template int LMDBAL::Cache::drop(const WriteTransaction& transaction) { - StorageCommon::ensureOpened(StorageCommon::dropMethodName); - TransactionID txn = StorageCommon::extractTransactionId(transaction, StorageCommon::dropMethodName); + iStorage::ensureOpened(iStorage::dropMethodName); + TransactionID txn = iStorage::extractTransactionId(transaction, iStorage::dropMethodName); int res = Storage::drop(txn); if (res != MDB_SUCCESS) @@ -897,3 +898,5 @@ void LMDBAL::Cache::destroyTransactionEntry(const Entry& entry) const { break; } } + +#endif //LMDBAL_CACHE_HPP diff --git a/src/cursor.h b/src/cursor.h index eef7cf1..c3ff362 100644 --- a/src/cursor.h +++ b/src/cursor.h @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_CURSOR_H +#define LMDBAL_CURSOR_H #include @@ -24,13 +25,19 @@ #include "base.h" #include "storage.h" #include "transaction.h" -#include "cursorcommon.h" namespace LMDBAL { template -class Cursor : public CursorCommon { +class Cursor { friend class Storage; +private: + enum State { /*** parent); @@ -41,6 +48,14 @@ public: Cursor& operator = (const Cursor& other) = delete; 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(); std::pair first(); @@ -57,10 +72,38 @@ public: void current(K& key, V& value) const; 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; + +private: + Storage* 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" + +#endif //LMDBAL_CURSOR_H diff --git a/src/cursor.hpp b/src/cursor.hpp index ed68e8f..519bd41 100644 --- a/src/cursor.hpp +++ b/src/cursor.hpp @@ -16,10 +16,10 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_CURSOR_HPP +#define LMDBAL_CURSOR_HPP #include "cursor.h" - #include /** @@ -41,6 +41,8 @@ * You are not supposed to instantiate or destory instances of this class yourself! */ +static uint32_t idCounter = 0; + /** * \brief Creates a cursor * @@ -48,9 +50,12 @@ */ template LMDBAL::Cursor::Cursor(Storage* parent): - CursorCommon(parent) + storage(parent), + cursor(nullptr), + state(closed), + id(++idCounter) { - parent->cursors[id] = this; + storage->cursors[id] = this; } /** @@ -60,7 +65,10 @@ LMDBAL::Cursor::Cursor(Storage* parent): */ template LMDBAL::Cursor::Cursor(): - CursorCommon() + storage(nullptr), + cursor(nullptr), + state(closed), + id(0) {} /** @@ -68,39 +76,57 @@ LMDBAL::Cursor::Cursor(): */ template LMDBAL::Cursor::Cursor(Cursor&& other): - CursorCommon(std::move(other)) + storage(other.storage), + cursor(other.cursor), + state(other.state), + id(other.id) { - if (!empty()) - static_cast*>(storage)->cursors[id] = this; + other.terminated(); + if (id != 0) + storage->cursors[id] = this; + + other.freed(); } /** - * \brief Move assignment operator + * \brief A private function that turns cursor into an empty one * - * Transfers other cursor into this one + * This function is called from LMDBAL::Storage, when it gets destroyed, but still has some valid. + * Those cursors will become empty, and can't be used anymore */ template LMDBAL::Cursor& LMDBAL::Cursor::operator = (Cursor&& other) { - if (!empty() && other.empty()) - static_cast*>(storage)->cursors.erase(id); + terminated(); - CursorCommon::operator=(std::move(other)); + if (id != 0) + storage->cursors.erase(id); - if (!empty()) - static_cast*>(storage)->cursors[id] = this; + storage = other.storage; + cursor = other.cursor; + state = other.state; + id = other.id; + + if (id != 0) { + other.freed(); + other.state = closed; + + storage->cursors[id] = this; + } return *this; } /** - * \brief Destroys this cursor + * \brief Destroys a cursor * * If the cursor wasn't properly closed - it's going to be upon destruction */ template LMDBAL::Cursor::~Cursor () { + close(); + if (id != 0) - static_cast*>(storage)->cursors.erase(id); + storage->cursors.erase(id); } /** @@ -114,9 +140,251 @@ void LMDBAL::Cursor::drop () { close(); if (id != 0) - static_cast*>(storage)->cursors.erase(id); + storage->cursors.erase(id); - reset(); + freed(); +} + +/** + * \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 +void LMDBAL::Cursor::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 +void LMDBAL::Cursor::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 +bool LMDBAL::Cursor::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 +void LMDBAL::Cursor::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 +void LMDBAL::Cursor::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 +void LMDBAL::Cursor::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 +void LMDBAL::Cursor::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 +void LMDBAL::Cursor::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 +void LMDBAL::Cursor::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 +bool LMDBAL::Cursor::opened () const { + return state != closed; } /** @@ -214,7 +482,7 @@ void LMDBAL::Cursor::prev (K& key, V& value) { * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb */ template -void LMDBAL::Cursor::current (K& key, V& value) const { +void LMDBAL::Cursor::current (K& key, V& value) const { operateCursorRead(key, value, MDB_GET_CURRENT, currentMethodName, currentOperationName); } @@ -337,16 +605,16 @@ std::pair LMDBAL::Cursor::current () const { template bool LMDBAL::Cursor::set (const K& key) { if (state == closed) - static_cast*>(storage)->throwCursorNotReady(setMethodName); + storage->throwCursorNotReady(setMethodName); - MDB_val mdbKey = static_cast*>(storage)->keySerializer.setData(key); - int result = static_cast*>(storage)->_mdbCursorSet(handle, mdbKey); + MDB_val mdbKey = storage->keySerializer.setData(key); + int result = storage->_mdbCursorSet(cursor, mdbKey); if (result == MDB_SUCCESS) return true; else if (result == MDB_NOTFOUND) return false; - static_cast*>(storage)->throwUnknown(result); + storage->throwUnknown(result); return false; //unreachable, just to suppress the warning } @@ -374,18 +642,20 @@ void LMDBAL::Cursor::operateCursorRead( const std::string& operationName ) const { if (state == closed) - static_cast*>(storage)->throwCursorNotReady(methodName); + storage->throwCursorNotReady(methodName); MDB_val mdbKey, mdbValue; - int result = static_cast*>(storage)->_mdbCursorGet(handle, mdbKey, mdbValue, operation); + int result = storage->_mdbCursorGet(cursor, mdbKey, mdbValue, operation); if (result != MDB_SUCCESS) - static_cast*>(storage)->throwNotFoundOrUnknown(result, operationName); + storage->throwNotFoundOrUnknown(result, operationName); - static_cast*>(storage)->keySerializer.deserialize(mdbKey, key); - static_cast*>(storage)->valueSerializer.deserialize(mdbValue, value); + storage->keySerializer.deserialize(mdbKey, key); + storage->valueSerializer.deserialize(mdbValue, value); if (state == openedPrivate) - static_cast*>(storage)->discoveredRecord(key, value); + storage->discoveredRecord(key, value); else - static_cast*>(storage)->discoveredRecord(key, value, static_cast*>(storage)->_mdbCursorTxn(handle)); + storage->discoveredRecord(key, value, storage->_mdbCursorTxn(cursor)); } + +#endif //LMDBAL_CURSOR_HPP diff --git a/src/cursorcommon.cpp b/src/cursorcommon.cpp deleted file mode 100644 index b8f1564..0000000 --- a/src/cursorcommon.cpp +++ /dev/null @@ -1,366 +0,0 @@ -/* - * LMDB Abstraction Layer. - * Copyright (C) 2023 Yury Gubich - * - * 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 . - */ - -#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); -} diff --git a/src/cursorcommon.h b/src/cursorcommon.h deleted file mode 100644 index bf9b312..0000000 --- a/src/cursorcommon.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * LMDB Abstraction Layer. - * Copyright (C) 2023 Yury Gubich - * - * 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 . - */ - -#pragma once - -#include -#include - -#include - -namespace LMDBAL { - -class Transaction; -class StorageCommon; - -class CursorCommon { - friend class Transaction; -protected: - enum State { /**. */ -#pragma once +#ifndef LMDBAL_EXCEPTIONS_H +#define LMDBAL_EXCEPTIONS_H #include #include @@ -40,7 +41,7 @@ public: /** * \brief Thrown if LMDBAL had issues creating or opening database directory */ -class Directory : public Exception { +class Directory: public Exception { public: /** * \brief Creates exception @@ -236,3 +237,5 @@ private: }; } + +#endif //LMDBAL_EXCEPTIONS_H diff --git a/src/operators.hpp b/src/operators.hpp index 9abb2d0..80353ac 100644 --- a/src/operators.hpp +++ b/src/operators.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_OPERATORS_HPP +#define LMDBAL_OPERATORS_HPP #include #include @@ -208,3 +209,5 @@ QDataStream& operator >> (QDataStream &in, std::list& container) { return in; } + +#endif //LMDBAL_OPERATORS_HPP diff --git a/src/serializer/CMakeLists.txt b/src/serializer/CMakeLists.txt index 2df8e47..0c0a633 100644 --- a/src/serializer/CMakeLists.txt +++ b/src/serializer/CMakeLists.txt @@ -16,4 +16,4 @@ set(HEADERS serializer_qbytearray.hpp ) -install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${LMDBAL_NAME_LOW}) +install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_LOW}) diff --git a/src/serializer/serializer.h b/src/serializer/serializer.h index bd7496a..734ac51 100644 --- a/src/serializer/serializer.h +++ b/src/serializer/serializer.h @@ -16,8 +16,8 @@ * along with this program. If not, see . */ -#pragma once - +#ifndef LMDBAL_SERIALIZER_H +#define LMDBAL_SERIALIZER_H #include @@ -68,3 +68,5 @@ private: #include "serializer_stdstring.hpp" #include "serializer_qstring.hpp" #include "serializer_qbytearray.hpp" + +#endif // LMDBAL_SERIALIZER_H diff --git a/src/serializer/serializer.hpp b/src/serializer/serializer.hpp index 03bfaed..80f4d90 100644 --- a/src/serializer/serializer.hpp +++ b/src/serializer/serializer.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_SERIALIZER_HPP +#define LMDBAL_SERIALIZER_HPP #include "serializer.h" @@ -114,7 +115,7 @@ T LMDBAL::Serializer::deserialize(const MDB_val& value) { template void LMDBAL::Serializer::deserialize(const MDB_val& value, T& result) { clear(); - bytes.setRawData(static_cast(value.mv_data), value.mv_size); + bytes.setRawData((char*)value.mv_data, value.mv_size); stream >> result; } @@ -154,7 +155,9 @@ MDB_val LMDBAL::Serializer::getData() { MDB_val val; val.mv_size = buffer.pos(); - val.mv_data = bytes.data(); + val.mv_data = (char*)bytes.data(); return val; } + +#endif //LMDBAL_SERIALIZER_HPP diff --git a/src/serializer/serializer_double.hpp b/src/serializer/serializer_double.hpp index be943b8..4582459 100644 --- a/src/serializer/serializer_double.hpp +++ b/src/serializer/serializer_double.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_SERIALIZER_DOUBLE_HPP +#define LMDBAL_SERIALIZER_DOUBLE_HPP namespace LMDBAL { @@ -52,5 +53,7 @@ private: } +#endif //LMDBAL_SERIALIZER_DOUBLE_HPP + diff --git a/src/serializer/serializer_float.hpp b/src/serializer/serializer_float.hpp index f4b84a9..be2c72d 100644 --- a/src/serializer/serializer_float.hpp +++ b/src/serializer/serializer_float.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_SERIALIZER_FLOAT_HPP +#define LMDBAL_SERIALIZER_FLOAT_HPP namespace LMDBAL { @@ -51,3 +52,8 @@ private: }; } + +#endif //LMDBAL_SERIALIZER_FLOAT_HPP + + + diff --git a/src/serializer/serializer_int16.hpp b/src/serializer/serializer_int16.hpp index 0baaaad..17e5ea2 100644 --- a/src/serializer/serializer_int16.hpp +++ b/src/serializer/serializer_int16.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_SERIALIZER_INT16_HPP +#define LMDBAL_SERIALIZER_INT16_HPP #include @@ -53,3 +54,5 @@ private: }; } + +#endif //LMDBAL_SERIALIZER_INT16_HPP diff --git a/src/serializer/serializer_int32.hpp b/src/serializer/serializer_int32.hpp index 339dfea..78405c5 100644 --- a/src/serializer/serializer_int32.hpp +++ b/src/serializer/serializer_int32.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_SERIALIZER_INT32_HPP +#define LMDBAL_SERIALIZER_INT32_HPP #include @@ -53,3 +54,8 @@ private: }; } + +#endif //LMDBAL_SERIALIZER_INT32_HPP + + + diff --git a/src/serializer/serializer_int64.hpp b/src/serializer/serializer_int64.hpp index 107d102..4992ae1 100644 --- a/src/serializer/serializer_int64.hpp +++ b/src/serializer/serializer_int64.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_SERIALIZER_INT64_HPP +#define LMDBAL_SERIALIZER_INT64_HPP #include @@ -53,3 +54,8 @@ private: }; } + +#endif //LMDBAL_SERIALIZER_INT64_HPP + + + diff --git a/src/serializer/serializer_int8.hpp b/src/serializer/serializer_int8.hpp index 6f68f18..eafd5c6 100644 --- a/src/serializer/serializer_int8.hpp +++ b/src/serializer/serializer_int8.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_SERIALIZER_INT8_HPP +#define LMDBAL_SERIALIZER_INT8_HPP #include @@ -53,3 +54,8 @@ private: }; } + +#endif //LMDBAL_SERIALIZER_INT8_HPP + + + diff --git a/src/serializer/serializer_qbytearray.hpp b/src/serializer/serializer_qbytearray.hpp index 2d3f494..6f1c5d1 100644 --- a/src/serializer/serializer_qbytearray.hpp +++ b/src/serializer/serializer_qbytearray.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_SERIALIZER_QBYTEARRAY_HPP +#define LMDBAL_SERIALIZER_QBYTEARRAY_HPP #include @@ -34,17 +35,7 @@ public: return value; }; void deserialize(const MDB_val& data, QByteArray& result) { -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - if (data.mv_size > static_cast(std::numeric_limits::max())) - throw std::runtime_error("Data size exceeds QByteArray capacity"); - - result.setRawData(static_cast(data.mv_data), static_cast(data.mv_size)); -#else - if (data.mv_size > static_cast(std::numeric_limits::max())) - throw std::runtime_error("Data size exceeds QByteArray capacity"); - - result.setRawData(static_cast(data.mv_data), static_cast(data.mv_size)); -#endif + result.setRawData((char*)data.mv_data, data.mv_size); } MDB_val setData(const QByteArray& data) { value = data; @@ -53,7 +44,7 @@ public: MDB_val getData() { MDB_val result; result.mv_data = value.data(); - result.mv_size = static_cast(value.size()); + result.mv_size = value.size(); return result; }; void clear() { @@ -65,3 +56,9 @@ private: }; } + +#endif //LMDBAL_SERIALIZER_QBYTEARRAY_HPP + + + + diff --git a/src/serializer/serializer_qstring.hpp b/src/serializer/serializer_qstring.hpp index a00aea5..1a36fcb 100644 --- a/src/serializer/serializer_qstring.hpp +++ b/src/serializer/serializer_qstring.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_SERIALIZER_QSTRING_HPP +#define LMDBAL_SERIALIZER_QSTRING_HPP #include #include @@ -31,33 +32,11 @@ public: ~Serializer() {}; QString deserialize(const MDB_val& data) { -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - if (data.mv_size > static_cast(std::numeric_limits::max())) - throw std::runtime_error("Data size exceeds QByteArray capacity"); - - value = QByteArray(static_cast(data.mv_data), static_cast(data.mv_size)); -#else - if (data.mv_size > static_cast(std::numeric_limits::max())) - throw std::runtime_error("Data size exceeds QByteArray capacity"); - - value = QByteArray(static_cast(data.mv_data), static_cast(data.mv_size)); -#endif - + value = QByteArray((char*)data.mv_data, data.mv_size); return QString::fromUtf8(value); }; void deserialize(const MDB_val& data, QString& result) { -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - if (data.mv_size > static_cast(std::numeric_limits::max())) - throw std::runtime_error("Data size exceeds QByteArray capacity"); - - value = QByteArray(static_cast(data.mv_data), static_cast(data.mv_size)); -#else - if (data.mv_size > static_cast(std::numeric_limits::max())) - throw std::runtime_error("Data size exceeds QByteArray capacity"); - - value = QByteArray(static_cast(data.mv_data), static_cast(data.mv_size)); -#endif - + value = QByteArray((char*)data.mv_data, data.mv_size); result = QString::fromUtf8(value); } MDB_val setData(const QString& data) { @@ -67,7 +46,7 @@ public: MDB_val getData() { MDB_val result; result.mv_data = value.data(); - result.mv_size = static_cast(value.size()); + result.mv_size = value.size(); return result; }; void clear() {}; //not possible; @@ -76,4 +55,11 @@ private: QByteArray value; }; -} \ No newline at end of file +} + +#endif //LMDBAL_SERIALIZER_QSTRING_HPP + + + + + diff --git a/src/serializer/serializer_stdstring.hpp b/src/serializer/serializer_stdstring.hpp index 659cb89..416cfd6 100644 --- a/src/serializer/serializer_stdstring.hpp +++ b/src/serializer/serializer_stdstring.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_SERIALIZER_STDSTRING_HPP +#define LMDBAL_SERIALIZER_STDSTRING_HPP #include @@ -34,7 +35,7 @@ public: return value; }; void deserialize(const MDB_val& data, std::string& result) { - result.assign(static_cast(data.mv_data), data.mv_size); + result.assign((char*)data.mv_data, data.mv_size); } MDB_val setData(const std::string& data) { value = data; @@ -42,7 +43,7 @@ public: }; MDB_val getData() { MDB_val result; - result.mv_data = const_cast(value.c_str()); + result.mv_data = (char*)value.c_str(); result.mv_size = value.size(); return result; }; @@ -53,3 +54,9 @@ private: }; } + +#endif //LMDBAL_SERIALIZER_STDSTRING_HPP + + + + diff --git a/src/serializer/serializer_uint16.hpp b/src/serializer/serializer_uint16.hpp index 8284f27..d648695 100644 --- a/src/serializer/serializer_uint16.hpp +++ b/src/serializer/serializer_uint16.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_SERIALIZER_UINT16_HPP +#define LMDBAL_SERIALIZER_UINT16_HPP #include @@ -53,3 +54,7 @@ private: }; } + +#endif //LMDBAL_SERIALIZER_UINT16_HPP + + diff --git a/src/serializer/serializer_uint32.hpp b/src/serializer/serializer_uint32.hpp index 83e9d35..211f151 100644 --- a/src/serializer/serializer_uint32.hpp +++ b/src/serializer/serializer_uint32.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_SERIALIZER_UINT32_HPP +#define LMDBAL_SERIALIZER_UINT32_HPP #include @@ -53,3 +54,5 @@ private: }; } + +#endif //LMDBAL_SERIALIZER_UINT32_HPP diff --git a/src/serializer/serializer_uint64.hpp b/src/serializer/serializer_uint64.hpp index f486082..250ec03 100644 --- a/src/serializer/serializer_uint64.hpp +++ b/src/serializer/serializer_uint64.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_SERIALIZER_UINT64_HPP +#define LMDBAL_SERIALIZER_UINT64_HPP #include @@ -53,3 +54,6 @@ private: }; } + +#endif //LMDBAL_SERIALIZER_UINT64_HPP + diff --git a/src/serializer/serializer_uint8.hpp b/src/serializer/serializer_uint8.hpp index 0bc73ab..31daca6 100644 --- a/src/serializer/serializer_uint8.hpp +++ b/src/serializer/serializer_uint8.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_SERIALIZER_UINT8_HPP +#define LMDBAL_SERIALIZER_UINT8_HPP #include @@ -53,3 +54,8 @@ private: }; } + +#endif //LMDBAL_SERIALIZER_UINT8_HPP + + + diff --git a/src/session.cpp b/src/session.cpp deleted file mode 100644 index a02a745..0000000 --- a/src/session.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* -* LMDB Abstraction Layer. - * Copyright (C) 2023 Yury Gubich - * - * 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 . - */ - -#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; -} - diff --git a/src/session.h b/src/session.h deleted file mode 100644 index c0f8da8..0000000 --- a/src/session.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -* LMDB Abstraction Layer. - * Copyright (C) 2023 Yury Gubich - * - * 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 . - */ - -#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(); -}; - -} diff --git a/src/storagecommon.cpp b/src/storage.cpp similarity index 64% rename from src/storagecommon.cpp rename to src/storage.cpp index 4feaaa7..cea474d 100644 --- a/src/storagecommon.cpp +++ b/src/storage.cpp @@ -16,20 +16,18 @@ * along with this program. If not, see . */ -#include "storagecommon.h" - -#include "cursorcommon.h" +#include "storage.h" #define UNUSED(x) (void)(x) /** - * \class LMDBAL::StorageCommon + * \class LMDBAL::iStorage * * \brief Storage interface * - * 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 - * from the heir code + * This is a 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 + * from the heirs code */ /** @@ -39,7 +37,7 @@ * \param[in] name - the name of the storage * \param[in] duplicates - true if key duplicates are allowed (false by default) */ -LMDBAL::StorageCommon::StorageCommon(Base* parent, const std::string& name, bool duplicates): +LMDBAL::iStorage::iStorage(Base* parent, const std::string& name, bool duplicates): dbi(), db(parent), name(name), @@ -49,12 +47,12 @@ LMDBAL::StorageCommon::StorageCommon(Base* parent, const std::string& name, bool /** * \brief Destroys a storage interface */ -LMDBAL::StorageCommon::~StorageCommon () = default; +LMDBAL::iStorage::~iStorage() {} /** - * \brief A private virtual function to close each storage in the database + * \brief A private virtual function I need to close each storage in the database */ -void LMDBAL::StorageCommon::close() { +void LMDBAL::iStorage::close() { mdb_dbi_close(db->environment, dbi); } @@ -69,7 +67,7 @@ void LMDBAL::StorageCommon::close() { * * \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error */ -LMDBAL::TransactionID LMDBAL::StorageCommon::extractTransactionId(const Transaction& txn, const std::string& action) const { +LMDBAL::TransactionID LMDBAL::iStorage::extractTransactionId(const Transaction& txn, const std::string& action) const { if (!txn.isActive()) throw TransactionTerminated(db->name, name, action); @@ -84,11 +82,11 @@ LMDBAL::TransactionID LMDBAL::StorageCommon::extractTransactionId(const Transact * \exception LMDBAL::Closed thrown if the database was closed * \exception LMDBAL::Unknown thrown if something unexpected happened */ -void LMDBAL::StorageCommon::drop() { +void LMDBAL::iStorage::drop() { ensureOpened(dropMethodName); TransactionID txn = beginTransaction(); - int rc = StorageCommon::drop(txn); + int rc = iStorage::drop(txn); if (rc != MDB_SUCCESS) { abortTransaction(txn); throw Unknown(db->name, mdb_strerror(rc), name); @@ -103,10 +101,10 @@ void LMDBAL::StorageCommon::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_ code otherwise */ -int LMDBAL::StorageCommon::drop(TransactionID transaction) { +int LMDBAL::iStorage::drop(TransactionID transaction) { return mdb_drop(transaction, dbi, 0); } @@ -115,24 +113,24 @@ int LMDBAL::StorageCommon::drop(TransactionID transaction) { * * 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_ code otherwise * * \exception LMDBAL::TransactionTerminated thrown if the transaction was not active */ -int LMDBAL::StorageCommon::drop(const WriteTransaction& txn) { +int LMDBAL::iStorage::drop(const WriteTransaction& txn) { ensureOpened(dropMethodName); return drop(extractTransactionId(txn, dropMethodName)); } /** - * \brief Helper function; throws an exception if the database is not opened + * \brief Helper function, thows 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 * * \exception LMDBAL::Closed thrown if the database was closed */ -void LMDBAL::StorageCommon::ensureOpened(const std::string& methodName) const { +void LMDBAL::iStorage::ensureOpened(const std::string& methodName) const { if (!isDBOpened()) throw Closed(methodName, db->name, name); } @@ -140,12 +138,12 @@ void LMDBAL::StorageCommon::ensureOpened(const std::string& methodName) const { /** * \brief Storage size * - * \returns number of records in the storage + * \returns amount of records in the storage * * \exception LMDBAL::Closed thrown if the database was closed * \exception LMDBAL::Unknown thrown if something unexpected happened */ -LMDBAL::SizeType LMDBAL::StorageCommon::count() const { +LMDBAL::SizeType LMDBAL::iStorage::count() const { ensureOpened(countMethodName); TransactionID txn = beginReadOnlyTransaction(); @@ -161,94 +159,34 @@ LMDBAL::SizeType LMDBAL::StorageCommon::count() const { 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) * - * \param[in] txn - transaction ID; can be read-only transaction - * \returns number of records in the storage + * \param[in] txn - transaction ID, can be read-only transaction + * \returns amount of records in the storage * * \exception LMDBAL::Unknown thrown if something unexpected happened */ -LMDBAL::SizeType LMDBAL::StorageCommon::count(TransactionID txn) const { +LMDBAL::SizeType LMDBAL::iStorage::count(TransactionID txn) const { MDB_stat stat; - if (const int rc = mdb_stat(txn, dbi, &stat); rc != MDB_SUCCESS) + int rc = mdb_stat(txn, dbi, &stat); + if (rc != MDB_SUCCESS) throw Unknown(db->name, mdb_strerror(rc), name); - if (stat.ms_entries > std::numeric_limits::max()) - throw Unknown(db->name, "Storage size exceeds it's limits", name); - - return static_cast(stat.ms_entries); + return stat.ms_entries; } /** * \brief Storage size (public transaction variant) * - * \param[in] txn - transaction; can be read-only transaction - * \returns number of records in the storage + * \param[in] txn - transaction, can be read-only transaction + * \returns amount of records in the storage * * \exception LMDBAL::Closed thrown if the database was closed * \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 */ -LMDBAL::SizeType LMDBAL::StorageCommon::count(const Transaction& txn) const { +LMDBAL::SizeType LMDBAL::iStorage::count(const Transaction& txn) const { ensureOpened(countMethodName); return count(extractTransactionId(txn, countMethodName)); } @@ -265,7 +203,7 @@ LMDBAL::SizeType LMDBAL::StorageCommon::count(const Transaction& txn) const { * \exception LMDBAL::Exist thrown if rc == MDB_KEYEXIST * \exception LMDBAL::Unknown thrown if rc != MDB_KEYEXIST */ -void LMDBAL::StorageCommon::throwDuplicateOrUnknown(int rc, TransactionID txn, const std::string& key) const { +void LMDBAL::iStorage::throwDuplicateOrUnknown(int rc, TransactionID txn, const std::string& key) const { abortTransaction(txn); throwDuplicateOrUnknown(rc, key); } @@ -282,7 +220,7 @@ void LMDBAL::StorageCommon::throwDuplicateOrUnknown(int rc, TransactionID txn, c * \exception LMDBAL::NotFound thrown if rc == MDB_NOTFOUND * \exception LMDBAL::Unknown thrown if rc != MDB_NOTFOUND */ -void LMDBAL::StorageCommon::throwNotFoundOrUnknown(int rc, LMDBAL::TransactionID txn, const std::string& key) const { +void LMDBAL::iStorage::throwNotFoundOrUnknown(int rc, LMDBAL::TransactionID txn, const std::string& key) const { abortTransaction(txn); throwNotFoundOrUnknown(rc, key); } @@ -298,7 +236,7 @@ void LMDBAL::StorageCommon::throwNotFoundOrUnknown(int rc, LMDBAL::TransactionID * \exception LMDBAL::Exist thrown if rc == MDB_KEYEXIST * \exception LMDBAL::Unknown thrown if rc != MDB_KEYEXIST */ -void LMDBAL::StorageCommon::throwDuplicateOrUnknown(int rc, const std::string& key) const { +void LMDBAL::iStorage::throwDuplicateOrUnknown(int rc, const std::string& key) const { if (rc == MDB_KEYEXIST) throwDuplicate(key); else @@ -316,7 +254,7 @@ void LMDBAL::StorageCommon::throwDuplicateOrUnknown(int rc, const std::string& k * \exception LMDBAL::NotFound thrown if rc == MDB_NOTFOUND * \exception LMDBAL::Unknown thrown if rc != MDB_NOTFOUND */ -void LMDBAL::StorageCommon::throwNotFoundOrUnknown(int rc, const std::string& key) const { +void LMDBAL::iStorage::throwNotFoundOrUnknown(int rc, const std::string& key) const { if (rc == MDB_NOTFOUND) throwNotFound(key); else @@ -333,7 +271,7 @@ void LMDBAL::StorageCommon::throwNotFoundOrUnknown(int rc, const std::string& ke * * \exception LMDBAL::Unknown thrown everytime */ -void LMDBAL::StorageCommon::throwUnknown(int rc, LMDBAL::TransactionID txn) const { +void LMDBAL::iStorage::throwUnknown(int rc, LMDBAL::TransactionID txn) const { abortTransaction(txn); throwUnknown(rc); } @@ -345,7 +283,7 @@ void LMDBAL::StorageCommon::throwUnknown(int rc, LMDBAL::TransactionID txn) cons * * \returns database name */ -const std::string & LMDBAL::StorageCommon::dbName() const { +const std::string & LMDBAL::iStorage::dbName() const { return db->name;} /** @@ -355,8 +293,8 @@ const std::string & LMDBAL::StorageCommon::dbName() const { * * \returns true if database is ipened, false otherwise */ -bool LMDBAL::StorageCommon::isDBOpened() const { - return db->opened();} +bool LMDBAL::iStorage::isDBOpened() const { + return db->opened;} /** * \brief Throws LMDBAL::Unknown @@ -367,7 +305,7 @@ bool LMDBAL::StorageCommon::isDBOpened() const { * * \exception LMDBAL::Unknown thrown everytime */ -void LMDBAL::StorageCommon::throwUnknown(int rc) const { +void LMDBAL::iStorage::throwUnknown(int rc) const { throw Unknown(db->name, mdb_strerror(rc), name);} /** @@ -379,7 +317,7 @@ void LMDBAL::StorageCommon::throwUnknown(int rc) const { * * \exception LMDBAL::Unknown thrown everytime */ -void LMDBAL::StorageCommon::throwUnknown(const std::string& message) const { +void LMDBAL::iStorage::throwUnknown(const std::string& message) const { throw Unknown(db->name, message, name);} /** @@ -391,7 +329,7 @@ void LMDBAL::StorageCommon::throwUnknown(const std::string& message) const { * * \exception LMDBAL::Exist thrown everytime */ -void LMDBAL::StorageCommon::throwDuplicate(const std::string& key) const { +void LMDBAL::iStorage::throwDuplicate(const std::string& key) const { throw Exist(key, db->name, name);} /** @@ -403,7 +341,7 @@ void LMDBAL::StorageCommon::throwDuplicate(const std::string& key) const { * * \exception LMDBAL::NotFound thrown everytime */ -void LMDBAL::StorageCommon::throwNotFound(const std::string& key) const { +void LMDBAL::iStorage::throwNotFound(const std::string& key) const { throw NotFound(key, db->name, name);} /** @@ -415,7 +353,7 @@ void LMDBAL::StorageCommon::throwNotFound(const std::string& key) const { * * \exception LMDBAL::CursorNotReady thrown everytime */ -void LMDBAL::StorageCommon::throwCursorNotReady(const std::string& method) const { +void LMDBAL::iStorage::throwCursorNotReady(const std::string& method) const { throw CursorNotReady(method, db->name, name);} /** @@ -425,7 +363,7 @@ void LMDBAL::StorageCommon::throwCursorNotReady(const std::string& method) const * * \returns read only transaction */ -LMDBAL::TransactionID LMDBAL::StorageCommon::beginReadOnlyTransaction() const { +LMDBAL::TransactionID LMDBAL::iStorage::beginReadOnlyTransaction() const { return db->beginPrivateReadOnlyTransaction(name);} /** @@ -435,7 +373,7 @@ LMDBAL::TransactionID LMDBAL::StorageCommon::beginReadOnlyTransaction() const { * * \returns read only transaction */ -LMDBAL::TransactionID LMDBAL::StorageCommon::beginTransaction() const { +LMDBAL::TransactionID LMDBAL::iStorage::beginTransaction() const { return db->beginPrivateTransaction(name);} /** @@ -443,7 +381,7 @@ LMDBAL::TransactionID LMDBAL::StorageCommon::beginTransaction() const { * * Ment to be called from heirs, name is reported to the database just to be displayed in std::exception::what() message */ -void LMDBAL::StorageCommon::abortTransaction(LMDBAL::TransactionID id) const { +void LMDBAL::iStorage::abortTransaction(LMDBAL::TransactionID id) const { db->abortPrivateTransaction(id, name);} /** @@ -453,7 +391,7 @@ void LMDBAL::StorageCommon::abortTransaction(LMDBAL::TransactionID id) const { * * \exception LMDBAL::Unknown thrown if something unexpected happened */ -void LMDBAL::StorageCommon::commitTransaction(LMDBAL::TransactionID id) { +void LMDBAL::iStorage::commitTransaction(LMDBAL::TransactionID id) { db->commitPrivateTransaction(id, name);} /** @@ -468,7 +406,7 @@ void LMDBAL::StorageCommon::commitTransaction(LMDBAL::TransactionID id) { * \param[in] txn - ID of started transaction * \param[in] readOnly - true if transaction is read-only, false otherwise */ -void LMDBAL::StorageCommon::transactionStarted(LMDBAL::TransactionID txn, bool readOnly) const { +void LMDBAL::iStorage::transactionStarted(LMDBAL::TransactionID txn, bool readOnly) const { UNUSED(txn); UNUSED(readOnly); } @@ -484,7 +422,7 @@ void LMDBAL::StorageCommon::transactionStarted(LMDBAL::TransactionID txn, bool r * * \param[in] txn - ID of started transaction */ -void LMDBAL::StorageCommon::transactionCommited(LMDBAL::TransactionID txn) { +void LMDBAL::iStorage::transactionCommited(LMDBAL::TransactionID txn) { UNUSED(txn);} /** @@ -498,7 +436,7 @@ void LMDBAL::StorageCommon::transactionCommited(LMDBAL::TransactionID txn) { * * \param[in] txn - ID of started transaction */ -void LMDBAL::StorageCommon::transactionAborted(LMDBAL::TransactionID txn) const { +void LMDBAL::iStorage::transactionAborted(LMDBAL::TransactionID txn) const { UNUSED(txn);} /** @@ -507,64 +445,65 @@ void LMDBAL::StorageCommon::transactionAborted(LMDBAL::TransactionID txn) const * It's a protected method that is called to optimise drop process * after the transaction is commited. Used just for optimisations. */ -void LMDBAL::StorageCommon::handleDrop() {} +void LMDBAL::iStorage::handleDrop() {} -int LMDBAL::StorageCommon::_mdbOpen(MDB_txn *txn, unsigned int flags) { +int LMDBAL::iStorage::_mdbOpen(MDB_txn *txn, unsigned int flags) { return mdb_dbi_open(txn, name.c_str(), flags, &dbi); } -int LMDBAL::StorageCommon::_mdbPut(MDB_txn* txn, MDB_val& key, MDB_val& data, unsigned int flags) { +int LMDBAL::iStorage::_mdbPut(MDB_txn* txn, MDB_val& key, MDB_val& data, unsigned int flags) { return mdb_put(txn, dbi, &key, &data, flags); } -int LMDBAL::StorageCommon::_mdbGet(MDB_txn* txn, MDB_val& key, MDB_val& data) const { +int LMDBAL::iStorage::_mdbGet(MDB_txn* txn, MDB_val& key, MDB_val& data) const { return mdb_get(txn, dbi, &key, &data); } -int LMDBAL::StorageCommon::_mdbDel(MDB_txn* txn, MDB_val& key) { +int LMDBAL::iStorage::_mdbDel(MDB_txn* txn, MDB_val& key) { return mdb_del(txn, dbi, &key, NULL); } -int LMDBAL::StorageCommon::_mdbDel(MDB_txn* txn, MDB_val& key, MDB_val& data) { +int LMDBAL::iStorage::_mdbDel(MDB_txn* txn, MDB_val& key, MDB_val& data) { return mdb_del(txn, dbi, &key, &data); } -int LMDBAL::StorageCommon::_mdbStat(MDB_txn* txn, MDB_stat& stat) const { +int LMDBAL::iStorage::_mdbStat(MDB_txn* txn, MDB_stat& stat) const { return mdb_stat(txn, dbi, &stat); } -int LMDBAL::StorageCommon::_mdbFlags(MDB_txn* txn, uint32_t& flags) const { +int LMDBAL::iStorage::_mdbFlags(MDB_txn* txn, uint32_t& flags) const { 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); } -void LMDBAL::StorageCommon::_mdbCursorClose(MDB_cursor *cursor) const { +void LMDBAL::iStorage::_mdbCursorClose(MDB_cursor *cursor) const { mdb_cursor_close(cursor); } -int LMDBAL::StorageCommon::_mdbCursorGet(MDB_cursor* cursor, MDB_val& key, MDB_val& data, MDB_cursor_op operation) const { +int LMDBAL::iStorage::_mdbCursorGet(MDB_cursor* cursor, MDB_val& key, MDB_val& data, MDB_cursor_op operation) const { return mdb_cursor_get(cursor, &key, &data, operation); } -int LMDBAL::StorageCommon::_mdbCursorSet(MDB_cursor* cursor, MDB_val& key) const { +int LMDBAL::iStorage::_mdbCursorSet(MDB_cursor* cursor, MDB_val& key) const { return mdb_cursor_get(cursor, &key, NULL, MDB_SET); } -int LMDBAL::StorageCommon::_mdbCursorDel(MDB_cursor* cursor, unsigned int flags) { +int LMDBAL::iStorage::_mdbCursorDel(MDB_cursor* cursor, unsigned int flags) { return mdb_cursor_del(cursor, flags); } -int LMDBAL::StorageCommon::_mdbCursorPut(MDB_cursor* cursor, MDB_val& key, MDB_val& data, unsigned int flags) { +int LMDBAL::iStorage::_mdbCursorPut(MDB_cursor* cursor, MDB_val& key, MDB_val& data, unsigned int flags) { return mdb_cursor_put(cursor, &key, &data, flags); } -int LMDBAL::StorageCommon::_mdbCursorRenew(MDB_txn* txn, MDB_cursor* cursor) const { +int LMDBAL::iStorage::_mdbCursorRenew(MDB_txn* txn, MDB_cursor* cursor) const { return mdb_cursor_renew(txn, cursor); } -MDB_txn* LMDBAL::StorageCommon::_mdbCursorTxn(MDB_cursor* cursor) const { +MDB_txn* LMDBAL::iStorage::_mdbCursorTxn(MDB_cursor* cursor) const { return mdb_cursor_txn(cursor); } diff --git a/src/storage.h b/src/storage.h index 844beb2..82735a7 100644 --- a/src/storage.h +++ b/src/storage.h @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_STORAGE_H +#define LMDBAL_STORAGE_H #include #include @@ -25,7 +26,6 @@ #include "serializer.h" #include "cursor.h" #include "transaction.h" -#include "storagecommon.h" class BaseTest; class DuplicatesTest; @@ -34,8 +34,105 @@ class StorageCursorTest; 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 mdb_dbi_open + * \returns MDB_SUCCESS if everything went smooth or MDB_ -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 + int makeStorage(MDB_txn* transaction, bool duplicates = false); + + template + static std::string toString(const T& value); +}; + template -class Storage : public StorageCommon { +class Storage : public iStorage { friend class ::BaseTest; friend class ::DuplicatesTest; friend class ::CacheCursorTest; @@ -63,7 +160,7 @@ protected: virtual uint32_t addRecords(const std::map& data, TransactionID txn, bool overwrite = false); public: - using StorageCommon::drop; + using iStorage::drop; virtual void addRecord(const K& key, const V& value); 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 @@ -102,3 +199,5 @@ protected: } #include "storage.hpp" + +#endif //LMDBAL_STORAGE_H diff --git a/src/storage.hpp b/src/storage.hpp index 7f89fc2..91756b3 100644 --- a/src/storage.hpp +++ b/src/storage.hpp @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef LMDBAL_STORAGE_HPP +#define LMDBAL_STORAGE_HPP #include "storage.h" #include "exceptions.h" @@ -46,7 +47,7 @@ */ template LMDBAL::Storage::Storage(Base* parent, const std::string& name, bool duplicates): - StorageCommon(parent, name, duplicates), + iStorage(parent, name, duplicates), keySerializer(), valueSerializer(), cursors() @@ -1015,7 +1016,7 @@ void LMDBAL::Storage::close() { for (const std::pair*>& pair : cursors) pair.second->terminated(); - StorageCommon::close(); + iStorage::close(); } /** @@ -1051,7 +1052,7 @@ void LMDBAL::Storage::destroyCursor(LMDBAL::Cursor& cursor) { cursor.close(); cursors.erase(itr); - cursor.reset(); + cursor.freed(); } /** @@ -1103,3 +1104,83 @@ void LMDBAL::Storage::discoveredRecord(const K& key, const V& value, Trans UNUSED(value); UNUSED(txn); } + +/** + * \brief A functiion to actually open MDB_dbi storage + * + * \tparam K type of keys in opening storage + * + * \param[in] transaction - lmdb transaction to call mdb_dbi_open, 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_ -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 +inline int LMDBAL::iStorage::makeStorage(MDB_txn* transaction, bool duplicates) { + unsigned int flags = MDB_CREATE; + if constexpr (std::is_integral::value) + flags |= MDB_INTEGERKEY; + + if (duplicates) { + flags |= MDB_DUPSORT; + + if constexpr (std::is_scalar::value) + flags |= MDB_DUPFIXED; + + if constexpr ( + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::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 +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 diff --git a/src/storagecommon.h b/src/storagecommon.h deleted file mode 100644 index d54c8d2..0000000 --- a/src/storagecommon.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * LMDB Abstraction Layer. - * Copyright (C) 2023 Yury Gubich - * - * 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 . - */ - -#pragma once - -#include -#include - -#include - -#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 mdb_dbi_open - * \returns MDB_SUCCESS if everything went smooth or MDB_ -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 - int makeStorage(MDB_txn* transaction, bool duplicates = false); - - template - static std::string toString(const T& value); -}; - -} - -#include "storagecommon.hpp" diff --git a/src/storagecommon.hpp b/src/storagecommon.hpp deleted file mode 100644 index c32ae6f..0000000 --- a/src/storagecommon.hpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * LMDB Abstraction Layer. - * Copyright (C) 2023 Yury Gubich - * - * 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 . - */ - -#pragma once - -#include "storagecommon.h" - -/** - * \brief A functiion to actually open MDB_dbi storage - * - * \tparam K type of keys in opening storage - * - * \param[in] transaction - lmdb transaction to call mdb_dbi_open, 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_ -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 -inline int LMDBAL::StorageCommon::makeStorage(MDB_txn* transaction, bool duplicates) { - unsigned int flags = MDB_CREATE; - if constexpr (std::is_integral::value) - flags |= MDB_INTEGERKEY; - - if (duplicates) { - flags |= MDB_DUPSORT; - - if constexpr (std::is_scalar::value) - flags |= MDB_DUPFIXED; - - if constexpr ( - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::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 -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; -} diff --git a/src/transaction.cpp b/src/transaction.cpp index f92e7e8..3632ad6 100644 --- a/src/transaction.cpp +++ b/src/transaction.cpp @@ -1,25 +1,5 @@ -/* - * LMDB Abstraction Layer. - * Copyright (C) 2023 Yury Gubich - * - * 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 . - */ - #include "transaction.h" -#include "cursorcommon.h" - /** * \class LMDBAL::Transaction * \brief Public read only transaction @@ -42,8 +22,7 @@ LMDBAL::Transaction::Transaction(): txn(nullptr), active(false), - parent(nullptr), - cursors() + parent(nullptr) {} /** @@ -52,11 +31,8 @@ LMDBAL::Transaction::Transaction(): LMDBAL::Transaction::Transaction(TransactionID txn, const Base* parent) : txn(txn), active(true), - parent(parent), - cursors() -{ - parent->transactions[txn] = this; -} + parent(parent) +{} /** * \brief Moves transaction to a new object @@ -64,14 +40,9 @@ LMDBAL::Transaction::Transaction(TransactionID txn, const Base* parent) : LMDBAL::Transaction::Transaction(Transaction&& other): txn(other.txn), active(other.active), - parent(other.parent), - cursors(other.cursors) + parent(other.parent) { - if (active) { - parent->transactions[txn] = this; - - other.reset(); - } + other.active = false; } /** @@ -85,21 +56,13 @@ LMDBAL::Transaction::~Transaction() { * \brief Move-assigns transaction to the new object */ LMDBAL::Transaction& LMDBAL::Transaction::operator=(Transaction&& other) { - if (this == &other) - return *this; - terminate(); txn = other.txn; active = other.active; parent = other.parent; - cursors = other.cursors; - if (active) { - parent->transactions[txn] = this; - - other.reset(); - } + other.active = false; return *this; } @@ -111,32 +74,11 @@ LMDBAL::Transaction& LMDBAL::Transaction::operator=(Transaction&& other) { */ void LMDBAL::Transaction::terminate() { if (active) { - closeCursors(); parent->abortTransaction(txn); - parent->transactions.erase(txn); - reset(); + active = false; } } - -/** - * \brief Resets inner transaction properties to inactive state - */ -void LMDBAL::Transaction::reset() { - active = false; - txn = nullptr; - parent = nullptr; - cursors.clear(); -} - -/** - * \brief Closes attached curors; - */ -void LMDBAL::Transaction::closeCursors () { - for (const std::pair& pair : cursors) - pair.second->terminated(); -} - /** * \brief Returns transaction states * @@ -190,12 +132,6 @@ LMDBAL::WriteTransaction::WriteTransaction(WriteTransaction&& other): Transaction(std::move(other)) {} -LMDBAL::WriteTransaction& LMDBAL::WriteTransaction::operator=(WriteTransaction&& other) { - Transaction::operator=(std::move(other)); - - return *this; -} - /** * \brief Aborts transaction cancelling all changes * @@ -212,9 +148,7 @@ void LMDBAL::WriteTransaction::abort() { */ void LMDBAL::WriteTransaction::commit() { if (active) { - closeCursors(); const_cast(parent)->commitTransaction(txn); - parent->transactions.erase(txn); - reset(); + active = false; } } diff --git a/src/transaction.h b/src/transaction.h index 4866f17..43bb69e 100644 --- a/src/transaction.h +++ b/src/transaction.h @@ -1,33 +1,13 @@ -/* - * LMDB Abstraction Layer. - * Copyright (C) 2023 Yury Gubich - * - * 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 . - */ - #pragma once #include "base.h" namespace LMDBAL { -class StorageCommon; -class CursorCommon; +class iStorage; class Transaction { friend class Base; - friend class StorageCommon; - friend class CursorCommon; + friend class iStorage; public: explicit Transaction(); explicit Transaction(Transaction&& other); @@ -41,14 +21,11 @@ public: protected: Transaction(TransactionID txn, const Base* parent); - void reset(); - void closeCursors(); protected: - TransactionID txn; /**<\brief Transaction inner handler*/ - bool active; /**<\brief Transaction state*/ - const Base* parent; /**<\brief Pointer to the database this transaction belongs to*/ - mutable std::map cursors; /**<\brief a collection of cursors curently opened under this transaction*/ + TransactionID txn; /**<\brief Transaction inner handler*/ + bool active; /**<\brief Transaction state*/ + const Base* parent; /**<\brief Pointer to the database this transaction belongs to*/ }; class WriteTransaction : public Transaction { @@ -58,7 +35,6 @@ public: explicit WriteTransaction(WriteTransaction&& other); WriteTransaction(const WriteTransaction& other) = delete; WriteTransaction& operator = (const WriteTransaction& other) = delete; - WriteTransaction& operator = (WriteTransaction&& other); void commit(); void abort(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 63e0701..93d54e3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,7 +20,7 @@ target_include_directories(runUnitTests PRIVATE ${Qt${QT_VERSION_MAJOR}Core_INCL target_link_libraries( runUnitTests GTest::gtest_main - ${LMDBAL_NAME} + ${PROJECT_NAME} Qt${QT_VERSION_MAJOR}::Core ) include(GoogleTest) diff --git a/test/basic.cpp b/test/basic.cpp index 36880b5..bf0bf39 100644 --- a/test/basic.cpp +++ b/test/basic.cpp @@ -3,7 +3,6 @@ #include "base.h" #include "storage.h" #include "cache.h" -#include "session.h" #include #include @@ -35,14 +34,13 @@ protected: } static void TearDownTestSuite() { - session.close(); + db->close(); db->removeDirectory(); delete db; db = nullptr; } static LMDBAL::Base* db; - static LMDBAL::Session session; LMDBAL::Storage* t1; LMDBAL::Storage* t2; @@ -52,20 +50,19 @@ protected: LMDBAL::Base* BaseTest::db = nullptr; -LMDBAL::Session BaseTest::session; TEST_F(BaseTest, RemovingDirectory) { EXPECT_EQ(db->removeDirectory(), true); } TEST_F(BaseTest, OpeningClosingDatabase) { - EXPECT_EQ(db->opened(), false); - session = db->open(); - EXPECT_EQ(db->opened(), true); - session.close(); - EXPECT_EQ(db->opened(), false); - session = db->open(); - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), false); + db->open(); + EXPECT_EQ(db->ready(), true); + db->close(); + EXPECT_EQ(db->ready(), false); + db->open(); + EXPECT_EQ(db->ready(), true); } TEST_F(BaseTest, Flags) { @@ -96,7 +93,7 @@ TEST_F(BaseTest, Flags) { } TEST_F(BaseTest, AddingIntegerKey) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); t1->addRecord(1, 2); t1->addRecord(2, 2); t1->addRecord(3, 15); @@ -104,7 +101,7 @@ TEST_F(BaseTest, AddingIntegerKey) { } TEST_F(BaseTest, AddingQStringKey) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); t2->addRecord("hello", "world"); t2->addRecord("aaa", "gagdfsdf"); t2->addRecord("sdfhga", "DSFFDG"); @@ -113,16 +110,17 @@ TEST_F(BaseTest, AddingQStringKey) { } TEST_F(BaseTest, AddingKeysToCache) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); c1->addRecord(2, "blah balah"); c1->addRecord(-4, "testing goes brrr"); - c1->addRecord(40, "whatever"); + c1->addRecord(140, "whatever"); c1->addRecord(-37, "aaaaa tss tsss tsss tsss aaaaaaa"); - EXPECT_EQ(c1->getRecord(40), "whatever"); + EXPECT_EQ(c1->getRecord(140), "whatever"); + EXPECT_EQ(c1->getRecord(-116), "whatever"); } TEST_F(BaseTest, AddingKeysToVariableCache) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); c2->addRecord("regrets", "blah balah"); c2->addRecord("fossil fingers", 842); c2->addRecord("preloaded cut", 539.75); @@ -135,7 +133,7 @@ TEST_F(BaseTest, AddingKeysToVariableCache) { } TEST_F(BaseTest, AddingRepeatingKey) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); EXPECT_THROW(t1->addRecord(3, 24), LMDBAL::Exist); EXPECT_EQ(t1->getRecord(3), 15); @@ -151,7 +149,7 @@ TEST_F(BaseTest, AddingRepeatingKey) { } TEST_F(BaseTest, GettingNotExistingKeys) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); EXPECT_THROW(t2->getRecord("almonds"), LMDBAL::NotFound); EXPECT_THROW(t1->getRecord(64), LMDBAL::NotFound); @@ -160,21 +158,21 @@ TEST_F(BaseTest, GettingNotExistingKeys) { } TEST_F(BaseTest, Persistence) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); uint32_t t1Size = t1->count(); uint32_t t2Size = t2->count(); uint32_t c1Size = c1->count(); uint32_t c2Size = c2->count(); + db->close(); delete db; - EXPECT_EQ(session.opened(), false); db = new LMDBAL::Base("testBase"); t1 = db->addStorage("table1"); t2 = db->addStorage("table2"); c1 = db->addCache("cache1"); c2 = db->addCache("cache2"); - session = db->open(); + db->open(); EXPECT_EQ(t1->count(), t1Size); EXPECT_EQ(t1->getRecord(3), 15); @@ -191,8 +189,8 @@ TEST_F(BaseTest, Persistence) { EXPECT_EQ(t2->count(), t2Size); EXPECT_EQ(c1->count(), c1Size); - EXPECT_EQ(c1->checkRecord(40), true); - EXPECT_EQ(c1->getRecord(40), "whatever"); + EXPECT_EQ(c1->checkRecord(-116), true); + EXPECT_EQ(c1->getRecord(-116), "whatever"); EXPECT_EQ(c1->checkRecord(-4), true); EXPECT_EQ(c1->getRecord(-4), "testing goes brrr"); EXPECT_EQ(c1->getRecord(-4), "testing goes brrr"); @@ -215,7 +213,7 @@ TEST_F(BaseTest, Persistence) { } TEST_F(BaseTest, CountAndDrop) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); EXPECT_EQ(t1->count(), 3); EXPECT_EQ(t2->count(), 4); EXPECT_EQ(c1->count(), 4); @@ -240,7 +238,7 @@ TEST_F(BaseTest, CountAndDrop) { } TEST_F(BaseTest, Change) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); EXPECT_EQ(t1->count(), 1); EXPECT_EQ(t2->count(), 1); EXPECT_EQ(c1->count(), 2); @@ -286,7 +284,7 @@ TEST_F(BaseTest, Change) { } TEST_F(BaseTest, Force) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); EXPECT_EQ(t1->forceRecord(58, 35), false); //changing EXPECT_EQ(t1->forceRecord(68, 36), true); //adding @@ -321,7 +319,7 @@ TEST_F(BaseTest, Force) { } TEST_F(BaseTest, ReadAll) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); std::map m1 = t1->readAll(); std::map m2 = t2->readAll(); @@ -352,7 +350,7 @@ TEST_F(BaseTest, ReadAll) { } TEST_F(BaseTest, ReplaceAll) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); t1->replaceAll({ {7, 48}, @@ -420,7 +418,7 @@ TEST_F(BaseTest, ReplaceAll) { } TEST_F(BaseTest, AddRecords) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); LMDBAL::SizeType s1 = t1->addRecords({ {5, 3}, diff --git a/test/cachecursor.cpp b/test/cachecursor.cpp index 64b4f42..7d9d440 100644 --- a/test/cachecursor.cpp +++ b/test/cachecursor.cpp @@ -4,7 +4,6 @@ #include "storage.h" #include "cache.h" #include "cursor.h" -#include "session.h" class CacheCursorTest : public ::testing::Test { protected: @@ -20,7 +19,7 @@ protected: db = new LMDBAL::Base("testBase"); db->addCache("table1"); db->addCache("empty"); - session = db->open(); + db->open(); } } @@ -31,7 +30,7 @@ protected: static void TearDownTestSuite() { cursor.drop(); transaction.terminate(); - session.close(); + db->close(); db->removeDirectory(); delete db; db = nullptr; @@ -40,7 +39,6 @@ protected: static LMDBAL::Base* db; static LMDBAL::Cursor cursor; static LMDBAL::Transaction transaction; - static LMDBAL::Session session; LMDBAL::Cache* cache; LMDBAL::Cache* emptyCache; @@ -49,7 +47,6 @@ protected: LMDBAL::Base* CacheCursorTest::db = nullptr; LMDBAL::Cursor CacheCursorTest::cursor; LMDBAL::Transaction CacheCursorTest::transaction; -LMDBAL::Session CacheCursorTest::session; static const std::map data({ {245665783, "bothering nerds"}, @@ -405,7 +402,7 @@ TEST_F(CacheCursorTest, CursorRAIIBehaviour) { TEST_F(CacheCursorTest, CornerCases) { transaction.terminate(); - EXPECT_THROW(cursor.current(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.current(), LMDBAL::Unknown); cursor.close(); LMDBAL::Cursor emptyCursor = emptyCache->createCursor(); @@ -439,40 +436,5 @@ TEST_F(CacheCursorTest, CornerCases) { EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); EXPECT_THROW(cursor.prev(), LMDBAL::NotFound); - - cursor.close(); } -TEST_F(CacheCursorTest, TerminatedTransaction) { - LMDBAL::Cursor 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()); -} diff --git a/test/cachetransaction.cpp b/test/cachetransaction.cpp index 5222852..e0bac76 100644 --- a/test/cachetransaction.cpp +++ b/test/cachetransaction.cpp @@ -4,7 +4,6 @@ #include "base.h" #include "cache.h" -#include "session.h" class CacheTransactionsTest : public testing::Test { protected: @@ -40,19 +39,18 @@ protected: db->addStorage("cache2"); } - session = db->open(); + db->open(); db->drop(); } static void TearDownTestSuite() { - session.close(); + db->close(); db->removeDirectory(); delete db; db = nullptr; } static LMDBAL::Base* db; - static LMDBAL::Session session; LMDBAL::Cache* c1; LMDBAL::Cache* c2; @@ -60,10 +58,9 @@ protected: LMDBAL::Base* CacheTransactionsTest::db = nullptr; -LMDBAL::Session CacheTransactionsTest::session; TEST_F(CacheTransactionsTest, Adding) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); EXPECT_EQ(c1->count(), 0); EXPECT_EQ(c2->count(), 0); @@ -93,7 +90,7 @@ TEST_F(CacheTransactionsTest, Adding) { } TEST_F(CacheTransactionsTest, Aborting) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); LMDBAL::SizeType s1 = c1->count(); LMDBAL::SizeType s2 = c2->count(); @@ -117,7 +114,7 @@ TEST_F(CacheTransactionsTest, Aborting) { } TEST_F(CacheTransactionsTest, Reading) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); LMDBAL::Transaction txn = db->beginReadOnlyTransaction(); @@ -135,7 +132,7 @@ TEST_F(CacheTransactionsTest, Reading) { } TEST_F(CacheTransactionsTest, ConcurentReading) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); LMDBAL::SizeType size = c1->count(); LMDBAL::WriteTransaction txn = db->beginTransaction(); @@ -171,7 +168,7 @@ TEST_F(CacheTransactionsTest, ConcurentReading) { TEST_F(CacheTransactionsTest, ConcurentModification) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); //if you start one writable transaction after another //in a single thread like so: @@ -185,7 +182,7 @@ TEST_F(CacheTransactionsTest, ConcurentModification) { int pid = fork(); if (pid == 0) { // I am the child - usleep(5); + usleep(1); std::cout << "beggining second transaction" << std::endl; LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause //and wait for the first transaction to get finished @@ -211,7 +208,7 @@ TEST_F(CacheTransactionsTest, ConcurentModification) { LMDBAL::WriteTransaction txn1 = db->beginTransaction(); std::cout << "putting parent thread to sleep for 5 ms" << std::endl; - usleep(10); + usleep(5); std::cout << "adding first transaction value" << std::endl; c1->addRecord(5, 812, txn1); @@ -234,11 +231,11 @@ TEST_F(CacheTransactionsTest, ConcurentModification) { } TEST_F(CacheTransactionsTest, RAIIResourceFree) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); int pid = fork(); if (pid == 0) { // I am the child - usleep(5); + usleep(1); std::cout << "beggining child transaction" << std::endl; LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause //and wait for the first transaction to get finished @@ -259,7 +256,7 @@ TEST_F(CacheTransactionsTest, RAIIResourceFree) { LMDBAL::WriteTransaction txn1 = db->beginTransaction(); std::cout << "putting parent thread to sleep for 5 ms" << std::endl; - usleep(10); + usleep(5); std::cout << "parent thread woke up" << std::endl; std::cout << "adding value from parent thread" << std::endl; @@ -282,26 +279,3 @@ TEST_F(CacheTransactionsTest, RAIIResourceFree) { EXPECT_EQ(c1->getRecord(221), 14); } -TEST_F(CacheTransactionsTest, TransactionTerminationOnClose) { - LMDBAL::WriteTransaction txn = db->beginTransaction(); - - c1->addRecord(578, 4552, txn); - - EXPECT_EQ(c1->getRecord(578, txn), 4552); - EXPECT_EQ(c1->checkRecord(578), false); - - 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); -} diff --git a/test/duplicates.cpp b/test/duplicates.cpp index 4bb77db..4fbe12a 100644 --- a/test/duplicates.cpp +++ b/test/duplicates.cpp @@ -7,7 +7,6 @@ #include "base.h" #include "storage.h" #include "cursor.h" -#include "session.h" class DuplicatesTest : public ::testing::Test { protected: @@ -36,19 +35,18 @@ protected: db->addStorage("intDouble", true); db->addStorage("floatLong", true); - session = db->open(); + db->open(); } } static void TearDownTestSuite() { - session.close(); + db->close(); db->removeDirectory(); delete db; db = nullptr; } static LMDBAL::Base* db; - static LMDBAL::Session session; LMDBAL::Storage* tu1; LMDBAL::Storage* tu2; @@ -58,7 +56,6 @@ protected: }; LMDBAL::Base* DuplicatesTest::db = nullptr; -LMDBAL::Session DuplicatesTest::session; TEST_F(DuplicatesTest, Flags) { uint32_t tu1Flags = getTU1Flags(); @@ -133,10 +130,7 @@ TEST_F(DuplicatesTest, Adding) { tu3->addRecord(7.20001, 4.00000001); //not sure how exactly, but it works EXPECT_EQ(tu3->count(), 7); - - std::set res72({-113, -53.5478, 697, 4, 4.00000001}); - EXPECT_EQ(res72.count(tu3->getRecord(7.2)), 1); - + EXPECT_EQ(tu3->getRecord(7.2), -113); float tu3dd = tu3->getRecord(5119); EXPECT_TRUE(tu3ds == tu3dd); EXPECT_EQ(tu3ds, tu3dd); @@ -150,11 +144,8 @@ TEST_F(DuplicatesTest, Adding) { EXPECT_THROW(tu4->addRecord(327, 79.624923), LMDBAL::Exist); EXPECT_EQ(tu4->count(), 4); - - std::set res327({463.28348, 79.624923}); - std::set res172({0.00001, 0.00000001}); - EXPECT_EQ(res172.count(tu4->getRecord(172)), 1); - EXPECT_EQ(res327.count(tu4->getRecord(327)), 1); + EXPECT_EQ(tu4->getRecord(172), 0.00000001); + EXPECT_EQ(tu4->getRecord(327), 463.28348); //since they are not int's they are compared sort of lexicographically tu5->addRecord(-84.7, 45656753); EXPECT_THROW(tu5->addRecord(-84.7, 45656753), LMDBAL::Exist); @@ -183,19 +174,13 @@ TEST_F(DuplicatesTest, Forcing) { tu1->addRecord(-56, 71); tu1->addRecord(-56, 274); tu1->addRecord(-56, 732); - - std::set res56({71, 274, 732}); EXPECT_EQ(tu1->count(), tu1Size += 3); EXPECT_TRUE(tu1->forceRecord(-56, 322)); - res56.insert(322); EXPECT_EQ(tu1->count(), tu1Size += 1); - EXPECT_EQ(res56.count(tu1->getRecord(-56)), 1); - - res56.insert(14); + EXPECT_EQ(tu1->getRecord(-56), 274); //like yeah, it's really counterintuitive, since it's compared byte by byte EXPECT_TRUE(tu1->forceRecord(-56, 14)); EXPECT_EQ(tu1->count(), tu1Size += 1); - EXPECT_EQ(res56.count(tu1->getRecord(-56)), 1); - + EXPECT_EQ(tu1->getRecord(-56), 14); EXPECT_FALSE(tu1->forceRecord(-56, 274)); EXPECT_EQ(tu1->count(), tu1Size); @@ -216,38 +201,26 @@ TEST_F(DuplicatesTest, Forcing) { tu3->addRecord(17.3, 93.21); tu3->addRecord(17.3, 6.6); tu3->addRecord(17.3, 105.1); - std::set res17({93.21, 6.6, 105.1}); - EXPECT_EQ(tu3->count(), tu3Size += 3); EXPECT_TRUE(tu3->forceRecord(17.3, 74.9)); - res17.insert(74.9); EXPECT_EQ(tu3->count(), tu3Size += 1); - EXPECT_EQ(res17.count(tu3->getRecord(17.3)), 1); - - EXPECT_TRUE(tu3->forceRecord(17.3, 5.1)); - res17.insert(5.1); + EXPECT_EQ(tu3->getRecord(17.3), 105.1f); //here too, really one should not use this function with duplicates, + EXPECT_TRUE(tu3->forceRecord(17.3, 5.1)); //unless he wishes for kinda randomish result EXPECT_EQ(tu3->count(), tu3Size += 1); - EXPECT_EQ(res17.count(tu3->getRecord(17.3)), 1); - + EXPECT_EQ(tu3->getRecord(17.3), 5.1f); EXPECT_FALSE(tu3->forceRecord(17.3, 93.21)); EXPECT_EQ(tu3->count(), tu3Size); LMDBAL::SizeType tu4Size = tu4->count(); tu4->addRecord(84, -359.109); tu4->addRecord(84, 2879.654); - std::set res84({-359.109, 2879.654}); - EXPECT_EQ(tu4->count(), tu4Size += 2); EXPECT_TRUE(tu4->forceRecord(84, 72.9)); - res84.insert(72.9); EXPECT_EQ(tu4->count(), tu4Size += 1); - EXPECT_EQ(res84.count(tu4->getRecord(84)), 1); - + EXPECT_EQ(tu4->getRecord(84), 2879.654); EXPECT_TRUE(tu4->forceRecord(84, 2679.5)); - res84.insert(2679.5); EXPECT_EQ(tu4->count(), tu4Size += 1); - EXPECT_EQ(res84.count(tu4->getRecord(84)), 1); - + EXPECT_EQ(tu4->getRecord(84), 2679.5); EXPECT_FALSE(tu4->forceRecord(84, -359.109)); EXPECT_EQ(tu4->count(), tu4Size); @@ -313,7 +286,6 @@ TEST_F(DuplicatesTest, Changing) { EXPECT_THROW(tu2->changeRecord("jeremy spins", -7), LMDBAL::Exist); LMDBAL::SizeType tu3Size = tu3->count(); - std::set res26; EXPECT_THROW(tu3->changeRecord(26.7, 68.22), LMDBAL::NotFound); EXPECT_EQ(tu3->count(), tu3Size); tu3->addRecord(26.7, 68.22); @@ -322,13 +294,11 @@ TEST_F(DuplicatesTest, Changing) { tu3->changeRecord(26.7, 68.22); //should just do nothing usefull, but work normally EXPECT_EQ(tu3->getRecord(26.7), 68.22f); tu3->changeRecord(26.7, 23.18); - res26.insert(23.18); EXPECT_EQ(tu3->count(), tu3Size); EXPECT_EQ(tu3->getRecord(26.7), 23.18f); tu3->addRecord(26.7, 22.16); - res26.insert(22.16); EXPECT_EQ(tu3->count(), tu3Size += 1); - EXPECT_EQ(res26.count(tu3->getRecord(26.7)), 1); + EXPECT_EQ(tu3->getRecord(26.7), 23.18f); tu3->changeRecord(26.7, 21.7); EXPECT_EQ(tu3->count(), tu3Size); EXPECT_EQ(tu3->getRecord(26.7), 21.7f); @@ -346,21 +316,17 @@ TEST_F(DuplicatesTest, Changing) { tu4->changeRecord(852, 6795.349); //should just do nothing usefull, but work normally EXPECT_EQ(tu4->getRecord(852), 6795.349); tu4->changeRecord(852, 13.54); - std::set res852({13.54}); EXPECT_EQ(tu4->count(), tu4Size); EXPECT_EQ(tu4->getRecord(852), 13.54); tu4->addRecord(852, 213.85); - res852.insert(213.85); EXPECT_EQ(tu4->count(), tu4Size += 1); - EXPECT_EQ(res852.count(tu4->getRecord(852)), 1); + EXPECT_EQ(tu4->getRecord(852), 13.54); tu4->changeRecord(852, 236.21); - res852.insert(236.21); EXPECT_EQ(tu4->count(), tu4Size); EXPECT_EQ(tu4->getRecord(852), 236.21); tu4->changeRecord(852, 46324.1135); - res852.insert(46324.1135); EXPECT_EQ(tu4->count(), tu4Size); - EXPECT_EQ(res852.count(tu4->getRecord(852)), 1); + EXPECT_EQ(tu4->getRecord(852), 213.85); EXPECT_THROW(tu4->changeRecord(852, 46324.1135), LMDBAL::Exist); } diff --git a/test/storagecursor.cpp b/test/storagecursor.cpp index d44e657..dde8d82 100644 --- a/test/storagecursor.cpp +++ b/test/storagecursor.cpp @@ -3,7 +3,6 @@ #include "base.h" #include "storage.h" #include "cursor.h" -#include "session.h" class StorageCursorTest : public ::testing::Test { protected: @@ -19,7 +18,7 @@ protected: db = new LMDBAL::Base("testBase"); db->addStorage("table1"); db->addStorage("empty"); - session = db->open(); + db->open(); } } @@ -30,7 +29,7 @@ protected: static void TearDownTestSuite() { cursor.drop(); transaction.terminate(); - session.close(); + db->close(); db->removeDirectory(); delete db; db = nullptr; @@ -39,7 +38,6 @@ protected: static LMDBAL::Base* db; static LMDBAL::Cursor cursor; static LMDBAL::Transaction transaction; - static LMDBAL::Session session; LMDBAL::Storage* table; LMDBAL::Storage* emptyTable; @@ -48,7 +46,6 @@ protected: LMDBAL::Base* StorageCursorTest::db = nullptr; LMDBAL::Cursor StorageCursorTest::cursor; LMDBAL::Transaction StorageCursorTest::transaction = LMDBAL::Transaction(); -LMDBAL::Session StorageCursorTest::session = LMDBAL::Session(); static const std::map data({ {245665783, "bothering nerds"}, @@ -383,7 +380,7 @@ TEST_F(StorageCursorTest, CursorRAIIBehaviour) { TEST_F(StorageCursorTest, CornerCases) { EXPECT_EQ(getTableCursorsSize(), 1); transaction.terminate(); - EXPECT_THROW(cursor.current(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.current(), LMDBAL::Unknown); cursor.close(); LMDBAL::Cursor emptyCursor = emptyTable->createCursor(); @@ -417,40 +414,4 @@ TEST_F(StorageCursorTest, CornerCases) { EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); EXPECT_THROW(cursor.prev(), LMDBAL::NotFound); - - cursor.close(); -} - -TEST_F(StorageCursorTest, TerminatedTransaction) { - LMDBAL::Cursor 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()); } diff --git a/test/storagetransaction.cpp b/test/storagetransaction.cpp index be0fc1d..4f04931 100644 --- a/test/storagetransaction.cpp +++ b/test/storagetransaction.cpp @@ -4,7 +4,6 @@ #include "base.h" #include "storage.h" -#include "session.h" class StorageTransactionsTest : public testing::Test { protected: @@ -40,19 +39,18 @@ protected: db->addStorage("table2"); } - session = db->open(); + db->open(); db->drop(); } static void TearDownTestSuite() { - session.close(); + db->close(); db->removeDirectory(); delete db; db = nullptr; } static LMDBAL::Base* db; - static LMDBAL::Session session; LMDBAL::Storage* t1; LMDBAL::Storage* t2; @@ -60,10 +58,9 @@ protected: LMDBAL::Base* StorageTransactionsTest::db = nullptr; -LMDBAL::Session StorageTransactionsTest::session; TEST_F(StorageTransactionsTest, Adding) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); EXPECT_EQ(t1->count(), 0); EXPECT_EQ(t2->count(), 0); @@ -93,7 +90,7 @@ TEST_F(StorageTransactionsTest, Adding) { } TEST_F(StorageTransactionsTest, Aborting) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); LMDBAL::SizeType s1 = t1->count(); LMDBAL::SizeType s2 = t2->count(); @@ -117,7 +114,7 @@ TEST_F(StorageTransactionsTest, Aborting) { } TEST_F(StorageTransactionsTest, Reading) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); LMDBAL::Transaction txn = db->beginReadOnlyTransaction(); @@ -135,7 +132,7 @@ TEST_F(StorageTransactionsTest, Reading) { } TEST_F(StorageTransactionsTest, ConcurentReading) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); LMDBAL::SizeType size = t1->count(); LMDBAL::WriteTransaction txn = db->beginTransaction(); @@ -170,7 +167,7 @@ TEST_F(StorageTransactionsTest, ConcurentReading) { } TEST_F(StorageTransactionsTest, ConcurentModification) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); //if you start one writable transaction after another //in a single thread like so: @@ -184,7 +181,7 @@ TEST_F(StorageTransactionsTest, ConcurentModification) { int pid = fork(); if (pid == 0) { // I am the child - usleep(5); + usleep(1); std::cout << "beggining second transaction" << std::endl; LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause //and wait for the first transaction to get finished @@ -210,7 +207,7 @@ TEST_F(StorageTransactionsTest, ConcurentModification) { LMDBAL::WriteTransaction txn1 = db->beginTransaction(); std::cout << "putting parent thread to sleep for 5 ms" << std::endl; - usleep(10); + usleep(5); std::cout << "adding first transaction value" << std::endl; t1->addRecord(5, 812, txn1); @@ -233,11 +230,11 @@ TEST_F(StorageTransactionsTest, ConcurentModification) { } TEST_F(StorageTransactionsTest, RAIIResourceFree) { - EXPECT_EQ(db->opened(), true); + EXPECT_EQ(db->ready(), true); int pid = fork(); if (pid == 0) { // I am the child - usleep(5); + usleep(1); std::cout << "beggining child transaction" << std::endl; LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause //and wait for the first transaction to get finished @@ -258,7 +255,7 @@ TEST_F(StorageTransactionsTest, RAIIResourceFree) { LMDBAL::WriteTransaction txn1 = db->beginTransaction(); std::cout << "putting parent thread to sleep for 5 ms" << std::endl; - usleep(10); + usleep(5); std::cout << "parent thread woke up" << std::endl; std::cout << "adding value from parent thread" << std::endl; @@ -280,27 +277,3 @@ TEST_F(StorageTransactionsTest, RAIIResourceFree) { std::cout << "checking the final result" << std::endl; EXPECT_EQ(t1->getRecord(221), 14); } - -TEST_F(StorageTransactionsTest, TransactionTerminationOnClose) { - LMDBAL::WriteTransaction txn = db->beginTransaction(); - - t1->addRecord(543, 229, txn); - - EXPECT_EQ(t1->getRecord(543, txn), 229); - EXPECT_EQ(t1->checkRecord(543), false); - - 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); -}