diff --git a/.gitignore b/.gitignore index e3fa2bcd..53d8c516 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ config.log config.status Makefile +sqlite_modern_cpp.pc tests/* !tests/*.cc diff --git a/README.md b/README.md index 6c45471b..4f2dd075 100644 --- a/README.md +++ b/README.md @@ -13,67 +13,67 @@ 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. - // 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; - - // slects 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; - }; - - // 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; - 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 (exception& e) { - cout << e.what() << endl; - } + 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. + // 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; + + // slects 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; + }; + + // 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; + 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 (exception& e) { + cout << e.what() << endl; + } } ``` @@ -84,16 +84,16 @@ 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. +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 @@ -101,72 +101,70 @@ 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 needs to be reset to be execute again. This will reset the bound values too! - ps.reset(); - - // If you dont need the returned values you can execute it like this - ps.execute(); // the statment will not be reset! - - // there is a convinience operator to execute and reset in one go - 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++; - } +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++; // Does reset the bound values +// or like this +ps.execute(); // Does NOT reset the bound values, but we can reset them manually: +ps.reset(); + +// 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++; // Equal to: ps.execute(); ps.reset(); +} ``` 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 save and modern way. +Take this example on how to deal with a database backup using SQLITEs own functions in a safe and modern way. ```c++ - try { - database backup("backup"); //Open the database file we want to backup to - - auto con = db.connection(); // get a handle to the DB we want to backup in our scope - // this way we are sure the DB is open and ok while we backup - - // Init Backup and make sure its freed on exit or exceptions! - auto state = - std::unique_ptr( - sqlite3_backup_init(backup.connection().get(), "main", con.get(), "main"), - sqlite3_backup_finish - ); - - if(state) { - int rc; - // Each iteration of this loop copies 500 database pages from database db to the backup database. - do { - rc = sqlite3_backup_step(state.get(), 500); - std::cout << "Remaining " << sqlite3_backup_remaining(state.get()) << "/" << sqlite3_backup_pagecount(state.get()) << "\n"; - } while(rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED); - } - } // Release allocated resources. +try { + database backup("backup"); //Open the database file we want to backup to + + auto con = db.connection(); // get a handle to the DB we want to backup in our scope + // this way we are sure the DB is open and ok while we backup + + // Init Backup and make sure its freed on exit or exceptions! + auto state = + std::unique_ptr( + sqlite3_backup_init(backup.connection().get(), "main", con.get(), "main"), + sqlite3_backup_finish + ); + + if(state) { + int rc; + // Each iteration of this loop copies 500 database pages from database db to the backup database. + do { + rc = sqlite3_backup_step(state.get(), 500); + std::cout << "Remaining " << sqlite3_backup_remaining(state.get()) << "/" << sqlite3_backup_pagecount(state.get()) << "\n"; + } while(rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED); + } +} // Release allocated resources. ``` Transactions @@ -174,23 +172,23 @@ 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 ... +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 ... ``` @@ -200,16 +198,16 @@ Use `std::vector` to store and retrieve blob data. `T` could be `char,short,int,long,long long, float or double`. ```c++ - db << "CREATE TABLE person (name TEXT, numbers BLOB);"; - db << "INSERT INTO person VALUES (?, ?)" << "bob" << vector { 1, 2, 3, 4}; - db << "INSERT INTO person VALUES (?, ?)" << "sara" << vector { 1.0, 2.0, 3.0, 4.0}; +db << "CREATE TABLE person (name TEXT, numbers BLOB);"; +db << "INSERT INTO person VALUES (?, ?)" << "bob" << vector { 1, 2, 3, 4}; +db << "INSERT INTO person VALUES (?, ?)" << "sara" << vector { 1.0, 2.0, 3.0, 4.0}; - vector numbers_bob; - db << "SELECT numbers from person where name = ?;" << "bob" >> numbers_bob; +vector numbers_bob; +db << "SELECT numbers from person where name = ?;" << "bob" >> numbers_bob; - db << "SELECT numbers from person where name = ?;" << "sara" >> [](vector numbers_sara){ - for(auto e : numbers_sara) cout << e << ' '; cout << endl; - }; +db << "SELECT numbers from person where name = ?;" << "sara" >> [](vector numbers_sara){ + for(auto e : numbers_sara) cout << e << ' '; cout << endl; +}; ``` NULL values @@ -257,28 +255,27 @@ 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 ... + 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 (exception& e) { cout << e.what() << endl; } + } + catch (exception& e) { cout << e.what() << endl; } } ``` @@ -287,57 +284,50 @@ NULL values (C++17) You can use `std::optional` as an alternative for `std::unique_ptr` to work with NULL values. ```c++ - #include - - struct User { - long long _id; - std::optional age; - std::optional name; - std::optional weight; - }; - - { - User user; - user.name = "bob"; - - // Same database as above - database db("dbfile.db"); - - // Here, age and weight will be inserted as NULL in the database. - db << "insert into user (age,name,weight) values (?,?,?);" - << user.age - << user.name - << user.weight; - - user._id = db.last_insert_rowid(); - } - - { - // Here, the User instance will retain the NULL value(s) from the database. - db << "select _id,age,name,weight from user where age > ? ;" - << 18 - >> [&](long long id, - std::optional age, - std::optional name - std::optional weight) { - - User user; - user._id = id; - user.age = age; - user.name = move(name); - user.weight = weight; - - cout << "id=" << user._id - << " age = " << (user.age ? to_string(*user.age) ? string("NULL")) - << " name = " << (user.name ? *user.name : string("NULL")) - << " weight = " << (user.weight ? to_string(*user.weight) : string(NULL)) - << endl; - }; - } +#include + +struct User { + long long _id; + std::optional age; + std::optional name; + std::optional weight; +}; + +int main() { + User user; + user.name = "bob"; + + // Same database as above + database db("dbfile.db"); + + // Here, age and weight will be inserted as NULL in the database. + db << "insert into user (age,name,weight) values (?,?,?);" + << user.age + << user.name + << user.weight; + user._id = db.last_insert_rowid(); + + // Here, the User instance will retain the NULL value(s) from the database. + db << "select _id,age,name,weight from user where age > ? ;" + << 18 + >> [&](long long id, + std::optional age, + std::optional name + std::optional weight) { + + cout << "id=" << _id + << " age = " << (age ? to_string(*age) ? string("NULL")) + << " name = " << (name ? *name : string("NULL")) + << " weight = " << (weight ? to_string(*weight) : string(NULL)) + << endl; + }; +} ``` If you do not have C++17 support, you can use boost optional instead by defining `_MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT` before importing the `sqlite_modern_cpp` header. +If the optional library is not available, the experimental/optional one will be used instead. + **Note: boost support is deprecated and will be removed in future versions.** Variant type support (C++17) @@ -382,24 +372,24 @@ sqlite::sqlite_exception has a `get_code()` member function to get the SQLITE3 e 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 (sqlite_exception& e) { - cerr << e.get_code() << ": " << e.what() << " during " - << e.get_sql() << endl; - } - /* you can catch specific exceptions as well, - catch(sqlite::errors::constraint e) { } */ - /* and even more specific exceptions - catch(sqlite::errors::constraint_primarykey e) { } */ +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 (sqlite_exception& e) { + cerr << e.get_code() << ": " << e.what() << " during " + << e.get_sql() << endl; +} +/* you can catch specific exceptions as well, + catch(sqlite::errors::constraint e) { } */ +/* and even more specific exceptions + catch(sqlite::errors::constraint_primarykey e) { } */ ``` You can also register a error logging function with `sqlite::error_log`. @@ -407,23 +397,23 @@ The `` header has to be included to make this function 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 (sqlite_exception& e) {} +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 (sqlite_exception& e) {} ``` Custom SQL functions @@ -432,16 +422,16 @@ 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);"; +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; +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'; - }; +db << "SELECT number, tgamma(number+1) FROM numbers;" >> [](double number, double factorial) { + cout << number << "! = " << factorial << '\n'; +}; ``` NDK support @@ -469,9 +459,10 @@ 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) + - 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 diff --git a/configure b/configure index 72b65037..da47d6d3 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for sqlite_modern_cpp version-0.7. +# Generated by GNU Autoconf 2.69 for sqlite_modern_cpp 3.2. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -577,8 +577,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='sqlite_modern_cpp' PACKAGE_TARNAME='sqlite_modern_cpp' -PACKAGE_VERSION='version-0.7' -PACKAGE_STRING='sqlite_modern_cpp version-0.7' +PACKAGE_VERSION='3.2' +PACKAGE_STRING='sqlite_modern_cpp 3.2' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1226,7 +1226,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sqlite_modern_cpp version-0.7 to adapt to many kinds of systems. +\`configure' configures sqlite_modern_cpp 3.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1288,7 +1288,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite_modern_cpp version-0.7:";; + short | recursive ) echo "Configuration of sqlite_modern_cpp 3.2:";; esac cat <<\_ACEOF @@ -1368,7 +1368,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite_modern_cpp configure version-0.7 +sqlite_modern_cpp configure 3.2 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1666,7 +1666,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sqlite_modern_cpp $as_me version-0.7, which was +It was created by sqlite_modern_cpp $as_me 3.2, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2614,6 +2614,47 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test $cvd_conf_test = 1 + then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + ts_success=yes + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + CXXFLAGS="$save_CXXFLAGS" + ts_success=no + fi + + + if test "" == "" + then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler flag -pedantic works" >&5 +$as_echo_n "checking if compiler flag -pedantic works... " >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking " >&5 +$as_echo_n "checking ... " >&6; } + fi + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -pedantic" + + + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int main(){} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + cvd_conf_test=1 +else + cvd_conf_test=0 +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + + + + + if test $cvd_conf_test = 1 then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 @@ -2889,7 +2930,7 @@ $as_echo "$PKGCONFIG_LIBDIR" >&6; } # This will be put into the pc file - VERSION=version-0.7 + VERSION=3.2 fi @@ -3899,7 +3940,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sqlite_modern_cpp $as_me version-0.7, which was +This file was extended by sqlite_modern_cpp $as_me 3.2, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -3952,7 +3993,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -sqlite_modern_cpp config.status version-0.7 +sqlite_modern_cpp config.status 3.2 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 49ece058..87b766b9 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(sqlite_modern_cpp, version-0.7) +AC_INIT(sqlite_modern_cpp, 3.2) AC_LANG(C++) AC_PROG_CXX @@ -56,6 +56,7 @@ define([TEST_AND_SET_CXXFLAG],[ TEST_AND_SET_CXXFLAG(-Wall) TEST_AND_SET_CXXFLAG(-Wextra) +TEST_AND_SET_CXXFLAG(-pedantic) TEST_AND_SET_CXXFLAG(-W) TEST_AND_SET_CXXFLAG(-O3) TEST_AND_SET_CXXFLAG(-Werror) diff --git a/hdr/sqlite_modern_cpp.h b/hdr/sqlite_modern_cpp.h index b493aaa2..344dedcb 100644 --- a/hdr/sqlite_modern_cpp.h +++ b/hdr/sqlite_modern_cpp.h @@ -8,12 +8,14 @@ #include #include #include -#include -#include + +#define MODERN_SQLITE_VERSION 3002008 #ifdef __has_include #if __cplusplus > 201402 && __has_include() #define MODERN_SQLITE_STD_OPTIONAL_SUPPORT +#elif __has_include() +#define MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT #endif #endif @@ -27,21 +29,45 @@ #include #endif +#ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT +#include +#define MODERN_SQLITE_STD_OPTIONAL_SUPPORT +#endif + #ifdef _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT #include #endif +#ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ +#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 100000 +#undef __cpp_lib_uncaught_exceptions +#endif +#endif + #include #include "sqlite_modern_cpp/errors.h" #include "sqlite_modern_cpp/utility/function_traits.h" #include "sqlite_modern_cpp/utility/uncaught_exceptions.h" +#include "sqlite_modern_cpp/utility/utf16_utf8.h" #ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT #include "sqlite_modern_cpp/utility/variant.h" #endif namespace sqlite { + + // std::optional support for NULL values + #ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT + #ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT + template + using optional = std::experimental::optional; + #else + template + using optional = std::optional; + #endif + #endif + class database; class database_binder; @@ -73,23 +99,15 @@ namespace sqlite { _stmt(std::move(other._stmt)), _inx(other._inx), execution_started(other.execution_started) { } - void reset() { - sqlite3_reset(_stmt.get()); - sqlite3_clear_bindings(_stmt.get()); - _inx = 1; - used(false); - } - void execute() { + _start_execute(); int hresult; - used(true); /* prevent from executing again when goes out of scope */ while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {} if(hresult != SQLITE_DONE) { errors::throw_sqlite_error(hresult, sql()); } - } std::string sql() { @@ -107,8 +125,10 @@ namespace sqlite { } void used(bool state) { - if(execution_started == true && state == true) { - throw errors::reexecution("Already used statement executed again! Please reset() first!",sql()); + if(!state) { + // We may have to reset first if we haven't done so already: + _next_index(); + --_inx; } execution_started = state; } @@ -123,9 +143,22 @@ namespace sqlite { bool execution_started = false; + int _next_index() { + if(execution_started && !_inx) { + sqlite3_reset(_stmt.get()); + sqlite3_clear_bindings(_stmt.get()); + } + return ++_inx; + } + void _start_execute() { + _next_index(); + _inx = 0; + used(true); + } + void _extract(std::function call_back) { int hresult; - used(true); + _start_execute(); while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) { call_back(); @@ -138,7 +171,7 @@ namespace sqlite { void _extract_single_value(std::function call_back) { int hresult; - used(true); + _start_execute(); if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) { call_back(); @@ -155,15 +188,9 @@ namespace sqlite { } } -#ifdef _MSC_VER - sqlite3_stmt* _prepare(const std::u16string& sql) { - return _prepare(std::wstring_convert, wchar_t>().to_bytes(reinterpret_cast(sql.c_str()))); - } -#else sqlite3_stmt* _prepare(const std::u16string& sql) { - return _prepare(std::wstring_convert, char16_t>().to_bytes(sql)); + return _prepare(utility::utf16_to_utf8(sql)); } -#endif sqlite3_stmt* _prepare(const std::string& sql) { int hresult; @@ -171,7 +198,7 @@ namespace sqlite { const char *remaining; hresult = sqlite3_prepare_v2(_db.get(), sql.data(), -1, &tmp, &remaining); if(hresult != SQLITE_OK) errors::throw_sqlite_error(hresult, sql); - if(!std::all_of(remaining, sql.data() + sql.size(), [](char ch) {return std::isblank(ch);})) + if(!std::all_of(remaining, sql.data() + sql.size(), [](char ch) {return std::isspace(ch);})) throw errors::more_statements("Multiple semicolon separated statements are unsupported", sql); return tmp; } @@ -229,8 +256,8 @@ namespace sqlite { #ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT - template friend database_binder& operator <<(database_binder& db, const std::optional& val); - template friend void get_col_from_db(database_binder& db, int inx, std::optional& o); + template friend database_binder& operator <<(database_binder& db, const optional& val); + template friend void get_col_from_db(database_binder& db, int inx, optional& o); #endif #ifdef _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT @@ -243,13 +270,13 @@ namespace sqlite { database_binder(std::shared_ptr db, std::u16string const & sql): _db(db), _stmt(_prepare(sql), sqlite3_finalize), - _inx(1) { + _inx(0) { } database_binder(std::shared_ptr db, std::string const & sql): _db(db), _stmt(_prepare(sql), sqlite3_finalize), - _inx(1) { + _inx(0) { } ~database_binder() noexcept(false) { @@ -365,7 +392,7 @@ namespace sqlite { }; inline OpenFlags operator|(const OpenFlags& a, const OpenFlags& b) { return static_cast(static_cast(a) | static_cast(b)); - }; + } enum class Encoding { ANY = SQLITE_ANY, UTF8 = SQLITE_UTF8, @@ -393,11 +420,7 @@ namespace sqlite { } database(const std::u16string &db_name, const sqlite_config &config = {}): _db(nullptr) { -#ifdef _MSC_VER - auto db_name_utf8 = std::wstring_convert, wchar_t>().to_bytes(reinterpret_cast(db_name.c_str())); -#else - auto db_name_utf8 = std::wstring_convert, char16_t>().to_bytes(db_name); -#endif + auto db_name_utf8 = utility::utf16_to_utf8(db_name); sqlite3* tmp = nullptr; auto ret = sqlite3_open_v2(db_name_utf8.data(), &tmp, static_cast(config.flags), config.zVfs); _db = std::shared_ptr(tmp, [=](sqlite3* ptr) { sqlite3_close_v2(ptr); }); // this will close the connection eventually when no longer needed. @@ -515,10 +538,9 @@ namespace sqlite { // int inline database_binder& operator<<(database_binder& db, const int& val) { int hresult; - if((hresult = sqlite3_bind_int(db._stmt.get(), db._inx, val)) != SQLITE_OK) { + if((hresult = sqlite3_bind_int(db._stmt.get(), db._next_index(), val)) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } inline void store_result_in_db(sqlite3_context* db, const int& val) { @@ -542,11 +564,10 @@ namespace sqlite { // sqlite_int64 inline database_binder& operator <<(database_binder& db, const sqlite_int64& val) { int hresult; - if((hresult = sqlite3_bind_int64(db._stmt.get(), db._inx, val)) != SQLITE_OK) { + if((hresult = sqlite3_bind_int64(db._stmt.get(), db._next_index(), val)) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } inline void store_result_in_db(sqlite3_context* db, const sqlite_int64& val) { @@ -570,11 +591,10 @@ namespace sqlite { // float inline database_binder& operator <<(database_binder& db, const float& val) { int hresult; - if((hresult = sqlite3_bind_double(db._stmt.get(), db._inx, double(val))) != SQLITE_OK) { + if((hresult = sqlite3_bind_double(db._stmt.get(), db._next_index(), double(val))) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } inline void store_result_in_db(sqlite3_context* db, const float& val) { @@ -598,11 +618,10 @@ namespace sqlite { // double inline database_binder& operator <<(database_binder& db, const double& val) { int hresult; - if((hresult = sqlite3_bind_double(db._stmt.get(), db._inx, val)) != SQLITE_OK) { + if((hresult = sqlite3_bind_double(db._stmt.get(), db._next_index(), val)) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } inline void store_result_in_db(sqlite3_context* db, const double& val) { @@ -628,10 +647,9 @@ namespace sqlite { void const* buf = reinterpret_cast(vec.data()); int bytes = vec.size() * sizeof(T); int hresult; - if((hresult = sqlite3_bind_blob(db._stmt.get(), db._inx, buf, bytes, SQLITE_TRANSIENT)) != SQLITE_OK) { + if((hresult = sqlite3_bind_blob(db._stmt.get(), db._next_index(), buf, bytes, SQLITE_TRANSIENT)) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } template inline void store_result_in_db(sqlite3_context* db, const std::vector& vec) { @@ -661,10 +679,9 @@ namespace sqlite { /* for nullptr support */ inline database_binder& operator <<(database_binder& db, std::nullptr_t) { int hresult; - if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) { + if((hresult = sqlite3_bind_null(db._stmt.get(), db._next_index())) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } inline void store_result_in_db(sqlite3_context* db, std::nullptr_t) { @@ -723,11 +740,10 @@ namespace sqlite { inline database_binder& operator <<(database_binder& db, const std::string& txt) { int hresult; - if((hresult = sqlite3_bind_text(db._stmt.get(), db._inx, txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) { + if((hresult = sqlite3_bind_text(db._stmt.get(), db._next_index(), txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } inline void store_result_in_db(sqlite3_context* db, const std::string& val) { @@ -754,11 +770,10 @@ namespace sqlite { inline database_binder& operator <<(database_binder& db, const std::u16string& txt) { int hresult; - if((hresult = sqlite3_bind_text16(db._stmt.get(), db._inx, txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) { + if((hresult = sqlite3_bind_text16(db._stmt.get(), db._next_index(), txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } inline void store_result_in_db(sqlite3_context* db, const std::u16string& val) { @@ -789,37 +804,40 @@ namespace sqlite { // std::optional support for NULL values #ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT - template inline database_binder& operator <<(database_binder& db, const std::optional& val) { + template inline database_binder& operator <<(database_binder& db, const optional& val) { if(val) { - return operator << (std::move(db), std::move(*val)); - } - int hresult; - if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) { - errors::throw_sqlite_error(hresult, db.sql()); + return db << std::move(*val); + } else { + return db << nullptr; } - - ++db._inx; - return db; } - template inline void store_result_in_db(sqlite3_context* db, const std::optional& val) { + template inline void store_result_in_db(sqlite3_context* db, const optional& val) { if(val) { store_result_in_db(db, *val); } sqlite3_result_null(db); } - template inline void get_col_from_db(database_binder& db, int inx, std::optional& o) { + template inline void get_col_from_db(database_binder& db, int inx, optional& o) { if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) { + #ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT + o = std::experimental::nullopt; + #else o.reset(); + #endif } else { OptionalT v; get_col_from_db(db, inx, v); o = std::move(v); } } - template inline void get_val_from_db(sqlite3_value *value, std::optional& o) { + template inline void get_val_from_db(sqlite3_value *value, optional& o) { if(sqlite3_value_type(value) == SQLITE_NULL) { + #ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT + o = std::experimental::nullopt; + #else o.reset(); + #endif } else { OptionalT v; get_val_from_db(value, v); @@ -832,15 +850,10 @@ namespace sqlite { #ifdef _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT template inline database_binder& operator <<(database_binder& db, const boost::optional& val) { if(val) { - return operator << (std::move(db), std::move(*val)); - } - int hresult; - if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) { - errors::throw_sqlite_error(hresult, db.sql()); + return db << std::move(*val); + } else { + return db << nullptr; } - - ++db._inx; - return db; } template inline void store_result_in_db(sqlite3_context* db, const boost::optional& val) { if(val) { @@ -892,10 +905,10 @@ namespace sqlite { #endif // Some ppl are lazy so we have a operator for proper prep. statemant handling. - void inline operator++(database_binder& db, int) { db.execute(); db.reset(); } + void inline operator++(database_binder& db, int) { db.execute(); } // Convert the rValue binder to a reference and call first op<<, its needed for the call that creates the binder (be carefull of recursion here!) - template database_binder& operator << (database_binder&& db, const T& val) { return db << val; } + template database_binder&& operator << (database_binder&& db, const T& val) { db << val; return std::move(db); } namespace sql_function_binder { template @@ -967,7 +980,7 @@ namespace sqlite { Values&&... values ) { static_cast(sqlite3_user_data(db))->first(std::forward(values)...); - }; + } template< typename ContextType, diff --git a/hdr/sqlite_modern_cpp/errors.h b/hdr/sqlite_modern_cpp/errors.h index 60faaecc..2b9ab75d 100644 --- a/hdr/sqlite_modern_cpp/errors.h +++ b/hdr/sqlite_modern_cpp/errors.h @@ -37,8 +37,8 @@ namespace sqlite { //Some additional errors are here for the C++ interface class more_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; }; class no_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; }; - class reexecution: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements need to be reset before calling them again class more_statements: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements can only contain one statement + class invalid_utf16: public sqlite_exception { using sqlite_exception::sqlite_exception; }; static void throw_sqlite_error(const int& error_code, const std::string &sql = "") { switch(error_code & 0xFF) { diff --git a/hdr/sqlite_modern_cpp/lists/error_codes.h b/hdr/sqlite_modern_cpp/lists/error_codes.h index 195aa7d4..5dfa0d37 100644 --- a/hdr/sqlite_modern_cpp/lists/error_codes.h +++ b/hdr/sqlite_modern_cpp/lists/error_codes.h @@ -1,3 +1,8 @@ +#if SQLITE_VERSION_NUMBER < 3010000 +#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) +#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) +#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) +#endif SQLITE_MODERN_CPP_ERROR_CODE(ERROR,error,) SQLITE_MODERN_CPP_ERROR_CODE(INTERNAL,internal,) SQLITE_MODERN_CPP_ERROR_CODE(PERM,perm,) @@ -41,12 +46,8 @@ SQLITE_MODERN_CPP_ERROR_CODE(IOERR,ioerr, SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,MMAP,ioerr,mmap) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,GETTEMPPATH,ioerr,gettemppath) SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,CONVPATH,ioerr,convpath) -#if SQLITE_VERSION_NUMBER >= 3009000 SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,VNODE,ioerr,vnode) -#endif -#if SQLITE_VERSION_NUMBER >= 3010000 SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,AUTH,ioerr,auth) -#endif ) SQLITE_MODERN_CPP_ERROR_CODE(CORRUPT,corrupt, SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CORRUPT,VTAB,corrupt,vtab) @@ -79,9 +80,6 @@ SQLITE_MODERN_CPP_ERROR_CODE(MISMATCH,mismatch,) SQLITE_MODERN_CPP_ERROR_CODE(MISUSE,misuse,) SQLITE_MODERN_CPP_ERROR_CODE(NOLFS,nolfs,) SQLITE_MODERN_CPP_ERROR_CODE(AUTH,auth, -#if SQLITE_VERSION_NUMBER >= 3009000 - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(AUTH,USER,auth,user) -#endif ) SQLITE_MODERN_CPP_ERROR_CODE(FORMAT,format,) SQLITE_MODERN_CPP_ERROR_CODE(RANGE,range,) diff --git a/hdr/sqlite_modern_cpp/utility/utf16_utf8.h b/hdr/sqlite_modern_cpp/utility/utf16_utf8.h new file mode 100644 index 00000000..ea21723f --- /dev/null +++ b/hdr/sqlite_modern_cpp/utility/utf16_utf8.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include + +#include "../errors.h" + +namespace sqlite { + namespace utility { + inline std::string utf16_to_utf8(const std::u16string &input) { + struct : std::codecvt { + } codecvt; + std::mbstate_t state{}; + std::string result((std::max)(input.size() * 3 / 2, std::size_t(4)), '\0'); + const char16_t *remaining_input = input.data(); + std::size_t produced_output = 0; + while(true) { + char *used_output; + switch(codecvt.out(state, remaining_input, &input[input.size()], + remaining_input, &result[produced_output], + &result[result.size() - 1] + 1, used_output)) { + case std::codecvt_base::ok: + result.resize(used_output - result.data()); + return result; + case std::codecvt_base::noconv: + // This should be unreachable + case std::codecvt_base::error: + throw errors::invalid_utf16("Invalid UTF-16 input", ""); + case std::codecvt_base::partial: + if(used_output == result.data() + produced_output) + throw errors::invalid_utf16("Unexpected end of input", ""); + produced_output = used_output - result.data(); + result.resize( + result.size() + + (std::max)((&input[input.size()] - remaining_input) * 3 / 2, + std::ptrdiff_t(4))); + } + } + } + } // namespace utility +} // namespace sqlite diff --git a/sqlite_modern_cpp.pc b/sqlite_modern_cpp.pc deleted file mode 100644 index bbcc4458..00000000 --- a/sqlite_modern_cpp.pc +++ /dev/null @@ -1,8 +0,0 @@ -prefix=/usr/local -includedir=${prefix}/include - -Name: sqlite_modern_cpp -Description: Modern C++ interface to sqlite3 -Version: version-0.7 -Libs: -lsqlite3 -Cflags: -I${includedir} diff --git a/tests/boost_optional.cc b/tests/boost_optional.cc index 32006de9..651a4f26 100644 --- a/tests/boost_optional.cc +++ b/tests/boost_optional.cc @@ -26,21 +26,10 @@ void select(database& db, bool should_be_null) { }; } -struct TmpFile { - string fname; - - TmpFile(): fname(tmpnam(nullptr)) {} - - ~TmpFile() { - remove(fname.c_str()); - } -}; - int main() { try { // creates a database file 'dbfile.db' if it does not exists. - TmpFile file; - database db(file.fname); + database db(":memory:"); db << "drop table if exists test"; db << diff --git a/tests/flags.cc b/tests/flags.cc index ed1f116d..77cb03d2 100644 --- a/tests/flags.cc +++ b/tests/flags.cc @@ -2,7 +2,7 @@ #include #include #include -#include +#include using namespace sqlite; using namespace std; @@ -10,15 +10,11 @@ struct TmpFile { string fname; - TmpFile(): fname(tmpnam(nullptr)) {} - - ~TmpFile() - { - remove(fname.c_str()); - } + TmpFile(): fname("./flags.db") { } + ~TmpFile() { remove(fname.c_str()); } }; -#if __BYTE_ORDER == __BIG_ENDIAN +#if BYTE_ORDER == BIG_ENDIAN #define OUR_UTF16 "UTF-16be" #else #define OUR_UTF16 "UTF-16le" diff --git a/tests/prepared_statment.cc b/tests/prepared_statment.cc index b50d356d..8080f0ca 100644 --- a/tests/prepared_statment.cc +++ b/tests/prepared_statment.cc @@ -16,10 +16,8 @@ int main() { pps >> test; // execute statement - pps.reset(); - pps << 4; // bind a rvalue - pps++; // and execute and reset + pps++; // and execute pps << 8 >> test; @@ -81,22 +79,9 @@ int main() { auto prep = db << "select ?"; prep << 5; - prep.execute(); - try { - prep.execute(); - exit(EXIT_FAILURE); - } catch(errors::reexecution& ex) { - // Thats ok here - } catch(...) { - exit(EXIT_FAILURE); - } - - prep.reset(); - prep << 6; prep.execute(); - } diff --git a/tests/shared_connection.cc b/tests/shared_connection.cc index 0a9942c5..e8ba5cb5 100644 --- a/tests/shared_connection.cc +++ b/tests/shared_connection.cc @@ -6,21 +6,10 @@ using namespace sqlite; using namespace std; -struct TmpFile { - string fname; - - TmpFile(): fname(tmpnam(nullptr)) {} - - ~TmpFile() { - remove(fname.c_str()); - } -}; - int main() { try { - TmpFile file; - database db(file.fname); + database db(":memory:"); { diff --git a/tests/simple_examples.cc b/tests/simple_examples.cc index 85c75c94..be2040c0 100644 --- a/tests/simple_examples.cc +++ b/tests/simple_examples.cc @@ -5,26 +5,13 @@ using namespace sqlite; using namespace std; -struct TmpFile -{ - string fname; - - TmpFile(): fname(tmpnam(nullptr)) {} - - ~TmpFile() - { - remove(fname.c_str()); - } -}; - int main() { try { - TmpFile file; - database db(file.fname); + database db(":memory:"); - db << "CREATE TABLE foo (a integer, b string);"; + db << "CREATE TABLE foo (a integer, b string);\n"; db << "INSERT INTO foo VALUES (?, ?)" << 1 << "hello"; db << "INSERT INTO foo VALUES (?, ?)" << 2 << "world"; diff --git a/tests/sqlcipher.cc b/tests/sqlcipher.cc index 87e32c29..eb1e259a 100644 --- a/tests/sqlcipher.cc +++ b/tests/sqlcipher.cc @@ -10,12 +10,8 @@ struct TmpFile { string fname; - TmpFile(): fname(tmpnam(nullptr)) {} - - ~TmpFile() - { - remove(fname.c_str()); - } + TmpFile(): fname("./sqlcipher.db") { } + ~TmpFile() { remove(fname.c_str()); } }; int main() diff --git a/tests/std_optional.cc b/tests/std_optional.cc index fd6ddeaf..b81ca27c 100644 --- a/tests/std_optional.cc +++ b/tests/std_optional.cc @@ -8,7 +8,7 @@ using namespace std; #ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT void insert(database& db, bool is_null) { int id = 1; - std::optional val; + sqlite::optional val; if(!is_null) val = 5; db << "delete from test where id = 1"; @@ -16,7 +16,7 @@ void insert(database& db, bool is_null) { } void select(database& db, bool should_be_null) { - db << "select id,val from test" >> [&](long long, std::optional val) { + db << "select id,val from test" >> [&](long long, sqlite::optional val) { if(should_be_null) { if(val) exit(EXIT_FAILURE); } else { diff --git a/tests/trycatchblocks.cc b/tests/trycatchblocks.cc index e2d98448..1c7b8e0a 100644 --- a/tests/trycatchblocks.cc +++ b/tests/trycatchblocks.cc @@ -1,91 +1,84 @@ -#include -#include -#include -#include -#include - -using namespace sqlite; -using std::string; - -struct TmpFile -{ - string fname; - - TmpFile(): fname(tmpnam(nullptr)) {} - - ~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; - } -}; - -int main( void ) -{ - // -------------------------------------------------------------------------- - // -- 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" ); - if ( !interf.TestData() ) { - exit( EXIT_FAILURE ); - } - } - - exit( EXIT_SUCCESS ); -} +#include +#include +#include +#include +#include + +using namespace sqlite; +using std::string; + +struct TmpFile { + string fname; + + TmpFile(): fname("./trycatchblocks.db") {} + ~TmpFile() { remove(fname.c_str()); } +}; + + +class DBInterface { + database db; + +public: + DBInterface( const string& fileName ) : db( fileName ) { } + + void LogRequest( const string& username, const string& ip, const string& request ) + { + try { + auto timestamp = std::to_string( time( nullptr ) ); + + db << + "create table if not exists log_request (" + " _id integer primary key autoincrement not null," + " username text," + " timestamp text," + " ip text," + " request text" + ");"; + db << "INSERT INTO log_request (username, timestamp, ip, request) VALUES (?,?,?,?);" + << username + << timestamp + << ip + << request; + } catch ( const std::exception& e ) { + std::cout << e.what() << std::endl; + } + } + + bool TestData( void ) { + try { + string username, timestamp, ip, request; + + db << "select username, timestamp, ip, request from log_request where username = ?" + << "test" + >> std::tie(username, timestamp, ip, request); + + if ( username == "test" && ip == "127.0.0.1" && request == "hello world" ) { + return true; + } + } catch ( const std::exception& e ) { + std::cout << e.what() << std::endl; + } + + return false; + } +}; + +int main( void ) +{ + // -------------------------------------------------------------------------- + // -- 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" ); + if ( !interf.TestData() ) { + exit( EXIT_FAILURE ); + } + } + + exit( EXIT_SUCCESS ); +}