mirror of
https://github.com/ikawrakow/ik_llama.cpp.git
synced 2026-03-05 03:20:00 +00:00
Pull in full sqlite_modern_cpp repo for the license as it is not attached to source files
This commit is contained in:
@@ -95,5 +95,6 @@ if (LLAMA_CURL)
|
||||
endif ()
|
||||
|
||||
target_include_directories(${TARGET} PUBLIC .)
|
||||
target_include_directories(${TARGET} PUBLIC ./sqlite_modern_cpp/hdr)
|
||||
target_compile_features (${TARGET} PUBLIC cxx_std_11)
|
||||
target_link_libraries (${TARGET} PRIVATE ${LLAMA_COMMON_EXTRA_LIBS} PUBLIC llama Threads::Threads)
|
||||
|
||||
9
common/sqlite_modern_cpp/.gitignore
vendored
Normal file
9
common/sqlite_modern_cpp/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
.deps
|
||||
|
||||
config.log
|
||||
|
||||
config.status
|
||||
compile_commands.json
|
||||
build/
|
||||
|
||||
26
common/sqlite_modern_cpp/.travis.yml
Normal file
26
common/sqlite_modern_cpp/.travis.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
language: 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
|
||||
100
common/sqlite_modern_cpp/.ycm_extra_conf.py
Normal file
100
common/sqlite_modern_cpp/.ycm_extra_conf.py
Normal file
@@ -0,0 +1,100 @@
|
||||
import 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
|
||||
}
|
||||
56
common/sqlite_modern_cpp/CMakeLists.txt
Normal file
56
common/sqlite_modern_cpp/CMakeLists.txt
Normal file
@@ -0,0 +1,56 @@
|
||||
cmake_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 $<$<CXX_COMPILER_ID:MSVC>:/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()
|
||||
21
common/sqlite_modern_cpp/License.txt
Normal file
21
common/sqlite_modern_cpp/License.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT 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.
|
||||
482
common/sqlite_modern_cpp/README.md
Normal file
482
common/sqlite_modern_cpp/README.md
Normal file
@@ -0,0 +1,482 @@
|
||||
[](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<iostream>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
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<int, string, double> 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,decltype(&sqlite3_backup_finish)>(
|
||||
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<T>` 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<int> { 1, 2, 3, 4};
|
||||
db << "INSERT INTO person VALUES (?, ?)" << "sara" << vector<double> { 1.0, 2.0, 3.0, 4.0};
|
||||
|
||||
vector<int> numbers_bob;
|
||||
db << "SELECT numbers from person where name = ?;" << "bob" >> numbers_bob;
|
||||
|
||||
db << "SELECT numbers from person where name = ?;" << "sara" >> [](vector<double> 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<T>` 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<int> { 1, 2 , 3};
|
||||
unique_ptr<string> ptr_null; // you can even bind empty unique_ptr<T>
|
||||
db << "INSERT INTO tbl VALUES (?, ?, ?, ?);" << 2 << nullptr << ptr_null << nullptr;
|
||||
|
||||
db << "select age,name,img from tbl where id = 1"
|
||||
>> [](unique_ptr<int> age_p, unique_ptr<string> name_p, unique_ptr<vector<int>> 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<int> age_p, unique_ptr<string> name_p, unique_ptr<vector<int>> 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<iostream>
|
||||
#include <sqlite_modern_cpp/sqlcipher.h>
|
||||
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<T>` as an alternative for `std::unique_ptr<T>` to work with NULL values.
|
||||
|
||||
```c++
|
||||
#include <sqlite_modern_cpp.h>
|
||||
|
||||
struct User {
|
||||
long long _id;
|
||||
std::optional<int> age;
|
||||
std::optional<string> name;
|
||||
std::optional<real> 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<int> age,
|
||||
std::optional<string> name
|
||||
std::optional<real> 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<int> { 1, 2, 3};
|
||||
db << "INSERT INTO tbl VALUES (?, ?);" << 2 << 2.5;
|
||||
|
||||
db << "select data from tbl where id = 1"
|
||||
>> [](std::variant<vector<int>, double> data) {
|
||||
if(data.index() != 1) {
|
||||
cerr << "ERROR: we expected a blob" << std::endl;
|
||||
}
|
||||
|
||||
for(auto i : get<vector<int>>(data)) cout << i << ","; cout << endl;
|
||||
};
|
||||
|
||||
db << "select data from tbl where id = 2"
|
||||
>> [](std::variant<vector<int>, double> data) {
|
||||
if(data.index() != 2) {
|
||||
cerr << "ERROR: we expected a real number" << std::endl;
|
||||
}
|
||||
|
||||
cout << get<double>(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<nullptr_t, sqlite_int64, double, string, vector<char>`.
|
||||
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 `<sqlite_modern_cpp/log.h>` 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)
|
||||
175
common/sqlite_modern_cpp/cmake/Catch.cmake
Normal file
175
common/sqlite_modern_cpp/cmake/Catch.cmake
Normal file
@@ -0,0 +1,175 @@
|
||||
# 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 ``<target>_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 ``<target>_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=$<TARGET_FILE:${TARGET}>"
|
||||
-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
|
||||
)
|
||||
77
common/sqlite_modern_cpp/cmake/CatchAddTests.cmake
Normal file
77
common/sqlite_modern_cpp/cmake/CatchAddTests.cmake
Normal file
@@ -0,0 +1,77 @@
|
||||
# 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}")
|
||||
542
common/sqlite_modern_cpp/cmake/HunterGate.cmake
Normal file
542
common/sqlite_modern_cpp/cmake/HunterGate.cmake
Normal file
@@ -0,0 +1,542 @@
|
||||
# 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()
|
||||
185
common/sqlite_modern_cpp/cmake/ParseAndAddCatchTests.cmake
Normal file
185
common/sqlite_modern_cpp/cmake/ParseAndAddCatchTests.cmake
Normal file
@@ -0,0 +1,185 @@
|
||||
#==================================================================================================#
|
||||
# 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()
|
||||
36
common/sqlite_modern_cpp/tests/blob_example.cc
Normal file
36
common/sqlite_modern_cpp/tests/blob_example.cc
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <catch2/catch.hpp>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
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<int> { 1, 2, 3, 4};
|
||||
db << "INSERT INTO person VALUES (?, ?)" << "jack" << vector<char> { '1', '2', '3', '4'};
|
||||
db << "INSERT INTO person VALUES (?, ?)" << "sara" << vector<double> { 1.0, 2.0, 3.0, 4.0};
|
||||
|
||||
vector<int> 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<char> 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<double> 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));
|
||||
}
|
||||
63
common/sqlite_modern_cpp/tests/error_log.cc
Normal file
63
common/sqlite_modern_cpp/tests/error_log.cc
Normal file
@@ -0,0 +1,63 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <sqlite_modern_cpp/log.h>
|
||||
#include <catch2/catch.hpp>
|
||||
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;
|
||||
}
|
||||
}
|
||||
23
common/sqlite_modern_cpp/tests/exception_dont_execute.cc
Normal file
23
common/sqlite_modern_cpp/tests/exception_dont_execute.cc
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <catch2/catch.hpp>
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <catch2/catch.hpp>
|
||||
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
|
||||
}
|
||||
51
common/sqlite_modern_cpp/tests/exceptions.cc
Normal file
51
common/sqlite_modern_cpp/tests/exceptions.cc
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <catch2/catch.hpp>
|
||||
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);
|
||||
}
|
||||
}
|
||||
80
common/sqlite_modern_cpp/tests/flags.cc
Normal file
80
common/sqlite_modern_cpp/tests/flags.cc
Normal file
@@ -0,0 +1,80 @@
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <sys/types.h>
|
||||
#include <catch2/catch.hpp>
|
||||
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);
|
||||
}
|
||||
}
|
||||
51
common/sqlite_modern_cpp/tests/functions.cc
Normal file
51
common/sqlite_modern_cpp/tests/functions.cc
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <catch2/catch.hpp>
|
||||
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'; */
|
||||
};
|
||||
}
|
||||
39
common/sqlite_modern_cpp/tests/functors.cc
Normal file
39
common/sqlite_modern_cpp/tests/functors.cc
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
using namespace sqlite;
|
||||
using namespace std;
|
||||
|
||||
struct tbl_functor {
|
||||
explicit tbl_functor(vector<pair<int, string> > &vec_) : vec(vec_) { }
|
||||
|
||||
void operator() ( int id, string name) {
|
||||
vec.push_back(make_pair(id, move(name)));
|
||||
}
|
||||
vector<pair<int,string> > &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<pair<int,string> > 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");
|
||||
}
|
||||
52
common/sqlite_modern_cpp/tests/lvalue_functor.cc
Normal file
52
common/sqlite_modern_cpp/tests/lvalue_functor.cc
Normal file
@@ -0,0 +1,52 @@
|
||||
#include<iostream>
|
||||
#include<sqlite_modern_cpp.h>
|
||||
#include<string>
|
||||
#include<vector>
|
||||
#include<catch2/catch.hpp>
|
||||
using namespace sqlite;
|
||||
using namespace std;
|
||||
|
||||
template<typename Target, typename... AttrTypes>
|
||||
struct builder {
|
||||
vector<Target> results;
|
||||
|
||||
void operator()(AttrTypes... args) {
|
||||
results.emplace_back(std::forward<AttrTypes&&>(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<user> all(sqlite::database& db) {
|
||||
builder<user, int, std::string, double> 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);
|
||||
}
|
||||
31
common/sqlite_modern_cpp/tests/mov_ctor.cc
Normal file
31
common/sqlite_modern_cpp/tests/mov_ctor.cc
Normal file
@@ -0,0 +1,31 @@
|
||||
// Fixing https://github.com/SqliteModernCpp/sqlite_modern_cpp/issues/63
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <memory>
|
||||
#include <catch2/catch.hpp>
|
||||
using namespace sqlite;
|
||||
using namespace std;
|
||||
|
||||
struct dbFront {
|
||||
std::unique_ptr<database_binder> 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<database_binder>(
|
||||
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);
|
||||
}
|
||||
20
common/sqlite_modern_cpp/tests/named.cc
Normal file
20
common/sqlite_modern_cpp/tests/named.cc
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <catch2/catch.hpp>
|
||||
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);
|
||||
}
|
||||
27
common/sqlite_modern_cpp/tests/nullptr_uniqueptr.cc
Normal file
27
common/sqlite_modern_cpp/tests/nullptr_uniqueptr.cc
Normal file
@@ -0,0 +1,27 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <catch2/catch.hpp>
|
||||
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<int> { 1, 2 , 3};
|
||||
unique_ptr<string> ptr_null;
|
||||
db << "INSERT INTO tbl VALUES (?, ?, ?, ?);" << 2 << nullptr << ptr_null << nullptr;
|
||||
|
||||
db << "select age,name,img from tbl where id = 1" >> [](unique_ptr<int> age_p, unique_ptr<string> name_p, unique_ptr<vector<int>> 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<int> age_p, unique_ptr<string> name_p, unique_ptr<vector<int>> img_p) {
|
||||
REQUIRE(age_p == nullptr);
|
||||
REQUIRE(name_p == nullptr);
|
||||
REQUIRE(img_p == nullptr);
|
||||
};
|
||||
}
|
||||
102
common/sqlite_modern_cpp/tests/prepared_statment.cc
Normal file
102
common/sqlite_modern_cpp/tests/prepared_statment.cc
Normal file
@@ -0,0 +1,102 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <catch2/catch.hpp>
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
64
common/sqlite_modern_cpp/tests/readme_example.cc
Normal file
64
common/sqlite_modern_cpp/tests/readme_example.cc
Normal file
@@ -0,0 +1,64 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include<iostream>
|
||||
#include <catch2/catch.hpp>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
|
||||
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<int, string, double> row : db << "select age,name,weight from user where age > ? ;" << 21) {
|
||||
REQUIRE((std::get<int>(row) == age && std::get<string>(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"});
|
||||
}
|
||||
27
common/sqlite_modern_cpp/tests/shared_connection.cc
Normal file
27
common/sqlite_modern_cpp/tests/shared_connection.cc
Normal file
@@ -0,0 +1,27 @@
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
36
common/sqlite_modern_cpp/tests/simple_examples.cc
Normal file
36
common/sqlite_modern_cpp/tests/simple_examples.cc
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <catch2/catch.hpp>
|
||||
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);
|
||||
}
|
||||
56
common/sqlite_modern_cpp/tests/sqlcipher.cc
Normal file
56
common/sqlite_modern_cpp/tests/sqlcipher.cc
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <sqlite_modern_cpp/sqlcipher.h>
|
||||
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";
|
||||
}
|
||||
}
|
||||
31
common/sqlite_modern_cpp/tests/std_optional.cc
Normal file
31
common/sqlite_modern_cpp/tests/std_optional.cc
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <iostream>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
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<int> 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<int> val) {
|
||||
REQUIRE(!val);
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
23
common/sqlite_modern_cpp/tests/string_view.cc
Normal file
23
common/sqlite_modern_cpp/tests/string_view.cc
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
|
||||
#ifdef MODERN_SQLITE_STRINGVIEW_SUPPORT
|
||||
#include <string_view>
|
||||
|
||||
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
|
||||
80
common/sqlite_modern_cpp/tests/trycatchblocks.cc
Normal file
80
common/sqlite_modern_cpp/tests/trycatchblocks.cc
Normal file
@@ -0,0 +1,80 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
57
common/sqlite_modern_cpp/tests/variant.cc
Normal file
57
common/sqlite_modern_cpp/tests/variant.cc
Normal file
@@ -0,0 +1,57 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <sqlite_modern_cpp.h>
|
||||
#include <catch2/catch.hpp>
|
||||
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<std::string, int, std::optional<float>> 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<std::monostate,std::string> 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<std::monostate, std::string> 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
|
||||
Reference in New Issue
Block a user