├── .cproject ├── .editorconfig ├── .gitbugtraq ├── .github ├── dependabot.yml └── workflows │ ├── cmake.yml │ ├── cmake_builtin_lib.yml │ ├── cmake_subdir_example.yml │ └── meson.yml ├── .gitignore ├── .gitmodules ├── .project ├── .travis.yml ├── CHANGELOG.md ├── CMakeLists.txt ├── Doxyfile ├── LICENSE.txt ├── README.md ├── TODO.txt ├── _config.yml ├── appveyor.yml ├── build.bat ├── build.sh ├── cmake ├── FindSQLite3.cmake └── SQLiteCppConfig.cmake.in ├── cpplint.py ├── docs └── README.md ├── examples ├── example1 │ ├── README.md │ ├── example.db3 │ ├── logo.png │ ├── main.cpp │ └── meson.build ├── example2 │ ├── CMakeLists.txt │ ├── README.md │ ├── build.bat │ ├── build.sh │ ├── meson.build │ └── src │ │ └── main.cpp └── meson.build ├── include └── SQLiteCpp │ ├── Assertion.h │ ├── Backup.h │ ├── Column.h │ ├── Database.h │ ├── Exception.h │ ├── ExecuteMany.h │ ├── SQLiteCpp.h │ ├── SQLiteCppExport.h │ ├── Savepoint.h │ ├── Statement.h │ ├── Transaction.h │ ├── Utils.h │ └── VariadicBind.h ├── meson.build ├── meson_options.txt ├── package.xml ├── sqlite3 ├── CMakeLists.txt ├── README.md ├── sqlite3.c └── sqlite3.h ├── src ├── Backup.cpp ├── Column.cpp ├── Database.cpp ├── Exception.cpp ├── Savepoint.cpp ├── Statement.cpp └── Transaction.cpp ├── subprojects ├── .gitignore ├── gtest.wrap └── sqlite3.wrap └── tests ├── Backup_test.cpp ├── Column_test.cpp ├── Database_test.cpp ├── Exception_test.cpp ├── ExecuteMany_test.cpp ├── Savepoint_test.cpp ├── Statement_test.cpp ├── Transaction_test.cpp └── VariadicBind_test.cpp /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # 4 space indentation 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | end_of_line = lf 10 | 11 | # 2 space indentation for CI configuration 12 | [*.yml] 13 | indent_style = space 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /.gitbugtraq: -------------------------------------------------------------------------------- 1 | # .gitbugtraq for Git GUIs (SmartGit/TortoiseGit) to show links to the Github issue tracker. 2 | # Instead of the repository root directory, it could be added as an additional section to $GIT_DIR/config. 3 | # (note that '\' need to be escaped). 4 | [bugtraq] 5 | url = https://github.com/SRombauts/SQLiteCpp/issues/%BUGID% 6 | loglinkregex = "#\\d+" 7 | logregex = \\d+ 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 2 | version: 2 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" # Location of package manifests 6 | schedule: 7 | interval: "monthly" 8 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: ${{ matrix.config.name }} 8 | runs-on: ${{ matrix.config.os }} 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | config: 13 | - { 14 | name: "Windows Latest MSVC", 15 | os: windows-latest, 16 | generator: "Visual Studio 17 2022", 17 | build_type: "Debug", 18 | cc: "cl", cxx: "cl", 19 | extra_path: "", 20 | } 21 | - { 22 | name: "Windows Latest MinGW", 23 | os: windows-latest, 24 | generator: "MinGW Makefiles", 25 | build_type: "Debug", 26 | cc: "gcc", cxx: "g++", 27 | extra_path: "C:\\ProgramData\\chocolatey\\lib\\mingw\\tools\\install\\mingw64\\bin", 28 | } 29 | - { 30 | name: "Ubuntu 24.04 GCC", 31 | os: ubuntu-24.04, 32 | generator: "Unix Makefiles", 33 | build_type: "Debug", 34 | cc: "gcc", cxx: "g++", 35 | extra_path: "", 36 | } 37 | - { 38 | name: "Ubuntu 22.04 GCC", 39 | os: ubuntu-22.04, 40 | generator: "Unix Makefiles", 41 | build_type: "Debug", 42 | cc: "gcc", cxx: "g++", 43 | extra_path: "", 44 | } 45 | - { 46 | name: "macOS Latest Clang", 47 | os: macos-latest, 48 | generator: "Unix Makefiles", 49 | build_type: "Debug", 50 | cc: "clang", cxx: "clang++", 51 | extra_path: "", 52 | } 53 | 54 | steps: 55 | - name: Checkout ${{ github.ref_name }} 56 | uses: actions/checkout@v4 57 | - run: git submodule update --init --recursive 58 | - name: set extra GITHUB_PATH ${{ matrix.config.extra_path }} (for MinGW) 59 | shell: bash 60 | run: echo "${{ matrix.config.extra_path }}" >> $GITHUB_PATH 61 | - name: set env CXX=${{ matrix.config.cxx }} 62 | shell: cmake -P {0} 63 | run: | 64 | set(ENV{CC} ${{ matrix.config.cc }}) 65 | set(ENV{CXX} ${{ matrix.config.cxx }}) 66 | - run: mkdir build 67 | - run: cmake -G "${{ matrix.config.generator }}" -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} -DBUILD_SHARED_LIBS=ON -DSQLITECPP_BUILD_TESTS=ON -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_RUN_CPPCHECK=OFF -DSQLITECPP_RUN_CPPLINT=OFF .. 68 | working-directory: build 69 | - run: cmake --build build --config ${{ matrix.config.build_type }} 70 | - run: ctest --verbose --output-on-failure --test-dir build 71 | -------------------------------------------------------------------------------- /.github/workflows/cmake_builtin_lib.yml: -------------------------------------------------------------------------------- 1 | name: CMake SQLite3 builtin library 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: ${{ matrix.config.name }} 8 | runs-on: ${{ matrix.config.os }} 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | config: 13 | - { 14 | name: "Ubuntu Latest GCC", 15 | os: ubuntu-latest, 16 | generator: "Unix Makefiles", 17 | } 18 | - { 19 | name: "macOS Latest Clang", 20 | os: macos-latest, 21 | generator: "Unix Makefiles", 22 | } 23 | 24 | steps: 25 | - name: Checkout ${{ github.ref_name }} 26 | uses: actions/checkout@v4 27 | - run: git submodule update --init --recursive 28 | - run: mkdir build 29 | - run: cmake -G "Unix Makefiles" -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=Debug -DSQLITECPP_INTERNAL_SQLITE=OFF -DSQLITE_OMIT_LOAD_EXTENSION=ON -DSQLITECPP_BUILD_TESTS=ON -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_RUN_CPPCHECK=OFF -DSQLITECPP_RUN_CPPLINT=OFF .. 30 | working-directory: build 31 | - run: cmake --build build --config Debug 32 | - run: ctest --verbose --output-on-failure --test-dir build 33 | -------------------------------------------------------------------------------- /.github/workflows/cmake_subdir_example.yml: -------------------------------------------------------------------------------- 1 | name: CMake build of example in subdirectory 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: ${{ matrix.config.name }} 8 | runs-on: ${{ matrix.config.os }} 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | config: 13 | - { 14 | name: "Windows Latest MSVC", 15 | os: windows-latest, 16 | build_type: "Debug", cc: "cl", cxx: "cl", 17 | } 18 | - { 19 | name: "Ubuntu Latest GCC", 20 | os: ubuntu-latest, 21 | build_type: "Debug", cc: "gcc", cxx: "g++" 22 | } 23 | - { 24 | name: "macOS Latest Clang", 25 | os: macos-latest, 26 | build_type: "Debug", cc: "clang", cxx: "clang++" 27 | } 28 | 29 | steps: 30 | - uses: actions/checkout@v4 31 | - name: configure 32 | shell: cmake -P {0} 33 | run: | 34 | set(ENV{CC} ${{matrix.config.cc}}) 35 | set(ENV{CXX} ${{matrix.config.cxx}}) 36 | - name: generate 37 | run: | 38 | cd examples/example2 39 | mkdir build 40 | cd build 41 | cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=${{matrix.config.build_type}} .. 42 | - name: build 43 | run: cmake --build examples/example2/build --config ${{matrix.config.build_type}} 44 | -------------------------------------------------------------------------------- /.github/workflows/meson.yml: -------------------------------------------------------------------------------- 1 | name: Meson build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: (Meson) ${{ matrix.config.name }} 8 | runs-on: ${{ matrix.config.os }} 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | config: 13 | - { 14 | name: "Windows Latest MSVC", 15 | os: windows-latest, 16 | cc: "cl", cxx: "cl", 17 | extra_path: "", 18 | requires_msvc: true, 19 | } 20 | - { 21 | name: "Windows Latest MinGW", 22 | os: windows-latest, 23 | cc: "gcc", cxx: "g++", 24 | extra_path: "C:\\ProgramData\\chocolatey\\lib\\mingw\\tools\\install\\mingw64\\bin", 25 | } 26 | - { 27 | name: "Windows Latest Clang", 28 | os: windows-latest, 29 | cc: "clang", cxx: "clang++", c_ld: "lld-link", cxx_ld: "lld-link", 30 | extra_path: "", 31 | } 32 | - { 33 | name: "Ubuntu Latest GCC", 34 | os: ubuntu-latest, 35 | cc: "gcc", cxx: "g++", 36 | extra_path: "" 37 | } 38 | - { 39 | name: "Ubuntu Latest Clang", 40 | os: ubuntu-latest, 41 | cc: "clang", cxx: "clang++", c_ld: "lld", cxx_ld: "lld", 42 | extra_path: "" 43 | } 44 | - { 45 | name: "macOS Latest Clang", 46 | os: macos-latest, 47 | cc: "clang", cxx: "clang++", 48 | extra_path: "" 49 | } 50 | 51 | steps: 52 | - uses: actions/checkout@v4 53 | # use msvc-dev-cmd to setup the environment for MSVC if needed 54 | - name: setup MSVC 55 | if: matrix.config.requires_msvc 56 | uses: ilammy/msvc-dev-cmd@v1 57 | - name: extra_path 58 | shell: bash 59 | run: echo "${{matrix.config.extra_path}}" >> $GITHUB_PATH 60 | - name: install prerequisites 61 | run: | 62 | # asuming that python and pipx are already installed 63 | pipx install meson ninja 64 | - name: setup meson project 65 | env: # set proper compilers and linkers for meson 66 | CC: ${{matrix.config.cc}} 67 | CXX: ${{matrix.config.cxx}} 68 | C_LD: ${{matrix.config.c_ld}} 69 | CXX_LD: ${{matrix.config.cxx_ld}} 70 | run: | 71 | # setup the build directory with tests and examples enabled 72 | meson setup builddir -DSQLITECPP_BUILD_TESTS=true -DSQLITECPP_BUILD_EXAMPLES=true --force-fallback-for=sqlite3 73 | - name: build meson project 74 | run: | 75 | # build the project 76 | meson compile -C builddir 77 | - name: test 78 | run: | 79 | # run the tests 80 | meson test -C builddir 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Debug 2 | Release 3 | build 4 | *.a 5 | 6 | # ignore clangd cache directory 7 | .cache 8 | .vs/ 9 | .vscode/ 10 | .vsconfig 11 | *.sln 12 | *.ncb 13 | *.suo 14 | *.user 15 | *sdf 16 | *.vc* 17 | *~ 18 | doc 19 | core 20 | *ipch 21 | .settings/ 22 | 23 | # do not track Visual Studio CMake settings 24 | CMakeSettings.json 25 | CMakeCache.txt 26 | CMakeFiles 27 | *.dir 28 | Testing 29 | Win32 30 | 31 | SQLiteCpp_example1 32 | SQLiteCpp_tests 33 | 34 | !FindSQLiteCpp.cmake 35 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "googletest"] 2 | path = googletest 3 | url = https://github.com/google/googletest.git 4 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | SQLiteC++ 4 | 5 | 6 | 7 | 8 | 9 | org.python.pydev.PyDevBuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.cdt.managedbuilder.core.genmakebuilder 15 | clean,full,incremental, 16 | 17 | 18 | ?name? 19 | 20 | 21 | 22 | org.eclipse.cdt.make.core.append_environment 23 | true 24 | 25 | 26 | org.eclipse.cdt.make.core.autoBuildTarget 27 | all 28 | 29 | 30 | org.eclipse.cdt.make.core.buildArguments 31 | -j 32 | 33 | 34 | org.eclipse.cdt.make.core.buildCommand 35 | make 36 | 37 | 38 | org.eclipse.cdt.make.core.cleanBuildTarget 39 | clean 40 | 41 | 42 | org.eclipse.cdt.make.core.contents 43 | org.eclipse.cdt.make.core.activeConfigSettings 44 | 45 | 46 | org.eclipse.cdt.make.core.enableAutoBuild 47 | false 48 | 49 | 50 | org.eclipse.cdt.make.core.enableCleanBuild 51 | true 52 | 53 | 54 | org.eclipse.cdt.make.core.enableFullBuild 55 | true 56 | 57 | 58 | org.eclipse.cdt.make.core.fullBuildTarget 59 | all 60 | 61 | 62 | org.eclipse.cdt.make.core.stopOnError 63 | true 64 | 65 | 66 | org.eclipse.cdt.make.core.useDefaultBuildCmd 67 | true 68 | 69 | 70 | 71 | 72 | org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder 73 | full,incremental, 74 | 75 | 76 | 77 | 78 | 79 | org.eclipse.cdt.core.cnature 80 | org.eclipse.cdt.core.ccnature 81 | org.eclipse.cdt.managedbuilder.core.managedBuildNature 82 | org.eclipse.cdt.managedbuilder.core.ScannerConfigNature 83 | org.python.pydev.pythonNature 84 | 85 | 86 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) 2 | 3 | language: cpp 4 | 5 | # Use Linux unless specified otherwise 6 | os: linux 7 | 8 | cache: 9 | apt: true 10 | 11 | env: 12 | global: 13 | - BUILD_TYPE=Debug 14 | - ASAN=ON 15 | - INTERNAL_SQLITE=ON 16 | - VALGRIND=OFF 17 | - TESTS=ON 18 | - SHARED_LIBS=OFF 19 | 20 | # Build variants (should test a reasonable number of combination of CMake options) 21 | jobs: 22 | include: 23 | 24 | ########################################################################## 25 | # GCC on Linux 26 | ########################################################################## 27 | 28 | # GCC 11.2.0 (Ubuntu Jammy 22.04) 29 | - dist: jammy 30 | env: 31 | - cc=gcc cxx=g++ 32 | 33 | # Clang 9.3.0 (Ubuntu Focal 20.04) 34 | - dist: focal 35 | env: 36 | - cc=gcc cxx=g++ 37 | 38 | # Coverity static code analysis (Ubuntu Bionic 18.04) 39 | - dist: bionic 40 | env: 41 | - COVERITY_SCAN_PROJECT_NAME=SRombauts/SQLiteCpp 42 | - COVERITY_SCAN_BRANCH_PATTERN=master 43 | - COVERITY_SCAN_NOTIFICATION_EMAIL=sebastien.rombauts@gmail.com 44 | - COVERITY_SCAN_BUILD_COMMAND_PREPEND="cmake ." 45 | - COVERITY_SCAN_BUILD_COMMAND="make -j8" 46 | # Encrypted COVERITY_SCAN_TOKEN, created via the "travis encrypt" command using the project repo's public key 47 | - secure: "Qm4d8NEDPBtYZCYav46uPEvDCtaRsjLXlkVS+C+WCJAPcwXCGkrr96wEi7RWcq2xD86QCh0XiqaPT+xdUmlohOYIovRhaaBmZ1lwIJ4GsG/ZR6xoFr3DYsZ3o4GyXk2vNXNxEl82AC+Xs6e6gkLOV9XRkBcjpVIvoIXgNlKWeGY=" 48 | 49 | # GCC 7.4.0 Debug build with GCov for coverage build (Ubuntu Bionic 18.04) 50 | - dist: bionic 51 | env: 52 | - cc=gcc cxx=g++ 53 | - GCOV=ON 54 | - COVERALLS=ON 55 | 56 | # GCC 7.4.0 Debug build with Valgrind instead of Address Sanitizer (Ubuntu Bionic 18.04) 57 | - dist: bionic 58 | env: 59 | - cc=gcc cxx=g++ 60 | - ASAN=OFF 61 | - VALGRIND=ON 62 | 63 | # GCC 7.4.0 Release build (Ubuntu Bionic 18.04) 64 | - dist: bionic 65 | env: 66 | - cc=gcc cxx=g++ 67 | - BUILD_TYPE=Release 68 | 69 | # GCC 7.4.0 Shared Libs (Ubuntu Bionic 18.04) 70 | - dist: bionic 71 | env: 72 | - cc=gcc cxx=g++ 73 | - SHARED_LIBS=ON 74 | 75 | # GCC 7.4.0 test linking with libsqlite3-dev package (Ubuntu Bionic 18.04) 76 | - dist: bionic 77 | env: 78 | - cc=gcc cxx=g++ 79 | - INTERNAL_SQLITE=OFF 80 | 81 | # GCC 5.4.0 (Ubuntu Xenial 16.04) 82 | - dist: xenial # Default 83 | env: 84 | - cc=gcc cxx=g++ 85 | 86 | # GCC 4.8.4 (Ubuntu Trusty 14.04) 87 | - dist: trusty 88 | env: 89 | - cc=gcc cxx=g++ 90 | - TESTS=OFF 91 | 92 | ########################################################################## 93 | # Clang on Linux 94 | ########################################################################## 95 | 96 | # Clang 7.0.0 (Ubuntu Jammy 22.04) 97 | - dist: jammy 98 | env: 99 | - cc=clang cxx=clang++ 100 | 101 | # Clang 7.0.0 (Ubuntu Focal 20.04) 102 | - dist: focal 103 | env: 104 | - cc=clang cxx=clang++ 105 | 106 | # Clang 7.0.0 (Ubuntu Bionic 18.04) 107 | - dist: bionic 108 | env: 109 | - cc=clang cxx=clang++ 110 | 111 | # Clang 7.0.0 (Ubuntu Xenial 16.04) 112 | - dist: xenial # Default 113 | env: 114 | - cc=clang cxx=clang++ 115 | 116 | # Clang 5.0.0 (Ubuntu Trusty 14.04) 117 | - dist: trusty 118 | env: 119 | - cc=clang cxx=clang++ 120 | - TESTS=OFF 121 | 122 | ########################################################################## 123 | # Clang on OSX 124 | ########################################################################## 125 | 126 | # XCode 14.2 - Apple clang 14.0.0 - macOS 12.6 127 | - os: osx 128 | osx_image: xcode14.2 129 | env: 130 | - cc=clang cxx=clang++ 131 | 132 | # Default XCode - Apple clang 9.1.0 - macOS 10.13 133 | - os: osx 134 | osx_image: xcode9.4 # Default 135 | env: 136 | - cc=clang cxx=clang++ 137 | 138 | # XCode 8.3 - Applec lang 8.1.0 - macOS 10.12 139 | - os: osx 140 | osx_image: xcode8.3 141 | env: 142 | - cc=clang cxx=clang++ 143 | - TESTS=OFF 144 | 145 | before_install: 146 | # Coverity: don't use addons.coverity_scan since it run on every job of the build matrix, which waste resources and exhausts quotas 147 | # Note: the job dedicated to Coverity need to only run the shell script and then exit (to not try to build and run unit tests etc.) 148 | - if [[ -n "$COVERITY_SCAN_PROJECT_NAME" ]] ; then curl -s https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh | bash ; exit 0 ; fi 149 | 150 | - if [[ "$INTERNAL_SQLITE" == "OFF" ]]; then sudo apt-get install libsqlite3-dev ; fi 151 | - if [[ "$VALGRIND" == "ON" ]]; then sudo apt-get install valgrind ; fi 152 | - if [[ "$COVERALLS" == "ON" ]]; then pip install --user cpp-coveralls ; fi 153 | 154 | # Set the compiler environment variables properly 155 | - export CC=${cc} 156 | - export CXX=${cxx} 157 | 158 | # scripts to run before build 159 | before_script: 160 | - mkdir build 161 | - cd build 162 | - cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_SHARED_LIBS=$SHARED_LIBS -DSQLITECPP_INTERNAL_SQLITE=$INTERNAL_SQLITE -DSQLITECPP_USE_ASAN=$ASAN -DSQLITECPP_USE_GCOV=$GCOV -DSQLITECPP_BUILD_EXAMPLES=$TESTS -DSQLITECPP_BUILD_TESTS=$TESTS .. 163 | 164 | # build examples, and run tests (ie make & make test) 165 | script: 166 | - cmake --build . 167 | - export ASAN_OPTIONS=verbosity=1:debug=1 168 | - if [[ "$TESTS" == "ON" ]]; then ctest --verbose --output-on-failure ; fi 169 | - if [[ "$VALGRIND" == "ON" ]]; then valgrind --leak-check=full --error-exitcode=1 bin/SQLiteCpp_example1 ; fi 170 | - if [[ "$VALGRIND" == "ON" ]]; then valgrind --leak-check=full --error-exitcode=1 bin/SQLiteCpp_tests ; fi 171 | 172 | # generate and publish GCov coveralls results 173 | after_success: 174 | - if [[ "$COVERALLS" == "ON" ]]; then coveralls --root .. -e examples -e googletest -e sqlite3 -e tests -E ".*feature_tests.*" -E ".*CompilerId.*" --gcov-options '\-lp' ; fi 175 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2012 Mar 30 2 | - Start of a new thin C++ SQLite wrapper 3 | 4 | 2012 Apr 2 5 | - The wrapper is functional 6 | - Added documentation and examples 7 | - Publication on GitHub 8 | 9 | Version 0.1.0 - 2012 Apr 4 10 | - Added a Database::exec() method to execute simple SQL statement 11 | - Added a version number like in sqlite3.h, starting with 0.1.0 12 | 13 | Version 0.2.0 - 2012 Apr 11 14 | - Added getLastInsertId() and setBusyTimout() 15 | - Added bind() by name methods 16 | 17 | Version 0.3.0 - 2012 Apr 16 18 | - Added an easy wrapper Database::execAngGet() 19 | 20 | Version 0.4.0 - 2012 Apr 23 21 | - Added a Database::tableExists() easy to use function 22 | 23 | Dec 10 2012 24 | - Added a Statement::exec() method to execute a one-step query with no expected result 25 | 26 | Version 0.5.0 - 2013 March 9 27 | - Added assert() on errors on destructors 28 | - Added getBytes() 29 | - Added getBlob(), getType() and isInteger/isFloat/isText/isBlob/isNull 30 | - Added bind() for binary blob data 31 | 32 | Version 0.5.1 - 2013 April 7 33 | - Added Column::getName() 34 | 35 | Version 0.6.0 - 2013 November 22 36 | - Renamed Column::getName() to Column::getOriginName() 37 | - Added Column::getName() 38 | 39 | Version 0.7.0 - 2014 January 9 40 | - Added Database::createFunction() 41 | - Added std::string version of existing APIs 42 | - Improved CMake with more build options and Doxygen auto-detection 43 | 44 | Version 0.8.0 - 2014 February 26 45 | - Database constructor support opening a database with a custom VFS (default to NULL) 46 | - Changed Column::getText() to return empty string "" by default instead of NULL pointer (to handle std::string conversion) 47 | 48 | Version 1.0.0 - 2015 May 3 49 | - Public headers file moved to include/ dir 50 | - Added support to biicode in CMakeLists.txt 51 | - Added Unit Tests 52 | - Added aBusyTimeoutMs parameter to Database() constructors 53 | - Added Database::getTotalChanges() 54 | - Added Database::getErrorCode() 55 | - Added Statement::clearBindings() 56 | - Added Statement::getColumn(aName) 57 | - Added Statement::getErrorCode() 58 | - Added Statement::getColumnName(aIndex) 59 | - Added Statement::getColumnOriginName(aIndex) 60 | 61 | Version 1.1.0 - 2015 May 18 62 | - Fixed valgrind error on Database destructor 63 | - Added Database::loadExtension 64 | 65 | Version 1.2.0 - 2015 September 9 66 | - Fixed build with GCC 5.1.0 67 | - Fixed MSVC release build warning 68 | - Fixed CppDepends warnings 69 | - Updated documentation on installation 70 | - Added Database::getHandle() 71 | 72 | Version 1.3.0 - 2015 November 1 73 | - Fixed build with Visual Studio 2015 74 | - Further improvements to README 75 | - Added Backup class 76 | 77 | Version 1.3.1 - 2016 February 10 78 | - Switch Linux/Mac build to the provided SQLite3 C library 79 | - Update SQLite3 from 3.8.8.3 to latest 3.10.2 (2016-01-20) 80 | - Remove warnings 81 | - Remove biicode support (defunct service, servers will shutdown the 16th of February 2016) 82 | 83 | Version 2.0.0 - 2016 July 25 84 | - Update SQLite3 from 3.10.2 to latest 3.13 (2016-05-18) 85 | - Move #include from headers to .cpp files only using forward declarations 86 | - Add Database::VERSION to reach SQLITE_VERSION without including sqlite3.h in application code 87 | - Add getLibVersion() and getLibVersionNumber() to get runtime version of the library 88 | - Better exception messages when Statements fail PR #84 89 | - Variadic templates for bind() (C++14) PR #85 90 | - Add Statement::bindNoCopy() methods for strings, using SQLITE_STATIC to avoid internal copy by SQLite3 PR #86 91 | - Add Statement::bind() overload for uint32_t, and Column::getUint() and cast operator to uint32_t PR #86 92 | - Use the new SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION from SQLite 3.13 for security reason 93 | - Rename Backup::remainingPageCount()/totalPageCount() to Backup::getRemainingPageCount()/getTotalPageCount() 94 | - Remove Column::errmsg() method : use Database or Statement equivalents 95 | - More unit tests, with code coverage status on the GitHub page 96 | - Do not force MSVC to use static runtime if unit-tests are not build 97 | 98 | Version 2.1.0 - 2017 July 18 99 | - Update SQLite3 from 3.13 to latest 3.19.3 (2017-06-08) 100 | - Fixed Incompatibility in 3.19.0 (to use older SQLite version set the CMake variable SQLITE_USE_LEGACY_STRUCT) #125 101 | - Fixed link error (inline in cpp) and compiler warnings (unused variable...) #96 102 | - Added ability to open encrypted databases (using SQLCipher, eg. libsqlcipher-dev) #107 103 | - Added convenience functions for constructing objects from a row #114 104 | - Added CMake install step #118 105 | - Fix warnings #119 106 | - Make cpplint.py Python-3 compatible #120 107 | - Link libssp when targeted #100 108 | - Removed redundant const #102 109 | 110 | Version 2.2.0 - 2017 Sept 19 111 | - Update SQLite3 from 3.19.3 to latest 3.20.1 (2017-08-24) #143 112 | - Added tryExecuteStep and tryReset #142 113 | - Removed virtual keywords from destructors #140 114 | - Removed misplaced noexcept keyword #139 115 | - Improved Exception class C++ conformance #138 116 | - Fix warnings #134 117 | - Deprecated Statement::isOk() to Statement::hasRow() 118 | 119 | Version 2.3.0 - 2019 March 3 120 | - Update SQLite3 from 3.20.1 to latest 3.27.2 (2019-02-25) #183 #187 121 | - Add Statement binding for long int values #147 122 | - Allows long int for bind when used with name #148 123 | - More cmake instructions for Linux #151 124 | - Add comparison with sqlite_orm #141 125 | - Fix Statement::bind truncates long integer to 32 bits on x86_64 Linux #155 126 | - Add a move constructor to Database #157 127 | - Added tests for all MSVC compilers available on AppVeyor (2013, 2015, 2017) #169 128 | - Update VariadicBind.h #172 129 | - Better CMake compatibility #170 130 | - Add implicit cast operator to char and short types #179 #180 131 | 132 | Version 2.4.0 - 2019 August 25 133 | - Update SQLite3 from 3.27.2 to 3.29.0 (2019-07-10) #217 134 | - #191 CMake Warning line 299 135 | - #190 Implement move constructors 136 | - #192 Add wrapper for bind parameter count 137 | - #197 Add tuple_bind and execute_many (requested by #24) 138 | - #199 Fix #156 misleading error message in exception from Statement::exec 139 | - #201 Add Statement::getExpandedSQL() to get the SQL text of prepared statement with bound parameters expanded 140 | - #211 Implement Database::backup() 141 | - #215 Disable implicit fallthrough warning when building internal sqlite3 142 | - #216 Set PROJECT_VERSION to fix CMP0048 Policy warnings 143 | 144 | Version 2.5.0 - 2019 December 31 145 | - Update SQLite3 from 3.29.0 to 3.30.1 (2019-10-10) 146 | - 100% Unit Test coverage 147 | - #212 fix sqlite3 compile properties (jzt) 148 | - #219 Disable cast-function-type warning when building internal sqlite (zxey) 149 | - #230 Fixed installation on other than Ubuntu GNU/Linux distributions (xvitaly) 150 | - #228 use transitive compile definitions via cmake (BioDataAnalysis/emmenlau) 151 | - #232 Added support of packaged GTest for running unit tests (xvitaly) 152 | - #231 Added SOVERSION field for shared library (xvitaly) 153 | - #229 Explicitly find and link against system sqlite library (xvitaly) 154 | - #235 Added support for cmake dependencies and version information (BioDataAnalysis/emmenlau) 155 | - #249 Added SQLite header parsing functionality and associated tests (patrick--) 156 | 157 | - #251 Added example for getHeaderInfo() 158 | 159 | Version 3.0.0 - 2020 January 31 160 | - C++11 is now required 161 | - CMake 3.1 minimum 162 | - Visual Studio 2015 minimum 163 | - Update Googletest to latest release 1.10 164 | - Add Github Actions continuous integration solution 165 | - Add Valgrind memcheck tool to Travis CI 166 | - Remove Statement::isOk() deprecated in 2.2.0 when renamed to Statement::hasRow() 167 | - Replace Database::backup() "C" implementation by calling the Backup class 168 | - #252 Run Valgrind memcheck on Travis CI 169 | - #253 Keep inline functions for GCov code coverage 170 | - #254 Re-enable Coverity static analysis 171 | - #256 Fix linking with system library (libsqlite3) 172 | - #242 Added a `getIndex` method and used it (KOLANICH) 173 | - #257 Improve Statement unit tests coverage (bind by name with a std::string) 174 | - #234 support for external sqlite3 (BioDataAnalysis/emmenlau) 175 | - #243 adding a pure attribute to getIndex() (KOLANICH) 176 | 177 | Version 3.1.0 - 2020 August 11 178 | - Update SQLite3 from 3.30.1 to 3.32.3 (2020-06-18) 179 | - #274 Install both cmake files into same lib directory from tcraigtyler 180 | - #275 Add a method on Statement to get the declared type of a column. from daniel-schmidt 181 | - #284 Add SQLITE_OPEN_FULLMUTEX flag from rwrx 182 | - #286 Add CMake option to toggle stack protection from chrisdalke 183 | - #287 Fixed installation on other than Ubuntu distributions from xvitaly 184 | - #288 Allow building of sqlite JSON1 extension when building internal sqlite library from zxey 185 | 186 | Version 3.1.1 - 2020 August 19 187 | - #292 Fix compilation if using SQLITE_HAS_CODEC from sum01 188 | - #293 Remove FindSQLiteCpp.cmake from sum01 189 | 190 | Version 3.2.0 - 2022 Septembre 18 191 | - #300 #316 #362 #368 Updated SQLite3 from 3.32.3 to 3.39.3 (2022-09-05) 192 | - #236 Disable explicit setting of MSVC runtime from BioDataAnalysis/emmenlau 193 | - #308 Fix build warning due to string truncation from stauffer-garmin 194 | - #311 Add Database::tryExec() from kcowolf 195 | - #313 [CMake] Add SQLITECPP_INCLUDE_SCRIPT option from past-due 196 | - #314 Add Database constructor for filesystem::path (#296) from ptrks 197 | - #295 Compile internal SQLite library with -ffunction-sections from smichaku 198 | - #299 Added Savepoint support from catalogm 199 | - #333 Added Database and Statement getChanges() 200 | - #305 Add other constants that work with sqlite3_open_v2 from LuAPi/more-flags 201 | - #333 Added Database and Statement method getChanges() from SRombauts/get-changes 202 | - #334 fix link for HAS_CODEC from linux-fan-dave/master 203 | - #338 fix load extension from paulo-coutinho/fix-load-extension 204 | - #335 from jagerman/older-macos-avoid-std-filesystem 205 | - #337 Add catkin configuration from ardabbour/master 206 | - #339 Allow specifying transaction behaviors DEFERRED, IMMEDIATE, and EXCLUSIVE from jjenkins278/transaction_behavior 207 | - #340 add HTML keywords and properly link up the links in docs/README.md from phoebe-leong/patch-1 208 | - #341 Install the package.xml file from ardabbour/patch-1 209 | - #352 add basic meson support from ninjaoflight/meson-support 210 | - #349 Refactoring of Statement and Column classes from Kacperos155/refactoring-Statement&Column 211 | - #359 Fix compilation issues earlier than iOS 13 212 | - #354 Windows improved support (meson) from ninjaoflight/windows-migration 213 | - #361 Fix Statement unit test using long from SRombauts/fix-statement-unit-tests-long-long-type 214 | - #346 Add compatible definition for std::experimental::filesystem from guoh27/master 215 | - #364 Removal of remaining long APIs from SRombauts/convert-remaining-long-types 216 | - #366 Add vcpkg installation instructions from FrankXie05/vcpkg-instructions 217 | - #360 Small improvements and code cleaning from Kacperos155/small_improvements 218 | 219 | Versions 3.2.1 - 2022 Decembre 12 220 | - #383 Update SQLite from 3.39.3 to 3.40.0 (2022-11-16) from SRombauts/update-sqlite-340 221 | - #370 Don't link anymore with Visual Studio's static runtime by default from SRombauts/dont-enforce-static-linking 222 | - #371 from SRombauts/appveyor-vs-2022 223 | - #277 from cuberite/cmake-scoping 224 | - #374 Update googletest from vuhailongkl97/master 225 | - #377 Some documentation fixes from cbielow/fix_doc 226 | - #380 [Meson] fixes for meson project from ninjaoflight/windows-support 227 | - #387 Ensure that TEXT column is UTF-8 encoded before using sqlite3_column_blob() from dougnazar 228 | - #385 disable SQLITECPP_USE_STACK_PROTECTION when on MinGW from SRombauts/mingw-disable-stack-protection 229 | - #386 [meson] Update SQLite from 3.39.3 to 3.40.0 from ninjaoflight/sqlite-meson-update 230 | - #389 [meson] add missing compile options from ninjaoflight/meson-fixes 231 | 232 | Version 3.3.0 - 2023 May 24 233 | - #393 Fix preprocessor issues from jowr/fix_preprocessor_issues 234 | - #394 check if SQLITE_OPEN_NOFOLLOW is defined from ninjaoflight/macos-11-fix 235 | - #391 meson project changes based on wrap submission review from ninjaoflight/meson-macos-fix 236 | - #390 fix incorrect work of savepoint from spoyler/save_point Sébastien Rombauts 12/15/2022 01:12 PM 237 | - #396 Rename Savepoint RollbackTo() and fix class comments and formatting from SRombauts/rename-savepoint-rollback-to 238 | - #384 Add Mingw GitHub actions from SRombauts/mingw-github-actions 239 | - #397 Add a Transaction::rollback() method from SRombauts/add-transaction-rollback 240 | - #395 add meson usage guide from ninjaoflight/meson-readme-guide 241 | - #401 Fix meson installation from dougnazar/fix_meson_install 242 | - #400 CMakr/meson Lint corrections from ninjaoflight/lint-corrections 243 | - #404 Add documentation for prepared statements in transactions from ewarchul/query_transactions_example 244 | - #399 add disable option for sqlite3_expanded_sql from ninjaoflight/optional-sqlite3_expanded_sql 245 | - #408 correct executable name in meson from ninjaoflight/patch-2 246 | - #407 Create Meson CI from ninjaoflight/patch-1 247 | - #409 Update package.xml from poshul/patch-1 248 | - #410 use checkout@v3 in CMake CI from ninjaoflight/fix-nodejs-warnings 249 | - #406 DLL export/import using BUILD_SHARED_LIBS from pierre-aimi/dllexport_import 250 | - #415 Remove mismatched else condition in CMakeLists.txt from Timmmm/patch-1 251 | - #413 Fix compiler warnings from ninjaoflight/fix-visibility-warning 252 | - #423 Update SQLite from 3.40.0 to 3.42.0 (2023-05-16) from SRombauts/update-sqlite 253 | 254 | Version 3.3.1 - 2023 Aug 27 255 | 256 | - #428 Add CMake option SQLITE_ENABLE_DBSTAT_VTAB and SQLITE_ENABLE_RTREE from SRombauts/cmake-sqlite-enable-dbstat-vtab 257 | - #434 Define SQLITECPP_COMPILE_DLL as PUBLIC from calumr/fix-dll-import 258 | - #439 Update CMake minimum version to 3.5 to get rid of a new deprecation warning with CMake 3.27 from SRombauts/cmake-update-minimum-version 259 | - #441 Cleanup of the Github "build" workflow from SRombauts/github-actions-improvements 260 | - Update usage of SQLITECPP_USE_STATIC_RUNTIME (#438) 261 | - Don't build the googlemock subproject, only the main googletest library 262 | - Declare BUILD_SHARED_LIBS option for discoverability (#440) 263 | - Set -DBUILD_SHARED_LIBS=ON by default on scripts and CI/CD (#442) 264 | - Update SQLite from 3.42.0 to 3.43.0 (2023-08-24) (#443) 265 | - Rename the original build.yml to cmake.yml vs meson.yml (#444) 266 | 267 | Version 3.3.2 - 2024 Aug 16 268 | 269 | - Fix and update Travis CI workflow (#450) 270 | - Update Googletest to v1.15.2 (#451) and (#478) 271 | - [Meson] update meson dependencies (#448) 272 | - Macos ci fix (#476) 273 | - Update meson dependencies [Meson only] (#475) 274 | - Update SQLite from 3.43.0 to 3.46.1 (2024-08-13) (#461) and (#477) 275 | - Explicitly =delete; Statement::bindNoCopy(..., std::string&&) (#469) 276 | 277 | Version 3.3.3 - 2025 May 20 278 | 279 | - Update SQLite from 3.46.1 to 3.49.2 (2025-05-07) (#505) 280 | - SQLiteCpp/Statement.h: add missing `` include (#488) 281 | - sqlite3: set SQLITE_OMIT_LOAD_EXTENSION (#496) 282 | - tests/Database_test.cpp: fix a warning around `#endif` (#489) 283 | - Add a Github Dependabot config file (#480) 284 | - Bump actions/checkout from 3 to 4 in (#482) 285 | - Replace all double-quoted string literals in unit test (#483) 286 | - Use explicit versions of Ubuntu images instead of latest (#484) 287 | - Test linking with builtin libsqlite3-dev package on Ubuntu (#485) 288 | - Add logic to use different FindPackage for python if cmake is above 3.12 (#454) 289 | - Update googletest to v1.16.0 (#506) 290 | - update meson dependencies (#508) 291 | 292 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 19 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 20 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | Add a Tutorial for SQLite newbies 2 | Add a real example in the form of a small interactive console application 3 | 4 | Improve Github Wiki pages with the FAQ: Installation, Examples, Tutorial, How to contribute 5 | Publish the Doxygen Documentation in the Github Pages (gh-pages branch) 6 | 7 | Missing features in v2.0.0: 8 | - #34: Better type for getColumn 9 | 10 | Missing documentation in v2.0.0: 11 | - explain the non-copyable property for RAII design 12 | - comment on returning error code instead of exception that shall not be thrown when expected (!?) 13 | 14 | Missing unit tests in v2.0.0: 15 | - Load Extension (not practicable, and easy to verify by code review) 16 | 17 | Advanced missing features: 18 | - Add optional usage of experimental sqlite3_trace() function to enable statistics 19 | - Aggregate ? 20 | 21 | - support for different transaction mode ? NO: too specific 22 | - operator<< binding ? NO: redundant with bind() 23 | - ATTACH Database ? NO: can already be done by "ATTACH" Statement 24 | 25 | Post an article to CodeProject: Is there a license issue ? 26 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 2 | 3 | # build format 4 | version: "{build}" 5 | 6 | # scripts that run after cloning repository 7 | install: 8 | - git submodule update --init --recursive 9 | 10 | image: 11 | # Note: reduced the number of images to test on AppVeyor, to speed up the build 12 | - Visual Studio 2022 13 | # - Visual Studio 2019 14 | # - Visual Studio 2017 15 | - Visual Studio 2015 16 | 17 | # configurations to add to build matrix 18 | configuration: 19 | - Debug 20 | - Release 21 | 22 | environment: 23 | matrix: 24 | - arch: Win32 25 | - arch: x64 26 | 27 | init: 28 | - echo %APPVEYOR_BUILD_WORKER_IMAGE% - %configuration% - %arch% 29 | - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (set vs=Visual Studio 15 2017) 30 | - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" (set vs=Visual Studio 14 2015) 31 | - if "%arch%"=="x64" (set generator="%vs% Win64") else (set generator="%vs%") 32 | # CMake uses a different grammar for Visual Studio 2019, with -A to specify architecture: 33 | - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" (set generator="Visual Studio 16 2019" -A %arch%) 34 | - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2022" (set generator="Visual Studio 17 2022" -A %arch%) 35 | - echo %generator% 36 | 37 | # scripts to run before build 38 | before_build: 39 | - mkdir build 40 | - cd build 41 | - cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_BUILD_TESTS=ON -DSQLITECPP_RUN_CPPCHECK=OFF .. -G %generator% 42 | 43 | # build examples, and run tests (ie make & make test) 44 | build_script: 45 | - cmake --build . --config %configuration% 46 | - ctest --output-on-failure 47 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | @REM Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 2 | @REM 3 | @REM Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 4 | @REM or copy at http://opensource.org/licenses/MIT) 5 | mkdir build 6 | cd build 7 | 8 | @REM Generate a Visual Studio solution for latest version found 9 | REM -DPYTHON_EXECUTABLE=D:\workspace\Corvus\UnrealEngine\Engine\Binaries\ThirdParty\Python\Win64\python.exe 10 | cmake -DBUILD_SHARED_LIBS=ON -DSQLITECPP_BUILD_TESTS=ON -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_RUN_CPPLINT=OFF .. 11 | @if ERRORLEVEL 1 goto onError 12 | 13 | @REM Build default configuration (ie 'Debug') 14 | cmake --build . 15 | @if ERRORLEVEL 1 goto onError 16 | 17 | @REM Build and run tests 18 | ctest --output-on-failure 19 | @if ERRORLEVEL 1 goto onError 20 | 21 | @goto onSuccess 22 | 23 | :onError 24 | @echo An error occured! 25 | :onSuccess 26 | @cd .. 27 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) 2012-2025 Sébastien Rombauts (sebastien.rombauts@gmail.com) 3 | # 4 | # Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 5 | # or copy at http://opensource.org/licenses/MIT) 6 | 7 | # exit on first error 8 | set -e 9 | 10 | mkdir -p build 11 | cd build 12 | 13 | # Generate a Makefile for GCC (or Clang, depending on CC/CXX envvar) 14 | cmake -DCMAKE_BUILD_TYPE=Debug -DSQLITECPP_USE_ASAN=ON -DSQLITECPP_USE_GCOV=OFF -DBUILD_SHARED_LIBS=ON -DSQLITECPP_BUILD_TESTS=ON -DSQLITECPP_BUILD_EXAMPLES=ON .. 15 | 16 | # Build (ie 'make') 17 | cmake --build . 18 | 19 | # Build and run unit-tests (ie 'make test') 20 | ctest --output-on-failure 21 | 22 | # And with Valgrind 23 | echo "Note: uncomment to run valgrind memcheck" 24 | #valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --error-exitcode=1 ./SQLiteCpp_example1 25 | #valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --error-exitcode=1 ./SQLiteCpp_tests 26 | -------------------------------------------------------------------------------- /cmake/FindSQLite3.cmake: -------------------------------------------------------------------------------- 1 | # Distributed under the OSI-approved BSD 3-Clause License. See accompanying 2 | # file Copyright.txt or https://cmake.org/licensing for details. 3 | 4 | #[=======================================================================[.rst: 5 | FindSQLite3 6 | ----------- 7 | 8 | Find the SQLite libraries, v3 9 | 10 | IMPORTED targets 11 | ^^^^^^^^^^^^^^^^ 12 | 13 | This module defines the following :prop_tgt:`IMPORTED` target: 14 | 15 | ``SQLite::SQLite3`` 16 | 17 | Result variables 18 | ^^^^^^^^^^^^^^^^ 19 | 20 | This module will set the following variables if found: 21 | 22 | ``SQLite3_INCLUDE_DIRS`` 23 | where to find sqlite3.h, etc. 24 | ``SQLite3_LIBRARIES`` 25 | the libraries to link against to use SQLite3. 26 | ``SQLite3_VERSION`` 27 | version of the SQLite3 library found 28 | ``SQLite3_FOUND`` 29 | TRUE if found 30 | 31 | #]=======================================================================] 32 | 33 | # Look for the necessary header 34 | find_path(SQLite3_INCLUDE_DIR NAMES sqlite3.h) 35 | mark_as_advanced(SQLite3_INCLUDE_DIR) 36 | 37 | # Look for the necessary library 38 | find_library(SQLite3_LIBRARY NAMES sqlite3 sqlite) 39 | mark_as_advanced(SQLite3_LIBRARY) 40 | 41 | # Extract version information from the header file 42 | if(SQLite3_INCLUDE_DIR) 43 | file(STRINGS ${SQLite3_INCLUDE_DIR}/sqlite3.h _ver_line 44 | REGEX "^#define SQLITE_VERSION *\"[0-9]+\\.[0-9]+\\.[0-9]+\"" 45 | LIMIT_COUNT 1) 46 | string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" 47 | SQLite3_VERSION "${_ver_line}") 48 | unset(_ver_line) 49 | endif() 50 | 51 | include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) 52 | find_package_handle_standard_args(SQLite3 53 | REQUIRED_VARS SQLite3_INCLUDE_DIR SQLite3_LIBRARY 54 | VERSION_VAR SQLite3_VERSION) 55 | 56 | # Create the imported target 57 | if(SQLite3_FOUND) 58 | set(SQLite3_INCLUDE_DIRS ${SQLite3_INCLUDE_DIR}) 59 | set(SQLite3_LIBRARIES ${SQLite3_LIBRARY}) 60 | if(NOT TARGET SQLite::SQLite3) 61 | add_library(SQLite::SQLite3 UNKNOWN IMPORTED) 62 | set_target_properties(SQLite::SQLite3 PROPERTIES 63 | IMPORTED_LOCATION "${SQLite3_LIBRARY}" 64 | INTERFACE_INCLUDE_DIRECTORIES "${SQLite3_INCLUDE_DIR}") 65 | endif() 66 | endif() 67 | -------------------------------------------------------------------------------- /cmake/SQLiteCppConfig.cmake.in: -------------------------------------------------------------------------------- 1 | include(CMakeFindDependencyMacro) 2 | if(NOT @SQLITECPP_INTERNAL_SQLITE@) 3 | find_dependency(SQLite3 REQUIRED) 4 | endif() 5 | if(@UNIX@) 6 | set(THREADS_PREFER_PTHREAD_FLAG @THREADS_PREFER_PTHREAD_FLAG@) 7 | find_dependency(Threads REQUIRED) 8 | endif() 9 | 10 | @PACKAGE_INIT@ 11 | 12 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 13 | check_required_components("@PROJECT_NAME@") 14 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | SQLiteC++ 2 | --------- 3 | 4 | [![release](https://img.shields.io/github/release/SRombauts/SQLiteCpp.svg)](https://github.com/SRombauts/SQLiteCpp/releases) 5 | [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/SRombauts/SQLiteCpp/blob/master/LICENSE.txt) 6 | [![Travis CI Linux Build Status](https://travis-ci.org/SRombauts/SQLiteCpp.svg?branch=master)](https://travis-ci.org/SRombauts/SQLiteCpp "Travis CI Linux Build Status") 7 | [![AppVeyor Windows Build status](https://ci.appveyor.com/api/projects/status/github/SRombauts/SQLiteCpp?svg=true)](https://ci.appveyor.com/project/SbastienRombauts/SQLiteCpp "AppVeyor Windows Build status") 8 | [![GitHub Actions Build status](https://github.com/SRombauts/SQLiteCpp/workflows/build/badge.svg)](https://github.com/SRombauts/SQLiteCpp/actions "GitHhub Actions Build status") 9 | [![Coveralls](https://img.shields.io/coveralls/SRombauts/SQLiteCpp.svg)](https://coveralls.io/github/SRombauts/SQLiteCpp "Coveralls test coverage") 10 | [![Coverity](https://img.shields.io/coverity/scan/14508.svg)](https://scan.coverity.com/projects/srombauts-sqlitecpp "Coverity Scan Build Status") 11 | [![Join the chat at https://gitter.im/SRombauts/SQLiteCpp](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/SRombauts/SQLiteCpp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 12 | 13 | SQLiteC++ (SQLiteCpp) is a lean and easy to use C++ SQLite3 wrapper. 14 | 15 | 16 | 17 | 18 | ## About SQLiteC++: 19 | 20 | SQLiteC++ offers an encapsulation around the native C APIs of SQLite, 21 | with a few intuitive and well documented C++ classes. 22 | 23 | ### License: 24 | 25 | Copyright (c) 2012-2023 Sébastien Rombauts (sebastien.rombauts@gmail.com) 26 | 27 | 28 | Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 29 | or copy at http://opensource.org/licenses/MIT) 30 | 31 | #### Note on redistribution of SQLite source files 32 | 33 | As stated by the MIT License, you are welcome to reuse, modify, and redistribute the SQLiteCpp source code 34 | the way you want it to, be it a git submodule, a subdirectory, or a selection of some source files. 35 | 36 | I would love a mention in your README, a web link to the SQLite repository, and a mention of the author, 37 | but none of those are mandatory. 38 | 39 | ### About SQLite underlying library: 40 | 41 | SQLite is a library that implements a serverless transactional SQL database engine. 42 | It is the most widely deployed SQL database engine in the world. 43 | All of the code and documentation in SQLite has been dedicated to the public domain by the authors. 44 | [http://www.sqlite.org/about.html](http://www.sqlite.org/about.html) 45 | 46 | ### The goals of SQLiteC++ are: 47 | 48 | - to offer the best of the existing simple C++ SQLite wrappers 49 | - to be elegantly written with good C++11 design, STL, exceptions and RAII idiom 50 | - to keep dependencies to a minimum (C++11 STL and SQLite3) 51 | - to be portable 52 | - to be light and fast 53 | - to be thread-safe only as much as SQLite "Multi-thread" mode (see below) 54 | - to have a good unit test coverage 55 | - to use API names sticking with those of the SQLite library 56 | - to be well documented with Doxygen tags, and with some good examples 57 | - to be well maintained 58 | - to use a permissive MIT license, similar to BSD or Boost, for proprietary/commercial usage 59 | 60 | It is designed using the Resource Acquisition Is Initialization (RAII) idiom 61 | (see [http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization](http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization)), 62 | and throwing exceptions in case of SQLite errors (except in destructors, 63 | where assert() are used instead). 64 | Each SQLiteC++ object must be constructed with a valid SQLite database connection, 65 | and then is always valid until destroyed. 66 | 67 | ### Supported platforms: 68 | 69 | Now requires a C++11 compiler. Use branch [sqlitecpp-2.x](https://github.com/SRombauts/SQLiteCpp/tree/sqlitecpp-2.x) for latest pre-C++11 developments. 70 | 71 | Developments and tests are done under the following OSs: 72 | - Ubuntu 14.04, 16.04 and 18.04 (Travis CI and Github Actions) 73 | - Windows 10, and Windows Server 2012 R2, Windows Server 2016, Windows Server 2022 (AppVeyor and Github Actions) 74 | - MacOS 10.11 and 11.7 (Travis CI and Github Actions) 75 | - Valgrind memcheck tool 76 | 77 | And the following IDEs/Compilers 78 | - GCC 4.8.4, 5.3.0, 7.1.1 and latest eg 9.4 (C++11, C++14, C++17) 79 | - Clang 5 and 7 (Travis CI) 80 | - AppleClang 8, 9 and 13 (Travis CI and Github Actions) 81 | - Xcode 8 & 9 (Travis CI) 82 | - Visual Studio Community/Entreprise 2022, 2019, 2017, and 2015 (AppVeyor and Github Actions) 83 | 84 | ### Dependencies 85 | 86 | - a modern C++11 STL implementation with GCC, Clang, or Visual Studio 2015 87 | - exception support (the class Exception inherits from std::runtime_error) 88 | - the SQLite library (3.7.15 minimum from 2012-12-12) either by linking to it dynamically or statically (install the libsqlite3-dev package under Debian/Ubuntu/Mint Linux), 89 | or by adding its source file in your project code base (source code provided in src/sqlite3 for Windows), 90 | with the `SQLITE_ENABLE_COLUMN_METADATA` macro defined (see http://www.sqlite.org/compile.html#enable_column_metadata). 91 | 92 | ## Getting started 93 | ### Installation 94 | 95 | To use this wrapper, you need to add the SQLiteC++ source files from the src/ directory 96 | in your project code base, and compile/link against the sqlite library. 97 | 98 | The easiest way to do this is to add the wrapper as a library. 99 | The "CMakeLists.txt" file defining the static library is provided in the root directory, 100 | so you simply have to add_subdirectory(SQLiteCpp) to you main CMakeLists.txt 101 | and link to the "SQLiteCpp" wrapper library. 102 | 103 | Example for Linux: 104 | ```cmake 105 | add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/thirdparty/SQLiteCpp) 106 | 107 | add_executable(main src/main.cpp) 108 | target_link_libraries(main 109 | SQLiteCpp 110 | sqlite3 111 | pthread 112 | dl 113 | ) 114 | ``` 115 | Thus this SQLiteCpp repository can be directly used as a Git submodule. 116 | See the [SQLiteCpp_Example](https://github.com/SRombauts/SQLiteCpp_Example) side repository for a standalone "from scratch" example. 117 | 118 | Under Debian/Ubuntu/Mint Linux, you can install the libsqlite3-dev package if you don't want to use the embedded sqlite3 library. 119 | 120 | ### Building example and unit-tests: 121 | 122 | Use git to clone the repository. Then init and update submodule "googletest". 123 | 124 | ```Shell 125 | git clone https://github.com/SRombauts/SQLiteCpp.git 126 | cd SQLiteCpp 127 | git submodule init 128 | git submodule update 129 | ``` 130 | 131 | ### Installing SQLiteCpp (vcpkg) 132 | 133 | Alternatively, you can build and install SQLiteCpp using [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager: 134 | 135 | ```bash or powershell 136 | git clone https://github.com/Microsoft/vcpkg.git 137 | cd vcpkg 138 | ./bootstrap-vcpkg.sh 139 | ./vcpkg integrate install 140 | ./vcpkg install sqlitecpp 141 | ``` 142 | 143 | The SQLiteCpp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. 144 | 145 | 146 | #### Using SQLiteCpp on a system-wide installation 147 | 148 | If you installed this package to your system, a `SQLiteCppConfig.cmake` file will be generated & installed to your system. 149 | This file lets you link against the SQLiteCpp library for use in your Cmake project. 150 | 151 | Here's an example of using this in your CMakeLists.txt 152 | ```cmake 153 | # You can optionally define a minimum version in this call 154 | find_package(SQLiteCpp REQUIRED) 155 | # For this example, lets say you created an target with add_executable (or add_library) called "my_target" 156 | # You can optionally declare PUBLIC or PRIVATE linkage here, depending on your needs. 157 | target_link_libraries(my_target PRIVATE SQLiteCpp) 158 | ``` 159 | 160 | #### CMake and tests 161 | A CMake configuration file is also provided for multi-platform support and testing. 162 | 163 | Typical generic build for MS Visual Studio under Windows (from [build.bat](build.bat)): 164 | 165 | ```Batchfile 166 | mkdir build 167 | cd build 168 | 169 | cmake .. # cmake .. -G "Visual Studio 16 2019" # for Visual Studio 2019 170 | @REM Generate a Visual Studio solution for latest version found 171 | cmake -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_BUILD_TESTS=ON .. 172 | 173 | @REM Build default configuration (ie 'Debug') 174 | cmake --build . 175 | 176 | @REM Build and run tests 177 | ctest --output-on-failure 178 | ``` 179 | 180 | Generating the Linux Makefile, building in Debug and executing the tests (from [build.sh](build.sh)): 181 | 182 | ```Shell 183 | mkdir Debug 184 | cd Debug 185 | 186 | # Generate a Makefile for GCC (or Clang, depanding on CC/CXX envvar) 187 | cmake -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_BUILD_TESTS=ON .. 188 | 189 | # Build (ie 'make') 190 | cmake --build . 191 | 192 | # Build and run unit-tests (ie 'make test') 193 | ctest --output-on-failure 194 | ``` 195 | 196 | #### CMake options 197 | 198 | * For more options on customizing the build, see the [CMakeLists.txt](https://github.com/SRombauts/SQLiteCpp/blob/master/CMakeLists.txt) file. 199 | 200 | #### Troubleshooting 201 | 202 | Under Linux, if you get multiple linker errors like "undefined reference to sqlite3_xxx", 203 | it's that you lack the "sqlite3" library: install the libsqlite3-dev package. 204 | 205 | If you get a single linker error "Column.cpp: undefined reference to sqlite3_column_origin_name", 206 | it's that your "sqlite3" library was not compiled with 207 | the `SQLITE_ENABLE_COLUMN_METADATA` macro defined (see [http://www.sqlite.org/compile.html#enable_column_metadata](http://www.sqlite.org/compile.html#enable_column_metadata)). 208 | You can: 209 | - either recompile the sqlite3 library provided by your distribution yourself (seek help online) 210 | - or turn off the `option(SQLITE_ENABLE_COLUMN_METADATA "Enable Column::getColumnOriginName(). Require support from sqlite3 library." ON)` in [CMakeFiles.txt](CMakeFiles.txt) (or other build system scripts) 211 | - or turn on the `option(SQLITECPP_INTERNAL_SQLITE "Add the internal SQLite3 source to the project." ON)` in [CMakeFiles.txt](CMakeFiles.txt) 212 | 213 | ### Continuous Integration 214 | 215 | This project is continuously tested under Ubuntu Linux with the gcc and clang compilers 216 | using the Travis CI community service with the above CMake building and testing procedure. 217 | It is also tested in the same way under Windows Server 2012 R2 with Visual Studio 2013 compiler 218 | using the AppVeyor continuous integration service. 219 | 220 | Detailed results can be seen online: 221 | - [https://travis-ci.org/SRombauts/SQLiteCpp](https://travis-ci.org/SRombauts/SQLiteCpp) 222 | - [https://ci.appveyor.com/project/SbastienRombauts/SQLiteCpp](https://ci.appveyor.com/project/SbastienRombauts/SQLiteCpp) 223 | 224 | ### Thread-safety 225 | 226 | SQLite supports three modes of thread safety, as describe in "SQLite And Multiple Threads": 227 | see [http://www.sqlite.org/threadsafe.html](http://www.sqlite.org/threadsafe.html) 228 | 229 | This SQLiteC++ wrapper does no add any locks (no mutexes) nor any other thread-safety mechanism 230 | above the SQLite library itself, by design, for lightness and speed. 231 | 232 | Thus, SQLiteC++ naturally supports the "Multi Thread" mode of SQLite: 233 | "In this mode, SQLite can be safely used by multiple threads 234 | provided that no single database connection is used simultaneously in two or more threads." 235 | 236 | But SQLiteC++ does not support the fully thread-safe "Serialized" mode of SQLite, 237 | because of the way it shares the underlying SQLite precompiled statement 238 | in a custom shared pointer (See the inner class "Statement::Ptr"). 239 | 240 | ### Valgrind memcheck 241 | 242 | Run valgrind to search for memory leaks in your application, the SQLiteCpp wrapper, or the sqlite3 library. 243 | Execute the following command under Unix like OS (Linux, MacOS or WSL2/Ubuntu under Windows Subsystem for Linux): 244 | 245 | ```Shell 246 | valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose build/SQLiteCpp_example1 247 | ``` 248 | 249 | or uncoment the line at the end of [build.sh](build.sh) 250 | 251 | ## Examples 252 | ### The first sample demonstrates how to query a database and get results: 253 | 254 | ```C++ 255 | try 256 | { 257 | // Open a database file 258 | SQLite::Database db("example.db3"); 259 | 260 | // Compile a SQL query, containing one parameter (index 1) 261 | SQLite::Statement query(db, "SELECT * FROM test WHERE size > ?"); 262 | 263 | // Bind the integer value 6 to the first parameter of the SQL query 264 | query.bind(1, 6); 265 | 266 | // Loop to execute the query step by step, to get rows of result 267 | while (query.executeStep()) 268 | { 269 | // Demonstrate how to get some typed column value 270 | int id = query.getColumn(0); 271 | const char* value = query.getColumn(1); 272 | int size = query.getColumn(2); 273 | 274 | std::cout << "row: " << id << ", " << value << ", " << size << std::endl; 275 | } 276 | } 277 | catch (std::exception& e) 278 | { 279 | std::cout << "exception: " << e.what() << std::endl; 280 | } 281 | ``` 282 | 283 | ### The second sample shows how to manage a transaction: 284 | 285 | ```C++ 286 | try 287 | { 288 | SQLite::Database db("transaction.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 289 | 290 | db.exec("DROP TABLE IF EXISTS test"); 291 | 292 | // Begin transaction 293 | SQLite::Transaction transaction(db); 294 | 295 | db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)"); 296 | 297 | int nb = db.exec("INSERT INTO test VALUES (NULL, \"test\")"); 298 | std::cout << "INSERT INTO test VALUES (NULL, \"test\")\", returned " << nb << std::endl; 299 | 300 | // Commit transaction 301 | transaction.commit(); 302 | } 303 | catch (std::exception& e) 304 | { 305 | std::cout << "exception: " << e.what() << std::endl; 306 | } 307 | ``` 308 | 309 | ### How to handle assertion in SQLiteC++: 310 | Exceptions shall not be used in destructors, so SQLiteC++ uses SQLITECPP_ASSERT() to check for errors in destructors. 311 | If you don't want assert() to be called, you have to enable and define an assert handler as shown below, 312 | and by setting the flag SQLITECPP_ENABLE_ASSERT_HANDLER when compiling the lib. 313 | 314 | ```C++ 315 | #ifdef SQLITECPP_ENABLE_ASSERT_HANDLER 316 | namespace SQLite 317 | { 318 | /// definition of the assertion handler enabled when SQLITECPP_ENABLE_ASSERT_HANDLER is defined in the project (CMakeList.txt) 319 | void assertion_failed(const char* apFile, const long apLine, const char* apFunc, const char* apExpr, const char* apMsg) 320 | { 321 | // Print a message to the standard error output stream, and abort the program. 322 | std::cerr << apFile << ":" << apLine << ":" << " error: assertion failed (" << apExpr << ") in " << apFunc << "() with message \"" << apMsg << "\"\n"; 323 | std::abort(); 324 | } 325 | } 326 | #endif 327 | ``` 328 | 329 | ## How to contribute 330 | ### GitHub website 331 | The most efficient way to help and contribute to this wrapper project is to 332 | use the tools provided by GitHub: 333 | - please fill bug reports and feature requests here: [https://github.com/SRombauts/SQLiteCpp/issues](https://github.com/SRombauts/SQLiteCpp/issues) 334 | - fork the repository, make some small changes and submit them with pull-request 335 | 336 | ### Contact 337 | You can also email me directly, I will try to answer questions and requests whenever I get the time for it. 338 | 339 | ### Coding Style Guidelines 340 | The source code use the CamelCase naming style variant where: 341 | - type names (class, struct, typedef, enums...) begin with a capital letter 342 | - files (.cpp/.h) are named like the class they contain 343 | - function and variable names begin with a lower case letter 344 | - member variables begin with a 'm', function arguments begin with a 'a', booleans with a 'b', pointers with a 'p' 345 | - each file, class, method and member variable is documented using Doxygen tags 346 | - braces on their own line 347 | See also [http://www.appinf.com/download/CppCodingStyleGuide.pdf](http://www.appinf.com/download/CppCodingStyleGuide.pdf) for good guidelines 348 | 349 | ## See also - Some other simple C++ SQLite wrappers: 350 | 351 | See bellow a short comparison of other wrappers done at the time of writing: 352 | - [sqdbcpp](http://code.google.com/p/sqdbcpp/): RAII design, simple, no dependencies, UTF-8/UTF-16, new BSD license 353 | - [sqlite3cc](http://ed.am/dev/sqlite3cc): uses boost, modern design, LPGPL 354 | - [sqlite3pp](https://github.com/iwongu/sqlite3pp): modern design inspired by boost, MIT License 355 | - [SQLite++](http://sqlitepp.berlios.de/): uses boost build system, Boost License 1.0 356 | - [CppSQLite](http://www.codeproject.com/Articles/6343/CppSQLite-C-Wrapper-for-SQLite/): famous Code Project but old design, BSD License 357 | - [easySQLite](http://code.google.com/p/easysqlite/): manages table as structured objects, complex 358 | - [sqlite_modern_cpp](https://github.com/keramer/sqlite_modern_cpp): modern C++11, all in one file, MIT license 359 | - [sqlite_orm](https://github.com/fnc12/sqlite_orm): modern C++14, header only all in one file, no raw string queries, BSD-3 license 360 | -------------------------------------------------------------------------------- /examples/example1/README.md: -------------------------------------------------------------------------------- 1 | examples/example1 - main example 2 | -------------------------------- 3 | 4 | SQLiteCpp_Example demonstrates how to use SQLiteCpp as a subdirectory of a CMake project. 5 | 6 | See also examples/example2 on how to use SQLiteCpp as a subdirectory of a CMake project -------------------------------------------------------------------------------- /examples/example1/example.db3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SRombauts/SQLiteCpp/1df768817e68529fe47870f8e9913a47343a822d/examples/example1/example.db3 -------------------------------------------------------------------------------- /examples/example1/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SRombauts/SQLiteCpp/1df768817e68529fe47870f8e9913a47343a822d/examples/example1/logo.png -------------------------------------------------------------------------------- /examples/example1/meson.build: -------------------------------------------------------------------------------- 1 | 2 | example1_sources = files('main.cpp') 3 | 4 | example1_args = [] 5 | 6 | ## under windows define _CRT_SECURE_NO_WARNINGS 7 | if host_machine.system() == 'windows' 8 | example1_args += ['-D_CRT_SECURE_NO_WARNINGS'] 9 | endif 10 | 11 | executable( 12 | 'SQLITECPP_sample_demo1', 13 | sources: example1_sources, 14 | dependencies: sqlitecpp_dep, 15 | # inherit the default options from sqlitecpp 16 | override_options: sqlitecpp_opts, 17 | cpp_args: example1_args, 18 | ) 19 | -------------------------------------------------------------------------------- /examples/example2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Example CMake file for compiling & linking a project with the the SQLiteCpp wrapper 2 | # 3 | # Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) 4 | # 5 | # Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 6 | # or copy at http://opensource.org/licenses/MIT) 7 | cmake_minimum_required(VERSION 3.1) # for "CMAKE_CXX_STANDARD" version 8 | project(SQLiteCpp_Example VERSION 2.0) 9 | 10 | # SQLiteC++ 3.x now requires C++11 compiler 11 | set(CMAKE_CXX_STANDARD 11) 12 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 13 | 14 | # Add SQLite3 C++ wrapper around sqlite3 library (and sqlite3 itself provided for ease of use) 15 | # Here you can set CMake variables to avoid building Example, as well as cpplint, cppcheck... 16 | # or set them in the cmake command line (see for instance provided build.bat/build.sh scripts) 17 | set(SQLITECPP_RUN_CPPCHECK OFF CACHE BOOL "" FORCE) 18 | set(SQLITECPP_RUN_CPPLINT OFF CACHE BOOL "" FORCE) 19 | set(SQLITECPP_USE_STATIC_RUNTIME OFF CACHE BOOL "" FORCE) 20 | add_subdirectory(../.. SQLiteCpp) # out-of-source build requires explicit subdir name for compilation artifacts 21 | 22 | # Add main.cpp example source code to the executable 23 | add_executable(SQLiteCpp_Example src/main.cpp) 24 | 25 | # Link SQLiteCpp_example1 with SQLiteCpp 26 | target_link_libraries(SQLiteCpp_Example SQLiteCpp) 27 | -------------------------------------------------------------------------------- /examples/example2/README.md: -------------------------------------------------------------------------------- 1 | examples/example2 - SQLiteCpp_Example 2 | ------------------------------------- 3 | 4 | SQLiteCpp_Example demonstrates how to use SQLiteCpp as a subdirectory of a CMake project. 5 | 6 | See https://github.com/SRombauts/SQLiteCpp_Example 7 | 8 | See also examples/example1 for the main example on how to use SQLiteCpp in a C++ project -------------------------------------------------------------------------------- /examples/example2/build.bat: -------------------------------------------------------------------------------- 1 | @REM Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) 2 | @REM 3 | @REM Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 4 | @REM or copy at http://opensource.org/licenses/MIT) 5 | mkdir build 6 | cd build 7 | 8 | @REM Generate a Visual Studio solution for latest version found 9 | cmake .. 10 | @if ERRORLEVEL 1 goto onError 11 | 12 | @REM Build default configuration (ie 'Debug') 13 | cmake --build . 14 | @if ERRORLEVEL 1 goto onError 15 | 16 | goto onSuccess 17 | 18 | :onError 19 | @echo An error occured! 20 | :onSuccess 21 | cd .. 22 | -------------------------------------------------------------------------------- /examples/example2/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) 2012-2023 Sébastien Rombauts (sebastien.rombauts@gmail.com) 3 | # 4 | # Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 5 | # or copy at http://opensource.org/licenses/MIT) 6 | 7 | # exit on first error 8 | set -e 9 | 10 | mkdir -p build 11 | cd build 12 | 13 | # Generate a Makefile for GCC (or Clang, depanding on CC/CXX envvar) 14 | cmake -DCMAKE_BUILD_TYPE=Debug .. 15 | 16 | # Build (ie 'make') 17 | cmake --build . 18 | 19 | -------------------------------------------------------------------------------- /examples/example2/meson.build: -------------------------------------------------------------------------------- 1 | example2_srcs = files('src/main.cpp') 2 | 3 | # if running on windows define _CRT_SECURE_NO_WARNINGS 4 | example2_args = [] 5 | 6 | executable( 7 | 'SQLITECPP_sample_demo2', 8 | example2_srcs, 9 | dependencies: sqlitecpp_dep, 10 | # inherit the default options from sqlitecpp 11 | override_options: sqlitecpp_opts, 12 | cpp_args: example2_args, 13 | ) 14 | -------------------------------------------------------------------------------- /examples/example2/src/main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file main.cpp 3 | * @brief A few short examples in a row. 4 | * 5 | * Demonstrates how-to use the SQLite++ wrapper 6 | * 7 | * Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) 8 | * 9 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 10 | * or copy at http://opensource.org/licenses/MIT) 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | 20 | #ifdef SQLITECPP_ENABLE_ASSERT_HANDLER 21 | namespace SQLite 22 | { 23 | /// definition of the assertion handler enabled when SQLITECPP_ENABLE_ASSERT_HANDLER is defined in the project (CMakeList.txt) 24 | void assertion_failed(const char* apFile, const long apLine, const char* apFunc, const char* apExpr, const char* apMsg) 25 | { 26 | // Print a message to the standard error output stream, and abort the program. 27 | std::cerr << apFile << ":" << apLine << ":" << " error: assertion failed (" << apExpr << ") in " << apFunc << "() with message \"" << apMsg << "\"\n"; 28 | std::abort(); 29 | } 30 | } 31 | #endif 32 | 33 | int main () 34 | { 35 | // Using SQLITE_VERSION would require #include which we want to avoid: use SQLite::VERSION if possible. 36 | // std::cout << "SQlite3 version " << SQLITE_VERSION << std::endl; 37 | std::cout << "SQlite3 version " << SQLite::VERSION << " (" << SQLite::getLibVersion() << ")" << std::endl; 38 | std::cout << "SQliteC++ version " << SQLITECPP_VERSION << std::endl; 39 | 40 | //////////////////////////////////////////////////////////////////////////// 41 | // Simple batch queries example : 42 | try 43 | { 44 | // Open a database file in create/write mode 45 | SQLite::Database db("test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 46 | std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n"; 47 | 48 | // Create a new table with an explicit "id" column aliasing the underlying rowid 49 | db.exec("DROP TABLE IF EXISTS test"); 50 | db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)"); 51 | 52 | // first row 53 | int nb = db.exec("INSERT INTO test VALUES (NULL, \"test\")"); 54 | std::cout << "INSERT INTO test VALUES (NULL, \"test\")\", returned " << nb << std::endl; 55 | 56 | // second row 57 | nb = db.exec("INSERT INTO test VALUES (NULL, \"second\")"); 58 | std::cout << "INSERT INTO test VALUES (NULL, \"second\")\", returned " << nb << std::endl; 59 | 60 | // update the second row 61 | nb = db.exec("UPDATE test SET value=\"second-updated\" WHERE id='2'"); 62 | std::cout << "UPDATE test SET value=\"second-updated\" WHERE id='2', returned " << nb << std::endl; 63 | 64 | // Check the results : expect two row of result 65 | SQLite::Statement query(db, "SELECT * FROM test"); 66 | std::cout << "SELECT * FROM test :\n"; 67 | while (query.executeStep()) 68 | { 69 | std::cout << "row (" << query.getColumn(0) << ", \"" << query.getColumn(1) << "\")\n"; 70 | } 71 | 72 | db.exec("DROP TABLE test"); 73 | } 74 | catch (std::exception& e) 75 | { 76 | std::cout << "SQLite exception: " << e.what() << std::endl; 77 | return EXIT_FAILURE; // unexpected error : exit the example program 78 | } 79 | remove("test.db3"); 80 | 81 | std::cout << "everything ok, quitting\n"; 82 | 83 | return EXIT_SUCCESS; 84 | } 85 | -------------------------------------------------------------------------------- /examples/meson.build: -------------------------------------------------------------------------------- 1 | subdir('example1') 2 | subdir('example2') -------------------------------------------------------------------------------- /include/SQLiteCpp/Assertion.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Assertion.h 3 | * @ingroup SQLiteCpp 4 | * @brief Definition of the SQLITECPP_ASSERT() macro. 5 | * 6 | * Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #pragma once 12 | 13 | #include 14 | 15 | /** 16 | * SQLITECPP_ASSERT SQLITECPP_ASSERT() is used in destructors, where exceptions shall not be thrown 17 | * 18 | * Define SQLITECPP_ENABLE_ASSERT_HANDLER at the project level 19 | * and define a SQLite::assertion_failed() assertion handler 20 | * to tell SQLiteC++ to use it instead of assert() when an assertion fail. 21 | */ 22 | #ifdef SQLITECPP_ENABLE_ASSERT_HANDLER 23 | 24 | // if an assert handler is provided by user code, use it instead of assert() 25 | namespace SQLite 26 | { 27 | 28 | // declaration of the assert handler to define in user code 29 | void assertion_failed(const char* apFile, const int apLine, const char* apFunc, 30 | const char* apExpr, const char* apMsg); 31 | 32 | #ifdef _MSC_VER 33 | #define __func__ __FUNCTION__ 34 | #endif 35 | // call the assert handler provided by user code 36 | #define SQLITECPP_ASSERT(expression, message) \ 37 | if (!(expression)) SQLite::assertion_failed(__FILE__, __LINE__, __func__, #expression, message) 38 | 39 | } // namespace SQLite 40 | 41 | #else 42 | 43 | // if no assert handler provided by user code, use standard assert() 44 | // (note: in release mode assert() does nothing) 45 | #define SQLITECPP_ASSERT(expression, message) assert(expression && message) 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /include/SQLiteCpp/Backup.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Backup.h 3 | * @ingroup SQLiteCpp 4 | * @brief Backup is used to backup a database file in a safe and online way. 5 | * 6 | * Copyright (c) 2015 Shibao HONG (shibaohong@outlook.com) 7 | * Copyright (c) 2015-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 8 | * 9 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 10 | * or copy at http://opensource.org/licenses/MIT) 11 | */ 12 | #pragma once 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | // Forward declaration to avoid inclusion of in a header 21 | struct sqlite3_backup; 22 | 23 | namespace SQLite 24 | { 25 | 26 | /** 27 | * @brief RAII encapsulation of a SQLite Database Backup process. 28 | * 29 | * A Backup object is used to backup a source database file to a destination database file 30 | * in a safe and online way. 31 | * 32 | * See also the a reference implementation of live backup taken from the official site: 33 | * https://www.sqlite.org/backup.html 34 | */ 35 | class SQLITECPP_API Backup 36 | { 37 | public: 38 | /** 39 | * @brief Initialize a SQLite Backup object. 40 | * 41 | * Initialize a SQLite Backup object for the source database and destination database. 42 | * The database name is "main" for the main database, "temp" for the temporary database, 43 | * or the name specified after the AS keyword in an ATTACH statement for an attached database. 44 | * 45 | * Exception is thrown in case of error, then the Backup object is NOT constructed. 46 | * 47 | * @param[in] aDestDatabase Destination database connection 48 | * @param[in] apDestDatabaseName Destination database name 49 | * @param[in] aSrcDatabase Source database connection 50 | * @param[in] apSrcDatabaseName Source database name 51 | * 52 | * @throw SQLite::Exception in case of error 53 | */ 54 | Backup(Database& aDestDatabase, 55 | const char* apDestDatabaseName, 56 | Database& aSrcDatabase, 57 | const char* apSrcDatabaseName); 58 | 59 | /** 60 | * @brief Initialize a SQLite Backup object. 61 | * 62 | * Initialize a SQLite Backup object for source database and destination database. 63 | * The database name is "main" for the main database, "temp" for the temporary database, 64 | * or the name specified after the AS keyword in an ATTACH statement for an attached database. 65 | * 66 | * Exception is thrown in case of error, then the Backup object is NOT constructed. 67 | * 68 | * @param[in] aDestDatabase Destination database connection 69 | * @param[in] aDestDatabaseName Destination database name 70 | * @param[in] aSrcDatabase Source database connection 71 | * @param[in] aSrcDatabaseName Source database name 72 | * 73 | * @throw SQLite::Exception in case of error 74 | */ 75 | Backup(Database& aDestDatabase, 76 | const std::string& aDestDatabaseName, 77 | Database& aSrcDatabase, 78 | const std::string& aSrcDatabaseName); 79 | 80 | /** 81 | * @brief Initialize a SQLite Backup object for main databases. 82 | * 83 | * Initialize a SQLite Backup object for source database and destination database. 84 | * Backup the main databases between the source and the destination. 85 | * 86 | * Exception is thrown in case of error, then the Backup object is NOT constructed. 87 | * 88 | * @param[in] aDestDatabase Destination database connection 89 | * @param[in] aSrcDatabase Source database connection 90 | * 91 | * @throw SQLite::Exception in case of error 92 | */ 93 | Backup(Database& aDestDatabase, 94 | Database& aSrcDatabase); 95 | 96 | // Backup is non-copyable 97 | Backup(const Backup&) = delete; 98 | Backup& operator=(const Backup&) = delete; 99 | 100 | /** 101 | * @brief Execute a step of backup with a given number of source pages to be copied 102 | * 103 | * Exception is thrown when SQLITE_IOERR_XXX, SQLITE_NOMEM, or SQLITE_READONLY is returned 104 | * in sqlite3_backup_step(). These errors are considered fatal, so there is no point 105 | * in retrying the call to executeStep(). 106 | * 107 | * @param[in] aNumPage The number of source pages to be copied, with a negative value meaning all remaining source pages 108 | * 109 | * @return SQLITE_OK/SQLITE_DONE/SQLITE_BUSY/SQLITE_LOCKED 110 | * 111 | * @throw SQLite::Exception in case of error 112 | */ 113 | int executeStep(const int aNumPage = -1); 114 | 115 | /// Return the number of source pages still to be backed up as of the most recent call to executeStep(). 116 | int getRemainingPageCount() const; 117 | 118 | /// Return the total number of pages in the source database as of the most recent call to executeStep(). 119 | int getTotalPageCount() const; 120 | 121 | private: 122 | // Deleter functor to use with smart pointers to close the SQLite database backup in an RAII fashion. 123 | struct Deleter 124 | { 125 | void operator()(sqlite3_backup* apBackup); 126 | }; 127 | 128 | std::unique_ptr mpSQLiteBackup; ///< Pointer to SQLite Database Backup Handle 129 | }; 130 | 131 | } // namespace SQLite 132 | -------------------------------------------------------------------------------- /include/SQLiteCpp/Column.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Column.h 3 | * @ingroup SQLiteCpp 4 | * @brief Encapsulation of a Column in a row of the result pointed by the prepared SQLite::Statement. 5 | * 6 | * Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | // Forward declarations to avoid inclusion of in a header 21 | struct sqlite3_stmt; 22 | 23 | namespace SQLite 24 | { 25 | 26 | SQLITECPP_API extern const int INTEGER; ///< SQLITE_INTEGER 27 | SQLITECPP_API extern const int FLOAT; ///< SQLITE_FLOAT 28 | SQLITECPP_API extern const int TEXT; ///< SQLITE_TEXT 29 | SQLITECPP_API extern const int BLOB; ///< SQLITE_BLOB 30 | SQLITECPP_API extern const int Null; ///< SQLITE_NULL 31 | 32 | /** 33 | * @brief Encapsulation of a Column in a row of the result pointed by the prepared Statement. 34 | * 35 | * A Column is a particular field of SQLite data in the current row of result 36 | * of the Statement : it points to a single cell. 37 | * 38 | * Its value can be expressed as a text, and, when applicable, as a numeric 39 | * (integer or floating point) or a binary blob. 40 | * 41 | * Thread-safety: a Column object shall not be shared by multiple threads, because : 42 | * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads 43 | * provided that no single database connection is used simultaneously in two or more threads." 44 | * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, 45 | * because of the way it shares the underling SQLite precompiled statement 46 | * in a custom shared pointer (See the inner class "Statement::Ptr"). 47 | */ 48 | class SQLITECPP_API Column 49 | { 50 | public: 51 | /** 52 | * @brief Encapsulation of a Column in a Row of the result. 53 | * 54 | * @param[in] aStmtPtr Shared pointer to the prepared SQLite Statement Object. 55 | * @param[in] aIndex Index of the column in the row of result, starting at 0 56 | */ 57 | explicit Column(const Statement::TStatementPtr& aStmtPtr, int aIndex); 58 | 59 | /** 60 | * @brief Return a pointer to the named assigned to this result column (potentially aliased) 61 | * 62 | * @see getOriginName() to get original column name (not aliased) 63 | */ 64 | const char* getName() const noexcept; 65 | 66 | #ifdef SQLITE_ENABLE_COLUMN_METADATA 67 | /** 68 | * @brief Return a pointer to the table column name that is the origin of this result column 69 | * 70 | * Require definition of the SQLITE_ENABLE_COLUMN_METADATA preprocessor macro : 71 | * - when building the SQLite library itself (which is the case for the Debian libsqlite3 binary for instance), 72 | * - and also when compiling this wrapper. 73 | */ 74 | const char* getOriginName() const noexcept; 75 | #endif 76 | 77 | /// Return the integer value of the column. 78 | int32_t getInt() const noexcept; 79 | /// Return the 32bits unsigned integer value of the column (note that SQLite3 does not support unsigned 64bits). 80 | uint32_t getUInt() const noexcept; 81 | /// Return the 64bits integer value of the column (note that SQLite3 does not support unsigned 64bits). 82 | int64_t getInt64() const noexcept; 83 | /// Return the double (64bits float) value of the column 84 | double getDouble() const noexcept; 85 | /** 86 | * @brief Return a pointer to the text value (NULL terminated string) of the column. 87 | * 88 | * @warning The value pointed at is only valid while the statement is valid (ie. not finalized), 89 | * thus you must copy it before using it beyond its scope (to a std::string for instance). 90 | */ 91 | const char* getText(const char* apDefaultValue = "") const noexcept; 92 | /** 93 | * @brief Return a pointer to the binary blob value of the column. 94 | * 95 | * @warning The value pointed at is only valid while the statement is valid (ie. not finalized), 96 | * thus you must copy it before using it beyond its scope (to a std::string for instance). 97 | */ 98 | const void* getBlob() const noexcept; 99 | /** 100 | * @brief Return a std::string for a TEXT or BLOB column. 101 | * 102 | * Note this correctly handles strings that contain null bytes. 103 | */ 104 | std::string getString() const; 105 | 106 | /** 107 | * @brief Return the type of the value of the column using sqlite3_column_type() 108 | * 109 | * Return either SQLite::INTEGER, SQLite::FLOAT, SQLite::TEXT, SQLite::BLOB, or SQLite::Null. 110 | * This type may change from one row to the next, since 111 | * SQLite stores data types dynamically for each value and not per column. 112 | * Use Statement::getColumnDeclaredType() to retrieve the declared column type from a SELECT statement. 113 | * 114 | * @warning After a type conversion (by a call to a getXxx on a Column of a Yyy type), 115 | * the value returned by sqlite3_column_type() is undefined. 116 | */ 117 | int getType() const noexcept; 118 | 119 | /// Test if the column is an integer type value (meaningful only before any conversion) 120 | bool isInteger() const noexcept 121 | { 122 | return (SQLite::INTEGER == getType()); 123 | } 124 | /// Test if the column is a floating point type value (meaningful only before any conversion) 125 | bool isFloat() const noexcept 126 | { 127 | return (SQLite::FLOAT == getType()); 128 | } 129 | /// Test if the column is a text type value (meaningful only before any conversion) 130 | bool isText() const noexcept 131 | { 132 | return (SQLite::TEXT == getType()); 133 | } 134 | /// Test if the column is a binary blob type value (meaningful only before any conversion) 135 | bool isBlob() const noexcept 136 | { 137 | return (SQLite::BLOB == getType()); 138 | } 139 | /// Test if the column is NULL (meaningful only before any conversion) 140 | bool isNull() const noexcept 141 | { 142 | return (SQLite::Null == getType()); 143 | } 144 | 145 | /** 146 | * @brief Return the number of bytes used by the text (or blob) value of the column 147 | * 148 | * Return either : 149 | * - size in bytes (not in characters) of the string returned by getText() without the '\0' terminator 150 | * - size in bytes of the string representation of the numerical value (integer or double) 151 | * - size in bytes of the binary blob returned by getBlob() 152 | * - 0 for a NULL value 153 | */ 154 | int getBytes() const noexcept; 155 | 156 | /// Alias returning the number of bytes used by the text (or blob) value of the column 157 | int size() const noexcept 158 | { 159 | return getBytes (); 160 | } 161 | 162 | /// Inline cast operators to basic types 163 | operator char() const 164 | { 165 | return static_cast(getInt()); 166 | } 167 | operator int8_t() const 168 | { 169 | return static_cast(getInt()); 170 | } 171 | operator uint8_t() const 172 | { 173 | return static_cast(getInt()); 174 | } 175 | operator int16_t() const 176 | { 177 | return static_cast(getInt()); 178 | } 179 | operator uint16_t() const 180 | { 181 | return static_cast(getInt()); 182 | } 183 | operator int32_t() const 184 | { 185 | return getInt(); 186 | } 187 | operator uint32_t() const 188 | { 189 | return getUInt(); 190 | } 191 | operator int64_t() const 192 | { 193 | return getInt64(); 194 | } 195 | operator double() const 196 | { 197 | return getDouble(); 198 | } 199 | /** 200 | * @brief Inline cast operator to char* 201 | * 202 | * @see getText 203 | */ 204 | operator const char*() const 205 | { 206 | return getText(); 207 | } 208 | /** 209 | * @brief Inline cast operator to void* 210 | * 211 | * @see getBlob 212 | */ 213 | operator const void*() const 214 | { 215 | return getBlob(); 216 | } 217 | 218 | /** 219 | * @brief Inline cast operator to std::string 220 | * 221 | * Handles BLOB or TEXT, which may contain null bytes within 222 | * 223 | * @see getString 224 | */ 225 | operator std::string() const 226 | { 227 | return getString(); 228 | } 229 | 230 | private: 231 | Statement::TStatementPtr mStmtPtr; ///< Shared Pointer to the prepared SQLite Statement Object 232 | int mIndex; ///< Index of the column in the row of result, starting at 0 233 | }; 234 | 235 | /** 236 | * @brief Standard std::ostream text inserter 237 | * 238 | * Insert the text value of the Column object, using getText(), into the provided stream. 239 | * 240 | * @param[in] aStream Stream to use 241 | * @param[in] aColumn Column object to insert into the provided stream 242 | * 243 | * @return Reference to the stream used 244 | */ 245 | SQLITECPP_API std::ostream& operator<<(std::ostream& aStream, const Column& aColumn); 246 | 247 | #if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1900) // c++14: Visual Studio 2015 248 | 249 | // Create an instance of T from the first N columns, see declaration in Statement.h for full details 250 | template 251 | T Statement::getColumns() 252 | { 253 | checkRow(); 254 | checkIndex(N - 1); 255 | return getColumns(std::make_integer_sequence{}); 256 | } 257 | 258 | // Helper function called by getColums 259 | template 260 | T Statement::getColumns(const std::integer_sequence) 261 | { 262 | return T{Column(mpPreparedStatement, Is)...}; 263 | } 264 | 265 | #endif 266 | 267 | } // namespace SQLite 268 | -------------------------------------------------------------------------------- /include/SQLiteCpp/Exception.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Exception.h 3 | * @ingroup SQLiteCpp 4 | * @brief Encapsulation of the error message from SQLite3 on a std::runtime_error. 5 | * 6 | * Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | // Forward declaration to avoid inclusion of in a header 18 | struct sqlite3; 19 | 20 | namespace SQLite 21 | { 22 | 23 | /** 24 | * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. 25 | */ 26 | class SQLITECPP_API Exception : public std::runtime_error 27 | { 28 | public: 29 | /** 30 | * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. 31 | * 32 | * @param[in] aErrorMessage The string message describing the SQLite error 33 | * @param[in] ret Return value from function call that failed. 34 | */ 35 | Exception(const char* aErrorMessage, int ret); 36 | 37 | Exception(const std::string& aErrorMessage, int ret) : 38 | Exception(aErrorMessage.c_str(), ret) 39 | { 40 | } 41 | 42 | /** 43 | * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. 44 | * 45 | * @param[in] aErrorMessage The string message describing the SQLite error 46 | */ 47 | explicit Exception(const char* aErrorMessage) : 48 | Exception(aErrorMessage, -1) // 0 would be SQLITE_OK, which doesn't make sense 49 | { 50 | } 51 | explicit Exception(const std::string& aErrorMessage) : 52 | Exception(aErrorMessage.c_str(), -1) // 0 would be SQLITE_OK, which doesn't make sense 53 | { 54 | } 55 | 56 | /** 57 | * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. 58 | * 59 | * @param[in] apSQLite The SQLite object, to obtain detailed error messages from. 60 | */ 61 | explicit Exception(sqlite3* apSQLite); 62 | 63 | /** 64 | * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. 65 | * 66 | * @param[in] apSQLite The SQLite object, to obtain detailed error messages from. 67 | * @param[in] ret Return value from function call that failed. 68 | */ 69 | Exception(sqlite3* apSQLite, int ret); 70 | 71 | /// Return the result code (if any, otherwise -1). 72 | int getErrorCode() const noexcept 73 | { 74 | return mErrcode; 75 | } 76 | 77 | /// Return the extended numeric result code (if any, otherwise -1). 78 | int getExtendedErrorCode() const noexcept 79 | { 80 | return mExtendedErrcode; 81 | } 82 | 83 | /// Return a string, solely based on the error code 84 | const char* getErrorStr() const noexcept; 85 | 86 | private: 87 | int mErrcode; ///< Error code value 88 | int mExtendedErrcode; ///< Detailed error code if any 89 | }; 90 | 91 | } // namespace SQLite 92 | -------------------------------------------------------------------------------- /include/SQLiteCpp/ExecuteMany.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ExecuteMany.h 3 | * @ingroup SQLiteCpp 4 | * @brief Convenience function to execute a Statement with multiple Parameter sets 5 | * 6 | * Copyright (c) 2019 Maximilian Bachmann (contact@maxbachmann.de) 7 | * Copyright (c) 2019-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 8 | * 9 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 10 | * or copy at http://opensource.org/licenses/MIT) 11 | */ 12 | #pragma once 13 | 14 | #if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015 15 | 16 | #include 17 | #include 18 | 19 | /// @cond 20 | #include 21 | #include 22 | #include 23 | 24 | namespace SQLite 25 | { 26 | 27 | /// @endcond 28 | 29 | /** 30 | * \brief Convenience function to execute a Statement with multiple Parameter sets once for each parameter set given. 31 | * 32 | * 33 | * This feature requires a c++14 capable compiler. 34 | * 35 | * \code{.cpp} 36 | * execute_many(db, "INSERT INTO test VALUES (?, ?)", 37 | * 1, 38 | * std::make_tuple(2), 39 | * std::make_tuple(3, "three") 40 | * ); 41 | * \endcode 42 | * @param aDatabase Database to use 43 | * @param apQuery Query to use with all parameter sets 44 | * @param aArg first tuple with parameters 45 | * @param aParams the following tuples with parameters 46 | */ 47 | template 48 | void execute_many(Database& aDatabase, const char* apQuery, Arg&& aArg, Types&&... aParams) 49 | { 50 | SQLite::Statement query(aDatabase, apQuery); 51 | bind_exec(query, std::forward(aArg)); 52 | (void)std::initializer_list 53 | { 54 | ((void)reset_bind_exec(query, std::forward(aParams)), 0)... 55 | }; 56 | } 57 | 58 | /** 59 | * \brief Convenience function to reset a statement and call bind_exec to 60 | * bind new values to the statement and execute it 61 | * 62 | * This feature requires a c++14 capable compiler. 63 | * 64 | * @param apQuery Query to use 65 | * @param aTuple Tuple to bind 66 | */ 67 | template 68 | void reset_bind_exec(Statement& apQuery, TupleT&& aTuple) 69 | { 70 | apQuery.reset(); 71 | bind_exec(apQuery, std::forward(aTuple)); 72 | } 73 | 74 | /** 75 | * \brief Convenience function to bind values a the statement and execute it 76 | * 77 | * This feature requires a c++14 capable compiler. 78 | * 79 | * @param apQuery Query to use 80 | * @param aTuple Tuple to bind 81 | */ 82 | template 83 | void bind_exec(Statement& apQuery, TupleT&& aTuple) 84 | { 85 | SQLite::bind(apQuery, std::forward(aTuple)); 86 | while (apQuery.executeStep()) {} 87 | } 88 | 89 | } // namespace SQLite 90 | 91 | #endif // c++14 92 | -------------------------------------------------------------------------------- /include/SQLiteCpp/SQLiteCpp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file SQLiteCpp.h 3 | * @ingroup SQLiteCpp 4 | * @brief SQLiteC++ is a smart and simple C++ SQLite3 wrapper. This file is only "easy include" for other files. 5 | * 6 | * Include this main header file in your project to gain access to all functionality provided by the wrapper. 7 | * 8 | * Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 9 | * 10 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 11 | * or copy at http://opensource.org/licenses/MIT) 12 | */ 13 | /** 14 | * @defgroup SQLiteCpp SQLiteC++ 15 | * @brief SQLiteC++ is a smart and simple C++ SQLite3 wrapper. This file is only "easy include" for other files. 16 | */ 17 | #pragma once 18 | 19 | 20 | // Include useful headers of SQLiteC++ 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | 30 | /** 31 | * @brief Version numbers for SQLiteC++ are provided in the same way as sqlite3.h 32 | * 33 | * The [SQLITECPP_VERSION] C preprocessor macro in the SQLiteC++.h header 34 | * evaluates to a string literal that is the SQLite version in the 35 | * format "X.Y.Z" where X is the major version number 36 | * and Y is the minor version number and Z is the release number. 37 | * 38 | * The [SQLITECPP_VERSION_NUMBER] C preprocessor macro resolves to an integer 39 | * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same 40 | * numbers used in [SQLITECPP_VERSION]. 41 | * 42 | * WARNING: shall always be updated in sync with PROJECT_VERSION in CMakeLists.txt 43 | */ 44 | #define SQLITECPP_VERSION "3.03.03" // 3.3.3 45 | #define SQLITECPP_VERSION_NUMBER 3003003 // 3.3.3 46 | 47 | -------------------------------------------------------------------------------- /include/SQLiteCpp/SQLiteCppExport.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file SQLiteCppExport.h 3 | * @ingroup SQLiteCpp 4 | * @brief File with macros needed in the generation of Windows DLLs 5 | * 6 | * 7 | * Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) 8 | * 9 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 10 | * or copy at http://opensource.org/licenses/MIT) 11 | */ 12 | 13 | #pragma once 14 | 15 | /* 16 | * #define SQLITECPP_COMPILE_DLL to compile a DLL under Windows 17 | * #define SQLITECPP_EXPORT to export symbols when creating the DLL, otherwise it defaults to importing symbols 18 | */ 19 | 20 | /* Windows DLL export/import */ 21 | #if defined(_WIN32)&& !defined(__GNUC__) && defined(SQLITECPP_COMPILE_DLL) 22 | #if SQLITECPP_DLL_EXPORT 23 | #define SQLITECPP_API __declspec(dllexport) 24 | #else 25 | #define SQLITECPP_API __declspec(dllimport) 26 | #endif 27 | #else 28 | #if __GNUC__ >= 4 29 | #define SQLITECPP_API __attribute__ ((visibility ("default"))) 30 | #else 31 | #define SQLITECPP_API 32 | #endif 33 | #endif 34 | 35 | #if defined(WIN32) && defined(SQLITECPP_COMPILE_DLL) 36 | #pragma warning( disable : 4251 ) 37 | #pragma warning( disable : 4275 ) 38 | #endif 39 | -------------------------------------------------------------------------------- /include/SQLiteCpp/Savepoint.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Savepoint.h 3 | * @ingroup SQLiteCpp 4 | * @brief A Savepoint is a way to group multiple SQL statements into an atomic 5 | * secured operation. Similar to a transaction while allowing child savepoints. 6 | * 7 | * Copyright (c) 2020 Kelvin Hammond (hammond.kelvin@gmail.com) 8 | * Copyright (c) 2020-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 9 | * 10 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt or 11 | * copy at http://opensource.org/licenses/MIT) 12 | */ 13 | #pragma once 14 | 15 | #include 16 | #include 17 | 18 | namespace SQLite 19 | { 20 | 21 | // Forward declaration 22 | class Database; 23 | 24 | /** 25 | * @brief RAII encapsulation of a SQLite Savepoint. 26 | * 27 | * SAVEPOINTs are a method of creating Transactions, similar to BEGIN and COMMIT, 28 | * except that the SAVEPOINT and RELEASE commands are named and may be nested.. 29 | * 30 | * Resource Acquisition Is Initialization (RAII) means that the Savepoint 31 | * begins in the constructor and is rolled back in the destructor (unless committed before), so that there is 32 | * no need to worry about memory management or the validity of the underlying SQLite Connection. 33 | * 34 | * This method also offers big performances improvements compared to 35 | * individually executed statements. 36 | * 37 | * Caveats: 38 | * 39 | * 1) Calling COMMIT or committing a parent transaction or RELEASE on a parent 40 | * savepoint will cause this savepoint to be released. 41 | * 42 | * 2) Calling ROLLBACK TO or rolling back a parent savepoint will cause this 43 | * savepoint to be rolled back. 44 | * 45 | * 3) This savepoint is not saved to the database until this and all savepoints 46 | * or transaction in the savepoint stack have been released or committed. 47 | * 48 | * See also: https://sqlite.org/lang_savepoint.html 49 | * 50 | * Thread-safety: a Savepoint object shall not be shared by multiple threads, because: 51 | * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads 52 | * provided that no single database connection is used simultaneously in two or more threads." 53 | * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, 54 | * because of the way it shares the underling SQLite precompiled statement 55 | * in a custom shared pointer (See the inner class "Statement::Ptr"). 56 | */ 57 | class SQLITECPP_API Savepoint 58 | { 59 | public: 60 | /** 61 | * @brief Begins the SQLite savepoint 62 | * 63 | * @param[in] aDatabase the SQLite Database Connection 64 | * @param[in] aName the name of the Savepoint 65 | * 66 | * Exception is thrown in case of error, then the Savepoint is NOT 67 | * initiated. 68 | */ 69 | Savepoint(Database& aDatabase, const std::string& aName); 70 | 71 | // Savepoint is non-copyable 72 | Savepoint(const Savepoint&) = delete; 73 | Savepoint& operator=(const Savepoint&) = delete; 74 | 75 | /** 76 | * @brief Safely rollback the savepoint if it has not been committed. 77 | */ 78 | ~Savepoint(); 79 | 80 | /** 81 | * @brief Commit and release the savepoint. 82 | */ 83 | void release(); 84 | 85 | /** 86 | * @brief Rollback to the savepoint, but don't release it. 87 | */ 88 | void rollbackTo(); 89 | // @deprecated same as rollbackTo(); 90 | void rollback() { rollbackTo(); } 91 | 92 | private: 93 | Database& mDatabase; ///< Reference to the SQLite Database Connection 94 | std::string msName; ///< Name of the Savepoint 95 | bool mbReleased = false; ///< True when release has been called 96 | }; 97 | 98 | } // namespace SQLite 99 | -------------------------------------------------------------------------------- /include/SQLiteCpp/Transaction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Transaction.h 3 | * @ingroup SQLiteCpp 4 | * @brief A Transaction is way to group multiple SQL statements into an atomic secured operation. 5 | * 6 | * Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #pragma once 12 | 13 | #include 14 | #include 15 | 16 | namespace SQLite 17 | { 18 | 19 | // Forward declaration 20 | class Database; 21 | 22 | /** 23 | * @brief Transaction behaviors when opening an SQLite transaction. 24 | * Names correspond directly to the behavior. 25 | */ 26 | enum class TransactionBehavior { 27 | DEFERRED, 28 | IMMEDIATE, 29 | EXCLUSIVE, 30 | }; 31 | 32 | /** 33 | * @brief RAII encapsulation of a SQLite Transaction. 34 | * 35 | * A Transaction is a way to group multiple SQL statements into an atomic secured operation; 36 | * either it succeeds, with all the changes committed to the database file, 37 | * or if it fails, all the changes are rolled back to the initial state. 38 | * 39 | * Resource Acquisition Is Initialization (RAII) means that the Transaction 40 | * begins in the constructor and is rolled back in the destructor (unless committed before), so that there is 41 | * no need to worry about memory management or the validity of the underlying SQLite Connection. 42 | * 43 | * This method also offers big performances improvements compared to individually executed statements. 44 | * 45 | * Thread-safety: a Transaction object shall not be shared by multiple threads, because : 46 | * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads 47 | * provided that no single database connection is used simultaneously in two or more threads." 48 | * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, 49 | * because of the way it shares the underling SQLite precompiled statement 50 | * in a custom shared pointer (See the inner class "Statement::Ptr"). 51 | */ 52 | class SQLITECPP_API Transaction 53 | { 54 | public: 55 | /** 56 | * @brief Begins the SQLite transaction using the default transaction behavior. 57 | * 58 | * @param[in] aDatabase the SQLite Database Connection 59 | * 60 | * Exception is thrown in case of error, then the Transaction is NOT initiated. 61 | */ 62 | explicit Transaction(Database& aDatabase); 63 | 64 | /** 65 | * @brief Begins the SQLite transaction with the specified behavior. 66 | * 67 | * @param[in] aDatabase the SQLite Database Connection 68 | * @param[in] behavior the requested transaction behavior 69 | * 70 | * Exception is thrown in case of error, then the Transaction is NOT initiated. 71 | */ 72 | explicit Transaction(Database& aDatabase, TransactionBehavior behavior); 73 | 74 | // Transaction is non-copyable 75 | Transaction(const Transaction&) = delete; 76 | Transaction& operator=(const Transaction&) = delete; 77 | 78 | /** 79 | * @brief Safely rollback the transaction if it has not been committed. 80 | */ 81 | ~Transaction(); 82 | 83 | /** 84 | * @brief Commit the transaction. 85 | */ 86 | void commit(); 87 | 88 | /** 89 | * @brief Rollback the transaction 90 | */ 91 | void rollback(); 92 | 93 | private: 94 | Database& mDatabase; ///< Reference to the SQLite Database Connection 95 | bool mbCommited = false; ///< True when commit has been called 96 | }; 97 | 98 | } // namespace SQLite 99 | -------------------------------------------------------------------------------- /include/SQLiteCpp/Utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Utils.h 3 | * @ingroup SQLiteCpp 4 | * @brief Definition of the SQLITECPP_PURE_FUNC macro. 5 | * 6 | * Copyright (c) 2012-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #pragma once 12 | 13 | // macro taken from https://github.com/nemequ/hedley/blob/master/hedley.h that was in public domain at this time 14 | #if defined(__GNUC__) || defined(__GNUG__) || defined(__clang__) ||\ 15 | (defined(__INTEL_COMPILER) && __INTEL_COMPILER > 1600) ||\ 16 | (defined(__ARMCC_VERSION) && __ARMCC_VERSION > 4010000) ||\ 17 | (\ 18 | defined(__TI_COMPILER_VERSION__) && (\ 19 | __TI_COMPILER_VERSION__ > 8003000 ||\ 20 | (__TI_COMPILER_VERSION__ > 7003000 && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))\ 21 | )\ 22 | ) 23 | #if defined(__has_attribute) 24 | #if !defined(SQLITECPP_PURE_FUNC) && __has_attribute(pure) 25 | #define SQLITECPP_PURE_FUNC __attribute__((pure)) 26 | #endif 27 | #endif 28 | #endif 29 | #if !defined(SQLITECPP_PURE_FUNC) 30 | #define SQLITECPP_PURE_FUNC 31 | #endif 32 | -------------------------------------------------------------------------------- /include/SQLiteCpp/VariadicBind.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file VariadicBind.h 3 | * @ingroup SQLiteCpp 4 | * @brief Convenience function for Statement::bind(...) 5 | * 6 | * Copyright (c) 2016 Paul Dreik (github@pauldreik.se) 7 | * Copyright (c) 2016-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 8 | * Copyright (c) 2019 Maximilian Bachmann (contact@maxbachmann.de) 9 | * 10 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 11 | * or copy at http://opensource.org/licenses/MIT) 12 | */ 13 | #pragma once 14 | 15 | #include 16 | 17 | #if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015 18 | #include 19 | #endif // c++14 20 | 21 | /// @cond 22 | #include 23 | #include 24 | 25 | namespace SQLite 26 | { 27 | /// @endcond 28 | 29 | /** 30 | * \brief Convenience function for calling Statement::bind(...) once for each argument given. 31 | * 32 | * This takes care of incrementing the index between each calls to bind. 33 | * 34 | * This feature requires a c++11 capable compiler. 35 | * 36 | * \code{.cpp} 37 | * SQLite::Statement stm("SELECT * FROM MyTable WHERE colA>? && colB=? && colC 48 | void bind(SQLite::Statement& query, const Args& ... args) 49 | { 50 | int pos = 0; 51 | (void)std::initializer_list{ 52 | ((void)query.bind(++pos, std::forward(args)), 0)... 53 | }; 54 | } 55 | 56 | #if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015 57 | 58 | /** 59 | * \brief Convenience function for calling Statement::bind(...) once for each parameter of a tuple, 60 | * by forwarding them to the variadic template 61 | * 62 | * This feature requires a c++14 capable compiler. 63 | * 64 | * \code{.cpp} 65 | * SQLite::Statement stm("SELECT * FROM MyTable WHERE colA>? && colB=? && colC 76 | void bind(SQLite::Statement& query, const std::tuple &tuple) 77 | { 78 | bind(query, tuple, std::index_sequence_for()); 79 | } 80 | 81 | /** 82 | * \brief Convenience function for calling Statement::bind(...) once for each parameter of a tuple, 83 | * by forwarding them to the variadic template. This function is just needed to convert the tuples 84 | * to parameter packs 85 | * 86 | * This feature requires a c++14 capable compiler. 87 | * 88 | * @param query statement 89 | * @param tuple tuple with values to bind 90 | */ 91 | template 92 | void bind(SQLite::Statement& query, const std::tuple &tuple, std::index_sequence) 93 | { 94 | bind(query, std::get(tuple)...); 95 | } 96 | #endif // c++14 97 | 98 | } // namespace SQLite 99 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'SQLiteCpp', 'cpp', 3 | # SQLiteCpp supports C++11 4 | # however newer versions of gtest requires C++14 5 | default_options: ['cpp_std=c++14', 'warning_level=3'], 6 | license: 'MIT', 7 | version: '3.3.3', 8 | ) 9 | 10 | cxx = meson.get_compiler('cpp') 11 | cpp_std = get_option('cpp_std') 12 | 13 | ## at best we might try to test if this code compiles 14 | ## testing for compilers or platforms is not reliable enough 15 | ## example: native clang on windows or mingw in windows 16 | unix_like_code = ''' 17 | #if defined(unix) || defined(__unix__) || defined(__unix) 18 | // do nothing 19 | #else 20 | # error "Non Unix-like OS" 21 | #endif 22 | ''' 23 | unix_like = cxx.compiles(unix_like_code, name : 'unix like environment') 24 | 25 | mingw_64_env_code = ''' 26 | #if defined(__MINGW64__) 27 | // do nothing 28 | #else 29 | # error "Non MinGW-W64 environment" 30 | #endif 31 | ''' 32 | mingw_64_env = cxx.compiles(mingw_64_env_code, name : 'MinGW-W64 environment') 33 | 34 | thread_dep = dependency('threads') 35 | # sqlite3 support 36 | sqlite3_dep = dependency( 37 | 'sqlite3', 38 | fallback: ['sqlite3', 'sqlite3_dep'] 39 | ) 40 | 41 | sqlitecpp_incl = [ 42 | include_directories('include') 43 | ] 44 | sqlitecpp_srcs = files( 45 | 'src/Backup.cpp', 46 | 'src/Column.cpp', 47 | 'src/Database.cpp', 48 | 'src/Exception.cpp', 49 | 'src/Savepoint.cpp', 50 | 'src/Statement.cpp', 51 | 'src/Transaction.cpp', 52 | ) 53 | sqlitecpp_args = cxx.get_supported_arguments( 54 | # included in meson by default 55 | # -Wall 56 | # included when warning_level=3 57 | #'-Wextra', 58 | #'-Wpedantic', 59 | '-Wswitch-enum', 60 | '-Wshadow', 61 | '-Wno-long-long', 62 | '-Wno-attributes', 63 | ) 64 | sqlitecpp_link = [] 65 | sqlitecpp_deps = [ 66 | sqlite3_dep, 67 | thread_dep, 68 | ] 69 | ## used to override the default sqlitecpp options like cpp standard 70 | sqlitecpp_opts = [] 71 | 72 | ## used to set required macros when using sqlitecpp 73 | sqlitecpp_dep_args = [] 74 | 75 | ## tests 76 | 77 | sqlitecpp_test_srcs = files( 78 | 'tests/Column_test.cpp', 79 | 'tests/Database_test.cpp', 80 | 'tests/Savepoint_test.cpp', 81 | 'tests/Statement_test.cpp', 82 | 'tests/Backup_test.cpp', 83 | 'tests/Transaction_test.cpp', 84 | 'tests/VariadicBind_test.cpp', 85 | 'tests/Exception_test.cpp', 86 | 'tests/ExecuteMany_test.cpp', 87 | ) 88 | sqlitecpp_test_args = [] 89 | 90 | 91 | ## using MSVC headers requires c++14, if not will show an error on xstddef as: 92 | ## 'auto' return without trailing return type; deduced return types are a C++14 extension 93 | if host_machine.system() == 'windows' 94 | ## check if the std version is less than c++14 95 | if cpp_std.version_compare(' 172 | int main() { 173 | sqlite3_enable_load_extension(0, 0); 174 | return 0; 175 | } 176 | ''', 177 | name: 'sqlite3_load_extension', 178 | dependencies: [sqlite3_dep]) 179 | if not sqlite3_load_extension_support 180 | message('warning: Detected bundled SQLite3 in OSX, but it does not support load extension') 181 | message('warning: SQLiteCpp will be built without load extension support') 182 | message('warning: You can disable this warning by setting SQLITE_OMIT_LOAD_EXTENSION to false') 183 | sqlitecpp_args += ['-DSQLITE_OMIT_LOAD_EXTENSION'] 184 | endif 185 | endif 186 | endif 187 | 188 | 189 | 190 | if unix_like 191 | sqlitecpp_args += [ 192 | # -fPIC is included by default in meson 193 | # 'fPIC', 194 | ] 195 | # add dl dependency 196 | libdl_dep = cxx.find_library('dl') 197 | sqlitecpp_deps += [ 198 | libdl_dep, 199 | ] 200 | endif 201 | 202 | if get_option('b_coverage') 203 | # Prevent the compiler from removing the unused inline functions so that they get tracked as "non-covered" 204 | sqlitecpp_args += [ 205 | '-fkeep-inline-functions', 206 | '-fkeep-static-functions', 207 | ] 208 | endif 209 | 210 | sqlitecpp_static_args = sqlitecpp_args 211 | sqlitecpp_static_dep_args = sqlitecpp_dep_args 212 | 213 | # if windows and shared library 214 | if host_machine.system() == 'windows' and get_option('default_library') == 'shared' 215 | # compile with SQLITECPP_COMPILE_DLL and SQLITECPP_DLL_EXPORT=1 216 | sqlitecpp_args += [ 217 | '-DSQLITECPP_COMPILE_DLL', 218 | '-DSQLITECPP_DLL_EXPORT', 219 | ] 220 | sqlitecpp_dep_args += [ 221 | # we just need to define SQLITECPP_COMPILE_DLL 222 | '-DSQLITECPP_COMPILE_DLL', 223 | ] 224 | endif 225 | 226 | 227 | libsqlitecpp = library( 228 | 'sqlitecpp', 229 | sqlitecpp_srcs, 230 | include_directories: sqlitecpp_incl, 231 | cpp_args: sqlitecpp_args, 232 | dependencies: sqlitecpp_deps, 233 | # override the default options 234 | override_options: sqlitecpp_opts, 235 | install: true, 236 | # API version for SQLiteCpp shared library. 237 | version: '0',) 238 | 239 | 240 | 241 | if get_option('SQLITECPP_BUILD_TESTS') 242 | # for the unit tests we need to link against a static version of SQLiteCpp 243 | if get_option('default_library') == 'static' 244 | # we do not need to recomplile the library 245 | libsqlitecpp_static = libsqlitecpp 246 | else 247 | libsqlitecpp_static = static_library( 248 | 'sqlitecpp_static', 249 | sqlitecpp_srcs, 250 | include_directories: sqlitecpp_incl, 251 | cpp_args: sqlitecpp_static_args, 252 | dependencies: sqlitecpp_deps, 253 | # override the default options 254 | override_options: sqlitecpp_opts,) 255 | endif 256 | endif 257 | 258 | install_subdir( 259 | 'include/SQLiteCpp', 260 | install_dir: get_option('includedir')) 261 | 262 | sqlitecpp_dep = declare_dependency( 263 | include_directories: sqlitecpp_incl, 264 | link_with: libsqlitecpp, 265 | compile_args: sqlitecpp_dep_args, 266 | ) 267 | if get_option('SQLITECPP_BUILD_TESTS') 268 | ## make the dependency static so the unit tests can link against it 269 | ## (mainly for windows as the symbols are not exported by default) 270 | sqlitecpp_static_dep = declare_dependency( 271 | include_directories: sqlitecpp_incl, 272 | link_with: libsqlitecpp_static, 273 | compile_args: sqlitecpp_static_dep_args, 274 | ) 275 | endif 276 | 277 | if get_option('SQLITECPP_BUILD_TESTS') 278 | gtest_dep = dependency( 279 | 'gtest', 280 | main : true, 281 | fallback: ['gtest', 'gtest_main_dep']) 282 | # check for the current version of gtest as newer versions require newer C++ standards 283 | if gtest_dep.found() 284 | gtest_version = gtest_dep.version() 285 | minimum_standard = 'none' 286 | required_std_format = 'current Gtest version requires at least @0@, setting the minimum standard to @0@. You can disable this warning by setting cpp_std to @0@ or newer.' 287 | ## gtest 1.17.0 requires c++17 while gtest 1.14.0 requires c++14 288 | if gtest_version.version_compare('>=1.17.0') and cpp_std.version_compare('=1.14.0') and cpp_std.version_compare(' 2 | 3 | SQLiteCpp 4 | 3.3.3 5 | A smart and easy to use C++ SQLite3 wrapper. 6 | 7 | Sébastien Rombauts 8 | 9 | MIT 10 | 11 | Sébastien Rombauts 12 | 13 | cmake 14 | 15 | libsqlite3-dev 16 | 17 | 18 | cmake 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /sqlite3/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMake file for compiling the sqlite3 static library under Windows (for ease of use) 2 | # 3 | # Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 4 | # 5 | # Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 6 | # or copy at http://opensource.org/licenses/MIT) 7 | 8 | # add sources of the "sqlite3" static library 9 | add_library(sqlite3 10 | sqlite3.c 11 | sqlite3.h 12 | ) 13 | 14 | if (WIN32) 15 | if (BUILD_SHARED_LIBS) 16 | add_definitions("-DSQLITE_API=__declspec(dllexport)") 17 | endif() 18 | endif() 19 | 20 | add_library(SQLite::SQLite3 ALIAS sqlite3) 21 | 22 | target_include_directories(sqlite3 23 | PUBLIC 24 | $ 25 | $) 26 | 27 | if (SQLITE_ENABLE_COLUMN_METADATA) 28 | # Enable the use of SQLite column metadata method 29 | # Require that the sqlite3 library is also compiled with this flag: 30 | target_compile_definitions(sqlite3 PUBLIC SQLITE_ENABLE_COLUMN_METADATA) 31 | endif (SQLITE_ENABLE_COLUMN_METADATA) 32 | 33 | if (SQLITE_ENABLE_RTREE) 34 | # Enable RTree extension when building sqlite3 35 | # See more here: https://sqlite.org/rtree.html 36 | target_compile_definitions(sqlite3 PUBLIC SQLITE_ENABLE_RTREE) 37 | message(STATUS "Compile sqlite3 with SQLITE_ENABLE_RTREE") 38 | endif (SQLITE_ENABLE_RTREE) 39 | 40 | if (SQLITE_ENABLE_DBSTAT_VTAB) 41 | # Enable DBSTAT extension when building sqlite3 42 | # See more here: https://www.sqlite.org/dbstat.html 43 | target_compile_definitions(sqlite3 PUBLIC SQLITE_ENABLE_DBSTAT_VTAB) 44 | message(STATUS "Compile sqlite3 with SQLITE_ENABLE_DBSTAT_VTAB") 45 | endif (SQLITE_ENABLE_DBSTAT_VTAB) 46 | 47 | if (SQLITE_OMIT_LOAD_EXTENSION) 48 | target_compile_definitions(sqlite3 PUBLIC SQLITE_OMIT_LOAD_EXTENSION) 49 | message(STATUS "Compile sqlite3 with SQLITE_OMIT_LOAD_EXTENSION") 50 | endif (SQLITE_OMIT_LOAD_EXTENSION) 51 | 52 | if (UNIX AND (CMAKE_COMPILER_IS_GNUCXX OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")) 53 | set_target_properties(sqlite3 PROPERTIES COMPILE_FLAGS "-fPIC") 54 | 55 | # Put each function in its own section to allow the linker garbage 56 | # collection to remove unused section and produced a smaller 57 | # statically-lined executables. 58 | target_compile_options(sqlite3 PRIVATE "-ffunction-sections") 59 | endif (UNIX AND (CMAKE_COMPILER_IS_GNUCXX OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")) 60 | 61 | if (UNIX AND CMAKE_COMPILER_IS_GNUCXX) 62 | if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) 63 | target_compile_options(sqlite3 PRIVATE "-Wimplicit-fallthrough=0") 64 | endif() 65 | if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0) 66 | target_compile_options(sqlite3 PRIVATE "-Wno-cast-function-type") 67 | endif() 68 | endif() 69 | 70 | # Allow the library to be installed via "make install" and found with "find_package" 71 | 72 | include(GNUInstallDirs) 73 | install(TARGETS sqlite3 74 | EXPORT ${PROJECT_NAME}Targets 75 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 76 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 77 | COMPONENT libraries) 78 | install(FILES sqlite3.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT headers) 79 | -------------------------------------------------------------------------------- /sqlite3/README.md: -------------------------------------------------------------------------------- 1 | sqlite3 2 | ------- 3 | 4 | Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 5 | 6 | "sqlite3.c" and "sqlite3.h" files from sqlite-amalgamation-3490200.zip (SQLite 3.49.2 2025-05-07) 7 | 8 | Those files are provided for easy setup and compatibility under Windows/Linux/MacOS. 9 | They are used by default by the CMake build. 10 | 11 | Use -DSQLITECPP_INTERNAL_SQLITE=OFF to link against the Linux "libsqlite3-dev" package instead. 12 | 13 | ### License: 14 | 15 | All of the code and documentation in SQLite has been dedicated to the public domain by the authors. 16 | -------------------------------------------------------------------------------- /src/Backup.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Backup.cpp 3 | * @ingroup SQLiteCpp 4 | * @brief Backup is used to backup a database file in a safe and online way. 5 | * 6 | * Copyright (c) 2015 Shibao HONG (shibaohong@outlook.com) 7 | * Copyright (c) 2015-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 8 | * 9 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 10 | * or copy at http://opensource.org/licenses/MIT) 11 | */ 12 | #include 13 | 14 | #include 15 | 16 | #include 17 | 18 | namespace SQLite 19 | { 20 | 21 | // Initialize resource for SQLite database backup 22 | Backup::Backup(Database& aDestDatabase, 23 | const char* apDestDatabaseName, 24 | Database& aSrcDatabase, 25 | const char* apSrcDatabaseName) 26 | { 27 | mpSQLiteBackup.reset(sqlite3_backup_init(aDestDatabase.getHandle(), 28 | apDestDatabaseName, 29 | aSrcDatabase.getHandle(), 30 | apSrcDatabaseName)); 31 | if (nullptr == mpSQLiteBackup) 32 | { 33 | // If an error occurs, the error code and message are attached to the destination database connection. 34 | throw SQLite::Exception(aDestDatabase.getHandle()); 35 | } 36 | } 37 | 38 | Backup::Backup(Database& aDestDatabase, 39 | const std::string& aDestDatabaseName, 40 | Database& aSrcDatabase, 41 | const std::string& aSrcDatabaseName) : 42 | Backup(aDestDatabase, aDestDatabaseName.c_str(), aSrcDatabase, aSrcDatabaseName.c_str()) 43 | { 44 | } 45 | 46 | Backup::Backup(Database &aDestDatabase, Database &aSrcDatabase) : 47 | Backup(aDestDatabase, "main", aSrcDatabase, "main") 48 | { 49 | } 50 | 51 | // Execute backup step with a given number of source pages to be copied 52 | int Backup::executeStep(const int aNumPage /* = -1 */) 53 | { 54 | const int res = sqlite3_backup_step(mpSQLiteBackup.get(), aNumPage); 55 | if (SQLITE_OK != res && SQLITE_DONE != res && SQLITE_BUSY != res && SQLITE_LOCKED != res) 56 | { 57 | throw SQLite::Exception(sqlite3_errstr(res), res); 58 | } 59 | return res; 60 | } 61 | 62 | // Get the number of remaining source pages to be copied in this backup process 63 | int Backup::getRemainingPageCount() const 64 | { 65 | return sqlite3_backup_remaining(mpSQLiteBackup.get()); 66 | } 67 | 68 | // Get the number of total source pages to be copied in this backup process 69 | int Backup::getTotalPageCount() const 70 | { 71 | return sqlite3_backup_pagecount(mpSQLiteBackup.get()); 72 | } 73 | 74 | // Release resource for SQLite database backup 75 | void SQLite::Backup::Deleter::operator()(sqlite3_backup* apBackup) 76 | { 77 | if (apBackup) 78 | { 79 | sqlite3_backup_finish(apBackup); 80 | } 81 | } 82 | 83 | } // namespace SQLite 84 | -------------------------------------------------------------------------------- /src/Column.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Column.cpp 3 | * @ingroup SQLiteCpp 4 | * @brief Encapsulation of a Column in a row of the result pointed by the prepared SQLite::Statement. 5 | * 6 | * Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #include 12 | 13 | #include 14 | 15 | #include 16 | 17 | namespace SQLite 18 | { 19 | 20 | const int INTEGER = SQLITE_INTEGER; 21 | const int FLOAT = SQLITE_FLOAT; 22 | const int TEXT = SQLITE_TEXT; 23 | const int BLOB = SQLITE_BLOB; 24 | const int Null = SQLITE_NULL; 25 | 26 | 27 | // Encapsulation of a Column in a row of the result pointed by the prepared Statement. 28 | Column::Column(const Statement::TStatementPtr& aStmtPtr, int aIndex) : 29 | mStmtPtr(aStmtPtr), 30 | mIndex(aIndex) 31 | { 32 | if (!aStmtPtr) 33 | { 34 | throw SQLite::Exception("Statement was destroyed"); 35 | } 36 | } 37 | 38 | // Return the named assigned to this result column (potentially aliased) 39 | const char* Column::getName() const noexcept 40 | { 41 | return sqlite3_column_name(mStmtPtr.get(), mIndex); 42 | } 43 | 44 | #ifdef SQLITE_ENABLE_COLUMN_METADATA 45 | // Return the name of the table column that is the origin of this result column 46 | const char* Column::getOriginName() const noexcept 47 | { 48 | return sqlite3_column_origin_name(mStmtPtr.get(), mIndex); 49 | } 50 | #endif 51 | 52 | // Return the integer value of the column specified by its index starting at 0 53 | int32_t Column::getInt() const noexcept 54 | { 55 | return sqlite3_column_int(mStmtPtr.get(), mIndex); 56 | } 57 | 58 | // Return the unsigned integer value of the column specified by its index starting at 0 59 | uint32_t Column::getUInt() const noexcept 60 | { 61 | return static_cast(getInt64()); 62 | } 63 | 64 | // Return the 64bits integer value of the column specified by its index starting at 0 65 | int64_t Column::getInt64() const noexcept 66 | { 67 | return sqlite3_column_int64(mStmtPtr.get(), mIndex); 68 | } 69 | 70 | // Return the double value of the column specified by its index starting at 0 71 | double Column::getDouble() const noexcept 72 | { 73 | return sqlite3_column_double(mStmtPtr.get(), mIndex); 74 | } 75 | 76 | // Return a pointer to the text value (NULL terminated string) of the column specified by its index starting at 0 77 | const char* Column::getText(const char* apDefaultValue /* = "" */) const noexcept 78 | { 79 | auto pText = reinterpret_cast(sqlite3_column_text(mStmtPtr.get(), mIndex)); 80 | return (pText ? pText : apDefaultValue); 81 | } 82 | 83 | // Return a pointer to the blob value (*not* NULL terminated) of the column specified by its index starting at 0 84 | const void* Column::getBlob() const noexcept 85 | { 86 | return sqlite3_column_blob(mStmtPtr.get(), mIndex); 87 | } 88 | 89 | // Return a std::string to a TEXT or BLOB column 90 | std::string Column::getString() const 91 | { 92 | // Note: using sqlite3_column_blob and not sqlite3_column_text 93 | // - no need for sqlite3_column_text to add a \0 on the end, as we're getting the bytes length directly 94 | // however, we need to call sqlite3_column_bytes() to ensure correct format. It's a noop on a BLOB 95 | // or a TEXT value with the correct encoding (UTF-8). Otherwise it'll do a conversion to TEXT (UTF-8). 96 | (void)sqlite3_column_bytes(mStmtPtr.get(), mIndex); 97 | auto data = static_cast(sqlite3_column_blob(mStmtPtr.get(), mIndex)); 98 | 99 | // SQLite docs: "The safest policy is to invoke… sqlite3_column_blob() followed by sqlite3_column_bytes()" 100 | // Note: std::string is ok to pass nullptr as first arg, if length is 0 101 | return std::string(data, sqlite3_column_bytes(mStmtPtr.get(), mIndex)); 102 | } 103 | 104 | // Return the type of the value of the column 105 | int Column::getType() const noexcept 106 | { 107 | return sqlite3_column_type(mStmtPtr.get(), mIndex); 108 | } 109 | 110 | // Return the number of bytes used by the text value of the column 111 | int Column::getBytes() const noexcept 112 | { 113 | return sqlite3_column_bytes(mStmtPtr.get(), mIndex); 114 | } 115 | 116 | // Standard std::ostream inserter 117 | std::ostream& operator<<(std::ostream& aStream, const Column& aColumn) 118 | { 119 | aStream.write(aColumn.getText(), aColumn.getBytes()); 120 | return aStream; 121 | } 122 | 123 | } // namespace SQLite 124 | -------------------------------------------------------------------------------- /src/Database.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Database.cpp 3 | * @ingroup SQLiteCpp 4 | * @brief Management of a SQLite Database Connection. 5 | * 6 | * Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #ifndef SQLITE_DETERMINISTIC 23 | #define SQLITE_DETERMINISTIC 0x800 24 | #endif // SQLITE_DETERMINISTIC 25 | 26 | namespace SQLite 27 | { 28 | 29 | const int OK = SQLITE_OK; 30 | const int OPEN_READONLY = SQLITE_OPEN_READONLY; 31 | const int OPEN_READWRITE = SQLITE_OPEN_READWRITE; 32 | const int OPEN_CREATE = SQLITE_OPEN_CREATE; 33 | const int OPEN_URI = SQLITE_OPEN_URI; 34 | const int OPEN_MEMORY = SQLITE_OPEN_MEMORY; 35 | const int OPEN_NOMUTEX = SQLITE_OPEN_NOMUTEX; 36 | const int OPEN_FULLMUTEX = SQLITE_OPEN_FULLMUTEX; 37 | const int OPEN_SHAREDCACHE = SQLITE_OPEN_SHAREDCACHE; 38 | const int OPEN_PRIVATECACHE = SQLITE_OPEN_PRIVATECACHE; 39 | // check if sqlite version is >= 3.31.0 and SQLITE_OPEN_NOFOLLOW is defined 40 | #if SQLITE_VERSION_NUMBER >= 3031000 && defined(SQLITE_OPEN_NOFOLLOW) 41 | const int OPEN_NOFOLLOW = SQLITE_OPEN_NOFOLLOW; 42 | #else 43 | const int OPEN_NOFOLLOW = 0; 44 | #endif 45 | 46 | const char* const VERSION = SQLITE_VERSION; 47 | const int VERSION_NUMBER = SQLITE_VERSION_NUMBER; 48 | 49 | // Return SQLite version string using runtime call to the compiled library 50 | const char* getLibVersion() noexcept 51 | { 52 | return sqlite3_libversion(); 53 | } 54 | 55 | // Return SQLite version number using runtime call to the compiled library 56 | int getLibVersionNumber() noexcept 57 | { 58 | return sqlite3_libversion_number(); 59 | } 60 | 61 | 62 | // Open the provided database UTF-8 filename with SQLite::OPEN_xxx provided flags. 63 | Database::Database(const char* apFilename, 64 | const int aFlags /* = SQLite::OPEN_READONLY*/, 65 | const int aBusyTimeoutMs /* = 0 */, 66 | const char* apVfs /* = nullptr*/) : 67 | mFilename(apFilename) 68 | { 69 | sqlite3* handle; 70 | const int ret = sqlite3_open_v2(apFilename, &handle, aFlags, apVfs); 71 | mSQLitePtr.reset(handle); 72 | if (SQLITE_OK != ret) 73 | { 74 | throw SQLite::Exception(handle, ret); 75 | } 76 | if (aBusyTimeoutMs > 0) 77 | { 78 | setBusyTimeout(aBusyTimeoutMs); 79 | } 80 | } 81 | 82 | // Deleter functor to use with smart pointers to close the SQLite database connection in an RAII fashion. 83 | void Database::Deleter::operator()(sqlite3* apSQLite) 84 | { 85 | const int ret = sqlite3_close(apSQLite); // Calling sqlite3_close() with a nullptr argument is a harmless no-op. 86 | 87 | // Avoid unreferenced variable warning when build in release mode 88 | (void) ret; 89 | 90 | // Only case of error is SQLITE_BUSY: "database is locked" (some statements are not finalized) 91 | // Never throw an exception in a destructor : 92 | SQLITECPP_ASSERT(SQLITE_OK == ret, "database is locked"); // See SQLITECPP_ENABLE_ASSERT_HANDLER 93 | } 94 | 95 | // Set a busy handler that sleeps for a specified amount of time when a table is locked. 96 | void Database::setBusyTimeout(const int aBusyTimeoutMs) 97 | { 98 | const int ret = sqlite3_busy_timeout(getHandle(), aBusyTimeoutMs); 99 | check(ret); 100 | } 101 | 102 | // Shortcut to execute one or multiple SQL statements without results (UPDATE, INSERT, ALTER, COMMIT, CREATE...). 103 | // Return the number of changes. 104 | int Database::exec(const char* apQueries) 105 | { 106 | const int ret = tryExec(apQueries); 107 | check(ret); 108 | 109 | // Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE only) 110 | return sqlite3_changes(getHandle()); 111 | } 112 | 113 | int Database::tryExec(const char* apQueries) noexcept 114 | { 115 | return sqlite3_exec(getHandle(), apQueries, nullptr, nullptr, nullptr); 116 | } 117 | 118 | // Shortcut to execute a one step query and fetch the first column of the result. 119 | // WARNING: Be very careful with this dangerous method: you have to 120 | // make a COPY OF THE result, else it will be destroy before the next line 121 | // (when the underlying temporary Statement and Column objects are destroyed) 122 | // this is an issue only for pointer type result (ie. char* and blob) 123 | // (use the Column copy-constructor) 124 | Column Database::execAndGet(const char* apQuery) 125 | { 126 | Statement query(*this, apQuery); 127 | (void)query.executeStep(); // Can return false if no result, which will throw next line in getColumn() 128 | return query.getColumn(0); 129 | } 130 | 131 | // Shortcut to test if a table exists. 132 | bool Database::tableExists(const char* apTableName) const 133 | { 134 | Statement query(*this, "SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?"); 135 | query.bind(1, apTableName); 136 | (void)query.executeStep(); // Cannot return false, as the above query always return a result 137 | return (1 == query.getColumn(0).getInt()); 138 | } 139 | 140 | // Get the rowid of the most recent successful INSERT into the database from the current connection. 141 | int64_t Database::getLastInsertRowid() const noexcept 142 | { 143 | return sqlite3_last_insert_rowid(getHandle()); 144 | } 145 | 146 | // Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table). 147 | int Database::getChanges() const noexcept 148 | { 149 | return sqlite3_changes(getHandle()); 150 | } 151 | 152 | // Get total number of rows modified by all INSERT, UPDATE or DELETE statement since connection. 153 | int Database::getTotalChanges() const noexcept 154 | { 155 | return sqlite3_total_changes(getHandle()); 156 | } 157 | 158 | // Return the numeric result code for the most recent failed API call (if any). 159 | int Database::getErrorCode() const noexcept 160 | { 161 | return sqlite3_errcode(getHandle()); 162 | } 163 | 164 | // Return the extended numeric result code for the most recent failed API call (if any). 165 | int Database::getExtendedErrorCode() const noexcept 166 | { 167 | return sqlite3_extended_errcode(getHandle()); 168 | } 169 | 170 | // Return UTF-8 encoded English language explanation of the most recent failed API call (if any). 171 | const char* Database::getErrorMsg() const noexcept 172 | { 173 | return sqlite3_errmsg(getHandle()); 174 | } 175 | 176 | // Attach a custom function to your sqlite database. Assumes UTF8 text representation. 177 | // Parameter details can be found here: http://www.sqlite.org/c3ref/create_function.html 178 | void Database::createFunction(const char* apFuncName, 179 | int aNbArg, 180 | bool abDeterministic, 181 | void* apApp, 182 | void (*apFunc)(sqlite3_context *, int, sqlite3_value **), 183 | void (*apStep)(sqlite3_context *, int, sqlite3_value **) /* = nullptr */, 184 | void (*apFinal)(sqlite3_context *) /* = nullptr */, // NOLINT(readability/casting) 185 | void (*apDestroy)(void *) /* = nullptr */) 186 | { 187 | int textRep = SQLITE_UTF8; 188 | // optimization if deterministic function (e.g. of nondeterministic function random()) 189 | if (abDeterministic) 190 | { 191 | textRep = textRep | SQLITE_DETERMINISTIC; 192 | } 193 | const int ret = sqlite3_create_function_v2(getHandle(), apFuncName, aNbArg, textRep, 194 | apApp, apFunc, apStep, apFinal, apDestroy); 195 | check(ret); 196 | } 197 | 198 | // Load an extension into the sqlite database. Only affects the current connection. 199 | // Parameter details can be found here: http://www.sqlite.org/c3ref/load_extension.html 200 | void Database::loadExtension(const char* apExtensionName, const char *apEntryPointName) 201 | { 202 | #ifdef SQLITE_OMIT_LOAD_EXTENSION 203 | // Unused 204 | (void)apExtensionName; 205 | (void)apEntryPointName; 206 | 207 | throw SQLite::Exception("sqlite extensions are disabled"); 208 | #else 209 | #ifdef SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION // Since SQLite 3.13 (2016-05-18): 210 | // Security warning: 211 | // It is recommended that the SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION method be used to enable only this interface. 212 | // The use of the sqlite3_enable_load_extension() interface should be avoided to keep the SQL load_extension() 213 | // disabled and prevent SQL injections from giving attackers access to extension loading capabilities. 214 | // (NOTE: not using nullptr: cannot pass object of non-POD type 'std::__1::nullptr_t' through variadic function) 215 | int ret = sqlite3_db_config(getHandle(), SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL); // NOTE: not using nullptr 216 | #else 217 | int ret = sqlite3_enable_load_extension(getHandle(), 1); 218 | #endif 219 | check(ret); 220 | 221 | ret = sqlite3_load_extension(getHandle(), apExtensionName, apEntryPointName, 0); 222 | check(ret); 223 | #endif 224 | } 225 | 226 | // Set the key for the current sqlite database instance. 227 | void Database::key(const std::string& aKey) const 228 | { 229 | int passLen = static_cast(aKey.length()); 230 | #ifdef SQLITE_HAS_CODEC 231 | if (passLen > 0) 232 | { 233 | const int ret = sqlite3_key(getHandle(), aKey.c_str(), passLen); 234 | check(ret); 235 | } 236 | #else // SQLITE_HAS_CODEC 237 | if (passLen > 0) 238 | { 239 | throw SQLite::Exception("No encryption support, recompile with SQLITE_HAS_CODEC to enable."); 240 | } 241 | #endif // SQLITE_HAS_CODEC 242 | } 243 | 244 | // Reset the key for the current sqlite database instance. 245 | void Database::rekey(const std::string& aNewKey) const 246 | { 247 | #ifdef SQLITE_HAS_CODEC 248 | int passLen = aNewKey.length(); 249 | if (passLen > 0) 250 | { 251 | const int ret = sqlite3_rekey(getHandle(), aNewKey.c_str(), passLen); 252 | check(ret); 253 | } 254 | else 255 | { 256 | const int ret = sqlite3_rekey(getHandle(), nullptr, 0); 257 | check(ret); 258 | } 259 | #else // SQLITE_HAS_CODEC 260 | static_cast(aNewKey); // silence unused parameter warning 261 | throw SQLite::Exception("No encryption support, recompile with SQLITE_HAS_CODEC to enable."); 262 | #endif // SQLITE_HAS_CODEC 263 | } 264 | 265 | // Test if a file contains an unencrypted database. 266 | bool Database::isUnencrypted(const std::string& aFilename) 267 | { 268 | if (aFilename.empty()) 269 | { 270 | throw SQLite::Exception("Could not open database, the aFilename parameter was empty."); 271 | } 272 | 273 | std::ifstream fileBuffer(aFilename.c_str(), std::ios::in | std::ios::binary); 274 | char header[16]; 275 | if (fileBuffer.is_open()) 276 | { 277 | fileBuffer.seekg(0, std::ios::beg); 278 | fileBuffer.getline(header, 16); 279 | fileBuffer.close(); 280 | } 281 | else 282 | { 283 | throw SQLite::Exception("Error opening file: " + aFilename); 284 | } 285 | 286 | return strncmp(header, "SQLite format 3\000", 16) == 0; 287 | } 288 | 289 | // Parse header data from a database. 290 | Header Database::getHeaderInfo(const std::string& aFilename) 291 | { 292 | Header h; 293 | unsigned char buf[100]; 294 | char* pBuf = reinterpret_cast(&buf[0]); 295 | char* pHeaderStr = reinterpret_cast(&h.headerStr[0]); 296 | 297 | if (aFilename.empty()) 298 | { 299 | throw SQLite::Exception("Filename parameter is empty"); 300 | } 301 | 302 | { 303 | std::ifstream fileBuffer(aFilename.c_str(), std::ios::in | std::ios::binary); 304 | if (fileBuffer.is_open()) 305 | { 306 | fileBuffer.seekg(0, std::ios::beg); 307 | fileBuffer.read(pBuf, 100); 308 | fileBuffer.close(); 309 | if (fileBuffer.gcount() < 100) 310 | { 311 | throw SQLite::Exception("File " + aFilename + " is too short"); 312 | } 313 | } 314 | else 315 | { 316 | throw SQLite::Exception("Error opening file " + aFilename); 317 | } 318 | } 319 | 320 | // If the "magic string" can't be found then header is invalid, corrupt or unreadable 321 | memcpy(pHeaderStr, pBuf, 16); 322 | pHeaderStr[15] = '\0'; 323 | if (strncmp(pHeaderStr, "SQLite format 3", 15) != 0) 324 | { 325 | throw SQLite::Exception("Invalid or encrypted SQLite header in file " + aFilename); 326 | } 327 | 328 | h.pageSizeBytes = (buf[16] << 8) | buf[17]; 329 | h.fileFormatWriteVersion = buf[18]; 330 | h.fileFormatReadVersion = buf[19]; 331 | h.reservedSpaceBytes = buf[20]; 332 | h.maxEmbeddedPayloadFrac = buf[21]; 333 | h.minEmbeddedPayloadFrac = buf[22]; 334 | h.leafPayloadFrac = buf[23]; 335 | 336 | h.fileChangeCounter = 337 | (buf[24] << 24) | 338 | (buf[25] << 16) | 339 | (buf[26] << 8) | 340 | (buf[27] << 0); 341 | 342 | h.databaseSizePages = 343 | (buf[28] << 24) | 344 | (buf[29] << 16) | 345 | (buf[30] << 8) | 346 | (buf[31] << 0); 347 | 348 | h.firstFreelistTrunkPage = 349 | (buf[32] << 24) | 350 | (buf[33] << 16) | 351 | (buf[34] << 8) | 352 | (buf[35] << 0); 353 | 354 | h.totalFreelistPages = 355 | (buf[36] << 24) | 356 | (buf[37] << 16) | 357 | (buf[38] << 8) | 358 | (buf[39] << 0); 359 | 360 | h.schemaCookie = 361 | (buf[40] << 24) | 362 | (buf[41] << 16) | 363 | (buf[42] << 8) | 364 | (buf[43] << 0); 365 | 366 | h.schemaFormatNumber = 367 | (buf[44] << 24) | 368 | (buf[45] << 16) | 369 | (buf[46] << 8) | 370 | (buf[47] << 0); 371 | 372 | h.defaultPageCacheSizeBytes = 373 | (buf[48] << 24) | 374 | (buf[49] << 16) | 375 | (buf[50] << 8) | 376 | (buf[51] << 0); 377 | 378 | h.largestBTreePageNumber = 379 | (buf[52] << 24) | 380 | (buf[53] << 16) | 381 | (buf[54] << 8) | 382 | (buf[55] << 0); 383 | 384 | h.databaseTextEncoding = 385 | (buf[56] << 24) | 386 | (buf[57] << 16) | 387 | (buf[58] << 8) | 388 | (buf[59] << 0); 389 | 390 | h.userVersion = 391 | (buf[60] << 24) | 392 | (buf[61] << 16) | 393 | (buf[62] << 8) | 394 | (buf[63] << 0); 395 | 396 | h.incrementalVaccumMode = 397 | (buf[64] << 24) | 398 | (buf[65] << 16) | 399 | (buf[66] << 8) | 400 | (buf[67] << 0); 401 | 402 | h.applicationId = 403 | (buf[68] << 24) | 404 | (buf[69] << 16) | 405 | (buf[70] << 8) | 406 | (buf[71] << 0); 407 | 408 | h.versionValidFor = 409 | (buf[92] << 24) | 410 | (buf[93] << 16) | 411 | (buf[94] << 8) | 412 | (buf[95] << 0); 413 | 414 | h.sqliteVersion = 415 | (buf[96] << 24) | 416 | (buf[97] << 16) | 417 | (buf[98] << 8) | 418 | (buf[99] << 0); 419 | 420 | return h; 421 | } 422 | 423 | void Database::backup(const char* apFilename, BackupType aType) 424 | { 425 | // Open the database file identified by apFilename 426 | Database otherDatabase(apFilename, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); 427 | 428 | // For a 'Save' operation, data is copied from the current Database to the other. A 'Load' is the reverse. 429 | Database& src = (aType == BackupType::Save ? *this : otherDatabase); 430 | Database& dest = (aType == BackupType::Save ? otherDatabase : *this); 431 | 432 | // Set up the backup procedure to copy between the "main" databases of each connection 433 | Backup bkp(dest, src); 434 | bkp.executeStep(); // Execute all steps at once 435 | 436 | // RAII Finish Backup an Close the other Database 437 | } 438 | 439 | } // namespace SQLite 440 | -------------------------------------------------------------------------------- /src/Exception.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Exception.cpp 3 | * @ingroup SQLiteCpp 4 | * @brief Encapsulation of the error message from SQLite3 on a std::runtime_error. 5 | * 6 | * Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #include 12 | 13 | #include 14 | 15 | namespace SQLite 16 | { 17 | 18 | Exception::Exception(const char* aErrorMessage, int ret) : 19 | std::runtime_error(aErrorMessage), 20 | mErrcode(ret), 21 | mExtendedErrcode(-1) 22 | { 23 | } 24 | 25 | Exception::Exception(sqlite3* apSQLite) : 26 | std::runtime_error(sqlite3_errmsg(apSQLite)), 27 | mErrcode(sqlite3_errcode(apSQLite)), 28 | mExtendedErrcode(sqlite3_extended_errcode(apSQLite)) 29 | { 30 | } 31 | 32 | Exception::Exception(sqlite3* apSQLite, int ret) : 33 | std::runtime_error(sqlite3_errmsg(apSQLite)), 34 | mErrcode(ret), 35 | mExtendedErrcode(sqlite3_extended_errcode(apSQLite)) 36 | { 37 | } 38 | 39 | // Return a string, solely based on the error code 40 | const char* Exception::getErrorStr() const noexcept 41 | { 42 | return sqlite3_errstr(mErrcode); 43 | } 44 | 45 | } // namespace SQLite 46 | -------------------------------------------------------------------------------- /src/Savepoint.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Savepoint.cpp 3 | * @ingroup SQLiteCpp 4 | * @brief A Savepoint is a way to group multiple SQL statements into an atomic 5 | * secured operation. Similar to a transaction while allowing child savepoints. 6 | * 7 | * Copyright (c) 2020 Kelvin Hammond (hammond.kelvin@gmail.com) 8 | * Copyright (c) 2020-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 9 | * 10 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt or 11 | * copy at http://opensource.org/licenses/MIT) 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace SQLite 20 | { 21 | 22 | // Begins the SQLite savepoint 23 | Savepoint::Savepoint(Database& aDatabase, const std::string& aName) 24 | : mDatabase(aDatabase), msName(aName) 25 | { 26 | // workaround because you cannot bind to SAVEPOINT 27 | // escape name for use in query 28 | Statement stmt(mDatabase, "SELECT quote(?)"); 29 | stmt.bind(1, msName); 30 | stmt.executeStep(); 31 | msName = stmt.getColumn(0).getText(); 32 | 33 | mDatabase.exec(std::string("SAVEPOINT ") + msName); 34 | } 35 | 36 | // Safely rollback the savepoint if it has not been committed. 37 | Savepoint::~Savepoint() 38 | { 39 | if (!mbReleased) 40 | { 41 | try 42 | { 43 | rollback(); 44 | release(); 45 | } 46 | catch (SQLite::Exception&) 47 | { 48 | // Never throw an exception in a destructor: error if already released, 49 | // but no harm is caused by this. 50 | } 51 | } 52 | } 53 | 54 | // Release the savepoint and commit 55 | void Savepoint::release() 56 | { 57 | if (!mbReleased) 58 | { 59 | mDatabase.exec(std::string("RELEASE SAVEPOINT ") + msName); 60 | mbReleased = true; 61 | } 62 | else 63 | { 64 | throw SQLite::Exception("Savepoint already released."); 65 | } 66 | } 67 | 68 | // Rollback to the savepoint, but don't release it 69 | void Savepoint::rollbackTo() 70 | { 71 | if (!mbReleased) 72 | { 73 | mDatabase.exec(std::string("ROLLBACK TO SAVEPOINT ") + msName); 74 | } 75 | else 76 | { 77 | throw SQLite::Exception("Savepoint already released."); 78 | } 79 | } 80 | 81 | } // namespace SQLite 82 | -------------------------------------------------------------------------------- /src/Statement.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Statement.cpp 3 | * @ingroup SQLiteCpp 4 | * @brief A prepared SQLite Statement is a compiled SQL query ready to be executed, pointing to a row of result. 5 | * 6 | * Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | // check for if SQLite3 version >= 3.14.0 21 | #if SQLITE_VERSION_NUMBER < 3014000 22 | #warning "SQLite3 version is less than 3.14.0, so expanded SQL is not available" 23 | #warning "To use expanded SQL, please upgrade to SQLite3 version 3.14.0 or later" 24 | #warning "If you want to disable this warning, define SQLITECPP_DISABLE_SQLITE3_EXPANDED_SQL" 25 | #warning "or use the specific project option in your build system" 26 | #warning "disabling expanded SQL support" 27 | #define SQLITECPP_DISABLE_SQLITE3_EXPANDED_SQL 28 | #endif 29 | 30 | 31 | namespace SQLite 32 | { 33 | 34 | Statement::Statement(const Database& aDatabase, const char* apQuery) : 35 | mQuery(apQuery), 36 | mpSQLite(aDatabase.getHandle()), 37 | mpPreparedStatement(prepareStatement()) // prepare the SQL query (needs Database friendship) 38 | { 39 | mColumnCount = sqlite3_column_count(mpPreparedStatement.get()); 40 | } 41 | 42 | Statement::Statement(Statement&& aStatement) noexcept : 43 | mQuery(std::move(aStatement.mQuery)), 44 | mpSQLite(aStatement.mpSQLite), 45 | mpPreparedStatement(std::move(aStatement.mpPreparedStatement)), 46 | mColumnCount(aStatement.mColumnCount), 47 | mbHasRow(aStatement.mbHasRow), 48 | mbDone(aStatement.mbDone), 49 | mColumnNames(std::move(aStatement.mColumnNames)) 50 | { 51 | aStatement.mpSQLite = nullptr; 52 | aStatement.mColumnCount = 0; 53 | aStatement.mbHasRow = false; 54 | aStatement.mbDone = false; 55 | } 56 | 57 | // Reset the statement to make it ready for a new execution (see also #clearBindings() below) 58 | void Statement::reset() 59 | { 60 | const int ret = tryReset(); 61 | check(ret); 62 | } 63 | 64 | int Statement::tryReset() noexcept 65 | { 66 | mbHasRow = false; 67 | mbDone = false; 68 | return sqlite3_reset(mpPreparedStatement.get()); 69 | } 70 | 71 | // Clears away all the bindings of a prepared statement (can be associated with #reset() above). 72 | void Statement::clearBindings() 73 | { 74 | const int ret = sqlite3_clear_bindings(getPreparedStatement()); 75 | check(ret); 76 | } 77 | 78 | int Statement::getIndex(const char * const apName) const 79 | { 80 | return sqlite3_bind_parameter_index(getPreparedStatement(), apName); 81 | } 82 | 83 | // Bind an 32bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 84 | void Statement::bind(const int aIndex, const int32_t aValue) 85 | { 86 | const int ret = sqlite3_bind_int(getPreparedStatement(), aIndex, aValue); 87 | check(ret); 88 | } 89 | 90 | // Bind a 32bits unsigned int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 91 | void Statement::bind(const int aIndex, const uint32_t aValue) 92 | { 93 | const int ret = sqlite3_bind_int64(getPreparedStatement(), aIndex, aValue); 94 | check(ret); 95 | } 96 | 97 | // Bind a 64bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 98 | void Statement::bind(const int aIndex, const int64_t aValue) 99 | { 100 | const int ret = sqlite3_bind_int64(getPreparedStatement(), aIndex, aValue); 101 | check(ret); 102 | } 103 | 104 | // Bind a double (64bits float) value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 105 | void Statement::bind(const int aIndex, const double aValue) 106 | { 107 | const int ret = sqlite3_bind_double(getPreparedStatement(), aIndex, aValue); 108 | check(ret); 109 | } 110 | 111 | // Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 112 | void Statement::bind(const int aIndex, const std::string& aValue) 113 | { 114 | const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, aValue.c_str(), 115 | static_cast(aValue.size()), SQLITE_TRANSIENT); 116 | check(ret); 117 | } 118 | 119 | // Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 120 | void Statement::bind(const int aIndex, const char* apValue) 121 | { 122 | const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, apValue, -1, SQLITE_TRANSIENT); 123 | check(ret); 124 | } 125 | 126 | // Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 127 | void Statement::bind(const int aIndex, const void* apValue, const int aSize) 128 | { 129 | const int ret = sqlite3_bind_blob(getPreparedStatement(), aIndex, apValue, aSize, SQLITE_TRANSIENT); 130 | check(ret); 131 | } 132 | 133 | // Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 134 | void Statement::bindNoCopy(const int aIndex, const std::string& aValue) 135 | { 136 | const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, aValue.c_str(), 137 | static_cast(aValue.size()), SQLITE_STATIC); 138 | check(ret); 139 | } 140 | 141 | // Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 142 | void Statement::bindNoCopy(const int aIndex, const char* apValue) 143 | { 144 | const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, apValue, -1, SQLITE_STATIC); 145 | check(ret); 146 | } 147 | 148 | // Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 149 | void Statement::bindNoCopy(const int aIndex, const void* apValue, const int aSize) 150 | { 151 | const int ret = sqlite3_bind_blob(getPreparedStatement(), aIndex, apValue, aSize, SQLITE_STATIC); 152 | check(ret); 153 | } 154 | 155 | // Bind a NULL value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 156 | void Statement::bind(const int aIndex) 157 | { 158 | const int ret = sqlite3_bind_null(getPreparedStatement(), aIndex); 159 | check(ret); 160 | } 161 | 162 | 163 | // Execute a step of the query to fetch one row of results 164 | bool Statement::executeStep() 165 | { 166 | const int ret = tryExecuteStep(); 167 | if ((SQLITE_ROW != ret) && (SQLITE_DONE != ret)) // on row or no (more) row ready, else it's a problem 168 | { 169 | if (ret == sqlite3_errcode(mpSQLite)) 170 | { 171 | throw SQLite::Exception(mpSQLite, ret); 172 | } 173 | else 174 | { 175 | throw SQLite::Exception("Statement needs to be reseted", ret); 176 | } 177 | } 178 | 179 | return mbHasRow; // true only if one row is accessible by getColumn(N) 180 | } 181 | 182 | // Execute a one-step query with no expected result, and return the number of changes. 183 | int Statement::exec() 184 | { 185 | const int ret = tryExecuteStep(); 186 | if (SQLITE_DONE != ret) // the statement has finished executing successfully 187 | { 188 | if (SQLITE_ROW == ret) 189 | { 190 | throw SQLite::Exception("exec() does not expect results. Use executeStep."); 191 | } 192 | else if (ret == sqlite3_errcode(mpSQLite)) 193 | { 194 | throw SQLite::Exception(mpSQLite, ret); 195 | } 196 | else 197 | { 198 | throw SQLite::Exception("Statement needs to be reseted", ret); 199 | } 200 | } 201 | 202 | // Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE) 203 | return sqlite3_changes(mpSQLite); 204 | } 205 | 206 | int Statement::tryExecuteStep() noexcept 207 | { 208 | if (mbDone) 209 | { 210 | return SQLITE_MISUSE; // Statement needs to be reseted ! 211 | } 212 | 213 | const int ret = sqlite3_step(mpPreparedStatement.get()); 214 | if (SQLITE_ROW == ret) // one row is ready : call getColumn(N) to access it 215 | { 216 | mbHasRow = true; 217 | } 218 | else 219 | { 220 | mbHasRow = false; 221 | mbDone = SQLITE_DONE == ret; // check if the query has finished executing 222 | } 223 | return ret; 224 | } 225 | 226 | 227 | // Return a copy of the column data specified by its index starting at 0 228 | // (use the Column copy-constructor) 229 | Column Statement::getColumn(const int aIndex) const 230 | { 231 | checkRow(); 232 | checkIndex(aIndex); 233 | 234 | // Share the Statement Object handle with the new Column created 235 | return Column(mpPreparedStatement, aIndex); 236 | } 237 | 238 | // Return a copy of the column data specified by its column name starting at 0 239 | // (use the Column copy-constructor) 240 | Column Statement::getColumn(const char* apName) const 241 | { 242 | checkRow(); 243 | const int index = getColumnIndex(apName); 244 | 245 | // Share the Statement Object handle with the new Column created 246 | return Column(mpPreparedStatement, index); 247 | } 248 | 249 | // Test if the column is NULL 250 | bool Statement::isColumnNull(const int aIndex) const 251 | { 252 | checkRow(); 253 | checkIndex(aIndex); 254 | return (SQLITE_NULL == sqlite3_column_type(getPreparedStatement(), aIndex)); 255 | } 256 | 257 | bool Statement::isColumnNull(const char* apName) const 258 | { 259 | checkRow(); 260 | const int index = getColumnIndex(apName); 261 | return (SQLITE_NULL == sqlite3_column_type(getPreparedStatement(), index)); 262 | } 263 | 264 | // Return the named assigned to the specified result column (potentially aliased) 265 | const char* Statement::getColumnName(const int aIndex) const 266 | { 267 | checkIndex(aIndex); 268 | return sqlite3_column_name(getPreparedStatement(), aIndex); 269 | } 270 | 271 | #ifdef SQLITE_ENABLE_COLUMN_METADATA 272 | // Return the named assigned to the specified result column (potentially aliased) 273 | const char* Statement::getColumnOriginName(const int aIndex) const 274 | { 275 | checkIndex(aIndex); 276 | return sqlite3_column_origin_name(getPreparedStatement(), aIndex); 277 | } 278 | #endif 279 | 280 | // Return the index of the specified (potentially aliased) column name 281 | int Statement::getColumnIndex(const char* apName) const 282 | { 283 | // Build the map of column index by name on first call 284 | if (mColumnNames.empty()) 285 | { 286 | for (int i = 0; i < mColumnCount; ++i) 287 | { 288 | const char* pName = sqlite3_column_name(getPreparedStatement(), i); 289 | mColumnNames[pName] = i; 290 | } 291 | } 292 | 293 | const auto iIndex = mColumnNames.find(apName); 294 | if (iIndex == mColumnNames.end()) 295 | { 296 | throw SQLite::Exception("Unknown column name."); 297 | } 298 | 299 | return iIndex->second; 300 | } 301 | 302 | const char* Statement::getColumnDeclaredType(const int aIndex) const 303 | { 304 | checkIndex(aIndex); 305 | const char * result = sqlite3_column_decltype(getPreparedStatement(), aIndex); 306 | if (!result) 307 | { 308 | throw SQLite::Exception("Could not determine declared column type."); 309 | } 310 | else 311 | { 312 | return result; 313 | } 314 | } 315 | 316 | // Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table). 317 | int Statement::getChanges() const noexcept 318 | { 319 | return sqlite3_changes(mpSQLite); 320 | } 321 | 322 | int Statement::getBindParameterCount() const noexcept 323 | { 324 | return sqlite3_bind_parameter_count(mpPreparedStatement.get()); 325 | } 326 | 327 | // Return the numeric result code for the most recent failed API call (if any). 328 | int Statement::getErrorCode() const noexcept 329 | { 330 | return sqlite3_errcode(mpSQLite); 331 | } 332 | 333 | // Return the extended numeric result code for the most recent failed API call (if any). 334 | int Statement::getExtendedErrorCode() const noexcept 335 | { 336 | return sqlite3_extended_errcode(mpSQLite); 337 | } 338 | 339 | // Return UTF-8 encoded English language explanation of the most recent failed API call (if any). 340 | const char* Statement::getErrorMsg() const noexcept 341 | { 342 | return sqlite3_errmsg(mpSQLite); 343 | } 344 | 345 | 346 | // Return a UTF-8 string containing the SQL text of prepared statement with bound parameters expanded. 347 | std::string Statement::getExpandedSQL() const { 348 | #ifdef SQLITECPP_DISABLE_SQLITE3_EXPANDED_SQL 349 | throw SQLite::Exception("this version of SQLiteCpp does not support expanded SQL"); 350 | #else 351 | char* expanded = sqlite3_expanded_sql(getPreparedStatement()); 352 | std::string expandedString(expanded); 353 | sqlite3_free(expanded); 354 | return expandedString; 355 | #endif 356 | } 357 | 358 | 359 | // Prepare SQLite statement object and return shared pointer to this object 360 | Statement::TStatementPtr Statement::prepareStatement() 361 | { 362 | sqlite3_stmt* statement; 363 | const int ret = sqlite3_prepare_v2(mpSQLite, mQuery.c_str(), static_cast(mQuery.size()), &statement, nullptr); 364 | if (SQLITE_OK != ret) 365 | { 366 | throw SQLite::Exception(mpSQLite, ret); 367 | } 368 | return Statement::TStatementPtr(statement, [](sqlite3_stmt* stmt) 369 | { 370 | sqlite3_finalize(stmt); 371 | }); 372 | } 373 | 374 | // Return prepered statement object or throw 375 | sqlite3_stmt* Statement::getPreparedStatement() const 376 | { 377 | sqlite3_stmt* ret = mpPreparedStatement.get(); 378 | if (ret) 379 | { 380 | return ret; 381 | } 382 | throw SQLite::Exception("Statement was not prepared."); 383 | } 384 | 385 | } // namespace SQLite 386 | -------------------------------------------------------------------------------- /src/Transaction.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Transaction.cpp 3 | * @ingroup SQLiteCpp 4 | * @brief A Transaction is way to group multiple SQL statements into an atomic secured operation. 5 | * 6 | * Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | namespace SQLite 19 | { 20 | 21 | // Begins the SQLite transaction 22 | Transaction::Transaction(Database& aDatabase, TransactionBehavior behavior) : 23 | mDatabase(aDatabase) 24 | { 25 | const char *stmt; 26 | switch (behavior) { 27 | case TransactionBehavior::DEFERRED: 28 | stmt = "BEGIN DEFERRED"; 29 | break; 30 | case TransactionBehavior::IMMEDIATE: 31 | stmt = "BEGIN IMMEDIATE"; 32 | break; 33 | case TransactionBehavior::EXCLUSIVE: 34 | stmt = "BEGIN EXCLUSIVE"; 35 | break; 36 | default: 37 | throw SQLite::Exception("invalid/unknown transaction behavior", SQLITE_ERROR); 38 | } 39 | mDatabase.exec(stmt); 40 | } 41 | 42 | // Begins the SQLite transaction 43 | Transaction::Transaction(Database &aDatabase) : 44 | mDatabase(aDatabase) 45 | { 46 | mDatabase.exec("BEGIN TRANSACTION"); 47 | } 48 | 49 | // Safely rollback the transaction if it has not been committed. 50 | Transaction::~Transaction() 51 | { 52 | if (false == mbCommited) 53 | { 54 | try 55 | { 56 | mDatabase.exec("ROLLBACK TRANSACTION"); 57 | } 58 | catch (SQLite::Exception&) 59 | { 60 | // Never throw an exception in a destructor: error if already rollbacked, but no harm is caused by this. 61 | } 62 | } 63 | } 64 | 65 | // Commit the transaction. 66 | void Transaction::commit() 67 | { 68 | if (false == mbCommited) 69 | { 70 | mDatabase.exec("COMMIT TRANSACTION"); 71 | mbCommited = true; 72 | } 73 | else 74 | { 75 | throw SQLite::Exception("Transaction already committed."); 76 | } 77 | } 78 | 79 | // Rollback the transaction 80 | void Transaction::rollback() 81 | { 82 | if (false == mbCommited) 83 | { 84 | mDatabase.exec("ROLLBACK TRANSACTION"); 85 | } 86 | else 87 | { 88 | throw SQLite::Exception("Transaction already committed."); 89 | } 90 | } 91 | 92 | } // namespace SQLite 93 | -------------------------------------------------------------------------------- /subprojects/.gitignore: -------------------------------------------------------------------------------- 1 | #ignore everything here 2 | * 3 | # but not the wrap files and the .gitignore 4 | !*.wrap 5 | !*.gitignore -------------------------------------------------------------------------------- /subprojects/gtest.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = googletest-1.15.0 3 | source_url = https://github.com/google/googletest/archive/refs/tags/v1.15.0.tar.gz 4 | source_filename = gtest-1.15.0.tar.gz 5 | source_hash = 7315acb6bf10e99f332c8a43f00d5fbb1ee6ca48c52f6b936991b216c586aaad 6 | patch_filename = gtest_1.15.0-1_patch.zip 7 | patch_url = https://wrapdb.mesonbuild.com/v2/gtest_1.15.0-1/get_patch 8 | patch_hash = 5f8e484c48fdc1029c7fd08807bd2615f8c9d16f90df6d81984f4f292752c925 9 | source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/gtest_1.15.0-1/gtest-1.15.0.tar.gz 10 | wrapdb_version = 1.15.0-1 11 | 12 | [provide] 13 | gtest = gtest_dep 14 | gtest_main = gtest_main_dep 15 | gmock = gmock_dep 16 | gmock_main = gmock_main_dep 17 | -------------------------------------------------------------------------------- /subprojects/sqlite3.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = sqlite-amalgamation-3490200 3 | source_url = https://www.sqlite.org/2025/sqlite-amalgamation-3490200.zip 4 | source_filename = sqlite-amalgamation-3490200.zip 5 | source_hash = 921fc725517a694df7df38a2a3dfede6684024b5788d9de464187c612afb5918 6 | patch_filename = sqlite3_3.49.2-1_patch.zip 7 | patch_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.49.2-1/get_patch 8 | patch_hash = e3eef046409329c5c1ca8308255caa2266710fc1b9d8695fdedd04cebe42a690 9 | source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/sqlite3_3.49.2-1/sqlite-amalgamation-3490200.zip 10 | wrapdb_version = 3.49.2-1 11 | 12 | [provide] 13 | sqlite3 = sqlite3_dep 14 | -------------------------------------------------------------------------------- /tests/Backup_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Backup_test.cpp 3 | * @ingroup tests 4 | * @brief Test of a SQLite Backup. 5 | * 6 | * Copyright (c) 2015 Shibao HONG (shibaohong@outlook.com) 7 | * Copyright (c) 2015-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 8 | * 9 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 10 | * or copy at http://opensource.org/licenses/MIT) 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include // for SQLITE_ERROR, SQLITE_RANGE and SQLITE_DONE 19 | 20 | #include 21 | 22 | #include 23 | 24 | TEST(Backup, initException) 25 | { 26 | remove("backup_test.db3"); 27 | { 28 | SQLite::Database srcDB("backup_test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 29 | srcDB.exec("CREATE TABLE backup_test (id INTEGER PRIMARY KEY, value TEXT)"); 30 | ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (1, 'first')")); 31 | ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (2, 'second')")); 32 | EXPECT_THROW(SQLite::Backup backup(srcDB, srcDB), SQLite::Exception); 33 | EXPECT_THROW(SQLite::Backup backup(srcDB, "main", srcDB, "main"), SQLite::Exception); 34 | const std::string name("main"); 35 | EXPECT_THROW(SQLite::Backup backup(srcDB, name, srcDB, name), SQLite::Exception); 36 | } 37 | remove("backup_test.db3"); 38 | } 39 | 40 | TEST(Backup, executeStepOne) 41 | { 42 | remove("backup_test.db3"); 43 | remove("backup_test.db3.backup"); 44 | { 45 | SQLite::Database srcDB("backup_test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 46 | srcDB.exec("CREATE TABLE backup_test (id INTEGER PRIMARY KEY, value TEXT)"); 47 | ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (1, 'first')")); 48 | ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (2, 'second')")); 49 | 50 | SQLite::Database destDB("backup_test.db3.backup", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 51 | SQLite::Backup backup(destDB, "main", srcDB, "main"); 52 | int res = backup.executeStep(1); // backup only one page at a time 53 | ASSERT_EQ(SQLite::OK, res); 54 | const int total = backup.getTotalPageCount(); 55 | ASSERT_EQ(2, total); 56 | int remaining = backup.getRemainingPageCount(); 57 | ASSERT_EQ(1, remaining); 58 | res = backup.executeStep(1); // backup the second and last page 59 | ASSERT_EQ(SQLITE_DONE, res); 60 | remaining = backup.getRemainingPageCount(); 61 | ASSERT_EQ(0, remaining); 62 | 63 | SQLite::Statement query(destDB, "SELECT * FROM backup_test ORDER BY id ASC"); 64 | ASSERT_TRUE(query.executeStep()); 65 | EXPECT_EQ(1, query.getColumn(0).getInt()); 66 | EXPECT_STREQ("first", query.getColumn(1)); 67 | ASSERT_TRUE(query.executeStep()); 68 | EXPECT_EQ(2, query.getColumn(0).getInt()); 69 | EXPECT_STREQ("second", query.getColumn(1)); 70 | } 71 | remove("backup_test.db3"); 72 | remove("backup_test.db3.backup"); 73 | } 74 | 75 | TEST(Backup, executeStepAll) 76 | { 77 | remove("backup_test.db3"); 78 | remove("backup_test.db3.backup"); 79 | { 80 | SQLite::Database srcDB("backup_test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 81 | srcDB.exec("CREATE TABLE backup_test (id INTEGER PRIMARY KEY, value TEXT)"); 82 | ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (1, 'first')")); 83 | ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (2, 'second')")); 84 | 85 | SQLite::Database destDB("backup_test.db3.backup", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 86 | SQLite::Backup backup(destDB, srcDB); 87 | const int res = backup.executeStep(); // uses default argument "-1" => execute all steps at once 88 | ASSERT_EQ(res, SQLITE_DONE); 89 | const int total = backup.getTotalPageCount(); 90 | ASSERT_EQ(2, total); 91 | const int remaining = backup.getRemainingPageCount(); 92 | ASSERT_EQ(0, remaining); 93 | 94 | SQLite::Statement query(destDB, "SELECT * FROM backup_test ORDER BY id ASC"); 95 | ASSERT_TRUE(query.executeStep()); 96 | EXPECT_EQ(1, query.getColumn(0).getInt()); 97 | EXPECT_STREQ("first", query.getColumn(1)); 98 | ASSERT_TRUE(query.executeStep()); 99 | EXPECT_EQ(2, query.getColumn(0).getInt()); 100 | EXPECT_STREQ("second", query.getColumn(1)); 101 | } 102 | remove("backup_test.db3"); 103 | remove("backup_test.db3.backup"); 104 | } 105 | 106 | TEST(Backup, executeStepException) 107 | { 108 | remove("backup_test.db3"); 109 | remove("backup_test.db3.backup"); 110 | { 111 | SQLite::Database srcDB("backup_test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 112 | srcDB.exec("CREATE TABLE backup_test (id INTEGER PRIMARY KEY, value TEXT)"); 113 | ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (1, 'first')")); 114 | ASSERT_EQ(1, srcDB.exec("INSERT INTO backup_test VALUES (2, 'second')")); 115 | { 116 | SQLite::Database destDB("backup_test.db3.backup", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 117 | (void)destDB; 118 | } 119 | { 120 | SQLite::Database destDB("backup_test.db3.backup", SQLite::OPEN_READONLY); 121 | SQLite::Backup backup(destDB, srcDB); 122 | EXPECT_THROW(backup.executeStep(), SQLite::Exception); 123 | } 124 | } 125 | remove("backup_test.db3"); 126 | remove("backup_test.db3.backup"); 127 | } 128 | -------------------------------------------------------------------------------- /tests/Column_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Column_test.cpp 3 | * @ingroup tests 4 | * @brief Test of a SQLiteCpp Column. 5 | * 6 | * Copyright (c) 2012-2025 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | 22 | static void test_column_basis(bool utf16) 23 | { 24 | // Create a new database 25 | SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 26 | EXPECT_EQ(SQLite::OK, db.getErrorCode()); 27 | EXPECT_EQ(SQLite::OK, db.getExtendedErrorCode()); 28 | 29 | if (utf16) 30 | { 31 | EXPECT_EQ(0, db.exec("PRAGMA encoding = 'UTF-16';")); 32 | } 33 | 34 | // Create a new table 35 | EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, msg TEXT, int INTEGER, double REAL, binary BLOB, empty TEXT)")); 36 | EXPECT_TRUE(db.tableExists("test")); 37 | EXPECT_TRUE(db.tableExists(std::string("test"))); 38 | EXPECT_EQ(0, db.getLastInsertRowid()); 39 | 40 | // Create a first row (autoid: 1) with all kind of data and a null value 41 | SQLite::Statement insert(db, "INSERT INTO test VALUES (NULL, 'first', -123, 0.123, ?, NULL)"); 42 | // Bind the blob value to the first parameter of the SQL query 43 | const char buffer[] = {'b', 'l', '\0', 'b'}; // "bl\0b" : 4 char, with a null byte inside 44 | const int size = sizeof(buffer); // size = 4 45 | const void* blob = &buffer; 46 | insert.bind(1, blob, size); 47 | // Execute the one-step query to insert the row 48 | EXPECT_EQ(1, insert.exec()); 49 | EXPECT_EQ(1, db.getLastInsertRowid()); 50 | EXPECT_EQ(1, db.getChanges()); 51 | EXPECT_EQ(1, db.getTotalChanges()); 52 | 53 | EXPECT_THROW(insert.exec(), SQLite::Exception); // exec() shall throw as it needs to be reseted 54 | 55 | // Compile a SQL query 56 | SQLite::Statement query(db, "SELECT * FROM test"); 57 | EXPECT_STREQ("SELECT * FROM test", query.getQuery().c_str()); 58 | EXPECT_EQ(6, query.getColumnCount ()); 59 | query.executeStep(); 60 | EXPECT_TRUE (query.hasRow()); 61 | EXPECT_FALSE(query.isDone()); 62 | 63 | // validates every variant of cast operators, and conversions of types 64 | { 65 | const int64_t id1 = query.getColumn(0); // operator int64_t() 66 | const int32_t id2 = query.getColumn(0); // operator int32_t() 67 | const int id3 = query.getColumn(0); // operator int32_t() 68 | const int16_t id4 = query.getColumn(0); // operator int32_t() 69 | const short id5 = query.getColumn(0); // operator int32_t() 70 | const int8_t id6 = query.getColumn(0); // operator int32_t() 71 | const char id7 = query.getColumn(0); // operator int32_t() 72 | const unsigned int uint1 = query.getColumn(0); // operator unsigned int() 73 | const uint32_t uint2 = query.getColumn(0); // operator unsigned int() 74 | const unsigned char uint3 = query.getColumn(0); // operator unsigned char() 75 | const unsigned short uint4 = query.getColumn(0); // operator unsigned short() 76 | const char* ptxt = query.getColumn(1); // operator const char*() 77 | const std::string msg = query.getColumn(1); // operator std::string() (or const char* with MSVC) 78 | const int integer = query.getColumn(2); // operator int() 79 | const double real = query.getColumn(3); // operator double() 80 | const void* pblob = query.getColumn(4); // operator void*() 81 | #if !defined(_MSC_VER) || _MSC_VER >= 1900 82 | // This implicit cast should use operator std::string() 83 | // but would fallback to const char* with MSVC 2010-2013 (witch does not work with the NULL char in the middle) 84 | const std::string sblob = query.getColumn(4); // operator std::string() 85 | #endif 86 | const void* pempty = query.getColumn(5); // operator void*() 87 | EXPECT_EQ(1, id1); 88 | EXPECT_EQ(1, id2); 89 | EXPECT_EQ(1, id3); 90 | EXPECT_EQ(1, id4); 91 | EXPECT_EQ(1, id5); 92 | EXPECT_EQ(1, id6); 93 | EXPECT_EQ(1, id7); 94 | EXPECT_EQ(1U, uint1); 95 | EXPECT_EQ(1U, uint2); 96 | EXPECT_EQ(1U, uint3); 97 | EXPECT_EQ(1U, uint4); 98 | EXPECT_STREQ("first", ptxt); 99 | EXPECT_EQ("first", msg); 100 | EXPECT_EQ(-123, integer); 101 | EXPECT_DOUBLE_EQ(0.123, real); 102 | EXPECT_EQ(0, memcmp("bl\0b", pblob, size)); 103 | #if !defined(_MSC_VER) || _MSC_VER >= 1900 104 | EXPECT_EQ((size_t)size, sblob.size()); 105 | EXPECT_EQ(0, memcmp("bl\0b", &sblob[0], size)); 106 | #endif 107 | EXPECT_EQ(NULL, pempty); 108 | } 109 | 110 | query.reset(); 111 | query.executeStep(); 112 | 113 | // validates every variant of explicit getters 114 | { 115 | int64_t id = query.getColumn(0).getInt64(); 116 | const unsigned int uint1 = query.getColumn(0).getUInt(); 117 | const uint32_t uint2 = query.getColumn(0).getUInt(); 118 | const std::string msg1 = query.getColumn(1).getString(); 119 | const char* ptxt = query.getColumn(1).getText(); 120 | const std::string msg2 = query.getColumn(1).getText(); 121 | const int integer = query.getColumn(2).getInt(); 122 | const double real = query.getColumn(3).getDouble(); 123 | const void* pblob = query.getColumn(4).getBlob(); 124 | const std::string sblob = query.getColumn(4).getString(); 125 | EXPECT_EQ(1, id); 126 | EXPECT_EQ(1U, uint1); 127 | EXPECT_EQ(1U, uint2); 128 | EXPECT_STREQ("first", ptxt); 129 | EXPECT_EQ("first", msg1); 130 | EXPECT_EQ("first", msg2); 131 | EXPECT_EQ(-123, integer); 132 | EXPECT_DOUBLE_EQ(0.123, real); 133 | EXPECT_EQ(0, memcmp("bl\0b", pblob, 4)); 134 | EXPECT_EQ(0, memcmp("bl\0b", &sblob[0], 4)); 135 | } 136 | 137 | // Validate getBytes(), getType(), isInteger(), isNull()... 138 | EXPECT_EQ(SQLite::INTEGER, query.getColumn(0).getType()); 139 | EXPECT_EQ(true, query.getColumn(0).isInteger()); 140 | EXPECT_EQ(false, query.getColumn(0).isFloat()); 141 | EXPECT_EQ(false, query.getColumn(0).isText()); 142 | EXPECT_EQ(false, query.getColumn(0).isBlob()); 143 | EXPECT_EQ(false, query.getColumn(0).isNull()); 144 | EXPECT_STREQ("1", query.getColumn(0).getText()); // convert to TEXT via text func 145 | EXPECT_EQ(1, query.getColumn(0).getBytes()); // size of the string "1" without the null terminator 146 | EXPECT_EQ(SQLite::TEXT, query.getColumn(1).getType()); 147 | EXPECT_EQ(false, query.getColumn(1).isInteger()); 148 | EXPECT_EQ(false, query.getColumn(1).isFloat()); 149 | EXPECT_EQ(true, query.getColumn(1).isText()); 150 | EXPECT_EQ(false, query.getColumn(1).isBlob()); 151 | EXPECT_EQ(false, query.getColumn(1).isNull()); 152 | EXPECT_STREQ("first", query.getColumn(1).getString().c_str()); // convert to TEXT via string func 153 | EXPECT_EQ(5, query.getColumn(1).getBytes()); // size of the string "first" 154 | EXPECT_EQ(SQLite::INTEGER, query.getColumn(2).getType()); 155 | EXPECT_EQ(true, query.getColumn(2).isInteger()); 156 | EXPECT_EQ(false, query.getColumn(2).isFloat()); 157 | EXPECT_EQ(false, query.getColumn(2).isText()); 158 | EXPECT_EQ(false, query.getColumn(2).isBlob()); 159 | EXPECT_EQ(false, query.getColumn(2).isNull()); 160 | EXPECT_STREQ("-123", query.getColumn(2).getText()); // convert to string 161 | EXPECT_EQ(4, query.getColumn(2).getBytes()); // size of the string "-123" 162 | EXPECT_EQ(SQLite::FLOAT, query.getColumn(3).getType()); 163 | EXPECT_EQ(false, query.getColumn(3).isInteger()); 164 | EXPECT_EQ(true, query.getColumn(3).isFloat()); 165 | EXPECT_EQ(false, query.getColumn(3).isText()); 166 | EXPECT_EQ(false, query.getColumn(3).isBlob()); 167 | EXPECT_EQ(false, query.getColumn(3).isNull()); 168 | EXPECT_STREQ("0.123", query.getColumn(3).getText()); // convert to string 169 | EXPECT_EQ(5, query.getColumn(3).getBytes()); // size of the string "0.123" 170 | EXPECT_EQ(SQLite::BLOB, query.getColumn(4).getType()); 171 | EXPECT_EQ(false, query.getColumn(4).isInteger()); 172 | EXPECT_EQ(false, query.getColumn(4).isFloat()); 173 | EXPECT_EQ(false, query.getColumn(4).isText()); 174 | EXPECT_EQ(true, query.getColumn(4).isBlob()); 175 | EXPECT_EQ(false, query.getColumn(4).isNull()); 176 | EXPECT_EQ(4, query.getColumn(4).getBytes()); // size of the blob "bl\0b" with the null char 177 | EXPECT_EQ(SQLite::Null, query.getColumn(5).getType()); 178 | EXPECT_EQ(false, query.getColumn(5).isInteger()); 179 | EXPECT_EQ(false, query.getColumn(5).isFloat()); 180 | EXPECT_EQ(false, query.getColumn(5).isText()); 181 | EXPECT_EQ(false, query.getColumn(5).isBlob()); 182 | EXPECT_EQ(true, query.getColumn(5).isNull()); 183 | EXPECT_STREQ("", query.getColumn(5).getText()); // convert to string 184 | EXPECT_EQ(0, query.getColumn(5).getBytes()); // size of the string "" without the null terminator 185 | 186 | query.reset(); 187 | query.executeStep(); 188 | 189 | // Use intermediate Column objects (this is not the recommended way to use the API) 190 | { 191 | const SQLite::Column id = query.getColumn(0); 192 | EXPECT_EQ(1, id.getInt64()); 193 | const SQLite::Column msg = query.getColumn(1); 194 | EXPECT_EQ("first", msg.getString()); 195 | const SQLite::Column integer = query.getColumn(2); 196 | EXPECT_EQ(-123, integer.getInt()); 197 | const SQLite::Column dbl = query.getColumn(3); 198 | EXPECT_DOUBLE_EQ(0.123, dbl.getDouble()); 199 | } 200 | } 201 | 202 | TEST(Column, basis) 203 | { 204 | test_column_basis(false); 205 | } 206 | 207 | TEST(Column, basis16) 208 | { 209 | test_column_basis(true); 210 | } 211 | 212 | TEST(Column, getName) 213 | { 214 | // Create a new database 215 | SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 216 | EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, msg TEXT)")); 217 | EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, 'first')")); 218 | 219 | // Compile a SQL query, using the "id" column name as-is, but aliasing the "msg" column with new name "value" 220 | SQLite::Statement query(db, "SELECT id, msg as value FROM test"); 221 | query.executeStep(); 222 | 223 | // Show how to get the aliased names of the result columns. 224 | const std::string name0 = query.getColumn(0).getName(); 225 | const std::string name1 = query.getColumn(1).getName(); 226 | EXPECT_EQ("id", name0); 227 | EXPECT_EQ("value", name1); 228 | 229 | #ifdef SQLITE_ENABLE_COLUMN_METADATA 230 | // Show how to get origin names of the table columns from which theses result columns come from. 231 | // Requires the SQLITE_ENABLE_COLUMN_METADATA preprocessor macro to be 232 | // also defined at compile times of the SQLite library itself. 233 | const std::string oname0 = query.getColumn(0).getOriginName(); 234 | const std::string oname1 = query.getColumn(1).getOriginName(); 235 | EXPECT_EQ("id", oname0); 236 | EXPECT_EQ("msg", oname1); 237 | #endif 238 | } 239 | 240 | TEST(Column, stream) 241 | { 242 | // Create a new database 243 | SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 244 | EXPECT_EQ(0, db.exec("CREATE TABLE test (msg TEXT)")); 245 | SQLite::Statement insert(db, "INSERT INTO test VALUES (?)"); 246 | 247 | // content to test 248 | const char str_[] = "stringwith\0embedded"; 249 | std::string str(str_, sizeof(str_)-1); 250 | 251 | insert.bind(1, str); 252 | // Execute the one-step query to insert the row 253 | EXPECT_EQ(1, insert.exec()); 254 | EXPECT_EQ(1, db.getChanges()); 255 | EXPECT_EQ(1, db.getTotalChanges()); 256 | 257 | SQLite::Statement query(db, "SELECT * FROM test"); 258 | query.executeStep(); 259 | std::stringstream ss; 260 | ss << query.getColumn(0); 261 | std::string content = ss.str(); 262 | EXPECT_EQ(content, str); 263 | } 264 | 265 | TEST(Column, shared_ptr) 266 | { 267 | // Create a new database 268 | SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 269 | EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, msg TEXT)")); 270 | EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (42, 'fortytwo')")); 271 | const char* query_str = "SELECT id, msg FROM test"; 272 | 273 | std::unique_ptr query{ new SQLite::Statement(db, query_str) }; 274 | query->executeStep(); 275 | 276 | auto column0 = query->getColumn(0); 277 | auto column1 = query->getColumn(1); 278 | query.reset(); 279 | 280 | EXPECT_EQ(42, column0.getInt()); 281 | EXPECT_STREQ("fortytwo", column1.getText()); 282 | 283 | query.reset(new SQLite::Statement(db, query_str)); 284 | query->executeStep(); 285 | column0 = query->getColumn(0); 286 | EXPECT_EQ(true, column0.isInteger()); 287 | query->executeStep(); // query is done 288 | 289 | // Undefined behavior 290 | // auto x = column0.getInt(); 291 | 292 | query.reset(); 293 | 294 | // Undefined behavior 295 | // auto x = column0.getInt(); 296 | // bool isInt = column0.isInteger(); 297 | 298 | EXPECT_STREQ("id", column0.getName()); 299 | } 300 | -------------------------------------------------------------------------------- /tests/Exception_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Transaction_test.cpp 3 | * @ingroup tests 4 | * @brief Test of a SQLite Transaction. 5 | * 6 | * Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | 12 | #include 13 | 14 | #include 15 | 16 | #include 17 | 18 | TEST(Exception, copy) 19 | { 20 | const SQLite::Exception ex1("some error", 2); 21 | const SQLite::Exception ex2 = ex1; 22 | EXPECT_STREQ(ex1.what(), ex2.what()); 23 | EXPECT_EQ(ex1.getErrorCode(), ex2.getErrorCode()); 24 | EXPECT_EQ(ex1.getExtendedErrorCode(), ex2.getExtendedErrorCode()); 25 | } 26 | 27 | // see http://eel.is/c++draft/exception#2 or http://www.cplusplus.com/reference/exception/exception/operator=/ 28 | // an assignment operator is expected to be avaiable 29 | TEST(Exception, assignment) 30 | { 31 | const char message[] = "some error"; 32 | const SQLite::Exception ex1(message, 1); 33 | SQLite::Exception ex2("another error", 2); 34 | 35 | ex2 = ex1; 36 | 37 | EXPECT_STREQ(ex2.what(), message); 38 | EXPECT_EQ(ex2.getErrorCode(), 1); 39 | EXPECT_EQ(ex2.getExtendedErrorCode(), -1); 40 | EXPECT_STREQ(ex2.getErrorStr(), "SQL logic error"); 41 | } 42 | 43 | TEST(Exception, throw_catch) 44 | { 45 | const char message[] = "some error"; 46 | try 47 | { 48 | throw SQLite::Exception(message); 49 | } 50 | catch (const std::runtime_error& ex) 51 | { 52 | EXPECT_STREQ(ex.what(), message); 53 | } 54 | } 55 | 56 | 57 | TEST(Exception, constructor) 58 | { 59 | const char msg1[] = "some error"; 60 | std::string msg2 = "another error"; 61 | { 62 | const SQLite::Exception ex(msg1); 63 | EXPECT_STREQ(ex.what(), msg1); 64 | EXPECT_EQ(ex.getErrorCode(), -1); 65 | EXPECT_EQ(ex.getExtendedErrorCode(), -1); 66 | EXPECT_STREQ("unknown error", ex.getErrorStr()); 67 | } 68 | { 69 | const SQLite::Exception ex(msg2); 70 | EXPECT_STREQ(ex.what(), msg2.c_str()); 71 | EXPECT_EQ(ex.getErrorCode(), -1); 72 | EXPECT_EQ(ex.getExtendedErrorCode(), -1); 73 | EXPECT_STREQ("unknown error", ex.getErrorStr()); 74 | } 75 | { 76 | const SQLite::Exception ex(msg1, 1); 77 | EXPECT_STREQ(ex.what(), msg1); 78 | EXPECT_EQ(ex.getErrorCode(), 1); 79 | EXPECT_EQ(ex.getExtendedErrorCode(), -1); 80 | EXPECT_STREQ(ex.getErrorStr(), "SQL logic error"); 81 | } 82 | { 83 | const SQLite::Exception ex(msg2, 2); 84 | EXPECT_STREQ(ex.what(), msg2.c_str()); 85 | EXPECT_EQ(ex.getErrorCode(), 2); 86 | EXPECT_EQ(ex.getExtendedErrorCode(), -1); 87 | EXPECT_STREQ(ex.getErrorStr(), "unknown error"); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /tests/ExecuteMany_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file VariadicBind_test.cpp 3 | * @ingroup tests 4 | * @brief Test of variadic bind 5 | * 6 | * Copyright (c) 2019 Maximilian Bachmann (contact@maxbachmann.de) 7 | * Copyright (c) 2019-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 8 | * 9 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 10 | * or copy at http://opensource.org/licenses/MIT) 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | 21 | #if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015 22 | TEST(ExecuteMany, invalid) 23 | { 24 | // Create a new database 25 | SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 26 | 27 | EXPECT_EQ(0, db.exec("DROP TABLE IF EXISTS test")); 28 | EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT DEFAULT 'default')")); 29 | EXPECT_TRUE(db.tableExists("test")); 30 | { 31 | execute_many(db, "INSERT INTO test VALUES (?, ?)", 32 | 1, 33 | std::make_tuple(2), 34 | std::make_tuple(3, "three") 35 | ); 36 | } 37 | // make sure the content is as expected 38 | { 39 | SQLite::Statement query(db, std::string{"SELECT id, value FROM test ORDER BY id"}); 40 | std::vector > results; 41 | while (query.executeStep()) 42 | { 43 | const int id = query.getColumn(0); 44 | std::string value = query.getColumn(1); 45 | results.emplace_back( id, std::move(value) ); 46 | } 47 | EXPECT_EQ(std::size_t(3), results.size()); 48 | 49 | EXPECT_EQ(std::make_pair(1,std::string{""}), results.at(0)); 50 | EXPECT_EQ(std::make_pair(2,std::string{""}), results.at(1)); 51 | EXPECT_EQ(std::make_pair(3,std::string{"three"}), results.at(2)); 52 | } 53 | } 54 | #endif // c++14 55 | -------------------------------------------------------------------------------- /tests/Savepoint_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Savepoint_test.cpp 3 | * @ingroup tests 4 | * @brief Test of a SQLite Savepoint. 5 | * 6 | * Copyright (c) 2020 Kelvin Hammond (hammond.kelvin@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt or 9 | * copy at http://opensource.org/licenses/MIT) 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | TEST(Savepoint, commitRollback) 22 | { 23 | // Create a new database 24 | SQLite::Database db(":memory:", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); 25 | EXPECT_EQ(SQLite::OK, db.getErrorCode()); 26 | 27 | { 28 | // Begin savepoint 29 | SQLite::Savepoint savepoint(db, "sp1"); 30 | 31 | EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)")); 32 | EXPECT_EQ(SQLite::OK, db.getErrorCode()); 33 | 34 | // Insert a first value 35 | EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, 'first')")); 36 | EXPECT_EQ(1, db.getLastInsertRowid()); 37 | 38 | // release savepoint 39 | savepoint.release(); 40 | 41 | // Commit again throw an exception 42 | EXPECT_THROW(savepoint.release(), SQLite::Exception); 43 | EXPECT_THROW(savepoint.rollback(), SQLite::Exception); 44 | } 45 | 46 | // Auto rollback if no release() before the end of scope 47 | { 48 | // Begin savepoint 49 | SQLite::Savepoint savepoint(db, "sp2"); 50 | 51 | // Insert a second value (that will be rollbacked) 52 | EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, 'third')")); 53 | EXPECT_EQ(2, db.getLastInsertRowid()); 54 | 55 | // end of scope: automatic rollback 56 | } 57 | 58 | // Auto rollback of a transaction on error / exception 59 | try 60 | { 61 | // Begin savepoint 62 | SQLite::Savepoint savepoint(db, "sp3"); 63 | 64 | // Insert a second value (that will be rollbacked) 65 | EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, 'second')")); 66 | EXPECT_EQ(2, db.getLastInsertRowid()); 67 | 68 | // Execute with an error => exception with auto-rollback 69 | db.exec("DesiredSyntaxError to raise an exception to rollback the transaction"); 70 | 71 | GTEST_FATAL_FAILURE_("we should never get there"); 72 | savepoint.release(); // We should never get there 73 | } 74 | catch (std::exception& e) 75 | { 76 | std::cout << "SQLite exception: " << e.what() << std::endl; 77 | // expected error, see above 78 | } 79 | 80 | // Double rollback with a manual command before the end of scope 81 | { 82 | // Begin savepoint 83 | SQLite::Savepoint savepoint(db, "sp4"); 84 | 85 | // Insert a second value (that will be rollbacked) 86 | EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, 'third')")); 87 | EXPECT_EQ(2, db.getLastInsertRowid()); 88 | 89 | // Execute a manual rollback 90 | savepoint.rollback(); 91 | 92 | // end of scope: the automatic rollback should not raise an error because it is harmless 93 | } 94 | 95 | // Check the results (expect only one row of result, as all other one have 96 | // been rollbacked) 97 | SQLite::Statement query(db, "SELECT * FROM test"); 98 | int nbRows = 0; 99 | while (query.executeStep()) 100 | { 101 | nbRows++; 102 | EXPECT_EQ(1, query.getColumn(0).getInt()); 103 | EXPECT_STREQ("first", query.getColumn(1).getText()); 104 | } 105 | EXPECT_EQ(1, nbRows); 106 | } 107 | -------------------------------------------------------------------------------- /tests/Transaction_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Transaction_test.cpp 3 | * @ingroup tests 4 | * @brief Test of a SQLite Transaction. 5 | * 6 | * Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | 21 | TEST(Transaction, commitRollback) 22 | { 23 | // Create a new database 24 | SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 25 | EXPECT_EQ(SQLite::OK, db.getErrorCode()); 26 | 27 | { 28 | // Begin transaction 29 | SQLite::Transaction transaction(db); 30 | 31 | EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)")); 32 | EXPECT_EQ(SQLite::OK, db.getErrorCode()); 33 | 34 | // Insert a first value 35 | EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, 'first')")); 36 | EXPECT_EQ(1, db.getLastInsertRowid()); 37 | 38 | // Commit transaction 39 | transaction.commit(); 40 | 41 | // Commit again throw an exception 42 | EXPECT_THROW(transaction.commit(), SQLite::Exception); 43 | } 44 | 45 | // ensure transactions with different types are well-formed 46 | { 47 | for (auto behavior : { 48 | SQLite::TransactionBehavior::DEFERRED, 49 | SQLite::TransactionBehavior::IMMEDIATE, 50 | SQLite::TransactionBehavior::EXCLUSIVE }) 51 | { 52 | SQLite::Transaction transaction(db, behavior); 53 | transaction.commit(); 54 | } 55 | 56 | EXPECT_THROW(SQLite::Transaction(db, static_cast(-1)), SQLite::Exception); 57 | } 58 | 59 | // Auto rollback if no commit() before the end of scope 60 | { 61 | // Begin transaction 62 | SQLite::Transaction transaction(db); 63 | 64 | // Insert a second value (that will be rollbacked) 65 | EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, 'third')")); 66 | EXPECT_EQ(2, db.getLastInsertRowid()); 67 | 68 | // end of scope: automatic rollback 69 | } 70 | 71 | // Auto rollback of a transaction on error/exception 72 | try 73 | { 74 | // Begin transaction 75 | SQLite::Transaction transaction(db); 76 | 77 | // Insert a second value (that will be rollbacked) 78 | EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, 'second')")); 79 | EXPECT_EQ(2, db.getLastInsertRowid()); 80 | 81 | // Execute with an error => exception with auto-rollback 82 | db.exec("DesiredSyntaxError to raise an exception to rollback the transaction"); 83 | 84 | GTEST_FATAL_FAILURE_("we should never get there"); 85 | transaction.commit(); // We should never get there 86 | } 87 | catch (std::exception& e) 88 | { 89 | std::cout << "SQLite exception: " << e.what() << std::endl; 90 | // expected error, see above 91 | } 92 | 93 | // Double rollback with a manual command before the end of scope 94 | { 95 | // Begin transaction 96 | SQLite::Transaction transaction(db); 97 | 98 | // Insert a second value (that will be rollbacked) 99 | EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, 'third')")); 100 | EXPECT_EQ(2, db.getLastInsertRowid()); 101 | 102 | // Execute a manual rollback 103 | transaction.rollback(); 104 | 105 | // end of scope: the automatic rollback should not raise an error because it is harmless 106 | } 107 | 108 | // Check the results (expect only one row of result, as all other one have been rollbacked) 109 | SQLite::Statement query(db, "SELECT * FROM test"); 110 | int nbRows = 0; 111 | while (query.executeStep()) 112 | { 113 | nbRows++; 114 | EXPECT_EQ(1, query.getColumn(0).getInt()); 115 | EXPECT_STREQ("first", query.getColumn(1).getText()); 116 | } 117 | EXPECT_EQ(1, nbRows); 118 | } 119 | -------------------------------------------------------------------------------- /tests/VariadicBind_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file VariadicBind_test.cpp 3 | * @ingroup tests 4 | * @brief Test of variadic bind 5 | * 6 | * Copyright (c) 2016 Paul Dreik (github@pauldreik.se) 7 | * Copyright (c) 2016-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 8 | * Copyright (c) 2019 Maximilian Bachmann (github@maxbachmann) 9 | * 10 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 11 | * or copy at http://opensource.org/licenses/MIT) 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include 21 | 22 | #if (__cplusplus >= 201103L) || ( defined(_MSC_VER) && (_MSC_VER >= 1800) ) // c++11: Visual Studio 2013 23 | TEST(VariadicBind, invalid) 24 | { 25 | // Create a new database 26 | SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); 27 | 28 | EXPECT_EQ(0, db.exec("DROP TABLE IF EXISTS test")); 29 | EXPECT_EQ(0, 30 | db.exec( 31 | "CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT DEFAULT 'default') ")); 32 | EXPECT_EQ(0, 33 | db.exec( 34 | "CREATE TABLE test2 (id INTEGER PRIMARY KEY, value TEXT DEFAULT 'default') ")); 35 | EXPECT_TRUE(db.tableExists("test")); 36 | EXPECT_TRUE(db.tableExists("test2")); 37 | 38 | { 39 | SQLite::Statement query(db, "INSERT INTO test VALUES (?, ?)"); 40 | 41 | // bind one argument less than expected - should be fine. 42 | // the unspecified argument should be set to null, not the default. 43 | SQLite::bind(query, 1); 44 | EXPECT_EQ(1, query.exec()); 45 | query.reset(); 46 | 47 | // bind all arguments - should work just fine 48 | SQLite::bind(query, 2, "two"); 49 | EXPECT_EQ(1, query.exec()); 50 | query.reset(); 51 | 52 | // bind too many arguments - should throw. 53 | EXPECT_THROW(SQLite::bind(query, 3, "three", 0), SQLite::Exception); 54 | EXPECT_EQ(1, query.exec()); 55 | } 56 | // make sure the content is as expected 57 | { 58 | SQLite::Statement query(db, std::string{"SELECT id, value FROM test ORDER BY id"}); 59 | std::vector > results; 60 | while (query.executeStep()) 61 | { 62 | const int id = query.getColumn(0); 63 | std::string value = query.getColumn(1); 64 | results.emplace_back( id, std::move(value) ); 65 | } 66 | EXPECT_EQ(std::size_t(3), results.size()); 67 | 68 | EXPECT_EQ(std::make_pair(1,std::string{""}), results.at(0)); 69 | EXPECT_EQ(std::make_pair(2,std::string{"two"}), results.at(1)); 70 | EXPECT_EQ(std::make_pair(3,std::string{"three"}), results.at(2)); 71 | } 72 | #if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015 73 | { 74 | SQLite::Statement query(db, "INSERT INTO test2 VALUES (?, ?)"); 75 | 76 | // bind one argument less than expected - should be fine. 77 | // the unspecified argument should be set to null, not the default. 78 | SQLite::bind(query, std::make_tuple(1)); 79 | EXPECT_EQ(1, query.exec()); 80 | query.reset(); 81 | 82 | // bind all arguments - should work just fine 83 | SQLite::bind(query, std::make_tuple(2, "two")); 84 | EXPECT_EQ(1, query.exec()); 85 | query.reset(); 86 | 87 | // bind too many arguments - should throw. 88 | EXPECT_THROW(SQLite::bind(query, std::make_tuple(3, "three", 0)), SQLite::Exception); 89 | EXPECT_EQ(1, query.exec()); 90 | } 91 | // make sure the content is as expected 92 | { 93 | SQLite::Statement query(db, std::string{"SELECT id, value FROM test2 ORDER BY id"}); 94 | std::vector > results; 95 | while (query.executeStep()) 96 | { 97 | const int id = query.getColumn(0); 98 | std::string value = query.getColumn(1); 99 | results.emplace_back( id, std::move(value) ); 100 | } 101 | EXPECT_EQ(std::size_t(3), results.size()); 102 | 103 | EXPECT_EQ(std::make_pair(1,std::string{""}), results.at(0)); 104 | EXPECT_EQ(std::make_pair(2,std::string{"two"}), results.at(1)); 105 | EXPECT_EQ(std::make_pair(3,std::string{"three"}), results.at(2)); 106 | } 107 | #endif // c++14 108 | } 109 | #endif // c++11 110 | --------------------------------------------------------------------------------