pax_global_header00006660000000000000000000000064145331450570014521gustar00rootroot0000000000000052 comment=6e3009973025e0016d5573529067714201338c80 sqlite_modern_cpp-dev/000077500000000000000000000000001453314505700153505ustar00rootroot00000000000000sqlite_modern_cpp-dev/.gitignore000066400000000000000000000001001453314505700173270ustar00rootroot00000000000000 .deps config.log config.status compile_commands.json build/ sqlite_modern_cpp-dev/.travis.yml000066400000000000000000000010161453314505700174570ustar00rootroot00000000000000language: cpp sudo: required dist: trusty cache: apt: true directories: - /home/travis/.hunter/ addons: apt: sources: - ubuntu-toolchain-r-test - george-edison55-precise-backports packages: - g++-5 - libsqlcipher-dev - cmake before_install: - export CXX="g++-5" CC="gcc-5" script: mkdir build && cd ./build && cmake .. && cmake --build . && ctest . # TODO: fix sqlcipher test # script: mkdir build && cd ./build && cmake -DENABLE_SQLCIPHER_TESTS=ON .. && make && ./tests sqlite_modern_cpp-dev/.ycm_extra_conf.py000066400000000000000000000057611453314505700210110ustar00rootroot00000000000000import os import ycm_core from clang_helpers import PrepareClangFlags def DirectoryOfThisScript(): return os.path.dirname(os.path.abspath(__file__)) # This is the single most important line in this script. Everything else is just nice to have but # not strictly necessary. compilation_database_folder = DirectoryOfThisScript() # This provides a safe fall-back if no compilation commands are available. You could also add a # includes relative to your project directory, for example. flags = [ '-Wall', '-std=c++14', '-x', 'c++', '-isystem', '/usr/local/include', '-isystem', '/usr/include', '-I.', ] database = ycm_core.CompilationDatabase(compilation_database_folder) SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ] def MakeRelativePathsInFlagsAbsolute( flags, working_directory ): if not working_directory: return list( flags ) new_flags = [] make_next_absolute = False path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ] for flag in flags: new_flag = flag if make_next_absolute: make_next_absolute = False if not flag.startswith( '/' ): new_flag = os.path.join( working_directory, flag ) for path_flag in path_flags: if flag == path_flag: make_next_absolute = True break if flag.startswith( path_flag ): path = flag[ len( path_flag ): ] new_flag = path_flag + os.path.join( working_directory, path ) break if new_flag: new_flags.append( new_flag ) return new_flags def IsHeaderFile( filename ): extension = os.path.splitext( filename )[ 1 ] return extension in [ '.h', '.hxx', '.hpp', '.hh' ] def GetCompilationInfoForFile( filename ): # The compilation_commands.json file generated by CMake does not have entries # for header files. So we do our best by asking the db for flags for a # corresponding source file, if any. If one exists, the flags for that file # should be good enough. if IsHeaderFile( filename ): basename = os.path.splitext( filename )[ 0 ] for extension in SOURCE_EXTENSIONS: replacement_file = basename + extension if os.path.exists( replacement_file ): compilation_info = database.GetCompilationInfoForFile( replacement_file ) if compilation_info.compiler_flags_: return compilation_info return None return database.GetCompilationInfoForFile( filename ) def FlagsForFile( filename, **kwargs ): if database: # Bear in mind that compilation_info.compiler_flags_ does NOT return a # python list, but a "list-like" StringVec object compilation_info = GetCompilationInfoForFile( filename ) if not compilation_info: return None final_flags = MakeRelativePathsInFlagsAbsolute( compilation_info.compiler_flags_, compilation_info.compiler_working_dir_ ) else: relative_to = DirectoryOfThisScript() final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to ) return { 'flags': final_flags, 'do_cache': True } sqlite_modern_cpp-dev/CMakeLists.txt000066400000000000000000000036471453314505700201220ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.0) option(ENABLE_SQLCIPHER_TESTS "enable sqlchipher test") # Creates the file compile_commands.json in the build directory. set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") set(HUNTER_TLS_VERIFY ON) include("cmake/HunterGate.cmake") include("cmake/Catch.cmake") HunterGate( URL "https://github.com/cpp-pm/hunter/archive/v0.24.15.tar.gz" SHA1 "8010d63d5ae611c564889d5fe12d3cb7a45703ac" ) project(SqliteModernCpp) hunter_add_package(Catch) hunter_add_package(sqlite3) find_package(Catch2 CONFIG REQUIRED) find_package(sqlite3 CONFIG REQUIRED) set(TEST_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tests) file(GLOB TEST_SOURCES ${TEST_SOURCE_DIR}/*.cc) IF(NOT ENABLE_SQLCIPHER_TESTS) list(REMOVE_ITEM TEST_SOURCES ${TEST_SOURCE_DIR}/sqlcipher.cc) ENDIF(NOT ENABLE_SQLCIPHER_TESTS) enable_testing() add_library (sqlite_modern_cpp INTERFACE) target_include_directories(sqlite_modern_cpp INTERFACE hdr/) add_executable(tests_runner ${TEST_SOURCES}) target_include_directories(tests_runner INTERFACE ${SQLITE3_INCLUDE_DIRS}) if(ENABLE_SQLCIPHER_TESTS) target_link_libraries(tests_runner Catch2::Catch2 sqlite_modern_cpp sqlite3::sqlite3 -lsqlcipher) else() target_link_libraries(tests_runner Catch2::Catch2 sqlite_modern_cpp sqlite3::sqlite3) endif() catch_discover_tests(tests_runner) target_compile_options(tests_runner PUBLIC $<$:/Zc:__cplusplus> ) # Place the file in the source directory, permitting us to place a single configuration file for YCM there. # YCM is the code-completion engine for (neo)vim https://github.com/Valloric/YouCompleteMe IF(EXISTS "${CMAKE_BINARY_DIR}/compile_commands.json") EXECUTE_PROCESS( COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/compile_commands.json ${CMAKE_SOURCE_DIR}/compile_commands.json ) ENDIF() sqlite_modern_cpp-dev/License.txt000066400000000000000000000020531453314505700174730ustar00rootroot00000000000000MIT License Copyright (c) 2017 aminroosta Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. sqlite_modern_cpp-dev/README.md000066400000000000000000000404401453314505700166310ustar00rootroot00000000000000[![Build Status](https://drone.io/github.com/aminroosta/sqlite_modern_cpp/status.png)](https://drone.io/github.com/aminroosta/sqlite_modern_cpp/latest) sqlite modern cpp wrapper ==== This library is a lightweight modern wrapper around sqlite C api . ```c++ #include #include using namespace sqlite; using namespace std; int main() { try { // creates a database file 'dbfile.db' if it does not exists. database db("dbfile.db"); // executes the query and creates a 'user' table db << "create table if not exists user (" " _id integer primary key autoincrement not null," " age int," " name text," " weight real" ");"; // inserts a new user record. // binds the fields to '?' . // note that only types allowed for bindings are : // int ,long, long long, float, double // string, u16string // sqlite3 only supports utf8 and utf16 strings, you should use std::string for utf8 and std::u16string for utf16. // If you're using C++17, `std::string_view` and `std::u16string_view` can be used as string types. // note that u"my text" is a utf16 string literal of type char16_t * . db << "insert into user (age,name,weight) values (?,?,?);" << 20 << u"bob" << 83.25; int age = 21; float weight = 68.5; string name = "jack"; db << u"insert into user (age,name,weight) values (?,?,?);" // utf16 query string << age << name << weight; cout << "The new record got assigned id " << db.last_insert_rowid() << endl; // selects from user table on a condition ( age > 18 ) and executes // the lambda for each row returned . db << "select age,name,weight from user where age > ? ;" << 18 >> [&](int age, string name, double weight) { cout << age << ' ' << name << ' ' << weight << endl; }; // a for loop can be used too: // with named variables for(auto &&row : db << "select age,name,weight from user where age > ? ;" << 18) { int age; string name; double weight; row >> age >> name >> weight; cout << age << ' ' << name << ' ' << weight << endl; } // or with a tuple for(tuple row : db << "select age,name,weight from user where age > ? ;" << 18) { cout << get<0>(row) << ' ' << get<1>(row) << ' ' << get<2>(row) << endl; } // selects the count(*) from user table // note that you can extract a single column single row result only to : int,long,long long,float,double,string,u16string int count = 0; db << "select count(*) from user" >> count; cout << "cout : " << count << endl; // you can also extract multiple column rows db << "select age, name from user where _id=1;" >> tie(age, name); cout << "Age = " << age << ", name = " << name << endl; // this also works and the returned value will be automatically converted to string string str_count; db << "select count(*) from user" >> str_count; cout << "scount : " << str_count << endl; } catch (const exception& e) { cout << e.what() << endl; } } ``` You can not execute multiple statements separated by semicolons in one go. Additional flags ---- You can pass additional open flags to SQLite by using a config object: ```c++ sqlite_config config; config.flags = OpenFlags::READONLY database db("some_db", config); int a; // Now you can only read from db auto ps = db << "select a from table where something = ? and anotherthing = ?" >> a; config.flags = OpenFlags::READWRITE | OpenFlags::CREATE; // This is the default config.encoding = Encoding::UTF16; // The encoding is respected only if you create a new database database db2("some_db2", config); // If some_db2 didn't exists before, it will be created with UTF-16 encoding. ``` Prepared Statements ---- It is possible to retain and reuse statments this will keep the query plan and in case of an complex query or many uses might increase the performance significantly. ```c++ database db(":memory:"); // if you use << on a sqlite::database you get a prepared statment back // this will not be executed till it gets destroyed or you execute it explicitly auto ps = db << "select a,b from table where something = ? and anotherthing = ?"; // get a prepared parsed and ready statment // first if needed bind values to it ps << 5; int tmp = 8; ps << tmp; // now you can execute it with `operator>>` or `execute()`. // If the statement was executed once it will not be executed again when it goes out of scope. // But beware that it will execute on destruction if it wasn't executed! ps >> [&](int a,int b){ ... }; // after a successfull execution the statment can be executed again, but the bound values are resetted. // If you dont need the returned values you can execute it like this ps.execute(); // or like this ps++; // To disable the execution of a statment when it goes out of scope and wasn't used ps.used(true); // or false if you want it to execute even if it was used // Usage Example: auto ps = db << "insert into complex_table_with_lots_of_indices values (?,?,?)"; int i = 0; while( i < 100000 ){ ps << long_list[i++] << long_list[i++] << long_list[i++]; ps++; } ``` Shared Connections ---- If you need the handle to the database connection to execute sqlite3 commands directly you can get a managed shared_ptr to it, so it will not close as long as you have a referenc to it. Take this example on how to deal with a database backup using SQLITEs own functions in a safe and modern way. ```c++ try { database backup("backup"); //Open the database file we want to backup to auto con = db.connection(); // get a handle to the DB we want to backup in our scope // this way we are sure the DB is open and ok while we backup // Init Backup and make sure its freed on exit or exceptions! auto state = std::unique_ptr( sqlite3_backup_init(backup.connection().get(), "main", con.get(), "main"), sqlite3_backup_finish ); if(state) { int rc; // Each iteration of this loop copies 500 database pages from database db to the backup database. do { rc = sqlite3_backup_step(state.get(), 500); std::cout << "Remaining " << sqlite3_backup_remaining(state.get()) << "/" << sqlite3_backup_pagecount(state.get()) << "\n"; } while(rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED); } } // Release allocated resources. ``` Transactions ---- You can use transactions with `begin;`, `commit;` and `rollback;` commands. ```c++ db << "begin;"; // begin a transaction ... db << "insert into user (age,name,weight) values (?,?,?);" << 20 << u"bob" << 83.25f; db << "insert into user (age,name,weight) values (?,?,?);" // utf16 string << 21 << u"jack" << 68.5; db << "commit;"; // commit all the changes. db << "begin;"; // begin another transaction .... db << "insert into user (age,name,weight) values (?,?,?);" // utf16 string << 19 << u"chirs" << 82.7; db << "rollback;"; // cancel this transaction ... ``` Blob ---- Use `std::vector` to store and retrieve blob data. `T` could be `char,short,int,long,long long, float or double`. ```c++ db << "CREATE TABLE person (name TEXT, numbers BLOB);"; db << "INSERT INTO person VALUES (?, ?)" << "bob" << vector { 1, 2, 3, 4}; db << "INSERT INTO person VALUES (?, ?)" << "sara" << vector { 1.0, 2.0, 3.0, 4.0}; vector numbers_bob; db << "SELECT numbers from person where name = ?;" << "bob" >> numbers_bob; db << "SELECT numbers from person where name = ?;" << "sara" >> [](vector numbers_sara){ for(auto e : numbers_sara) cout << e << ' '; cout << endl; }; ``` NULL values ---- If you have databases where some rows may be null, you can use `std::unique_ptr` to retain the NULL values between C++ variables and the database. ```c++ db << "CREATE TABLE tbl (id integer,age integer, name string, img blob);"; db << "INSERT INTO tbl VALUES (?, ?, ?, ?);" << 1 << 24 << "bob" << vector { 1, 2 , 3}; unique_ptr ptr_null; // you can even bind empty unique_ptr db << "INSERT INTO tbl VALUES (?, ?, ?, ?);" << 2 << nullptr << ptr_null << nullptr; db << "select age,name,img from tbl where id = 1" >> [](unique_ptr age_p, unique_ptr name_p, unique_ptr> img_p) { if(age_p == nullptr || name_p == nullptr || img_p == nullptr) { cerr << "ERROR: values should not be null" << std::endl; } cout << "age:" << *age_p << " name:" << *name_p << " img:"; for(auto i : *img_p) cout << i << ","; cout << endl; }; db << "select age,name,img from tbl where id = 2" >> [](unique_ptr age_p, unique_ptr name_p, unique_ptr> img_p) { if(age_p != nullptr || name_p != nullptr || img_p != nullptr) { cerr << "ERROR: values should be nullptr" << std::endl; exit(EXIT_FAILURE); } cout << "OK all three values are nullptr" << endl; }; ``` SQLCipher ---- We have native support for [SQLCipher](https://www.zetetic.net/sqlcipher/). If you want to use encrypted databases, include the `sqlite_moder_cpp/sqlcipher.h` header. Then create a `sqlcipher_database` instead. ```c++ #include #include using namespace sqlite; using namespace std; int main() { try { // creates a database file 'dbfile.db' if it does not exists with password 'secret' sqlcipher_config config; config.key = secret; sqlcipher_database db("dbfile.db", config); // executes the query and creates a 'user' table db << "create table if not exists user (" " _id integer primary key autoincrement not null," " age int," " name text," " weight real" ");"; // More queries ... db.rekey("new_secret"); // Change the password of the already encrypted database. // Even more queries .. } catch (const exception& e) { cout << e.what() << endl; } } ``` NULL values (C++17) ---- You can use `std::optional` as an alternative for `std::unique_ptr` to work with NULL values. ```c++ #include struct User { long long _id; std::optional age; std::optional name; std::optional weight; }; int main() { User user; user.name = "bob"; // Same database as above database db("dbfile.db"); // Here, age and weight will be inserted as NULL in the database. db << "insert into user (age,name,weight) values (?,?,?);" << user.age << user.name << user.weight; user._id = db.last_insert_rowid(); // Here, the User instance will retain the NULL value(s) from the database. db << "select _id,age,name,weight from user where age > ? ;" << 18 >> [&](long long id, std::optional age, std::optional name std::optional weight) { cout << "id=" << _id << " age = " << (age ? to_string(*age) ? string("NULL")) << " name = " << (name ? *name : string("NULL")) << " weight = " << (weight ? to_string(*weight) : string(NULL)) << endl; }; } ``` If the optional library is not available, the experimental/optional one will be used instead. Variant type support (C++17) ---- If your columns may have flexible types, you can use C++17's `std::variant` to extract the value. ```c++ db << "CREATE TABLE tbl (id integer, data);"; db << "INSERT INTO tbl VALUES (?, ?);" << 1 << vector { 1, 2, 3}; db << "INSERT INTO tbl VALUES (?, ?);" << 2 << 2.5; db << "select data from tbl where id = 1" >> [](std::variant, double> data) { if(data.index() != 1) { cerr << "ERROR: we expected a blob" << std::endl; } for(auto i : get>(data)) cout << i << ","; cout << endl; }; db << "select data from tbl where id = 2" >> [](std::variant, double> data) { if(data.index() != 2) { cerr << "ERROR: we expected a real number" << std::endl; } cout << get(data) << endl; }; ``` If you read a specific type and this type does not match the actual type in the SQlite database, yor data will be converted. This does not happen if you use a `variant`. If the `variant` does an alternative of the same value type, an `mismatch` exception will be thrown. The value types are NULL, integer, real number, text and BLOB. To support all possible values, you can use `variant`. It is also possible to use a variant with `std::monostate` in order to catch null values. Errors ---- On error, the library throws an error class indicating the type of error. The error classes are derived from the SQLITE3 error names, so if the error code is SQLITE_CONSTRAINT, the error class thrown is sqlite::errors::constraint. SQLite3 extended error names are supported too. So there is e.g. a class sqlite::errors::constraint_primarykey derived from sqlite::errors::constraint. Note that all errors are derived from sqlite::sqlite_exception and that itself is derived from std::runtime_exception. sqlite::sqlite_exception has a `get_code()` member function to get the SQLITE3 error code or `get_extended_code()` to get the extended error code. Additionally you can use `get_sql()` to see the SQL statement leading to the error. ```c++ database db(":memory:"); db << "create table person (id integer primary key not null, name text);"; try { db << "insert into person (id, name) values (?,?)" << 1 << "jack"; // inserting again to produce error db << "insert into person (id, name) values (?,?)" << 1 << "jack"; } /* if you are trying to catch all sqlite related exceptions * make sure to catch them by reference */ catch (const sqlite_exception& e) { cerr << e.get_code() << ": " << e.what() << " during " << e.get_sql() << endl; } /* you can catch specific exceptions as well, catch(const sqlite::errors::constraint &e) { } */ /* and even more specific exceptions catch(const sqlite::errors::constraint_primarykey &e) { } */ ``` You can also register a error logging function with `sqlite::error_log`. The `` header has to be included to make this function available. The call to `sqlite::error_log` has to be the first call to any `sqlite_modern_cpp` function by your program. ```c++ error_log( [&](sqlite_exception& e) { cerr << e.get_code() << ": " << e.what() << endl; }, [&](errors::misuse& e) { /* You can behave differently to specific errors */ } ); database db(":memory:"); db << "create table person (id integer primary key not null, name text);"; try { db << "insert into person (id, name) values (?,?)" << 1 << "jack"; // inserting again to produce error db << "insert into person (id, name) values (?,?)" << 1 << "jack"; } catch (const sqlite_exception& e) {} ``` Custom SQL functions ---- To extend SQLite with custom functions, you just implement them in C++: ```c++ database db(":memory:"); db.define("tgamma", [](double i) {return std::tgamma(i);}); db << "CREATE TABLE numbers (number INTEGER);"; for(auto i=0; i!=10; ++i) db << "INSERT INTO numbers VALUES (?);" << i; db << "SELECT number, tgamma(number+1) FROM numbers;" >> [](double number, double factorial) { cout << number << "! = " << factorial << '\n'; }; ``` NDK support ---- Just Make sure you are using the full path of your database file : `sqlite::database db("/data/data/com.your.package/dbfile.db")`. Installation ---- The project is header only. Simply point your compiler at the hdr/ directory. Contributing ---- Install cmake and build the project. Dependencies will be installed automatically (using hunter). ```bash mkdir build cd ./build cmake .. make ``` Breaking Changes ---- See breaking changes documented in each [Release](https://github.com/aminroosta/sqlite_modern_cpp/releases). Package managers ---- Pull requests are welcome :wink: - [AUR](https://aur.archlinux.org/packages/sqlite_modern_cpp/) Arch Linux - maintainer [Nissar Chababy](https://github.com/funilrys) - Nuget (TODO [nuget.org](https://www.nuget.org/)) - Conan (TODO [conan.io](https://conan.io/)) - [vcpkg](https://github.com/Microsoft/vcpkg) ## License MIT license - [http://www.opensource.org/licenses/mit-license.php](http://www.opensource.org/licenses/mit-license.php) sqlite_modern_cpp-dev/cmake/000077500000000000000000000000001453314505700164305ustar00rootroot00000000000000sqlite_modern_cpp-dev/cmake/.DS_Store000066400000000000000000000140041453314505700201120ustar00rootroot00000000000000Bud1%  @€ @€ @€ @ E%DSDB`€ @€ @€ @sqlite_modern_cpp-dev/cmake/Catch.cmake000066400000000000000000000156241453314505700204640ustar00rootroot00000000000000# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #[=======================================================================[.rst: Catch ----- This module defines a function to help use the Catch test framework. The :command:`catch_discover_tests` discovers tests by asking the compiled test executable to enumerate its tests. This does not require CMake to be re-run when tests change. However, it may not work in a cross-compiling environment, and setting test properties is less convenient. This command is intended to replace use of :command:`add_test` to register tests, and will create a separate CTest test for each Catch test case. Note that this is in some cases less efficient, as common set-up and tear-down logic cannot be shared by multiple test cases executing in the same instance. However, it provides more fine-grained pass/fail information to CTest, which is usually considered as more beneficial. By default, the CTest test name is the same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``. .. command:: catch_discover_tests Automatically add tests with CTest by querying the compiled test executable for available tests:: catch_discover_tests(target [TEST_SPEC arg1...] [EXTRA_ARGS arg1...] [WORKING_DIRECTORY dir] [TEST_PREFIX prefix] [TEST_SUFFIX suffix] [PROPERTIES name1 value1...] [TEST_LIST var] ) ``catch_discover_tests`` sets up a post-build command on the test executable that generates the list of tests by parsing the output from running the test with the ``--list-test-names-only`` argument. This ensures that the full list of tests is obtained. Since test discovery occurs at build time, it is not necessary to re-run CMake when the list of tests changes. However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set in order to function in a cross-compiling environment. Additionally, setting properties on tests is somewhat less convenient, since the tests are not available at CMake time. Additional test properties may be assigned to the set of tests as a whole using the ``PROPERTIES`` option. If more fine-grained test control is needed, custom content may be provided through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES` directory property. The set of discovered tests is made accessible to such a script via the ``_TESTS`` variable. The options are: ``target`` Specifies the Catch executable, which must be a known CMake executable target. CMake will substitute the location of the built executable when running the test. ``TEST_SPEC arg1...`` Specifies test cases, wildcarded test cases, tags and tag expressions to pass to the Catch executable with the ``--list-test-names-only`` argument. ``EXTRA_ARGS arg1...`` Any extra arguments to pass on the command line to each test case. ``WORKING_DIRECTORY dir`` Specifies the directory in which to run the discovered test cases. If this option is not provided, the current binary directory is used. ``TEST_PREFIX prefix`` Specifies a ``prefix`` to be prepended to the name of each discovered test case. This can be useful when the same test executable is being used in multiple calls to ``catch_discover_tests()`` but with different ``TEST_SPEC`` or ``EXTRA_ARGS``. ``TEST_SUFFIX suffix`` Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may be specified. ``PROPERTIES name1 value1...`` Specifies additional properties to be set on all tests discovered by this invocation of ``catch_discover_tests``. ``TEST_LIST var`` Make the list of tests available in the variable ``var``, rather than the default ``_TESTS``. This can be useful when the same test executable is being used in multiple calls to ``catch_discover_tests()``. Note that this variable is only available in CTest. #]=======================================================================] #------------------------------------------------------------------------------ function(catch_discover_tests TARGET) cmake_parse_arguments( "" "" "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST" "TEST_SPEC;EXTRA_ARGS;PROPERTIES" ${ARGN} ) if(NOT _WORKING_DIRECTORY) set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") endif() if(NOT _TEST_LIST) set(_TEST_LIST ${TARGET}_TESTS) endif() ## Generate a unique name based on the extra arguments string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS}") string(SUBSTRING ${args_hash} 0 7 args_hash) # Define rule to generate test list for aforementioned test executable set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake") set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake") get_property(crosscompiling_emulator TARGET ${TARGET} PROPERTY CROSSCOMPILING_EMULATOR ) add_custom_command( TARGET ${TARGET} POST_BUILD BYPRODUCTS "${ctest_tests_file}" COMMAND "${CMAKE_COMMAND}" -D "TEST_TARGET=${TARGET}" -D "TEST_EXECUTABLE=$" -D "TEST_EXECUTOR=${crosscompiling_emulator}" -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}" -D "TEST_SPEC=${_TEST_SPEC}" -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}" -D "TEST_PROPERTIES=${_PROPERTIES}" -D "TEST_PREFIX=${_TEST_PREFIX}" -D "TEST_SUFFIX=${_TEST_SUFFIX}" -D "TEST_LIST=${_TEST_LIST}" -D "CTEST_FILE=${ctest_tests_file}" -P "${_CATCH_DISCOVER_TESTS_SCRIPT}" VERBATIM ) file(WRITE "${ctest_include_file}" "if(EXISTS \"${ctest_tests_file}\")\n" " include(\"${ctest_tests_file}\")\n" "else()\n" " add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n" "endif()\n" ) if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0") # Add discovered tests to directory TEST_INCLUDE_FILES set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}" ) else() # Add discovered tests as directory TEST_INCLUDE_FILE if possible get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET) if (NOT ${test_include_file_set}) set_property(DIRECTORY PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}" ) else() message(FATAL_ERROR "Cannot set more than one TEST_INCLUDE_FILE" ) endif() endif() endfunction() ############################################################################### set(_CATCH_DISCOVER_TESTS_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake ) sqlite_modern_cpp-dev/cmake/CatchAddTests.cmake000066400000000000000000000041041453314505700221070ustar00rootroot00000000000000# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. set(prefix "${TEST_PREFIX}") set(suffix "${TEST_SUFFIX}") set(spec ${TEST_SPEC}) set(extra_args ${TEST_EXTRA_ARGS}) set(properties ${TEST_PROPERTIES}) set(script) set(suite) set(tests) function(add_command NAME) set(_args "") foreach(_arg ${ARGN}) if(_arg MATCHES "[^-./:a-zA-Z0-9_]") set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument else() set(_args "${_args} ${_arg}") endif() endforeach() set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE) endfunction() # Run test executable to get list of available tests if(NOT EXISTS "${TEST_EXECUTABLE}") message(FATAL_ERROR "Specified test executable '${TEST_EXECUTABLE}' does not exist" ) endif() execute_process( COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-names-only OUTPUT_VARIABLE output RESULT_VARIABLE result ) # Catch --list-test-names-only reports the number of tests, so 0 is... surprising if(${result} EQUAL 0) message(WARNING "Test executable '${TEST_EXECUTABLE}' contains no tests!\n" ) elseif(${result} LESS 0) message(FATAL_ERROR "Error running test executable '${TEST_EXECUTABLE}':\n" " Result: ${result}\n" " Output: ${output}\n" ) endif() string(REPLACE "\n" ";" output "${output}") # Parse output foreach(line ${output}) # Test name; strip spaces to get just the name... string(REGEX REPLACE "^ +" "" test "${line}") # ...and add to script add_command(add_test "${prefix}${test}${suffix}" ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" "${test}" ${extra_args} ) add_command(set_tests_properties "${prefix}${test}${suffix}" PROPERTIES WORKING_DIRECTORY "${TEST_WORKING_DIR}" ${properties} ) list(APPEND tests "${prefix}${test}${suffix}") endforeach() # Create a list of all discovered tests, which users may use to e.g. set # properties on the tests add_command(set ${TEST_LIST} ${tests}) # Write CTest script file(WRITE "${CTEST_FILE}" "${script}") sqlite_modern_cpp-dev/cmake/HunterGate.cmake000066400000000000000000000411711453314505700215040ustar00rootroot00000000000000# Copyright (c) 2013-2017, Ruslan Baratov # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # This is a gate file to Hunter package manager. # Include this file using `include` command and add package you need, example: # # cmake_minimum_required(VERSION 3.0) # # include("cmake/HunterGate.cmake") # HunterGate( # URL "https://github.com/path/to/hunter/archive.tar.gz" # SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d" # ) # # project(MyProject) # # hunter_add_package(Foo) # hunter_add_package(Boo COMPONENTS Bar Baz) # # Projects: # * https://github.com/hunter-packages/gate/ # * https://github.com/ruslo/hunter option(HUNTER_ENABLED "Enable Hunter package manager support" ON) if(HUNTER_ENABLED) if(CMAKE_VERSION VERSION_LESS "3.0") message(FATAL_ERROR "At least CMake version 3.0 required for hunter dependency management." " Update CMake or set HUNTER_ENABLED to OFF.") endif() endif() include(CMakeParseArguments) # cmake_parse_arguments option(HUNTER_STATUS_PRINT "Print working status" ON) option(HUNTER_STATUS_DEBUG "Print a lot info" OFF) set(HUNTER_WIKI "https://github.com/ruslo/hunter/wiki") function(hunter_gate_status_print) foreach(print_message ${ARGV}) if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG) message(STATUS "[hunter] ${print_message}") endif() endforeach() endfunction() function(hunter_gate_status_debug) foreach(print_message ${ARGV}) if(HUNTER_STATUS_DEBUG) string(TIMESTAMP timestamp) message(STATUS "[hunter *** DEBUG *** ${timestamp}] ${print_message}") endif() endforeach() endfunction() function(hunter_gate_wiki wiki_page) message("------------------------------ WIKI -------------------------------") message(" ${HUNTER_WIKI}/${wiki_page}") message("-------------------------------------------------------------------") message("") message(FATAL_ERROR "") endfunction() function(hunter_gate_internal_error) message("") foreach(print_message ${ARGV}) message("[hunter ** INTERNAL **] ${print_message}") endforeach() message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") message("") hunter_gate_wiki("error.internal") endfunction() function(hunter_gate_fatal_error) cmake_parse_arguments(hunter "" "WIKI" "" "${ARGV}") string(COMPARE EQUAL "${hunter_WIKI}" "" have_no_wiki) if(have_no_wiki) hunter_gate_internal_error("Expected wiki") endif() message("") foreach(x ${hunter_UNPARSED_ARGUMENTS}) message("[hunter ** FATAL ERROR **] ${x}") endforeach() message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") message("") hunter_gate_wiki("${hunter_WIKI}") endfunction() function(hunter_gate_user_error) hunter_gate_fatal_error(${ARGV} WIKI "error.incorrect.input.data") endfunction() function(hunter_gate_self root version sha1 result) string(COMPARE EQUAL "${root}" "" is_bad) if(is_bad) hunter_gate_internal_error("root is empty") endif() string(COMPARE EQUAL "${version}" "" is_bad) if(is_bad) hunter_gate_internal_error("version is empty") endif() string(COMPARE EQUAL "${sha1}" "" is_bad) if(is_bad) hunter_gate_internal_error("sha1 is empty") endif() string(SUBSTRING "${sha1}" 0 7 archive_id) if(EXISTS "${root}/cmake/Hunter") set(hunter_self "${root}") else() set( hunter_self "${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked" ) endif() set("${result}" "${hunter_self}" PARENT_SCOPE) endfunction() # Set HUNTER_GATE_ROOT cmake variable to suitable value. function(hunter_gate_detect_root) # Check CMake variable string(COMPARE NOTEQUAL "${HUNTER_ROOT}" "" not_empty) if(not_empty) set(HUNTER_GATE_ROOT "${HUNTER_ROOT}" PARENT_SCOPE) hunter_gate_status_debug("HUNTER_ROOT detected by cmake variable") return() endif() # Check environment variable string(COMPARE NOTEQUAL "$ENV{HUNTER_ROOT}" "" not_empty) if(not_empty) set(HUNTER_GATE_ROOT "$ENV{HUNTER_ROOT}" PARENT_SCOPE) hunter_gate_status_debug("HUNTER_ROOT detected by environment variable") return() endif() # Check HOME environment variable string(COMPARE NOTEQUAL "$ENV{HOME}" "" result) if(result) set(HUNTER_GATE_ROOT "$ENV{HOME}/.hunter" PARENT_SCOPE) hunter_gate_status_debug("HUNTER_ROOT set using HOME environment variable") return() endif() # Check SYSTEMDRIVE and USERPROFILE environment variable (windows only) if(WIN32) string(COMPARE NOTEQUAL "$ENV{SYSTEMDRIVE}" "" result) if(result) set(HUNTER_GATE_ROOT "$ENV{SYSTEMDRIVE}/.hunter" PARENT_SCOPE) hunter_gate_status_debug( "HUNTER_ROOT set using SYSTEMDRIVE environment variable" ) return() endif() string(COMPARE NOTEQUAL "$ENV{USERPROFILE}" "" result) if(result) set(HUNTER_GATE_ROOT "$ENV{USERPROFILE}/.hunter" PARENT_SCOPE) hunter_gate_status_debug( "HUNTER_ROOT set using USERPROFILE environment variable" ) return() endif() endif() hunter_gate_fatal_error( "Can't detect HUNTER_ROOT" WIKI "error.detect.hunter.root" ) endfunction() macro(hunter_gate_lock dir) if(NOT HUNTER_SKIP_LOCK) if("${CMAKE_VERSION}" VERSION_LESS "3.2") hunter_gate_fatal_error( "Can't lock, upgrade to CMake 3.2 or use HUNTER_SKIP_LOCK" WIKI "error.can.not.lock" ) endif() hunter_gate_status_debug("Locking directory: ${dir}") file(LOCK "${dir}" DIRECTORY GUARD FUNCTION) hunter_gate_status_debug("Lock done") endif() endmacro() function(hunter_gate_download dir) string( COMPARE NOTEQUAL "$ENV{HUNTER_DISABLE_AUTOINSTALL}" "" disable_autoinstall ) if(disable_autoinstall AND NOT HUNTER_RUN_INSTALL) hunter_gate_fatal_error( "Hunter not found in '${dir}'" "Set HUNTER_RUN_INSTALL=ON to auto-install it from '${HUNTER_GATE_URL}'" "Settings:" " HUNTER_ROOT: ${HUNTER_GATE_ROOT}" " HUNTER_SHA1: ${HUNTER_GATE_SHA1}" WIKI "error.run.install" ) endif() string(COMPARE EQUAL "${dir}" "" is_bad) if(is_bad) hunter_gate_internal_error("Empty 'dir' argument") endif() string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" is_bad) if(is_bad) hunter_gate_internal_error("HUNTER_GATE_SHA1 empty") endif() string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" is_bad) if(is_bad) hunter_gate_internal_error("HUNTER_GATE_URL empty") endif() set(done_location "${dir}/DONE") set(sha1_location "${dir}/SHA1") set(build_dir "${dir}/Build") set(cmakelists "${dir}/CMakeLists.txt") hunter_gate_lock("${dir}") if(EXISTS "${done_location}") # while waiting for lock other instance can do all the job hunter_gate_status_debug("File '${done_location}' found, skip install") return() endif() file(REMOVE_RECURSE "${build_dir}") file(REMOVE_RECURSE "${cmakelists}") file(MAKE_DIRECTORY "${build_dir}") # check directory permissions # Disabling languages speeds up a little bit, reduces noise in the output # and avoids path too long windows error file( WRITE "${cmakelists}" "cmake_minimum_required(VERSION 3.0)\n" "project(HunterDownload LANGUAGES NONE)\n" "include(ExternalProject)\n" "ExternalProject_Add(\n" " Hunter\n" " URL\n" " \"${HUNTER_GATE_URL}\"\n" " URL_HASH\n" " SHA1=${HUNTER_GATE_SHA1}\n" " DOWNLOAD_DIR\n" " \"${dir}\"\n" " TLS_VERIFY\n" " ON\n" " SOURCE_DIR\n" " \"${dir}/Unpacked\"\n" " CONFIGURE_COMMAND\n" " \"\"\n" " BUILD_COMMAND\n" " \"\"\n" " INSTALL_COMMAND\n" " \"\"\n" ")\n" ) if(HUNTER_STATUS_DEBUG) set(logging_params "") else() set(logging_params OUTPUT_QUIET) endif() hunter_gate_status_debug("Run generate") # Need to add toolchain file too. # Otherwise on Visual Studio + MDD this will fail with error: # "Could not find an appropriate version of the Windows 10 SDK installed on this machine" if(EXISTS "${CMAKE_TOOLCHAIN_FILE}") get_filename_component(absolute_CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE) set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${absolute_CMAKE_TOOLCHAIN_FILE}") else() # 'toolchain_arg' can't be empty set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=") endif() string(COMPARE EQUAL "${CMAKE_MAKE_PROGRAM}" "" no_make) if(no_make) set(make_arg "") else() # Test case: remove Ninja from PATH but set it via CMAKE_MAKE_PROGRAM set(make_arg "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}") endif() execute_process( COMMAND "${CMAKE_COMMAND}" "-H${dir}" "-B${build_dir}" "-G${CMAKE_GENERATOR}" "${toolchain_arg}" ${make_arg} WORKING_DIRECTORY "${dir}" RESULT_VARIABLE download_result ${logging_params} ) if(NOT download_result EQUAL 0) hunter_gate_internal_error("Configure project failed") endif() hunter_gate_status_print( "Initializing Hunter workspace (${HUNTER_GATE_SHA1})" " ${HUNTER_GATE_URL}" " -> ${dir}" ) execute_process( COMMAND "${CMAKE_COMMAND}" --build "${build_dir}" WORKING_DIRECTORY "${dir}" RESULT_VARIABLE download_result ${logging_params} ) if(NOT download_result EQUAL 0) hunter_gate_internal_error("Build project failed") endif() file(REMOVE_RECURSE "${build_dir}") file(REMOVE_RECURSE "${cmakelists}") file(WRITE "${sha1_location}" "${HUNTER_GATE_SHA1}") file(WRITE "${done_location}" "DONE") hunter_gate_status_debug("Finished") endfunction() # Must be a macro so master file 'cmake/Hunter' can # apply all variables easily just by 'include' command # (otherwise PARENT_SCOPE magic needed) macro(HunterGate) if(HUNTER_GATE_DONE) # variable HUNTER_GATE_DONE set explicitly for external project # (see `hunter_download`) set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) endif() # First HunterGate command will init Hunter, others will be ignored get_property(_hunter_gate_done GLOBAL PROPERTY HUNTER_GATE_DONE SET) if(NOT HUNTER_ENABLED) # Empty function to avoid error "unknown function" function(hunter_add_package) endfunction() set( _hunter_gate_disabled_mode_dir "${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/disabled-mode" ) if(EXISTS "${_hunter_gate_disabled_mode_dir}") hunter_gate_status_debug( "Adding \"disabled-mode\" modules: ${_hunter_gate_disabled_mode_dir}" ) list(APPEND CMAKE_PREFIX_PATH "${_hunter_gate_disabled_mode_dir}") endif() elseif(_hunter_gate_done) hunter_gate_status_debug("Secondary HunterGate (use old settings)") hunter_gate_self( "${HUNTER_CACHED_ROOT}" "${HUNTER_VERSION}" "${HUNTER_SHA1}" _hunter_self ) include("${_hunter_self}/cmake/Hunter") else() set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_LIST_DIR}") string(COMPARE NOTEQUAL "${PROJECT_NAME}" "" _have_project_name) if(_have_project_name) hunter_gate_fatal_error( "Please set HunterGate *before* 'project' command. " "Detected project: ${PROJECT_NAME}" WIKI "error.huntergate.before.project" ) endif() cmake_parse_arguments( HUNTER_GATE "LOCAL" "URL;SHA1;GLOBAL;FILEPATH" "" ${ARGV} ) string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" _empty_sha1) string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" _empty_url) string( COMPARE NOTEQUAL "${HUNTER_GATE_UNPARSED_ARGUMENTS}" "" _have_unparsed ) string(COMPARE NOTEQUAL "${HUNTER_GATE_GLOBAL}" "" _have_global) string(COMPARE NOTEQUAL "${HUNTER_GATE_FILEPATH}" "" _have_filepath) if(_have_unparsed) hunter_gate_user_error( "HunterGate unparsed arguments: ${HUNTER_GATE_UNPARSED_ARGUMENTS}" ) endif() if(_empty_sha1) hunter_gate_user_error("SHA1 suboption of HunterGate is mandatory") endif() if(_empty_url) hunter_gate_user_error("URL suboption of HunterGate is mandatory") endif() if(_have_global) if(HUNTER_GATE_LOCAL) hunter_gate_user_error("Unexpected LOCAL (already has GLOBAL)") endif() if(_have_filepath) hunter_gate_user_error("Unexpected FILEPATH (already has GLOBAL)") endif() endif() if(HUNTER_GATE_LOCAL) if(_have_global) hunter_gate_user_error("Unexpected GLOBAL (already has LOCAL)") endif() if(_have_filepath) hunter_gate_user_error("Unexpected FILEPATH (already has LOCAL)") endif() endif() if(_have_filepath) if(_have_global) hunter_gate_user_error("Unexpected GLOBAL (already has FILEPATH)") endif() if(HUNTER_GATE_LOCAL) hunter_gate_user_error("Unexpected LOCAL (already has FILEPATH)") endif() endif() hunter_gate_detect_root() # set HUNTER_GATE_ROOT # Beautify path, fix probable problems with windows path slashes get_filename_component( HUNTER_GATE_ROOT "${HUNTER_GATE_ROOT}" ABSOLUTE ) hunter_gate_status_debug("HUNTER_ROOT: ${HUNTER_GATE_ROOT}") if(NOT HUNTER_ALLOW_SPACES_IN_PATH) string(FIND "${HUNTER_GATE_ROOT}" " " _contain_spaces) if(NOT _contain_spaces EQUAL -1) hunter_gate_fatal_error( "HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces." "Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error" "(Use at your own risk!)" WIKI "error.spaces.in.hunter.root" ) endif() endif() string( REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+[-_a-z0-9]*" HUNTER_GATE_VERSION "${HUNTER_GATE_URL}" ) string(COMPARE EQUAL "${HUNTER_GATE_VERSION}" "" _is_empty) if(_is_empty) set(HUNTER_GATE_VERSION "unknown") endif() hunter_gate_self( "${HUNTER_GATE_ROOT}" "${HUNTER_GATE_VERSION}" "${HUNTER_GATE_SHA1}" _hunter_self ) set(_master_location "${_hunter_self}/cmake/Hunter") if(EXISTS "${HUNTER_GATE_ROOT}/cmake/Hunter") # Hunter downloaded manually (e.g. by 'git clone') set(_unused "xxxxxxxxxx") set(HUNTER_GATE_SHA1 "${_unused}") set(HUNTER_GATE_VERSION "${_unused}") else() get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE) set(_done_location "${_archive_id_location}/DONE") set(_sha1_location "${_archive_id_location}/SHA1") # Check Hunter already downloaded by HunterGate if(NOT EXISTS "${_done_location}") hunter_gate_download("${_archive_id_location}") endif() if(NOT EXISTS "${_done_location}") hunter_gate_internal_error("hunter_gate_download failed") endif() if(NOT EXISTS "${_sha1_location}") hunter_gate_internal_error("${_sha1_location} not found") endif() file(READ "${_sha1_location}" _sha1_value) string(COMPARE EQUAL "${_sha1_value}" "${HUNTER_GATE_SHA1}" _is_equal) if(NOT _is_equal) hunter_gate_internal_error( "Short SHA1 collision:" " ${_sha1_value} (from ${_sha1_location})" " ${HUNTER_GATE_SHA1} (HunterGate)" ) endif() if(NOT EXISTS "${_master_location}") hunter_gate_user_error( "Master file not found:" " ${_master_location}" "try to update Hunter/HunterGate" ) endif() endif() include("${_master_location}") set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) endif() endmacro() sqlite_modern_cpp-dev/cmake/ParseAndAddCatchTests.cmake000066400000000000000000000241061453314505700235310ustar00rootroot00000000000000#==================================================================================================# # supported macros # # - TEST_CASE, # # - SCENARIO, # # - TEST_CASE_METHOD, # # - CATCH_TEST_CASE, # # - CATCH_SCENARIO, # # - CATCH_TEST_CASE_METHOD. # # # # Usage # # 1. make sure this module is in the path or add this otherwise: # # set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") # # 2. make sure that you've enabled testing option for the project by the call: # # enable_testing() # # 3. add the lines to the script for testing target (sample CMakeLists.txt): # # project(testing_target) # # set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") # # enable_testing() # # # # find_path(CATCH_INCLUDE_DIR "catch.hpp") # # include_directories(${INCLUDE_DIRECTORIES} ${CATCH_INCLUDE_DIR}) # # # # file(GLOB SOURCE_FILES "*.cpp") # # add_executable(${PROJECT_NAME} ${SOURCE_FILES}) # # # # include(ParseAndAddCatchTests) # # ParseAndAddCatchTests(${PROJECT_NAME}) # # # # The following variables affect the behavior of the script: # # # # PARSE_CATCH_TESTS_VERBOSE (Default OFF) # # -- enables debug messages # # PARSE_CATCH_TESTS_NO_HIDDEN_TESTS (Default OFF) # # -- excludes tests marked with [!hide], [.] or [.foo] tags # # PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME (Default ON) # # -- adds fixture class name to the test name # # PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME (Default ON) # # -- adds cmake target name to the test name # # PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS (Default OFF) # # -- causes CMake to rerun when file with tests changes so that new tests will be discovered # # # #==================================================================================================# cmake_minimum_required(VERSION 2.8.8) option(PARSE_CATCH_TESTS_VERBOSE "Print Catch to CTest parser debug messages" OFF) option(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS "Exclude tests with [!hide], [.] or [.foo] tags" OFF) option(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME "Add fixture class name to the test name" ON) option(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME "Add target name to the test name" ON) option(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS "Add test file to CMAKE_CONFIGURE_DEPENDS property" OFF) function(PrintDebugMessage) if(PARSE_CATCH_TESTS_VERBOSE) message(STATUS "ParseAndAddCatchTests: ${ARGV}") endif() endfunction() # This removes the contents between # - block comments (i.e. /* ... */) # - full line comments (i.e. // ... ) # contents have been read into '${CppCode}'. # !keep partial line comments function(RemoveComments CppCode) string(ASCII 2 CMakeBeginBlockComment) string(ASCII 3 CMakeEndBlockComment) string(REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${CppCode} "${${CppCode}}") string(REGEX REPLACE "\\*/" "${CMakeEndBlockComment}" ${CppCode} "${${CppCode}}") string(REGEX REPLACE "${CMakeBeginBlockComment}[^${CMakeEndBlockComment}]*${CMakeEndBlockComment}" "" ${CppCode} "${${CppCode}}") string(REGEX REPLACE "\n[ \t]*//+[^\n]+" "\n" ${CppCode} "${${CppCode}}") set(${CppCode} "${${CppCode}}" PARENT_SCOPE) endfunction() # Worker function function(ParseFile SourceFile TestTarget) # According to CMake docs EXISTS behavior is well-defined only for full paths. get_filename_component(SourceFile ${SourceFile} ABSOLUTE) if(NOT EXISTS ${SourceFile}) message(WARNING "Cannot find source file: ${SourceFile}") return() endif() PrintDebugMessage("parsing ${SourceFile}") file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME) # Remove block and fullline comments RemoveComments(Contents) # Find definition of test names string(REGEX MATCHALL "[ \t]*(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^\)]+\\)+[ \t\n]*{+[ \t]*(//[^\n]*[Tt][Ii][Mm][Ee][Oo][Uu][Tt][ \t]*[0-9]+)*" Tests "${Contents}") if(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS AND Tests) PrintDebugMessage("Adding ${SourceFile} to CMAKE_CONFIGURE_DEPENDS property") set_property( DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${SourceFile} ) endif() foreach(TestName ${Tests}) # Strip newlines string(REGEX REPLACE "\\\\\n|\n" "" TestName "${TestName}") # Get test type and fixture if applicable string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^,^\"]*" TestTypeAndFixture "${TestName}") string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)" TestType "${TestTypeAndFixture}") string(REPLACE "${TestType}(" "" TestFixture "${TestTypeAndFixture}") # Get string parts of test definition string(REGEX MATCHALL "\"+([^\\^\"]|\\\\\")+\"+" TestStrings "${TestName}") # Strip wrapping quotation marks string(REGEX REPLACE "^\"(.*)\"$" "\\1" TestStrings "${TestStrings}") string(REPLACE "\";\"" ";" TestStrings "${TestStrings}") # Validate that a test name and tags have been provided list(LENGTH TestStrings TestStringsLength) if(TestStringsLength GREATER 2 OR TestStringsLength LESS 1) message(FATAL_ERROR "You must provide a valid test name and tags for all tests in ${SourceFile}") endif() # Assign name and tags list(GET TestStrings 0 Name) if("${TestType}" STREQUAL "SCENARIO") set(Name "Scenario: ${Name}") endif() if(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME AND TestFixture) set(CTestName "${TestFixture}:${Name}") else() set(CTestName "${Name}") endif() if(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME) set(CTestName "${TestTarget}:${CTestName}") endif() # add target to labels to enable running all tests added from this target set(Labels ${TestTarget}) if(TestStringsLength EQUAL 2) list(GET TestStrings 1 Tags) string(TOLOWER "${Tags}" Tags) # remove target from labels if the test is hidden if("${Tags}" MATCHES ".*\\[!?(hide|\\.)\\].*") list(REMOVE_ITEM Labels ${TestTarget}) endif() string(REPLACE "]" ";" Tags "${Tags}") string(REPLACE "[" "" Tags "${Tags}") endif() list(APPEND Labels ${Tags}) list(FIND Labels "!hide" IndexOfHideLabel) set(HiddenTagFound OFF) foreach(label ${Labels}) string(REGEX MATCH "^!hide|^\\." result ${label}) if(result) set(HiddenTagFound ON) break() endif(result) endforeach(label) if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound}) PrintDebugMessage("Skipping test \"${CTestName}\" as it has [!hide], [.] or [.foo] label") else() PrintDebugMessage("Adding test \"${CTestName}\"") if(Labels) PrintDebugMessage("Setting labels to ${Labels}") endif() # Add the test and set its properties add_test(NAME "\"${CTestName}\"" COMMAND ${TestTarget} ${Name} ${AdditionalCatchParameters}) set_tests_properties("\"${CTestName}\"" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran" LABELS "${Labels}") endif() endforeach() endfunction() # entry point function(ParseAndAddCatchTests TestTarget) PrintDebugMessage("Started parsing ${TestTarget}") get_target_property(SourceFiles ${TestTarget} SOURCES) PrintDebugMessage("Found the following sources: ${SourceFiles}") foreach(SourceFile ${SourceFiles}) ParseFile(${SourceFile} ${TestTarget}) endforeach() PrintDebugMessage("Finished parsing ${TestTarget}") endfunction() sqlite_modern_cpp-dev/hdr/000077500000000000000000000000001453314505700161255ustar00rootroot00000000000000sqlite_modern_cpp-dev/hdr/sqlite_modern_cpp.h000066400000000000000000000506041453314505700220120ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #define MODERN_SQLITE_VERSION 3002008 #include #include "sqlite_modern_cpp/type_wrapper.h" #include "sqlite_modern_cpp/errors.h" #include "sqlite_modern_cpp/utility/function_traits.h" #include "sqlite_modern_cpp/utility/uncaught_exceptions.h" #include "sqlite_modern_cpp/utility/utf16_utf8.h" namespace sqlite { class database; class database_binder; template class binder; typedef std::shared_ptr connection_type; template struct index_binding_helper { index_binding_helper(const index_binding_helper &) = delete; #if __cplusplus < 201703 || _MSVC_LANG <= 201703 index_binding_helper(index_binding_helper &&) = default; #endif typename std::conditional::type index; T value; }; template auto named_parameter(const char *name, T &&arg) { return index_binding_helper{name, std::forward(arg)}; } template auto indexed_parameter(int index, T &&arg) { return index_binding_helper{index, std::forward(arg)}; } class row_iterator; class database_binder { public: // database_binder is not copyable database_binder() = delete; database_binder(const database_binder& other) = delete; database_binder& operator=(const database_binder&) = delete; database_binder(database_binder&& other) : _db(std::move(other._db)), _stmt(std::move(other._stmt)), _inx(other._inx), execution_started(other.execution_started) { } void execute(); std::string sql() { #if SQLITE_VERSION_NUMBER >= 3014000 auto sqlite_deleter = [](void *ptr) {sqlite3_free(ptr);}; std::unique_ptr str(sqlite3_expanded_sql(_stmt.get()), sqlite_deleter); return str ? str.get() : original_sql(); #else return original_sql(); #endif } std::string original_sql() { return sqlite3_sql(_stmt.get()); } void used(bool state) { if(!state) { // We may have to reset first if we haven't done so already: _next_index(); --_inx; } execution_started = state; } bool used() const { return execution_started; } row_iterator begin(); row_iterator end(); private: std::shared_ptr _db; std::unique_ptr _stmt; utility::UncaughtExceptionDetector _has_uncaught_exception; int _inx; bool execution_started = false; int _next_index() { if(execution_started && !_inx) { sqlite3_reset(_stmt.get()); sqlite3_clear_bindings(_stmt.get()); } return ++_inx; } sqlite3_stmt* _prepare(u16str_ref sql) { return _prepare(utility::utf16_to_utf8(sql)); } sqlite3_stmt* _prepare(str_ref sql) { int hresult; sqlite3_stmt* tmp = nullptr; const char *remaining; hresult = sqlite3_prepare_v2(_db.get(), sql.data(), sql.length(), &tmp, &remaining); if(hresult != SQLITE_OK) errors::throw_sqlite_error(hresult, sql, sqlite3_errmsg(_db.get())); if(!std::all_of(remaining, sql.data() + sql.size(), [](char ch) {return std::isspace(ch);})) throw errors::more_statements("Multiple semicolon separated statements are unsupported", sql); return tmp; } template friend database_binder& operator<<(database_binder& db, T&&); template friend database_binder& operator<<(database_binder& db, index_binding_helper); template friend database_binder& operator<<(database_binder& db, index_binding_helper); friend void operator++(database_binder& db, int); public: database_binder(std::shared_ptr db, u16str_ref sql): _db(db), _stmt(_prepare(sql), sqlite3_finalize), _inx(0) { } database_binder(std::shared_ptr db, str_ref sql): _db(db), _stmt(_prepare(sql), sqlite3_finalize), _inx(0) { } ~database_binder() noexcept(false) { /* Will be executed if no >>op is found, but not if an exception is in mid flight */ if(!used() && !_has_uncaught_exception && _stmt) { execute(); } } friend class row_iterator; }; class row_iterator { public: class value_type { public: value_type(database_binder *_binder): _binder(_binder) {}; template typename std::enable_if::value, value_type &>::type operator >>(T &result) { result = get_col_from_db(_binder->_stmt.get(), next_index++, result_type()); return *this; } template value_type &operator >>(std::tuple& values) { values = handle_tuple::type...>>(std::index_sequence_for()); next_index += sizeof...(Types); return *this; } template value_type &operator >>(std::tuple&& values) { return *this >> values; } template operator std::tuple() { std::tuple value; *this >> value; return value; } explicit operator bool() { return sqlite3_column_count(_binder->_stmt.get()) >= next_index; } private: template Tuple handle_tuple(std::index_sequence) { return Tuple( get_col_from_db( _binder->_stmt.get(), next_index + Index, result_type::type>())...); } database_binder *_binder; int next_index = 0; }; using difference_type = std::ptrdiff_t; using pointer = value_type*; using reference = value_type&; using iterator_category = std::input_iterator_tag; row_iterator() = default; explicit row_iterator(database_binder &binder): _binder(&binder) { _binder->_next_index(); _binder->_inx = 0; _binder->used(true); ++*this; } reference operator*() const { return value;} pointer operator->() const { return std::addressof(**this); } row_iterator &operator++() { switch(int result = sqlite3_step(_binder->_stmt.get())) { case SQLITE_ROW: value = {_binder}; break; case SQLITE_DONE: _binder = nullptr; break; default: exceptions::throw_sqlite_error(result, _binder->sql(), sqlite3_errmsg(_binder->_db.get())); } return *this; } friend inline bool operator ==(const row_iterator &a, const row_iterator &b) { return a._binder == b._binder; } friend inline bool operator !=(const row_iterator &a, const row_iterator &b) { return !(a==b); } private: database_binder *_binder = nullptr; mutable value_type value{_binder}; // mutable, because `changing` the value is just reading it }; inline row_iterator database_binder::begin() { return row_iterator(*this); } inline row_iterator database_binder::end() { return row_iterator(); } namespace detail { template void _extract_single_value(database_binder &binder, Callback call_back) { auto iter = binder.begin(); if(iter == binder.end()) throw errors::no_rows("no rows to extract: exactly 1 row expected", binder.sql(), SQLITE_DONE); call_back(*iter); if(++iter != binder.end()) throw errors::more_rows("not all rows extracted", binder.sql(), SQLITE_ROW); } } inline void database_binder::execute() { for(auto &&row : *this) (void)row; } namespace detail { template using void_t = void; template struct sqlite_direct_result : std::false_type {}; template struct sqlite_direct_result< T, void_t() >> std::declval())> > : std::true_type {}; } template inline typename std::enable_if::value>::type operator>>(database_binder &binder, Result&& value) { detail::_extract_single_value(binder, [&value] (row_iterator::value_type &row) { row >> std::forward(value); }); } template inline typename std::enable_if::value>::type operator>>(database_binder &db_binder, Function&& func) { using traits = utility::function_traits; for(auto &&row : db_binder) { binder::run(row, func); } } template inline decltype(auto) operator>>(database_binder &&binder, Result&& value) { return binder >> std::forward(value); } namespace sql_function_binder { template< typename ContextType, std::size_t Count, typename Functions > inline void step( sqlite3_context* db, int count, sqlite3_value** vals ); template< std::size_t Count, typename Functions, typename... Values > inline typename std::enable_if<(sizeof...(Values) && sizeof...(Values) < Count), void>::type step( sqlite3_context* db, int count, sqlite3_value** vals, Values&&... values ); template< std::size_t Count, typename Functions, typename... Values > inline typename std::enable_if<(sizeof...(Values) == Count), void>::type step( sqlite3_context* db, int, sqlite3_value**, Values&&... values ); template< typename ContextType, typename Functions > inline void final(sqlite3_context* db); template< std::size_t Count, typename Function, typename... Values > inline typename std::enable_if<(sizeof...(Values) < Count), void>::type scalar( sqlite3_context* db, int count, sqlite3_value** vals, Values&&... values ); template< std::size_t Count, typename Function, typename... Values > inline typename std::enable_if<(sizeof...(Values) == Count), void>::type scalar( sqlite3_context* db, int, sqlite3_value**, Values&&... values ); } enum class OpenFlags { READONLY = SQLITE_OPEN_READONLY, READWRITE = SQLITE_OPEN_READWRITE, CREATE = SQLITE_OPEN_CREATE, NOMUTEX = SQLITE_OPEN_NOMUTEX, FULLMUTEX = SQLITE_OPEN_FULLMUTEX, SHAREDCACHE = SQLITE_OPEN_SHAREDCACHE, PRIVATECACH = SQLITE_OPEN_PRIVATECACHE, URI = SQLITE_OPEN_URI }; inline OpenFlags operator|(const OpenFlags& a, const OpenFlags& b) { return static_cast(static_cast(a) | static_cast(b)); } enum class Encoding { ANY = SQLITE_ANY, UTF8 = SQLITE_UTF8, UTF16 = SQLITE_UTF16 }; struct sqlite_config { OpenFlags flags = OpenFlags::READWRITE | OpenFlags::CREATE; const char *zVfs = nullptr; Encoding encoding = Encoding::ANY; }; class database { protected: std::shared_ptr _db; public: database(const std::string &db_name, const sqlite_config &config = {}): _db(nullptr) { sqlite3* tmp = nullptr; auto ret = sqlite3_open_v2(db_name.data(), &tmp, static_cast(config.flags), config.zVfs); _db = std::shared_ptr(tmp, [=](sqlite3* ptr) { sqlite3_close_v2(ptr); }); // this will close the connection eventually when no longer needed. if(ret != SQLITE_OK) errors::throw_sqlite_error(_db ? sqlite3_extended_errcode(_db.get()) : ret, {}, sqlite3_errmsg(_db.get())); sqlite3_extended_result_codes(_db.get(), true); if(config.encoding == Encoding::UTF16) *this << R"(PRAGMA encoding = "UTF-16";)"; } database(const std::u16string &db_name, const sqlite_config &config = {}): database(utility::utf16_to_utf8(db_name), config) { if (config.encoding == Encoding::ANY) *this << R"(PRAGMA encoding = "UTF-16";)"; } database(std::shared_ptr db): _db(db) {} database_binder operator<<(str_ref sql) { return database_binder(_db, sql); } database_binder operator<<(u16str_ref sql) { return database_binder(_db, sql); } connection_type connection() const { return _db; } sqlite3_int64 last_insert_rowid() const { return sqlite3_last_insert_rowid(_db.get()); } int rows_modified() const { return sqlite3_changes(_db.get()); } template void define(const std::string &name, Function&& func) { typedef utility::function_traits traits; auto funcPtr = new auto(std::forward(func)); if(int result = sqlite3_create_function_v2( _db.get(), name.data(), traits::arity, SQLITE_UTF8, funcPtr, sql_function_binder::scalar::type>, nullptr, nullptr, [](void* ptr){ delete static_cast(ptr); })) errors::throw_sqlite_error(result, {}, sqlite3_errmsg(_db.get())); } template void define(const std::string &name, StepFunction&& step, FinalFunction&& final) { typedef utility::function_traits traits; using ContextType = typename std::remove_reference>::type; auto funcPtr = new auto(std::make_pair(std::forward(step), std::forward(final))); if(int result = sqlite3_create_function_v2( _db.get(), name.c_str(), traits::arity - 1, SQLITE_UTF8, funcPtr, nullptr, sql_function_binder::step::type>, sql_function_binder::final::type>, [](void* ptr){ delete static_cast(ptr); })) errors::throw_sqlite_error(result, {}, sqlite3_errmsg(_db.get())); } }; template class binder { private: template < typename Function, std::size_t Index > using nth_argument_type = typename utility::function_traits< Function >::template argument; public: // `Boundary` needs to be defaulted to `Count` so that the `run` function // template is not implicitly instantiated on class template instantiation. // Look up section 14.7.1 _Implicit instantiation_ of the ISO C++14 Standard // and the [dicussion](https://github.com/aminroosta/sqlite_modern_cpp/issues/8) // on Github. template< typename Function, typename... Values, std::size_t Boundary = Count > static typename std::enable_if<(sizeof...(Values) < Boundary), void>::type run( row_iterator::value_type& row, Function&& function, Values&&... values ) { typename std::decay>::type value; row >> value; run(row, function, std::forward(values)..., std::move(value)); } template< typename Function, typename... Values, std::size_t Boundary = Count > static typename std::enable_if<(sizeof...(Values) == Boundary), void>::type run( row_iterator::value_type&, Function&& function, Values&&... values ) { function(std::move(values)...); } }; // Some ppl are lazy so we have a operator for proper prep. statemant handling. void inline operator++(database_binder& db, int) { db.execute(); } template database_binder &operator<<(database_binder& db, index_binding_helper val) { db._next_index(); --db._inx; int result = bind_col_in_db(db._stmt.get(), val.index, std::forward(val.value)); if(result != SQLITE_OK) exceptions::throw_sqlite_error(result, db.sql(), sqlite3_errmsg(db._db.get())); return db; } template database_binder &operator<<(database_binder& db, index_binding_helper val) { db._next_index(); --db._inx; int index = sqlite3_bind_parameter_index(db._stmt.get(), val.index); if(!index) throw errors::unknown_binding("The given binding name is not valid for this statement", db.sql()); int result = bind_col_in_db(db._stmt.get(), index, std::forward(val.value)); if(result != SQLITE_OK) exceptions::throw_sqlite_error(result, db.sql(), sqlite3_errmsg(db._db.get())); return db; } template database_binder &operator<<(database_binder& db, T&& val) { int result = bind_col_in_db(db._stmt.get(), db._next_index(), std::forward(val)); if(result != SQLITE_OK) exceptions::throw_sqlite_error(result, db.sql(), sqlite3_errmsg(db._db.get())); return db; } // Convert the rValue binder to a reference and call first op<<, its needed for the call that creates the binder (be carefull of recursion here!) template database_binder operator << (database_binder&& db, const T& val) { db << val; return std::move(db); } template database_binder operator << (database_binder&& db, index_binding_helper val) { db << index_binding_helper{val.index, std::forward(val.value)}; return std::move(db); } namespace sql_function_binder { template struct AggregateCtxt { T obj; bool constructed = true; }; template< typename ContextType, std::size_t Count, typename Functions > inline void step( sqlite3_context* db, int count, sqlite3_value** vals ) { auto ctxt = static_cast*>(sqlite3_aggregate_context(db, sizeof(AggregateCtxt))); if(!ctxt) return; try { if(!ctxt->constructed) new(ctxt) AggregateCtxt(); step(db, count, vals, ctxt->obj); return; } catch(const sqlite_exception &e) { sqlite3_result_error_code(db, e.get_code()); sqlite3_result_error(db, e.what(), -1); } catch(const std::exception &e) { sqlite3_result_error(db, e.what(), -1); } catch(...) { sqlite3_result_error(db, "Unknown error", -1); } if(ctxt && ctxt->constructed) ctxt->~AggregateCtxt(); } template< std::size_t Count, typename Functions, typename... Values > inline typename std::enable_if<(sizeof...(Values) && sizeof...(Values) < Count), void>::type step( sqlite3_context* db, int count, sqlite3_value** vals, Values&&... values ) { using arg_type = typename std::remove_cv< typename std::remove_reference< typename utility::function_traits< typename Functions::first_type >::template argument >::type >::type; step( db, count, vals, std::forward(values)..., get_val_from_db(vals[sizeof...(Values) - 1], result_type())); } template< std::size_t Count, typename Functions, typename... Values > inline typename std::enable_if<(sizeof...(Values) == Count), void>::type step( sqlite3_context* db, int, sqlite3_value**, Values&&... values ) { static_cast(sqlite3_user_data(db))->first(std::forward(values)...); } template< typename ContextType, typename Functions > inline void final(sqlite3_context* db) { auto ctxt = static_cast*>(sqlite3_aggregate_context(db, sizeof(AggregateCtxt))); try { if(!ctxt) return; if(!ctxt->constructed) new(ctxt) AggregateCtxt(); store_result_in_db(db, static_cast(sqlite3_user_data(db))->second(ctxt->obj)); } catch(const sqlite_exception &e) { sqlite3_result_error_code(db, e.get_code()); sqlite3_result_error(db, e.what(), -1); } catch(const std::exception &e) { sqlite3_result_error(db, e.what(), -1); } catch(...) { sqlite3_result_error(db, "Unknown error", -1); } if(ctxt && ctxt->constructed) ctxt->~AggregateCtxt(); } template< std::size_t Count, typename Function, typename... Values > inline typename std::enable_if<(sizeof...(Values) < Count), void>::type scalar( sqlite3_context* db, int count, sqlite3_value** vals, Values&&... values ) { using arg_type = typename std::remove_cv< typename std::remove_reference< typename utility::function_traits::template argument >::type >::type; scalar( db, count, vals, std::forward(values)..., get_val_from_db(vals[sizeof...(Values)], result_type())); } template< std::size_t Count, typename Function, typename... Values > inline typename std::enable_if<(sizeof...(Values) == Count), void>::type scalar( sqlite3_context* db, int, sqlite3_value**, Values&&... values ) { try { store_result_in_db(db, (*static_cast(sqlite3_user_data(db)))(std::forward(values)...)); } catch(const sqlite_exception &e) { sqlite3_result_error_code(db, e.get_code()); sqlite3_result_error(db, e.what(), -1); } catch(const std::exception &e) { sqlite3_result_error(db, e.what(), -1); } catch(...) { sqlite3_result_error(db, "Unknown error", -1); } } } } sqlite_modern_cpp-dev/hdr/sqlite_modern_cpp/000077500000000000000000000000001453314505700216345ustar00rootroot00000000000000sqlite_modern_cpp-dev/hdr/sqlite_modern_cpp/errors.h000066400000000000000000000056241453314505700233300ustar00rootroot00000000000000#pragma once #include #include #include namespace sqlite { class sqlite_exception: public std::runtime_error { public: sqlite_exception(const char* msg, str_ref sql, int code = -1): runtime_error(msg), code(code), sql(sql) {} sqlite_exception(int code, str_ref sql, const char *msg = nullptr): runtime_error(msg ? msg : sqlite3_errstr(code)), code(code), sql(sql) {} int get_code() const {return code & 0xFF;} int get_extended_code() const {return code;} std::string get_sql() const {return sql;} const char *errstr() const {return code == -1 ? "Unknown error" : sqlite3_errstr(code);} private: int code; std::string sql; }; namespace errors { //One more or less trivial derived error class for each SQLITE error. //Note the following are not errors so have no classes: //SQLITE_OK, SQLITE_NOTICE, SQLITE_WARNING, SQLITE_ROW, SQLITE_DONE // //Note these names are exact matches to the names of the SQLITE error codes. #define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \ class name: public sqlite_exception { using sqlite_exception::sqlite_exception; };\ derived #define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \ class base ## _ ## sub: public base { using base::base; }; #include "lists/error_codes.h" #undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED #undef SQLITE_MODERN_CPP_ERROR_CODE //Some additional errors are here for the C++ interface class more_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; }; class no_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; }; class more_statements: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements can only contain one statement class invalid_utf16: public sqlite_exception { using sqlite_exception::sqlite_exception; }; class unknown_binding: public sqlite_exception { using sqlite_exception::sqlite_exception; }; static void throw_sqlite_error(const int& error_code, str_ref sql = "", const char *errmsg = nullptr) { switch(error_code & 0xFF) { #define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \ case SQLITE_ ## NAME: switch(error_code) { \ derived \ case SQLITE_ ## NAME: \ default: throw name(error_code, sql); \ } #if SQLITE_VERSION_NUMBER < 3010000 #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) #endif #define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \ case SQLITE_ ## BASE ## _ ## SUB: throw base ## _ ## sub(error_code, sql, errmsg); #include "lists/error_codes.h" #undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED #undef SQLITE_MODERN_CPP_ERROR_CODE default: throw sqlite_exception(error_code, sql, errmsg); } } } namespace exceptions = errors; } sqlite_modern_cpp-dev/hdr/sqlite_modern_cpp/lists/000077500000000000000000000000001453314505700227725ustar00rootroot00000000000000sqlite_modern_cpp-dev/hdr/sqlite_modern_cpp/lists/error_codes.h000066400000000000000000000114501453314505700254520ustar00rootroot00000000000000SQLITE_MODERN_CPP_ERROR_CODE(ERROR,error,) SQLITE_MODERN_CPP_ERROR_CODE(INTERNAL,internal,) SQLITE_MODERN_CPP_ERROR_CODE(PERM,perm,) SQLITE_MODERN_CPP_ERROR_CODE(ABORT,abort, SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(ABORT,ROLLBACK,abort,rollback) ) SQLITE_MODERN_CPP_ERROR_CODE(BUSY,busy, SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BUSY,RECOVERY,busy,recovery) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BUSY,SNAPSHOT,busy,snapshot) ) SQLITE_MODERN_CPP_ERROR_CODE(LOCKED,locked, SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(LOCKED,SHAREDCACHE,locked,sharedcache) ) SQLITE_MODERN_CPP_ERROR_CODE(NOMEM,nomem,) SQLITE_MODERN_CPP_ERROR_CODE(READONLY,readonly,) SQLITE_MODERN_CPP_ERROR_CODE(INTERRUPT,interrupt,) SQLITE_MODERN_CPP_ERROR_CODE(IOERR,ioerr, SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,READ,ioerr,read) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SHORT_READ,ioerr,short_read) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,WRITE,ioerr,write) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,FSYNC,ioerr,fsync) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,DIR_FSYNC,ioerr,dir_fsync) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,TRUNCATE,ioerr,truncate) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,FSTAT,ioerr,fstat) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,UNLOCK,ioerr,unlock) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,RDLOCK,ioerr,rdlock) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,DELETE,ioerr,delete) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,BLOCKED,ioerr,blocked) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,NOMEM,ioerr,nomem) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,ACCESS,ioerr,access) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,CHECKRESERVEDLOCK,ioerr,checkreservedlock) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,LOCK,ioerr,lock) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,CLOSE,ioerr,close) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,DIR_CLOSE,ioerr,dir_close) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SHMOPEN,ioerr,shmopen) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SHMSIZE,ioerr,shmsize) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SHMLOCK,ioerr,shmlock) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SHMMAP,ioerr,shmmap) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SEEK,ioerr,seek) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,DELETE_NOENT,ioerr,delete_noent) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,MMAP,ioerr,mmap) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,GETTEMPPATH,ioerr,gettemppath) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,CONVPATH,ioerr,convpath) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,VNODE,ioerr,vnode) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,AUTH,ioerr,auth) ) SQLITE_MODERN_CPP_ERROR_CODE(CORRUPT,corrupt, SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CORRUPT,VTAB,corrupt,vtab) ) SQLITE_MODERN_CPP_ERROR_CODE(NOTFOUND,notfound,) SQLITE_MODERN_CPP_ERROR_CODE(FULL,full,) SQLITE_MODERN_CPP_ERROR_CODE(CANTOPEN,cantopen, SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN,NOTEMPDIR,cantopen,notempdir) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN,ISDIR,cantopen,isdir) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN,FULLPATH,cantopen,fullpath) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN,CONVPATH,cantopen,convpath) ) SQLITE_MODERN_CPP_ERROR_CODE(PROTOCOL,protocol,) SQLITE_MODERN_CPP_ERROR_CODE(EMPTY,empty,) SQLITE_MODERN_CPP_ERROR_CODE(SCHEMA,schema,) SQLITE_MODERN_CPP_ERROR_CODE(TOOBIG,toobig,) SQLITE_MODERN_CPP_ERROR_CODE(CONSTRAINT,constraint, SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,CHECK,constraint,check) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,COMMITHOOK,constraint,commithook) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,FOREIGNKEY,constraint,foreignkey) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,FUNCTION,constraint,function) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,NOTNULL,constraint,notnull) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,PRIMARYKEY,constraint,primarykey) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,TRIGGER,constraint,trigger) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,UNIQUE,constraint,unique) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,VTAB,constraint,vtab) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,ROWID,constraint,rowid) ) SQLITE_MODERN_CPP_ERROR_CODE(MISMATCH,mismatch,) SQLITE_MODERN_CPP_ERROR_CODE(MISUSE,misuse,) SQLITE_MODERN_CPP_ERROR_CODE(NOLFS,nolfs,) SQLITE_MODERN_CPP_ERROR_CODE(AUTH,auth, ) SQLITE_MODERN_CPP_ERROR_CODE(FORMAT,format,) SQLITE_MODERN_CPP_ERROR_CODE(RANGE,range,) SQLITE_MODERN_CPP_ERROR_CODE(NOTADB,notadb,) SQLITE_MODERN_CPP_ERROR_CODE(NOTICE,notice, SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(NOTICE,RECOVER_WAL,notice,recover_wal) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(NOTICE,RECOVER_ROLLBACK,notice,recover_rollback) ) SQLITE_MODERN_CPP_ERROR_CODE(WARNING,warning, SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(WARNING,AUTOINDEX,warning,autoindex) ) sqlite_modern_cpp-dev/hdr/sqlite_modern_cpp/log.h000066400000000000000000000101141453314505700225630ustar00rootroot00000000000000#include "errors.h" #include #include #include #include namespace sqlite { namespace detail { template using void_t = void; template struct is_callable : std::false_type {}; template struct is_callable()(std::declval()...))>> : std::true_type {}; template class FunctorOverload: public Functor, public FunctorOverload { public: template FunctorOverload(Functor1 &&functor, Remaining &&... remaining): Functor(std::forward(functor)), FunctorOverload(std::forward(remaining)...) {} using Functor::operator(); using FunctorOverload::operator(); }; template class FunctorOverload: public Functor { public: template FunctorOverload(Functor1 &&functor): Functor(std::forward(functor)) {} using Functor::operator(); }; template class WrapIntoFunctor: public Functor { public: template WrapIntoFunctor(Functor1 &&functor): Functor(std::forward(functor)) {} using Functor::operator(); }; template class WrapIntoFunctor { ReturnType(*ptr)(Arguments...); public: WrapIntoFunctor(ReturnType(*ptr)(Arguments...)): ptr(ptr) {} ReturnType operator()(Arguments... arguments) { return (*ptr)(std::forward(arguments)...); } }; inline void store_error_log_data_pointer(std::shared_ptr ptr) { static std::shared_ptr stored; stored = std::move(ptr); } template std::shared_ptr::type> make_shared_inferred(T &&t) { return std::make_shared::type>(std::forward(t)); } } template typename std::enable_if::value>::type error_log(Handler &&handler); template typename std::enable_if::value>::type error_log(Handler &&handler); template typename std::enable_if=2>::type error_log(Handler &&...handler) { return error_log(detail::FunctorOverload::type>...>(std::forward(handler)...)); } template typename std::enable_if::value>::type error_log(Handler &&handler) { return error_log(std::forward(handler), [](const sqlite_exception&) {}); } template typename std::enable_if::value>::type error_log(Handler &&handler) { auto ptr = detail::make_shared_inferred([handler = std::forward(handler)](int error_code, const char *errstr) mutable { switch(error_code & 0xFF) { #define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \ case SQLITE_ ## NAME: switch(error_code) { \ derived \ default: handler(errors::name(errstr, "", error_code)); \ };break; #define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \ case SQLITE_ ## BASE ## _ ## SUB: \ handler(errors::base ## _ ## sub(errstr, "", error_code)); \ break; #include "lists/error_codes.h" #undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED #undef SQLITE_MODERN_CPP_ERROR_CODE default: handler(sqlite_exception(errstr, "", error_code)); \ } }); sqlite3_config(SQLITE_CONFIG_LOG, static_cast([](void *functor, int error_code, const char *errstr) { (*static_cast(functor))(error_code, errstr); }), ptr.get()); detail::store_error_log_data_pointer(std::move(ptr)); } } sqlite_modern_cpp-dev/hdr/sqlite_modern_cpp/sqlcipher.h000066400000000000000000000023071453314505700240010ustar00rootroot00000000000000#pragma once #ifndef SQLITE_HAS_CODEC #define SQLITE_HAS_CODEC #endif #include "../sqlite_modern_cpp.h" namespace sqlite { struct sqlcipher_config : public sqlite_config { std::string key; }; class sqlcipher_database : public database { public: sqlcipher_database(std::string db, const sqlcipher_config &config): database(db, config) { set_key(config.key); } sqlcipher_database(std::u16string db, const sqlcipher_config &config): database(db, config) { set_key(config.key); } void set_key(const std::string &key) { if(auto ret = sqlite3_key(_db.get(), key.data(), key.size())) errors::throw_sqlite_error(ret); } void set_key(const std::string &key, const std::string &db_name) { if(auto ret = sqlite3_key_v2(_db.get(), db_name.c_str(), key.data(), key.size())) errors::throw_sqlite_error(ret); } void rekey(const std::string &new_key) { if(auto ret = sqlite3_rekey(_db.get(), new_key.data(), new_key.size())) errors::throw_sqlite_error(ret); } void rekey(const std::string &new_key, const std::string &db_name) { if(auto ret = sqlite3_rekey_v2(_db.get(), db_name.c_str(), new_key.data(), new_key.size())) errors::throw_sqlite_error(ret); } }; } sqlite_modern_cpp-dev/hdr/sqlite_modern_cpp/type_wrapper.h000066400000000000000000000417071453314505700245370ustar00rootroot00000000000000#pragma once #include #include #include #include #ifdef __has_include #if (__cplusplus >= 201703 || _MSVC_LANG >= 201703) && __has_include() #define MODERN_SQLITE_STRINGVIEW_SUPPORT #endif #endif #ifdef __has_include #if (__cplusplus > 201402 || _MSVC_LANG > 201402) && __has_include() #define MODERN_SQLITE_STD_OPTIONAL_SUPPORT #elif __has_include() && __apple_build_version__ < 11000000 #define MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT #endif #endif #ifdef __has_include #if (__cplusplus > 201402 || _MSVC_LANG > 201402) && __has_include() #define MODERN_SQLITE_STD_VARIANT_SUPPORT #endif #endif #ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT #include #endif #ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT #include #define MODERN_SQLITE_STD_OPTIONAL_SUPPORT #endif #ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT #include #endif #ifdef MODERN_SQLITE_STRINGVIEW_SUPPORT #include namespace sqlite { typedef const std::string_view str_ref; typedef const std::u16string_view u16str_ref; } #else namespace sqlite { typedef const std::string& str_ref; typedef const std::u16string& u16str_ref; } #endif #include #include "errors.h" namespace sqlite { template struct has_sqlite_type : std::false_type {}; template using is_sqlite_value = std::integral_constant::value || has_sqlite_type::value || has_sqlite_type::value || has_sqlite_type::value || has_sqlite_type::value >; template struct has_sqlite_type : has_sqlite_type {}; template struct has_sqlite_type : has_sqlite_type {}; template struct has_sqlite_type : has_sqlite_type {}; template struct result_type { using type = T; constexpr result_type() = default; template::value>> constexpr result_type(result_type) { } }; // int template<> struct has_sqlite_type : std::true_type {}; inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const int& val) { return sqlite3_bind_int(stmt, inx, val); } inline void store_result_in_db(sqlite3_context* db, const int& val) { sqlite3_result_int(db, val); } inline int get_col_from_db(sqlite3_stmt* stmt, int inx, result_type) { return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 : sqlite3_column_int(stmt, inx); } inline int get_val_from_db(sqlite3_value *value, result_type) { return sqlite3_value_type(value) == SQLITE_NULL ? 0 : sqlite3_value_int(value); } // sqlite_int64 template<> struct has_sqlite_type : std::true_type {}; inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const sqlite_int64& val) { return sqlite3_bind_int64(stmt, inx, val); } inline void store_result_in_db(sqlite3_context* db, const sqlite_int64& val) { sqlite3_result_int64(db, val); } inline sqlite_int64 get_col_from_db(sqlite3_stmt* stmt, int inx, result_type) { return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 : sqlite3_column_int64(stmt, inx); } inline sqlite3_int64 get_val_from_db(sqlite3_value *value, result_type) { return sqlite3_value_type(value) == SQLITE_NULL ? 0 : sqlite3_value_int64(value); } // float template<> struct has_sqlite_type : std::true_type {}; inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const float& val) { return sqlite3_bind_double(stmt, inx, double(val)); } inline void store_result_in_db(sqlite3_context* db, const float& val) { sqlite3_result_double(db, val); } inline float get_col_from_db(sqlite3_stmt* stmt, int inx, result_type) { return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 : sqlite3_column_double(stmt, inx); } inline float get_val_from_db(sqlite3_value *value, result_type) { return sqlite3_value_type(value) == SQLITE_NULL ? 0 : sqlite3_value_double(value); } // double template<> struct has_sqlite_type : std::true_type {}; inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const double& val) { return sqlite3_bind_double(stmt, inx, val); } inline void store_result_in_db(sqlite3_context* db, const double& val) { sqlite3_result_double(db, val); } inline double get_col_from_db(sqlite3_stmt* stmt, int inx, result_type) { return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 : sqlite3_column_double(stmt, inx); } inline double get_val_from_db(sqlite3_value *value, result_type) { return sqlite3_value_type(value) == SQLITE_NULL ? 0 : sqlite3_value_double(value); } /* for nullptr support */ template<> struct has_sqlite_type : std::true_type {}; inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, std::nullptr_t) { return sqlite3_bind_null(stmt, inx); } inline void store_result_in_db(sqlite3_context* db, std::nullptr_t) { sqlite3_result_null(db); } #ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT template<> struct has_sqlite_type : std::true_type {}; inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, std::monostate) { return sqlite3_bind_null(stmt, inx); } inline void store_result_in_db(sqlite3_context* db, std::monostate) { sqlite3_result_null(db); } inline std::monostate get_col_from_db(sqlite3_stmt* stmt, int inx, result_type) { return std::monostate(); } inline std::monostate get_val_from_db(sqlite3_value *value, result_type) { return std::monostate(); } #endif // str_ref template<> struct has_sqlite_type : std::true_type {}; inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, str_ref val) { return sqlite3_bind_text(stmt, inx, val.data(), val.length(), SQLITE_TRANSIENT); } // Convert char* to string_view to trigger op<<(..., const str_ref ) template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char(&STR)[N]) { return sqlite3_bind_text(stmt, inx, &STR[0], N-1, SQLITE_TRANSIENT); } inline std::string get_col_from_db(sqlite3_stmt* stmt, int inx, result_type) { if ( sqlite3_column_type(stmt, inx) == SQLITE_NULL ) { return std::string(); } char const * ptr = reinterpret_cast(sqlite3_column_text(stmt, inx)); // call sqlite3_column_text explicitely before sqlite3_column_bytes: it may convert the value to text return std::string(ptr, sqlite3_column_bytes(stmt, inx)); } inline std::string get_val_from_db(sqlite3_value *value, result_type) { if ( sqlite3_value_type(value) == SQLITE_NULL ) { return std::string(); } char const * ptr = reinterpret_cast(sqlite3_value_text(value)); // call sqlite3_column_text explicitely before sqlite3_column_bytes: it may convert the value to text return std::string(ptr, sqlite3_value_bytes(value)); } inline void store_result_in_db(sqlite3_context* db, str_ref val) { sqlite3_result_text(db, val.data(), val.length(), SQLITE_TRANSIENT); } // u16str_ref template<> struct has_sqlite_type : std::true_type {}; inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, u16str_ref val) { return sqlite3_bind_text16(stmt, inx, val.data(), sizeof(char16_t) * val.length(), SQLITE_TRANSIENT); } // Convert char* to string_view to trigger op<<(..., const str_ref ) template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char16_t(&STR)[N]) { return sqlite3_bind_text16(stmt, inx, &STR[0], sizeof(char16_t) * (N-1), SQLITE_TRANSIENT); } inline std::u16string get_col_from_db(sqlite3_stmt* stmt, int inx, result_type) { if ( sqlite3_column_type(stmt, inx) == SQLITE_NULL ) { return std::u16string(); } char16_t const * ptr = reinterpret_cast(sqlite3_column_text16(stmt, inx)); // call sqlite3_column_text16 explicitely before sqlite3_column_bytes16: it may convert the value to text return std::u16string(ptr, sqlite3_column_bytes16(stmt, inx)); } inline std::u16string get_val_from_db(sqlite3_value *value, result_type) { if ( sqlite3_value_type(value) == SQLITE_NULL ) { return std::u16string(); } char16_t const * ptr = reinterpret_cast(sqlite3_value_text16(value)); return std::u16string(ptr, sqlite3_value_bytes16(value)); } inline void store_result_in_db(sqlite3_context* db, u16str_ref val) { sqlite3_result_text16(db, val.data(), sizeof(char16_t) * val.length(), SQLITE_TRANSIENT); } // Other integer types template struct has_sqlite_type::value>::type> : std::true_type {}; template::value>::type> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const Integral& val) { return bind_col_in_db(stmt, inx, static_cast(val)); } template::type>> inline void store_result_in_db(sqlite3_context* db, const Integral& val) { store_result_in_db(db, static_cast(val)); } template::value>::type> inline Integral get_col_from_db(sqlite3_stmt* stmt, int inx, result_type) { return get_col_from_db(stmt, inx, result_type()); } template::value>::type> inline Integral get_val_from_db(sqlite3_value *value, result_type) { return get_val_from_db(value, result_type()); } // vector template struct has_sqlite_type, SQLITE_BLOB, void> : std::true_type {}; template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::vector& vec) { void const* buf = reinterpret_cast(vec.data()); int bytes = vec.size() * sizeof(T); return sqlite3_bind_blob(stmt, inx, buf, bytes, SQLITE_TRANSIENT); } template inline void store_result_in_db(sqlite3_context* db, const std::vector& vec) { void const* buf = reinterpret_cast(vec.data()); int bytes = vec.size() * sizeof(T); sqlite3_result_blob(db, buf, bytes, SQLITE_TRANSIENT); } template inline std::vector get_col_from_db(sqlite3_stmt* stmt, int inx, result_type>) { if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) { return {}; } T const* buf = reinterpret_cast(sqlite3_column_blob(stmt, inx)); int bytes = sqlite3_column_bytes(stmt, inx); return std::vector(buf, buf + bytes/sizeof(T)); } template inline std::vector get_val_from_db(sqlite3_value *value, result_type>) { if(sqlite3_value_type(value) == SQLITE_NULL) { return {}; } T const* buf = reinterpret_cast(sqlite3_value_blob(value)); int bytes = sqlite3_value_bytes(value); return std::vector(buf, buf + bytes/sizeof(T)); } /* for unique_ptr support */ template struct has_sqlite_type, Type, void> : has_sqlite_type {}; template struct has_sqlite_type, SQLITE_NULL, void> : std::true_type {}; template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::unique_ptr& val) { return val ? bind_col_in_db(stmt, inx, *val) : bind_col_in_db(stmt, inx, nullptr); } template inline std::unique_ptr get_col_from_db(sqlite3_stmt* stmt, int inx, result_type>) { if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) { return nullptr; } return std::make_unique(get_col_from_db(stmt, inx, result_type())); } template inline std::unique_ptr get_val_from_db(sqlite3_value *value, result_type>) { if(sqlite3_value_type(value) == SQLITE_NULL) { return nullptr; } return std::make_unique(get_val_from_db(value, result_type())); } // std::optional support for NULL values #ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT #ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT template using optional = std::experimental::optional; #else template using optional = std::optional; #endif template struct has_sqlite_type, Type, void> : has_sqlite_type {}; template struct has_sqlite_type, SQLITE_NULL, void> : std::true_type {}; template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const optional& val) { return val ? bind_col_in_db(stmt, inx, *val) : bind_col_in_db(stmt, inx, nullptr); } template inline void store_result_in_db(sqlite3_context* db, const optional& val) { if(val) store_result_in_db(db, *val); else sqlite3_result_null(db); } template inline optional get_col_from_db(sqlite3_stmt* stmt, int inx, result_type>) { #ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) { return std::experimental::nullopt; } return std::experimental::make_optional(get_col_from_db(stmt, inx, result_type())); #else if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) { return std::nullopt; } return std::make_optional(get_col_from_db(stmt, inx, result_type())); #endif } template inline optional get_val_from_db(sqlite3_value *value, result_type>) { #ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT if(sqlite3_value_type(value) == SQLITE_NULL) { return std::experimental::nullopt; } return std::experimental::make_optional(get_val_from_db(value, result_type())); #else if(sqlite3_value_type(value) == SQLITE_NULL) { return std::nullopt; } return std::make_optional(get_val_from_db(value, result_type())); #endif } #endif #ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT namespace detail { template struct tag_trait : U { using tag = T; }; } template struct has_sqlite_type, Type, void> : std::disjunction>...> {}; namespace detail { template, Type>> inline std::variant variant_select_type(Callback &&callback) { if constexpr(first_compatible::value) return callback(result_type()); else throw errors::mismatch("The value is unsupported by this variant.", "", SQLITE_MISMATCH); } template inline decltype(auto) variant_select(int type, Callback &&callback) { switch(type) { case SQLITE_NULL: return variant_select_type(std::forward(callback)); case SQLITE_INTEGER: return variant_select_type(std::forward(callback)); case SQLITE_FLOAT: return variant_select_type(std::forward(callback)); case SQLITE_TEXT: return variant_select_type(std::forward(callback)); case SQLITE_BLOB: return variant_select_type(std::forward(callback)); } #ifdef _MSC_VER __assume(false); #else __builtin_unreachable(); #endif } } template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::variant& val) { return std::visit([&](auto &&opt) {return bind_col_in_db(stmt, inx, std::forward(opt));}, val); } template inline void store_result_in_db(sqlite3_context* db, const std::variant& val) { std::visit([&](auto &&opt) {store_result_in_db(db, std::forward(opt));}, val); } template inline std::variant get_col_from_db(sqlite3_stmt* stmt, int inx, result_type>) { return detail::variant_select(sqlite3_column_type(stmt, inx), [&](auto v) { return std::variant(std::in_place_type, get_col_from_db(stmt, inx, v)); }); } template inline std::variant get_val_from_db(sqlite3_value *value, result_type>) { return detail::variant_select(sqlite3_value_type(value), [&](auto v) { return std::variant(std::in_place_type, get_val_from_db(value, v)); }); } #endif } sqlite_modern_cpp-dev/hdr/sqlite_modern_cpp/utility/000077500000000000000000000000001453314505700233375ustar00rootroot00000000000000sqlite_modern_cpp-dev/hdr/sqlite_modern_cpp/utility/function_traits.h000066400000000000000000000023411453314505700267230ustar00rootroot00000000000000#pragma once #include #include namespace sqlite { namespace utility { template struct function_traits; template struct function_traits : public function_traits< decltype(&std::remove_reference::type::operator()) > { }; template < typename ClassType, typename ReturnType, typename... Arguments > struct function_traits< ReturnType(ClassType::*)(Arguments...) const > : function_traits { }; /* support the non-const operator () * this will work with user defined functors */ template < typename ClassType, typename ReturnType, typename... Arguments > struct function_traits< ReturnType(ClassType::*)(Arguments...) > : function_traits { }; template < typename ReturnType, typename... Arguments > struct function_traits< ReturnType(*)(Arguments...) > { typedef ReturnType result_type; using argument_tuple = std::tuple; template using argument = typename std::tuple_element< Index, argument_tuple >::type; static const std::size_t arity = sizeof...(Arguments); }; } } sqlite_modern_cpp-dev/hdr/sqlite_modern_cpp/utility/uncaught_exceptions.h000066400000000000000000000021231453314505700275650ustar00rootroot00000000000000#pragma once #include #include #include // Consider that std::uncaught_exceptions is available if explicitly indicated // by the standard library, if compiler advertises full C++17 support or, as a // special case, for MSVS 2015+ (which doesn't define __cplusplus correctly by // default as of 2017.7 version and couldn't do it at all until it). #ifndef MODERN_SQLITE_UNCAUGHT_EXCEPTIONS_SUPPORT #ifdef __cpp_lib_uncaught_exceptions #define MODERN_SQLITE_UNCAUGHT_EXCEPTIONS_SUPPORT #elif __cplusplus >= 201703L #define MODERN_SQLITE_UNCAUGHT_EXCEPTIONS_SUPPORT #elif defined(_MSC_VER) && _MSC_VER >= 1900 #define MODERN_SQLITE_UNCAUGHT_EXCEPTIONS_SUPPORT #endif #endif namespace sqlite { namespace utility { #ifdef MODERN_SQLITE_UNCAUGHT_EXCEPTIONS_SUPPORT class UncaughtExceptionDetector { public: operator bool() { return count != std::uncaught_exceptions(); } private: int count = std::uncaught_exceptions(); }; #else class UncaughtExceptionDetector { public: operator bool() { return std::uncaught_exception(); } }; #endif } } sqlite_modern_cpp-dev/hdr/sqlite_modern_cpp/utility/utf16_utf8.h000066400000000000000000000026001453314505700254210ustar00rootroot00000000000000#pragma once #include #include #include #include "../errors.h" namespace sqlite { namespace utility { inline std::string utf16_to_utf8(u16str_ref input) { struct : std::codecvt { } codecvt; std::mbstate_t state{}; std::string result((std::max)(input.size() * 3 / 2, std::size_t(4)), '\0'); const char16_t *remaining_input = input.data(); std::size_t produced_output = 0; while(true) { char *used_output; switch(codecvt.out(state, remaining_input, input.data() + input.size(), remaining_input, &result[produced_output], &result[result.size() - 1] + 1, used_output)) { case std::codecvt_base::ok: result.resize(used_output - result.data()); return result; case std::codecvt_base::noconv: // This should be unreachable case std::codecvt_base::error: throw errors::invalid_utf16("Invalid UTF-16 input", ""); case std::codecvt_base::partial: if(used_output == result.data() + produced_output) throw errors::invalid_utf16("Unexpected end of input", ""); produced_output = used_output - result.data(); result.resize( result.size() + (std::max)((input.data() + input.size() - remaining_input) * 3 / 2, std::ptrdiff_t(4))); } } } } // namespace utility } // namespace sqlite sqlite_modern_cpp-dev/tests/000077500000000000000000000000001453314505700165125ustar00rootroot00000000000000sqlite_modern_cpp-dev/tests/blob_example.cc000066400000000000000000000025521453314505700214560ustar00rootroot00000000000000#include #include #include #include #include #include using namespace sqlite; using namespace std; TEST_CASE("Blob does work", "[blob]") { database db(":memory:"); db << "CREATE TABLE person (name TEXT, numbers BLOB);"; db << "INSERT INTO person VALUES (?, ?)" << "bob" << vector { 1, 2, 3, 4}; db << "INSERT INTO person VALUES (?, ?)" << "jack" << vector { '1', '2', '3', '4'}; db << "INSERT INTO person VALUES (?, ?)" << "sara" << vector { 1.0, 2.0, 3.0, 4.0}; vector numbers_bob; db << "SELECT numbers from person where name = ?;" << "bob" >> numbers_bob; REQUIRE(numbers_bob.size() == 4); REQUIRE((numbers_bob[0] == 1 && numbers_bob[1] == 2 && numbers_bob[2] == 3 && numbers_bob[3] == 4)); vector numbers_jack; db << "SELECT numbers from person where name = ?;" << "jack" >> numbers_jack; REQUIRE(numbers_jack.size() == 4); REQUIRE((numbers_jack[0] == '1' && numbers_jack[1] == '2' && numbers_jack[2] == '3' && numbers_jack[3] == '4')); vector numbers_sara; db << "SELECT numbers from person where name = ?;" << "sara" >> numbers_sara; REQUIRE(numbers_sara.size() == 4); REQUIRE((numbers_sara[0] == 1.0 && numbers_sara[1] == 2.0 && numbers_sara[2] == 3.0 && numbers_sara[3] == 4.0)); } sqlite_modern_cpp-dev/tests/error_log.cc000066400000000000000000000037441453314505700210230ustar00rootroot00000000000000#include #include #include #include #include #include #include #include using namespace sqlite; using namespace std; struct TrackErrors { TrackErrors() : constraint_called{false}, primarykey_called{false} { error_log( [this](errors::constraint) { constraint_called = true; }, [this](errors::constraint_primarykey e) { primarykey_called = true; } // We are not registering the unique key constraint: // For a unique key error the first handler (errors::constraint) will be called instead. ); } bool constraint_called; bool primarykey_called; /* bool unique_called; */ }; // Run before main, before any other sqlite function. static TrackErrors track; TEST_CASE("error_log works", "[log]") { database db(":memory:"); db << "CREATE TABLE person (id integer primary key not null, name TEXT unique);"; SECTION("An extended error code gets called when registered") { try { db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "jack"; // triger primarykey constraint of 'id' db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "bob"; } catch (const errors::constraint& e) { } REQUIRE(track.primarykey_called == true); REQUIRE(track.constraint_called == false); track.primarykey_called = false; } SECTION("Parent gets called when the exact error code is not registered") { try { db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "jack"; // trigger unique constraint of 'name' db << "INSERT INTO person (id,name) VALUES (?,?)" << 2 << "jack"; } catch (const errors::constraint& e) { } REQUIRE(track.primarykey_called == false); REQUIRE(track.constraint_called == true); track.constraint_called = false; } } sqlite_modern_cpp-dev/tests/exception_dont_execute.cc000066400000000000000000000011041453314505700235610ustar00rootroot00000000000000#include #include #include #include #include #include using namespace sqlite; using namespace std; TEST_CASE("Prepared statement will not execute on exceptions", "[prepared_statements]") { database db(":memory:"); db << "CREATE TABLE person (id integer primary key not null, name TEXT not null);"; try { auto stmt = db << "INSERT INTO person (id,name) VALUES (?,?)"; throw 1; } catch (int) { } int count; db << "select count(*) from person" >> count; REQUIRE(count == 0); } sqlite_modern_cpp-dev/tests/exception_dont_execute_nested.cc000066400000000000000000000011601453314505700251250ustar00rootroot00000000000000#include #include #include #include #include #include using namespace sqlite; using namespace std; struct A { ~A() { database db(":memory:"); db << "CREATE TABLE person (id integer primary key not null, name TEXT not null);"; try { auto stmt = db << "INSERT INTO person (id,name) VALUES (?,?)"; throw 1; } catch (int) { } } }; TEST_CASE("Nested prepered statements wont execute", "[nested_prepared_statements]") { #ifdef MODERN_SQLITE_UNCAUGHT_EXCEPTIONS_SUPPORT try { A a; throw 1; } catch(int) { } #else #endif } sqlite_modern_cpp-dev/tests/exceptions.cc000066400000000000000000000031461453314505700212060ustar00rootroot00000000000000#include #include #include #include #include #include #include using namespace sqlite; using namespace std; TEST_CASE("exceptions are thrown", "[exceptions]") { database db(":memory:"); db << "CREATE TABLE person (id integer primary key not null, name TEXT);"; bool expception_thrown = false; std::string get_sql_result; #if SQLITE_VERSION_NUMBER >= 3014000 std::string expedted_sql = "INSERT INTO person (id,name) VALUES (1,'jack')"; #else std::string expedted_sql = "INSERT INTO person (id,name) VALUES (?,?)"; #endif SECTION("Parent exception works") { try { db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "jack"; // inserting again to produce error db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "jack"; } catch (errors::constraint& e) { expception_thrown = true; get_sql_result = e.get_sql(); } REQUIRE(expception_thrown == true); REQUIRE(get_sql_result == expedted_sql); } SECTION("Extended exception works") { try { db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "jack"; // inserting again to produce error db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "jack"; } catch (errors::constraint_primarykey& e) { expception_thrown = true; get_sql_result = e.get_sql(); } REQUIRE(expception_thrown == true); REQUIRE(get_sql_result == expedted_sql); } } sqlite_modern_cpp-dev/tests/flags.cc000066400000000000000000000042461453314505700201230ustar00rootroot00000000000000#include #include #include #include #include #include using namespace sqlite; using namespace std; struct TmpFile { string fname; TmpFile(): fname("./flags.db") { } ~TmpFile() { remove(fname.c_str()); } }; #ifdef _WIN32 #define OUR_UTF16 "UTF-16le" #elif BYTE_ORDER == BIG_ENDIAN #define OUR_UTF16 "UTF-16be" #else #define OUR_UTF16 "UTF-16le" #endif TEST_CASE("flags work", "[flags]") { TmpFile file; sqlite::sqlite_config cfg; std::string enc; SECTION("PRAGMA endcoding is UTF-8 for string literals") { database db(":memory:", cfg); db << "PRAGMA encoding;" >> enc; REQUIRE(enc == "UTF-8"); } SECTION("encoding is UTF-16 for u"" prefixed string literals") { database db(u":memory:", cfg); db << "PRAGMA encoding;" >> enc; REQUIRE(enc == OUR_UTF16); } SECTION("we can set encoding to UTF-8 with flags") { cfg.encoding = Encoding::UTF8; database db(u":memory:", cfg); db << "PRAGMA encoding;" >> enc; REQUIRE(enc == "UTF-8"); } SECTION("we can set encoding to UTF-16 with flags") { cfg.encoding = Encoding::UTF16; database db(u":memory:", cfg); db << "PRAGMA encoding;" >> enc; REQUIRE(enc == OUR_UTF16); } SECTION("we can set encoding to UTF-16 with flags for on disk databases") { cfg.encoding = Encoding::UTF16; database db(file.fname, cfg); db << "PRAGMA encoding;" >> enc; REQUIRE(enc == OUR_UTF16); } SECTION("READONLY flag works") { { database db(file.fname, cfg); db << "CREATE TABLE foo (a string);"; db << "INSERT INTO foo VALUES (?)" << "hello"; } cfg.flags = sqlite::OpenFlags::READONLY; database db(file.fname, cfg); string str; db << "SELECT a FROM foo;" >> str; REQUIRE(str == "hello"); bool failed = false; try { db << "INSERT INTO foo VALUES (?)" << "invalid"; } catch(errors::readonly&) { failed = true; } REQUIRE(failed == true); } } sqlite_modern_cpp-dev/tests/functions.cc000066400000000000000000000034061453314505700210340ustar00rootroot00000000000000#include #include #include #include #include using namespace sqlite; using namespace std; int add_integers(int i, int j) { return i+j; } TEST_CASE("sql functions work", "[functions]") { database db(":memory:"); db.define("my_new_concat", [](std::string i, std::string j) {return i+j;}); db.define("my_new_concat", [](std::string i, std::string j, std::string k) {return i+j+k;}); db.define("add_integers", &add_integers); std::string test1, test3; int test2 = 0; db << "select my_new_concat('Hello ','world!')" >> test1; db << "select add_integers(1,1)" >> test2; db << "select my_new_concat('a','b','c')" >> test3; REQUIRE(test1 == "Hello world!"); REQUIRE(test2 == 2); REQUIRE(test3 == "abc"); db.define("my_count", [](int &i, int) {++i;}, [](int &i) {return i;}); db.define("my_concat_aggregate", [](std::string &stored, std::string current) {stored += current;}, [](std::string &stored) {return stored;}); db << "create table countable(i, s)"; db << "insert into countable values(1, 'a')"; db << "insert into countable values(2, 'b')"; db << "insert into countable values(3, 'c')"; db << "select my_count(i) from countable" >> test2; db << "select my_concat_aggregate(s) from countable order by i" >> test3; REQUIRE(test2 == 3); REQUIRE(test3 == "abc"); db.define("tgamma", [](double i) {return std::tgamma(i);}); db << "CREATE TABLE numbers (number INTEGER);"; for(auto i=0; i!=10; ++i) db << "INSERT INTO numbers VALUES (?);" << i; db << "SELECT number, tgamma(number+1) FROM numbers;" >> [](double number, double factorial) { /* cout << number << "! = " << factorial << '\n'; */ }; } sqlite_modern_cpp-dev/tests/functors.cc000066400000000000000000000017311453314505700206660ustar00rootroot00000000000000#include #include #include #include #include #include using namespace sqlite; using namespace std; struct tbl_functor { explicit tbl_functor(vector > &vec_) : vec(vec_) { } void operator() ( int id, string name) { vec.push_back(make_pair(id, move(name))); } vector > &vec; }; TEST_CASE("functors work", "[functors]") { database db(":memory:"); db << "CREATE TABLE tbl (id integer, name string);"; db << "INSERT INTO tbl VALUES (?, ?);" << 1 << "hello"; db << "INSERT INTO tbl VALUES (?, ?);" << 2 << "world"; vector > vec; db << "select id,name from tbl;" >> tbl_functor(vec); REQUIRE(vec.size() == 2); vec.clear(); tbl_functor functor(vec); db << "select id,name from tbl;" >> functor; REQUIRE(vec.size() == 2); REQUIRE(vec[0].first == 1); REQUIRE(vec[0].second == "hello"); } sqlite_modern_cpp-dev/tests/lvalue_functor.cc000066400000000000000000000025111453314505700220500ustar00rootroot00000000000000#include #include #include #include #include using namespace sqlite; using namespace std; template struct builder { vector results; void operator()(AttrTypes... args) { results.emplace_back(std::forward(args)...); }; }; struct user { int age; string name; double weight; user(int age, string name, double weight) : age(age), name(name), weight(weight) { } static std::vector all(sqlite::database& db) { builder person_builder; db << "SELECT * FROM user;" >> person_builder; return std::move(person_builder.results); // move to avoid copying data ;-) }; }; TEST_CASE("lvalue functors work", "[lvalue_functor]") { database db(":memory:"); db << "create table if not exists user (" " age int," " name text," " weight real" ");"; db << "insert into user (age,name,weight) values (?,?,?);" << 20 << u"chandler" << 83.25; db << "insert into user (age,name,weight) values (?,?,?);" << 21 << u"monika" << 86.25; db << "insert into user (age,name,weight) values (?,?,?);" << 22 << u"ross" << 88.25; auto users = user::all(db); REQUIRE(users.size() == 3); } sqlite_modern_cpp-dev/tests/mov_ctor.cc000066400000000000000000000014541453314505700206550ustar00rootroot00000000000000// Fixing https://github.com/SqliteModernCpp/sqlite_modern_cpp/issues/63 #include #include #include #include #include using namespace sqlite; using namespace std; struct dbFront { std::unique_ptr storedProcedure; database db; dbFront(): db(":memory:") { db << "CREATE TABLE tbl (id integer, name string);"; // the temporary moved object should not run _execute() function on destruction. storedProcedure = std::make_unique( db << "INSERT INTO tbl VALUES (?, ?);" ); } }; TEST_CASE("database lifecycle", "move_ctor") { bool failed = false; try { dbFront dbf; } catch(const sqlite_exception& e) { failed = true; } catch(...) { failed = true; } REQUIRE(failed == false); } sqlite_modern_cpp-dev/tests/named.cc000066400000000000000000000007371453314505700201140ustar00rootroot00000000000000#include #include #include #include using namespace sqlite; using namespace std; TEST_CASE("binding named parameters works", "[named]") { database db(":memory:"); db << "CREATE TABLE foo (a,b);"; int a = 1; db << "INSERT INTO foo VALUES (:first,:second)" << named_parameter(":second", 2) << named_parameter(":first", a); db << "SELECT b FROM foo WHERE a=?;" << 1 >> a; REQUIRE(a == 2); }sqlite_modern_cpp-dev/tests/nullptr_uniqueptr.cc000066400000000000000000000020511453314505700226330ustar00rootroot00000000000000#include #include #include #include #include using namespace std; using namespace sqlite; TEST_CASE("nullptr & unique_ptr", "[null_ptr_unique_ptr]") { database db(":memory:"); db << "CREATE TABLE tbl (id integer,age integer, name string, img blob);"; db << "INSERT INTO tbl VALUES (?, ?, ?, ?);" << 1 << 24 << "bob" << vector { 1, 2 , 3}; unique_ptr ptr_null; db << "INSERT INTO tbl VALUES (?, ?, ?, ?);" << 2 << nullptr << ptr_null << nullptr; db << "select age,name,img from tbl where id = 1" >> [](unique_ptr age_p, unique_ptr name_p, unique_ptr> img_p) { REQUIRE(age_p != nullptr); REQUIRE(name_p != nullptr); REQUIRE(img_p != nullptr); }; db << "select age,name,img from tbl where id = 2" >> [](unique_ptr age_p, unique_ptr name_p, unique_ptr> img_p) { REQUIRE(age_p == nullptr); REQUIRE(name_p == nullptr); REQUIRE(img_p == nullptr); }; } sqlite_modern_cpp-dev/tests/prepared_statment.cc000066400000000000000000000051051453314505700225430ustar00rootroot00000000000000#include #include #include #include using namespace sqlite; using namespace std; TEST_CASE("prepared statements work", "[prepared_statement]") { database db(":memory:"); auto pps = db << "select ?"; // get a prepared parsed and ready statment int test = 4; pps << test; // set a bound var pps >> test; // execute statement REQUIRE(test == 4); pps << 4; // bind a rvalue pps++; // and execute pps << 8 >> test; REQUIRE(test == 8); auto pps2 = db << "select 1,2"; // multiple extract test pps2 >> [](int a, int b) { REQUIRE(a == 1); REQUIRE(b == 2); }; auto pps3 = db << "select ?,?,?"; test = 2; pps3 << 1 << test << 3 >> [](int a, int b, int c) { REQUIRE(a == 1); REQUIRE(b == 2); REQUIRE(c == 3); }; test = 1; db << "select ?,?" << test << 5 >> test; // and mow everything together REQUIRE(test == 1); test = 2; db << "select ?,?,?" << 1 << test << 3 >> [](int a, int b, int c) { REQUIRE(a == 1); REQUIRE(b == 2); REQUIRE(c == 3); }; db << "select ?" << test; // noVal db << "select ?,?" << test << 1; db << "select ?,?" << 1 << test; db << "select ?,?" << 1 << 1; db << "select ?,?" << test << test; db << "select ?" << test >> test; // lVal db << "select ?,?" << test << 1 >> test; db << "select ?,?" << 1 << test >> test; db << "select ?,?" << 1 << 1 >> test; db << "select ?,?" << test << test >> test; int q = 0; test = 1; db << "select ?" << test >> [&](int t) { q = t; }; // rVal REQUIRE(q == 1); db << "select ?,?" << test << 1 >> [&](int t, int p) { q = t + p; }; db << "select ?,?" << 1 << test >> [&](int t, int p) { q = t + p; }; db << "select ?,?" << 1 << 1 >> [&](int t, int p) { q = t + p; }; db << "select ?,?" << test << test >> [&](int t, int p) { q = t + p; }; db << "select ?,?,?" << test << 1 << test; // mix db << "select ?,?,?" << 1 << test << 1; db << "select ?,?,?" << 1 << 1 << test; db << "select ?,?,?" << 1 << 1 << 1; db << "select ?,?,?" << test << test << test; { auto pps4 = db << "select ?,?,?"; // reuse (pps4 << test << 1 << test)++; (pps4 << 1 << test << 1)++; (pps4 << 1 << 1 << test)++; (pps4 << 1 << 1 << 1)++; (pps4 << test << test << test)++; } { auto prep = db << "select ?"; prep << 5; prep.execute(); prep << 6; prep.execute(); } } sqlite_modern_cpp-dev/tests/readme_example.cc000066400000000000000000000036321453314505700217750ustar00rootroot00000000000000#define CATCH_CONFIG_MAIN #include #include #include using namespace sqlite; using namespace std; TEST_CASE("README Example Works", "[readme]") { database db(":memory:"); db << "create table if not exists user (" " _id integer primary key autoincrement not null," " age int," " name text," " weight real" ");"; db << "insert into user (age,name,weight) values (?,?,?);" << 20 << u"bob" << 83.25; int age = 22; float weight = 68.5; string name = "jack"; db << u"insert into user (age,name,weight) values (?,?,?);" // utf16 query string << age << name << weight; REQUIRE(db.last_insert_rowid() != 0); db << "select age,name,weight from user where age > ? ;" << 21 >> [&](int _age, string _name, double _weight) { REQUIRE((_age == age && _name == name)); }; for(auto &&row : db << "select age,name,weight from user where age > ? ;" << 21) { int _age; string _name; double _weight; row >> _age >> _name >> _weight; REQUIRE((_age == age && _name == name)); } for(std::tuple row : db << "select age,name,weight from user where age > ? ;" << 21) { REQUIRE((std::get(row) == age && std::get(row) == name)); } // selects the count(*) from user table // note that you can extract a single culumn single row result only to : int,long,long,float,double,string,u16string int count = 0; db << "select count(*) from user" >> count; REQUIRE(count == 2); db << "select age, name from user where _id=1;" >> tie(age, name); // this also works and the returned value will be automatically converted to string string str_count; db << "select count(*) from user" >> str_count; REQUIRE(str_count == string{"2"}); } sqlite_modern_cpp-dev/tests/shared_connection.cc000066400000000000000000000010411453314505700225020ustar00rootroot00000000000000#include #include #include #include #include using namespace sqlite; using namespace std; TEST_CASE("shared connections work fine", "[shared_connection]") { database db(":memory:"); { auto con = db.connection(); { database db2(con); int test = 0; db2 << "select 1" >> test; REQUIRE(test == 1); } int test = 0; db << "select 1" >> test; REQUIRE(test == 1); } } sqlite_modern_cpp-dev/tests/simple_examples.cc000066400000000000000000000015751453314505700222200ustar00rootroot00000000000000#include #include #include #include #include using namespace sqlite; using namespace std; TEST_CASE("simple examples", "[examples]") { database db(":memory:"); db << "CREATE TABLE foo (a integer, b string);\n"; db << "INSERT INTO foo VALUES (?, ?)" << 1 << "hello"; db << "INSERT INTO foo VALUES (?, ?)" << 2 << "world"; string str; db << "SELECT b from FOO where a=?;" << 2L >> str; REQUIRE(str == "world"); std::string sql("select 1+1"); long test = 0; db << sql >> test; REQUIRE(test == 2); db << "UPDATE foo SET b=? WHERE a=?;" << "hi" << 1L; db << "SELECT b FROM foo WHERE a=?;" << 1L >> str; REQUIRE(str == "hi"); REQUIRE(db.rows_modified() == 1); db << "UPDATE foo SET b=?;" << "hello world"; REQUIRE(db.rows_modified() == 2); } sqlite_modern_cpp-dev/tests/sqlcipher.cc000066400000000000000000000025371453314505700210220ustar00rootroot00000000000000#include #include #include #include #include using namespace sqlite; using namespace std; struct TmpFile { string fname; TmpFile(): fname("./sqlcipher.db") { } ~TmpFile() { remove(fname.c_str()); } }; TEST_CASE("sqlcipher works", "[sqlcipher]") { TmpFile file; sqlcipher_config config; { config.key = "DebugKey"; sqlcipher_database db(file.fname, config); db << "CREATE TABLE foo (a integer, b string);"; db << "INSERT INTO foo VALUES (?, ?)" << 1 << "hello"; db << "INSERT INTO foo VALUES (?, ?)" << 2 << "world"; string str; db << "SELECT b from FOO where a=?;" << 2 >> str; REQUIRE(str == "world"); } bool failed = false; try { config.key = "DebugKey2"; sqlcipher_database db(file.fname, config); db << "INSERT INTO foo VALUES (?, ?)" << 3 << "fail"; } catch(const errors::notadb&) { failed = true; // Expected, wrong key } REQUIRE(failed == true); { config.key = "DebugKey"; sqlcipher_database db(file.fname, config); db.rekey("DebugKey2"); } { config.key = "DebugKey2"; sqlcipher_database db(file.fname, config); db << "INSERT INTO foo VALUES (?, ?)" << 3 << "fail"; } } sqlite_modern_cpp-dev/tests/std_optional.cc000066400000000000000000000014621453314505700215230ustar00rootroot00000000000000#include #include #include using namespace sqlite; using namespace std; #ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT TEST_CASE("std::optional works", "[optional]") { database db(":memory:"); db << "drop table if exists test"; db << "create table if not exists test (" " id integer primary key," " val int" ");"; db << "insert into test(id,val) values(?,?)" << 1 << 5; db << "select id,val from test" >> [&](long long, sqlite::optional val) { REQUIRE(val); }; db << "delete from test where id = 1"; db << "insert into test(id,val) values(?,?)" << 1 << nullptr; db << "select id,val from test" >> [&](long long, sqlite::optional val) { REQUIRE(!val); }; } #endif sqlite_modern_cpp-dev/tests/string_view.cc000066400000000000000000000012671453314505700213670ustar00rootroot00000000000000#include #include #ifdef MODERN_SQLITE_STRINGVIEW_SUPPORT #include using namespace sqlite; TEST_CASE("std::string_view works", "[string_view]") { database db(":memory:"); db << "CREATE TABLE foo (a integer, b string);\n"; const std::string_view test1 = "null terminated string view"; db << "INSERT INTO foo VALUES (?, ?)" << 1 << test1; std::string str; db << "SELECT b from FOO where a=?;" << 1 >> str; REQUIRE(test1 == str); const char s[] = "hello world"; std::string_view test2(&s[0], 2); db << "INSERT INTO foo VALUES (?,?)" << 2 << test2; db << "SELECT b from FOO where a=?" << 2 >> str; REQUIRE(str == "he"); } #endif sqlite_modern_cpp-dev/tests/trycatchblocks.cc000066400000000000000000000044161453314505700220450ustar00rootroot00000000000000#include #include #include #include #include #include using namespace sqlite; using std::string; struct TmpFile { string fname; TmpFile(): fname("./trycatchblocks.db") {} ~TmpFile() { remove(fname.c_str()); } }; class DBInterface { database db; public: DBInterface( const string& fileName ) : db( fileName ) { } void LogRequest( const string& username, const string& ip, const string& request ) { try { auto timestamp = std::to_string( time( nullptr ) ); db << "create table if not exists log_request (" " _id integer primary key autoincrement not null," " username text," " timestamp text," " ip text," " request text" ");"; db << "INSERT INTO log_request (username, timestamp, ip, request) VALUES (?,?,?,?);" << username << timestamp << ip << request; } catch ( const std::exception& e ) { std::cout << e.what() << std::endl; } } bool TestData( void ) { try { string username, timestamp, ip, request; db << "select username, timestamp, ip, request from log_request where username = ?" << "test" >> std::tie(username, timestamp, ip, request); if ( username == "test" && ip == "127.0.0.1" && request == "hello world" ) { return true; } } catch ( const std::exception& e ) { std::cout << e.what() << std::endl; } return false; } }; TEST_CASE("try catch blocks", "[trycatchblocks]") { // -------------------------------------------------------------------------- // -- Test if writing to disk works properly from within a catch block. // -------------------------------------------------------------------------- try { throw "hello"; } catch ( ... ) { TmpFile tmpF; DBInterface interf(tmpF.fname); interf.LogRequest( "test", "127.0.0.1", "hello world" ); REQUIRE(interf.TestData() == true); } } sqlite_modern_cpp-dev/tests/variant.cc000066400000000000000000000026051453314505700204700ustar00rootroot00000000000000#include #include #include #include using namespace sqlite; using namespace std; #ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT TEST_CASE("std::variant works", "[variant]") { database db(":memory:"); db << "CREATE TABLE foo (a);"; std::variant> v; v = 1; db << "INSERT INTO foo VALUES (?)" << v; v = "a"; db << "INSERT INTO foo VALUES (?)" << v; db << "SELECT a FROM foo WHERE a=?;" << 1 >> v; REQUIRE(v.index() == 1); REQUIRE(std::get<1>(v) == 1); db << "SELECT NULL" >> v; REQUIRE(!std::get<2>(v)); db << "SELECT 0.0" >> v; REQUIRE(std::get<2>(v)); } TEST_CASE("std::monostate is a nullptr substitute", "[monostate]") { database db(":memory:"); db << "CREATE TABLE foo (a);"; std::variant v; v=std::monostate(); db << "INSERT INTO foo VALUES (?)" << v; db << "INSERT INTO foo VALUES (?)" << "This isn't a monostate!"; bool found_null = false, found_string = false; db << "SELECT * FROM foo" >> [&](std::variant z) { if(z.index() == 0) { found_null = true; } else { found_string = true; } }; REQUIRE((found_null && found_string)); db << "SELECT NULL" >> v; REQUIRE(v.index() == 0); } #endif