GithubHelp home page GithubHelp logo

fnc12 / sqlite_orm Goto Github PK

View Code? Open in Web Editor NEW
2.1K 64.0 298.0 8.09 MB

❤️ SQLite ORM light header only library for modern C++

License: GNU Affero General Public License v3.0

C++ 98.03% CMake 1.64% C 0.33%
sqlite-orm crud orm cplusplus cplusplus-14 sqlite cpp modern-cpp sql sqliteorm

sqlite_orm's Introduction

Sublime's custom image

C++ SQLite GitHub Actions CMake Stack Overflow PayPal Twitter Patreon

SQLite ORM

SQLite ORM light header only library for modern C++. Please read the license precisely. The project has AGPL license for open source project and MIT license after purchasing it for 50$ (using PayPal or any different way (contact using email [email protected])).

Status

Branch Travis Appveyor
master Build Status Build status
dev Build Status Build status

Advantages

  • No raw string queries
  • Intuitive syntax
  • Comfortable interface - one code line per single query
  • Built with modern C++14 features (no macros and external scripts)
  • CRUD support
  • Pure select query support
  • Prepared statements support
  • UNION, EXCEPT and INTERSECT support
  • STL compatible
  • Custom types binding support
  • BLOB support - maps to std::vector<char> or one can bind your custom type
  • FOREIGN KEY support
  • Composite key support
  • JOIN support
  • Transactions support
  • Migrations functionality
  • Powerful conditions
  • ORDER BY and LIMIT, OFFSET support
  • GROUP BY / DISTINCT support
  • INDEX support
  • Follows single responsibility principle - no need write code inside your data model classes
  • Easy integration - single header only lib.
  • The only dependency - libsqlite3
  • C++ standard code style
  • In memory database support - provide :memory: or empty filename
  • COLLATE support
  • Limits setting/getting support
  • User defined functions support

sqlite_orm library allows to create easy data model mappings to your database schema. It is built to manage (CRUD) objects with a primary key and without it. It also allows you to specify table names and column names explicitly no matter how your classes actually named. Take a look at example:

struct User{
    int id;
    std::string firstName;
    std::string lastName;
    int birthDate;
    std::unique_ptr<std::string> imageUrl;
    int typeId;
};

struct UserType {
    int id;
    std::string name;
};

So we have database with predefined schema like

CREATE TABLE users (id integer primary key autoincrement, first_name text not null, last_name text not null, birth_date integer not null, image_url text, type_id integer not null)

CREATE TABLE user_types (id integer primary key autoincrement, name text not null DEFAULT 'name_placeholder')

Now we tell sqlite_orm library about our schema and provide database filename. We create storage service object that has CRUD interface. Also we create every table and every column. All code is intuitive and minimalistic.

using namespace sqlite_orm;
auto storage = make_storage("db.sqlite",
                            make_table("users",
                                       make_column("id", &User::id, primary_key().autoincrement()),
                                       make_column("first_name", &User::firstName),
                                       make_column("last_name", &User::lastName),
                                       make_column("birth_date", &User::birthDate),
                                       make_column("image_url", &User::imageUrl),
                                       make_column("type_id", &User::typeId)),
                            make_table("user_types",
                                       make_column("id", &UserType::id, primary_key().autoincrement()),
                                       make_column("name", &UserType::name, default_value("name_placeholder"))));

Too easy isn't it? You do not have to specify mapped type explicitly - it is deduced from your member pointers you pass during making a column (for example: &User::id). To create a column you have to pass two arguments at least: its name in the table and your mapped class member pointer. You can also add extra arguments to tell your storage about column's constraints like primary_key, autoincrement, default_value, unique or generated_always_as (order isn't important; not_null is deduced from type automatically).

More details about making storage can be found in tutorial.

If your datamodel classes have private or protected members to map to sqlite then you can make a storage with setter and getter functions. More info in the example.

CRUD

Let's create and insert new User into our database. First we need to create a User object with any id and call insert function. It will return id of just created user or throw exception if something goes wrong.

User user{-1, "Jonh", "Doe", 664416000, std::make_unique<std::string>("url_to_heaven"), 3 };
    
auto insertedId = storage.insert(user);
cout << "insertedId = " << insertedId << endl;      //  insertedId = 8
user.id = insertedId;

User secondUser{-1, "Alice", "Inwonder", 831168000, {} , 2};
insertedId = storage.insert(secondUser);
secondUser.id = insertedId;

Note: if we need to insert a new user with specified id call storage.replace(user); instead of insert.

Next let's get our user by id.

try{
    auto user = storage.get<User>(insertedId);
    cout << "user = " << user.firstName << " " << user.lastName << endl;
}catch(std::system_error e) {
    cout << e.what() << endl;
}catch(...){
    cout << "unknown exeption" << endl;
}

Probably you may not like throwing exceptions. Me too. Exception std::system_error is thrown because return type in get function is not nullable. You can use alternative version get_pointer which returns std::unique_ptr and doesn't throw not_found_exception if nothing found - just returns nullptr.

if(auto user = storage.get_pointer<User>(insertedId)){
    cout << "user = " << user->firstName << " " << user->lastName << endl;
}else{
    cout << "no user with id " << insertedId << endl;
}

std::unique_ptr is used as optional in sqlite_orm. Of course there is class optional in C++14 located at std::experimental::optional. But we don't want to use it until it is experimental.

We can also update our user. It updates row by id provided in user object and sets all other non primary_key fields to values stored in the passed user object. So you can just assign members to user object you want and call update

user.firstName = "Nicholas";
user.imageUrl = "https://cdn1.iconfinder.com/data/icons/man-icon-set/100/man_icon-21-512.png"
storage.update(user);

Also there is a non-CRUD update version update_all:

storage.update_all(set(c(&User::lastName) = "Hardey",
                       c(&User::typeId) = 2),
                   where(c(&User::firstName) == "Tom"));

And delete. To delete you have to pass id only, not whole object. Also we need to explicitly tell which class of object we want to delete. Function name is remove not delete cause delete is a reserved word in C++.

storage.remove<User>(insertedId)

Also we can extract all objects into std::vector.

auto allUsers = storage.get_all<User>();
cout << "allUsers (" << allUsers.size() << "):" << endl;
for(auto &user : allUsers) {
    cout << storage.dump(user) << endl; //  dump returns std::string with json-like style object info. For example: { id : '1', first_name : 'Jonh', last_name : 'Doe', birth_date : '664416000', image_url : 'https://cdn1.iconfinder.com/data/icons/man-icon-set/100/man_icon-21-512.png', type_id : '3' }
}

And one can specify return container type explicitly: let's get all users in std::list, not std::vector:

auto allUsersList = storage.get_all<User, std::list<User>>();

Container must be STL compatible (must have push_back(T&&) function in this case).

get_all can be too heavy for memory so you can iterate row by row (i.e. object by object):

for(auto &user : storage.iterate<User>()) {
    cout << storage.dump(user) << endl;
}

iterate member function returns adapter object that has begin and end member functions returning iterators that fetch object on dereference operator call.

CRUD functions get, get_pointer, remove, update (not insert) work only if your type has a primary key column. If you try to get an object that is mapped to your storage but has no primary key column a std::system_error will be thrown cause sqlite_orm cannot detect an id. If you want to know how to perform a storage without primary key take a look at date_time.cpp example in examples folder.

Prepared statements

Prepared statements are strongly typed.

//  SELECT doctor_id
//  FROM visits
//  WHERE LENGTH(patient_name) > 8
auto selectStatement = storage.prepare(select(&Visit::doctor_id, where(length(&Visit::patient_name) > 8)));
cout << "selectStatement = " << selectStatement.sql() << endl;  //  prints "SELECT doctor_id FROM ..."
auto rows = storage.execute(selectStatement); //  rows is std::vector<decltype(Visit::doctor_id)>

//  SELECT doctor_id
//  FROM visits
//  WHERE LENGTH(patient_name) > 11
get<0>(selectStatement) = 11;
auto rows2 = storage.execute(selectStatement);

get<N>(statement) function call allows you to access fields to bind them to your statement.

Aggregate Functions

//  SELECT AVG(id) FROM users
auto averageId = storage.avg(&User::id);    
cout << "averageId = " << averageId << endl;        //  averageId = 4.5
    
//  SELECT AVG(birth_date) FROM users
auto averageBirthDate = storage.avg(&User::birthDate);  
cout << "averageBirthDate = " << averageBirthDate << endl;      //  averageBirthDate = 6.64416e+08
  
//  SELECT COUNT(*) FROM users
auto usersCount = storage.count<User>();    
cout << "users count = " << usersCount << endl;     //  users count = 8

//  SELECT COUNT(id) FROM users
auto countId = storage.count(&User::id);    
cout << "countId = " << countId << endl;        //  countId = 8

//  SELECT COUNT(image_url) FROM users
auto countImageUrl = storage.count(&User::imageUrl);   
cout << "countImageUrl = " << countImageUrl << endl;      //  countImageUrl = 5

//  SELECT GROUP_CONCAT(id) FROM users
auto concatedUserId = storage.group_concat(&User::id);      
cout << "concatedUserId = " << concatedUserId << endl;      //  concatedUserId = 1,2,3,4,5,6,7,8

//  SELECT GROUP_CONCAT(id, "---") FROM users
auto concatedUserIdWithDashes = storage.group_concat(&User::id, "---");     
cout << "concatedUserIdWithDashes = " << concatedUserIdWithDashes << endl;      //  concatedUserIdWithDashes = 1---2---3---4---5---6---7---8

//  SELECT MAX(id) FROM users
if(auto maxId = storage.max(&User::id)){    
    cout << "maxId = " << *maxId <<endl;    //  maxId = 12  (maxId is std::unique_ptr<int>)
}else{
    cout << "maxId is null" << endl;
}
    
//  SELECT MAX(first_name) FROM users
if(auto maxFirstName = storage.max(&User::firstName)){ 
    cout << "maxFirstName = " << *maxFirstName << endl; //  maxFirstName = Jonh (maxFirstName is std::unique_ptr<std::string>)
}else{
    cout << "maxFirstName is null" << endl;
}

//  SELECT MIN(id) FROM users
if(auto minId = storage.min(&User::id)){    
    cout << "minId = " << *minId << endl;   //  minId = 1 (minId is std::unique_ptr<int>)
}else{
    cout << "minId is null" << endl;
}

//  SELECT MIN(last_name) FROM users
if(auto minLastName = storage.min(&User::lastName)){
    cout << "minLastName = " << *minLastName << endl;   //  minLastName = Doe
}else{
    cout << "minLastName is null" << endl;
}

//  SELECT SUM(id) FROM users
if(auto sumId = storage.sum(&User::id)){    //  sumId is std::unique_ptr<int>
    cout << "sumId = " << *sumId << endl;
}else{
    cout << "sumId is null" << endl;
}

//  SELECT TOTAL(id) FROM users
auto totalId = storage.total(&User::id);
cout << "totalId = " << totalId << endl;    //  totalId is double (always)

Where conditions

You also can select objects with custom where conditions with =, !=, >, >=, <, <=, IN, BETWEEN and LIKE.

For example: let's select users with id lesser than 10:

//  SELECT * FROM users WHERE id < 10
auto idLesserThan10 = storage.get_all<User>(where(c(&User::id) < 10));
cout << "idLesserThan10 count = " << idLesserThan10.size() << endl;
for(auto &user : idLesserThan10) {
    cout << storage.dump(user) << endl;
}

Or select all users who's first name is not equal "John":

//  SELECT * FROM users WHERE first_name != 'John'
auto notJohn = storage.get_all<User>(where(c(&User::firstName) != "John"));
cout << "notJohn count = " << notJohn.size() << endl;
for(auto &user : notJohn) {
    cout << storage.dump(user) << endl;
}

By the way one can implement not equal in a different way using C++ negation operator:

auto notJohn2 = storage.get_all<User>(where(not (c(&User::firstName) == "John")));

You can use ! and not in this case cause they are equal. Also you can chain several conditions with and and or operators. Let's try to get users with query with conditions like where id >= 5 and id <= 7 and not id = 6:

auto id5and7 = storage.get_all<User>(where(c(&User::id) <= 7 and c(&User::id) >= 5 and not (c(&User::id) == 6)));
cout << "id5and7 count = " << id5and7.size() << endl;
for(auto &user : id5and7) {
    cout << storage.dump(user) << endl;
}

Or let's just export two users with id 10 or id 16 (of course if these users exist):

auto id10or16 = storage.get_all<User>(where(c(&User::id) == 10 or c(&User::id) == 16));
cout << "id10or16 count = " << id10or16.size() << endl;
for(auto &user : id10or16) {
    cout << storage.dump(user) << endl;
}

In fact you can chain together any number of different conditions with any operator from and, or and not. All conditions are templated so there is no runtime overhead. And this makes sqlite_orm the most powerful sqlite C++ ORM library!

Moreover you can use parentheses to set the priority of query conditions:

auto cuteConditions = storage.get_all<User>(where((c(&User::firstName) == "John" or c(&User::firstName) == "Alex") and c(&User::id) == 4));  //  where (first_name = 'John' or first_name = 'Alex') and id = 4
cout << "cuteConditions count = " << cuteConditions.size() << endl; //  cuteConditions count = 1
cuteConditions = storage.get_all<User>(where(c(&User::firstName) == "John" or (c(&User::firstName) == "Alex" and c(&User::id) == 4)));   //  where first_name = 'John' or (first_name = 'Alex' and id = 4)
cout << "cuteConditions count = " << cuteConditions.size() << endl; //  cuteConditions count = 2

Also we can implement get by id with get_all and where like this:

//  SELECT * FROM users WHERE ( 2 = id )
auto idEquals2 = storage.get_all<User>(where(2 == c(&User::id)));
cout << "idEquals2 count = " << idEquals2.size() << endl;
if(idEquals2.size()){
    cout << storage.dump(idEquals2.front()) << endl;
}else{
    cout << "user with id 2 doesn't exist" << endl;
}

Lets try the IN operator:

//  SELECT * FROM users WHERE id IN (2, 4, 6, 8, 10)
auto evenLesserTen10 = storage.get_all<User>(where(in(&User::id, {2, 4, 6, 8, 10})));
cout << "evenLesserTen10 count = " << evenLesserTen10.size() << endl;
for(auto &user : evenLesserTen10) {
    cout << storage.dump(user) << endl;
}

//  SELECT * FROM users WHERE last_name IN ("Doe", "White")
auto doesAndWhites = storage.get_all<User>(where(in(&User::lastName, {"Doe", "White"})));
cout << "doesAndWhites count = " << doesAndWhites.size() << endl;
for(auto &user : doesAndWhites) {
    cout << storage.dump(user) << endl;
}

And BETWEEN:

//  SELECT * FROM users WHERE id BETWEEN 66 AND 68
auto betweenId = storage.get_all<User>(where(between(&User::id, 66, 68)));
cout << "betweenId = " << betweenId.size() << endl;
for(auto &user : betweenId) {
    cout << storage.dump(user) << endl;
}

And even LIKE:

//  SELECT * FROM users WHERE last_name LIKE 'D%'
auto whereNameLike = storage.get_all<User>(where(like(&User::lastName, "D%")));
cout << "whereNameLike = " << whereNameLike.size() << endl;
for(auto &user : whereNameLike) {
    cout << storage.dump(user) << endl;
}

Looks like magic but it works very simple. Cute function c (column) takes a class member pointer and returns a special expression middle object that can be used with operators overloaded in ::sqlite_orm namespace. Operator overloads act just like functions

  • is_equal
  • is_not_equal
  • greater_than
  • greater_or_equal
  • lesser_than
  • lesser_or_equal
  • is_null
  • is_not_null

that simulate binary comparison operator so they take 2 arguments: left hand side and right hand side. Arguments may be either member pointer of mapped class or any other expression (core/aggregate function, literal or subexpression). Binary comparison functions map arguments to text to be passed to sqlite engine to process query. Member pointers are being mapped to column names and literals/variables/constants to '?' and then are bound automatically. Next where function places brackets around condition and adds "WHERE" keyword before condition text. Next resulted string appends to a query string and is being processed further.

If you omit where function in get_all it will return all objects from a table:

auto allUsers = storage.get_all<User>();

Also you can use remove_all function to perform DELETE FROM ... WHERE query with the same type of conditions.

storage.remove_all<User>(where(c(&User::id) < 100));

Raw select

If you need to extract only a single column (SELECT %column_name% FROM %table_name% WHERE %conditions%) you can use a non-CRUD select function:

//  SELECT id FROM users
auto allIds = storage.select(&User::id);    
cout << "allIds count = " << allIds.size() << endl; //  allIds is std::vector<int>
for(auto &id : allIds) {
    cout << id << " ";
}
cout << endl;

//  SELECT id FROM users WHERE last_name = 'Doe'
auto doeIds = storage.select(&User::id, where(c(&User::lastName) == "Doe"));
cout << "doeIds count = " << doeIds.size() << endl; //  doeIds is std::vector<int>
for(auto &doeId : doeIds) {
    cout << doeId << " ";
}
cout << endl;

//  SELECT last_name FROM users WHERE id < 300
auto allLastNames = storage.select(&User::lastName, where(c(&User::id) < 300));    
cout << "allLastNames count = " << allLastNames.size() << endl; //  allLastNames is std::vector<std::string>
for(auto &lastName : allLastNames) {
    cout << lastName << " ";
}
cout << endl;

//  SELECT id FROM users WHERE image_url IS NULL
auto idsWithoutUrls = storage.select(&User::id, where(is_null(&User::imageUrl)));
for(auto id : idsWithoutUrls) {
    cout << "id without image url " << id << endl;
}

//  SELECT id FROM users WHERE image_url IS NOT NULL
auto idsWithUrl = storage.select(&User::id, where(is_not_null(&User::imageUrl)));
for(auto id : idsWithUrl) {
    cout << "id with image url " << id << endl;
}
auto idsWithUrl2 = storage.select(&User::id, where(not is_null(&User::imageUrl)));
assert(std::equal(idsWithUrl2.begin(),
                  idsWithUrl2.end(),
                  idsWithUrl.begin()));

Also you're able to select several column in a vector of tuples. Example:

//  `SELECT first_name, last_name FROM users WHERE id > 250 ORDER BY id`
auto partialSelect = storage.select(columns(&User::firstName, &User::lastName),
                                    where(c(&User::id) > 250),
                                    order_by(&User::id));
cout << "partialSelect count = " << partialSelect.size() << endl;
for(auto &t : partialSelect) {
    auto &firstName = std::get<0>(t);
    auto &lastName = std::get<1>(t);
    cout << firstName << " " << lastName << endl;
}

ORDER BY support

ORDER BY query option can be applied to get_all and select functions just like where but with order_by function. It can be mixed with WHERE in a single query. Examples:

//  `SELECT * FROM users ORDER BY id`
auto orderedUsers = storage.get_all<User>(order_by(&User::id));
cout << "orderedUsers count = " << orderedUsers.size() << endl;
for(auto &user : orderedUsers) {
    cout << storage.dump(user) << endl;
}

//  `SELECT * FROM users WHERE id < 250 ORDER BY first_name`
auto orderedUsers2 = storage.get_all<User>(where(c(&User::id) < 250), order_by(&User::firstName));
cout << "orderedUsers2 count = " << orderedUsers2.size() << endl;
for(auto &user : orderedUsers2) {
    cout << storage.dump(user) << endl;
}

//  `SELECT * FROM users WHERE id > 100 ORDER BY first_name ASC`
auto orderedUsers3 = storage.get_all<User>(where(c(&User::id) > 100), order_by(&User::firstName).asc());
cout << "orderedUsers3 count = " << orderedUsers3.size() << endl;
for(auto &user : orderedUsers3) {
    cout << storage.dump(user) << endl;
}

//  `SELECT * FROM users ORDER BY id DESC`
auto orderedUsers4 = storage.get_all<User>(order_by(&User::id).desc());
cout << "orderedUsers4 count = " << orderedUsers4.size() << endl;
for(auto &user : orderedUsers4) {
    cout << storage.dump(user) << endl;
}

//  `SELECT first_name FROM users ORDER BY ID DESC`
auto orderedFirstNames = storage.select(&User::firstName, order_by(&User::id).desc());
cout << "orderedFirstNames count = " << orderedFirstNames.size() << endl;
for(auto &firstName : orderedFirstNames) {
    cout << "firstName = " << firstName << endl;
}

LIMIT and OFFSET

There are three available versions of LIMIT/OFFSET options:

  • LIMIT %limit%
  • LIMIT %limit% OFFSET %offset%
  • LIMIT %offset%, %limit%

All these versions available with the same interface:

//  `SELECT * FROM users WHERE id > 250 ORDER BY id LIMIT 5`
auto limited5 = storage.get_all<User>(where(c(&User::id) > 250),
                                      order_by(&User::id),
                                      limit(5));
cout << "limited5 count = " << limited5.size() << endl;
for(auto &user : limited5) {
    cout << storage.dump(user) << endl;
}

//  `SELECT * FROM users WHERE id > 250 ORDER BY id LIMIT 5, 10`
auto limited5comma10 = storage.get_all<User>(where(c(&User::id) > 250),
                                             order_by(&User::id),
                                             limit(5, 10));
cout << "limited5comma10 count = " << limited5comma10.size() << endl;
for(auto &user : limited5comma10) {
    cout << storage.dump(user) << endl;
}

//  `SELECT * FROM users WHERE id > 250 ORDER BY id LIMIT 5 OFFSET 10`
auto limit5offset10 = storage.get_all<User>(where(c(&User::id) > 250),
                                            order_by(&User::id),
                                            limit(5, offset(10)));
cout << "limit5offset10 count = " << limit5offset10.size() << endl;
for(auto &user : limit5offset10) {
    cout << storage.dump(user) << endl;
}

Please beware that queries LIMIT 5, 10 and LIMIT 5 OFFSET 10 mean different. LIMIT 5, 10 means LIMIT 10 OFFSET 5.

JOIN support

You can perform simple JOIN, CROSS JOIN, INNER JOIN, LEFT JOIN or LEFT OUTER JOIN in your query. Instead of joined table specify mapped type. Example for doctors and visits:

//  SELECT a.doctor_id, a.doctor_name,
//      c.patient_name, c.vdate
//  FROM doctors a
//  LEFT JOIN visits c
//  ON a.doctor_id=c.doctor_id;
auto rows = storage2.select(columns(&Doctor::id, &Doctor::name, &Visit::patientName, &Visit::vdate),
                            left_join<Visit>(on(c(&Doctor::id) == &Visit::doctorId)));  //  one `c` call is enough cause operator overloads are templated
for(auto &row : rows) {
    cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) << endl;
}
cout << endl;

Simple JOIN:

//  SELECT a.doctor_id,a.doctor_name,
//      c.patient_name,c.vdate
//  FROM doctors a
//  JOIN visits c
//  ON a.doctor_id=c.doctor_id;
rows = storage2.select(columns(&Doctor::id, &Doctor::name, &Visit::patientName, &Visit::vdate),
                       join<Visit>(on(c(&Doctor::id) == &Visit::doctorId)));
for(auto &row : rows) {
    cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) << endl;
}
cout << endl;

Two INNER JOINs in one query:

//  SELECT
//      trackid,
//      tracks.name AS Track,
//      albums.title AS Album,
//      artists.name AS Artist
//  FROM
//      tracks
//  INNER JOIN albums ON albums.albumid = tracks.albumid
//  INNER JOIN artists ON artists.artistid = albums.artistid;
auto innerJoinRows2 = storage.select(columns(&Track::trackId, &Track::name, &Album::title, &Artist::name),
                                     inner_join<Album>(on(c(&Album::albumId) == &Track::albumId)),
                                     inner_join<Artist>(on(c(&Artist::artistId) == &Album::artistId)));
//  innerJoinRows2 is std::vector<std::tuple<decltype(Track::trackId), decltype(Track::name), decltype(Album::title), decltype(Artist::name)>>

More join examples can be found in examples folder.

Migrations functionality

There are no explicit up and down functions that are used to be used in migrations. Instead sqlite_orm offers sync_schema function that takes responsibility of comparing actual db file schema with one you specified in make_storage call and if something is not equal it alters or drops/creates schema.

storage.sync_schema();
//  or
storage.sync_schema(true);

Please beware that sync_schema doesn't guarantee that data will be saved. It tries to save it only. Below you can see rules list that sync_schema follows during call:

  • if there are excess tables exist in db they are ignored (not dropped)
  • every table from storage is compared with it's db analog and
    • if table doesn't exist it is created
    • if table exists its colums are being compared with table_info from db and
      • if there are columns in db that do not exist in storage (excess) table will be dropped and recreated if preserve is false, and table will be copied into temporary table without excess columns, source table will be dropped, copied table will be renamed to source table (sqlite remove column technique) if preserve is true. preserve is the first argument in sync_schema function. It's default value is false. Beware that setting it to true may take time for copying table rows.
      • if there are columns in storage that do not exist in db they will be added using 'ALTER TABLE ... ADD COLUMN ...' command and table data will not be dropped but if any of added columns is null but has not default value table will be dropped and recreated
      • if there is any column existing in both db and storage but differs by any of properties (type, pk, notnull) table will be dropped and recreated (dflt_value isn't checked cause there can be ambiguity in default values, please beware).

The best practice is to call this function right after storage creation.

Transactions

There are three ways to begin and commit/rollback transactions:

  • explicitly call begin_transaction();, rollback(); or commit(); functions
  • use transaction function which begins transaction implicitly and takes a lambda argument which returns true for commit and false for rollback. All storage calls performed in lambda can be commited or rollbacked by returning true or false.
  • use transaction_guard function which returns a guard object which works just like lock_guard for std::mutex.

Example for explicit call:

auto secondUser = storage.get<User>(2);

storage.begin_transaction();
secondUser.typeId = 3;
storage.update(secondUser);
storage.rollback(); //  or storage.commit();

secondUser = storage.get<decltype(secondUser)>(secondUser.id);
assert(secondUser.typeId != 3);

Example for implicit call:

storage.transaction([&] () mutable {    //  mutable keyword allows make non-const function calls
    auto secondUser = storage.get<User>(2);
    secondUser.typeId = 1;
    storage.update(secondUser);
    auto gottaRollback = bool(rand() % 2);
    if(gottaRollback){  //  dummy condition for test
        return false;   //  exits lambda and calls ROLLBACK
    }
    return true;        //  exits lambda and calls COMMIT
});

The second way guarantees that commit or rollback will be called. You can use either way.

Trancations are useful with changes sqlite function that returns number of rows modified.

storage.transaction([&] () mutable {
    storage.remove_all<User>(where(c(&User::id) < 100));
    auto usersRemoved = storage.changes();
    cout << "usersRemoved = " << usersRemoved << endl;
    return true;
});

It will print a number of deleted users (rows). But if you call changes without a transaction and your database is located in file not in RAM the result will be 0 always cause sqlite_orm opens and closes connection every time you call a function without a transaction.

Also a transaction function returns true if transaction is commited and false if it is rollbacked. It can be useful if your next actions depend on transaction result:

auto commited = storage.transaction([&] () mutable {    
    auto secondUser = storage.get<User>(2);
    secondUser.typeId = 1;
    storage.update(secondUser);
    auto gottaRollback = bool(rand() % 2);
    if(gottaRollback){  //  dummy condition for test
        return false;   //  exits lambda and calls ROLLBACK
    }
    return true;        //  exits lambda and calls COMMIT
});
if(commited){
    cout << "Commited successfully, go on." << endl;
}else{
    cerr << "Commit failed, process an error" << endl;
}

Example for transaction_guard function:

try{
  auto guard = storage.transaction_guard(); //  calls BEGIN TRANSACTION and returns guard object
  user.name = "Paul";
  auto notExisting = storage.get<User>(-1); //  exception is thrown here, guard calls ROLLBACK in its destructor
  guard.commit();
}catch(...){
  cerr << "exception" << endl;
}

In memory database

To manage in memory database just provide :memory: or "" instead as filename to make_storage.

Comparison with other C++ libs

sqlite_orm SQLiteCpp hiberlite ODB
Schema sync yes no yes no
Single responsibility principle yes yes no no
STL compatible yes no no no
No raw string queries yes no yes yes
Transactions yes yes no yes
Custom types binding yes no yes yes
Doesn't use macros and/or external codegen scripts yes yes no no
Aggregate functions yes yes no yes
Prepared statements yes yes no no

Notes

To work well your data model class must be default constructable and must not have const fields mapped to database cause they are assigned during queries. Otherwise code won't compile on line with member assignment operator.

For more details please check the project wiki.

Installation

Note: Installation is not necessary if you plan to use the fetchContent method, see below in Usage.

Use a popular package manager like vcpkg and just install it with the vcpkg install sqlite-orm command.

Or you build it from source:

git clone https://github.com/fnc12/sqlite_orm.git sqlite_orm
cd sqlite_orm
cmake -B build
cmake --build build --target install

You might need admin rights for the last command.

Usage

CMake

If you use cmake, there are two supported ways how to use it with cmake (if another works as well or should be supported, open an issue).

Either way you choose, the include path as well as the dependency sqlite3 will be set automatically on your target. So usage is straight forward, but you need to have installed sqlite3 on your system (see Requirements below)

Find Package

If you have installed the lib system wide and it's in your PATH, you can use find_package to include it in cmake. It will make a target sqlite_orm::sqlite_orm available which you can link against. Have a look at examples/find_package for a full example.

find_package(SqliteOrm REQUIRED)

target_link_libraries(main PRIVATE sqlite_orm::sqlite_orm)

Fetch Content (Recommended)

Alternatively, cmake can download the project directly from github during configure stage and therefore you don't need to install the lib before. Againt a target sqlite_orm::sqlite_orm will be available which you can link against. Have a look at examples/fetch_content for a full example.

No CMake

If you want to use the lib directly with Make or something else, just set the inlcude path correctly (should be correct on Linux already), so sqlite_orm/sqlite_orm.h is found. As this is a header only lib, there is nothing more you have to do.

Requirements

  • C++14 compatible compiler (not C++11 cause of templated lambdas in the lib).
  • Sqlite3 installed on your system and in the path, so cmake can find it (or linked to you project if you don't use cmake)

Video from conference

Video from conference

SqliteMan

In case you need a native SQLite client for macOS or Windows 10 you can use SqliteMan https://sqliteman.dev. It is not a commercial. It is a free native client being developed by the maintainer of this repo.

sqlite_orm's People

Contributors

air-h-128k-il avatar air2 avatar andrei-datcu avatar aphage avatar cybrilla-rajaravivarma avatar denzor200 avatar dershokus avatar donaldtrump88 avatar doozyx avatar f-michaut avatar farwaykorse avatar firedaemon-cto avatar fnc12 avatar h3ndrk avatar jer-irl avatar juandent avatar leon0402 avatar mishal23 avatar oost avatar osom8979 avatar overlordff avatar rotolof avatar shuai132 avatar soroshsabz avatar trueqbit avatar undisputed-seraphim avatar vittorioromeo avatar vmnet4 avatar weyzu avatar wiresock avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sqlite_orm's Issues

std::vector<std::string> class member

class Vect
{
std::vectorstd::string _Vec;
};

decltype(auto) GetStorage()
{
return make_storage( "Vector",
make_table("vec",
make_column("R", &Vect::_Vec)
)
);
}

How to store and restore std::vector in and from database?

Way to make data members private?

I'm guessing this will require quite some work, but is it possible to create a class or struct with private data members and persist it? Maybe pass a member function as a way to retrieve it? Or maybe declare a friend class inside the class or structure so that the private members can be made available? Seems like that'd be a really nice thing if implemented

Foreign keys documentation/example

It looks like in #10 some support for foreign keys has been added, and the readme seems to suggest so as well in the features. Is there any chance that someone could write a short example or two for the readme or wiki?

Countributions

I made some changes to the code. This is first time that I am using github.
Can you anyone tell me procedure to commit code?
Or I can send code to the developers and they can merge it?

Development documentation

Thanks for providing such a good library. It serves my purpose most of times. But sometimes, it does not have functionality which I need and I can enhance the functionaliy most of times by myself. The biggest limitation for me is lack of development documentation like purpose of each class, class type(e.g. implementation, binders, row converters etc.). It will be great if you provide some documentation in code(e.g. doxygen documetation), class diagrams, collaboration diagrams. I used doxygen before. Doxygen creates the diagrams automatically.
Have a good day.

Qt compatibility

Hi.
How can someone change the library to work with Qt's Sqlite driver and Qt Data types (QList, QString etc) ?
Is it possible?

"no member named 'get_impl' in 'sqlite_orm::storage_impl<>'"

Hey, thank you for creating this library first of all. I'm running into a slight problem initializing and storing the Storage object. In another issue it was recommended to use the trick used in this example.

However I run into the error no member named 'get_impl' in 'sqlite_orm::storage_impl<>' with the following implementation:

#define PERSONALWIKI_STORAGE_H

#include "sqlite_orm/sqlite_orm.h"
#include "../entry.hpp"

using namespace sqlite_orm;

const std::string DATABASE = "/Users/jeffrey/Documents/test.db";

auto initStorage(const std::string &path) {
    return make_storage(path,
        make_table("entry",
                   make_column("id", &Entry::id, autoincrement(), primary_key()),
                   make_column("name", &Entry::name),
                   make_column("content", &Entry::content)
        )
    );
}

typedef decltype(initStorage("")) Storage;

static std::shared_ptr<Storage> storage;

class StorageService {
public:
    static std::shared_ptr<Storage> getStorage() {
        return std::make_shared<Storage>(initStorage(DATABASE));
    }
};


#endif //PERSONALWIKI_STORAGE_H

Where entry.hpp contains:

#ifndef PERSONALWIKI_ENTRY_H
#define PERSONALWIKI_ENTRY_H

struct Entry {
    int id;
    std::string name;
    std::string content;
};

enum EntryType {
    Web,
    Text
};

#endif

I'm not the most experienced C++ programmer so I might be doing something silly. Thanks in advance.

Private field in class?

Maybe add support getter and setter for object class? Like this:

storage = make_storage("db.sqlite",
                               make_table("players",
                                          make_column("id",
                                                      &Player::getId,
                                                      &Player::setId,
                                                      autoincrement(),
                                                      primary_key())));

lifetype of make_storage return instance

I am little confused about lifetype and usage of make_storage return instance.

  1. Is it supposed to be singleton? If yes, how can I open another connection to DB(for long operations) while keeping the instance active for small queries?
  2. What is best way from performance and memory perspective? Creating storage instance as per need or keeping it stored it statically?
  3. Is there any way to store the storage instance as class member?

Storing a null (value 0x0) in Blob

Hi,
I'm storing a vector (matrix dump) into a BLOB in SQLite.
This works fine until a 0 value is encountered. While reading, it stops at the 0 and the rest is garbage.
The data in the database also stops printing after the first 0 value, although the select length(data) from blob; returns the correct length (100 in this case).
After reading the data back it gives me the length 9 (where the first 0 is forced), always.
Not sure if it's me, here's the code to reproduce

#include <bits/stdc++.h>
#include "sqlite_orm.h"

using namespace std;
using namespace sqlite_orm;

typedef char byte;

struct BlobData {
    vector<char> data;
};

byte* generateData(int size) {
    byte *data = (byte*)malloc(size*sizeof(byte));

    for (int i = 0; i < size; ++i) {
        if ((i+1) % 10 == 0) {
            data[i] = 0;
        } else {
            data[i] = (rand() % 100) + 1;
        }
    }

    return data;
}

auto getStorage(string dbpath) {
    auto table = make_table("blob",
                                        make_column("data", &BlobData::data)
                                    );
    auto storage = make_storage(dbpath, 
                                    table
                                );
    return storage;
}

void newDB(string dbname) {
    auto storage = getStorage(dbname);
    storage.sync_schema();
    storage.remove_all<BlobData>();
}

void writeBlob(string dbname, byte* data, int size) {
    auto storage = getStorage(dbname);

    BlobData d;
    // d.data = string( reinterpret_cast<char const*>(data), size );
    std::vector<char> v(data, data+size);
    d.data = v;
    cout << "Write Size: " << d.data.size() << endl;
    storage.insert(d);
}

void readBlob(string dbname) {
    auto storage = getStorage(dbname);

    vector<BlobData> vd = storage.get_all<BlobData>();
    for (int i = 0; i < vd.size(); ++i)
    {   
        cout << "Read Size " << vd[i].data.size() << endl;
        for (int j = 0; j < vd[i].data.size(); ++j)
        {
            printf ("%d ", vd[i].data[j]);
        }
        cout << endl;
    }
}

int main(int argc, char const *argv[])
{
    int size = 100;
    string dbname = "blob.db";
    byte* data = generateData(size);

    newDB(dbname);
    writeBlob(dbname, data, size);
    readBlob(dbname);

    return 0;
}

update() compile error when called twice

env: vs2017 15.5
below code seems not work and report error in line 4929

auto table1 = make_table(...);
auto table2  = make_table(...);
auto storage1 = make_storage(table1);
auto storage2 = make_storage(table2);

for(storage1.iterate<A>())
{
   if(...)
   {
       storage1.update()
   }
}

for(storage2.iterate<B>())
{
   if(...)
   {
       storage2.update()
   }
}

BTW I'm a fresher to c++11, is that a wrong way to use it?

Consider escaping all column names

Column names in some places, like insert or insert_range, are escaped. But in some other places, like update or remove, they're not escaped. If you inconveniently have a field named "index", update() will throw a runtime error:

near "index": syntax error

CSV import to populate database

I am considering to use CSV file to populate database.

There are two choices to do that

  1. Use build-in facility of sqlite to import CSV format. I tried to find some examples to deal with it, but could not find any useful.
  2. Parsing and doing insert/replace on database: This option will have to read and parse CSV. It is kind of error-prone.

What do you think about option 1? Do you know how to implement it?

C++11 Support

Hi,

How difficult you think it is to re-implement the parts that are done in C++14 for sqlite_orm to be C++11 compatible?

Thanks.

Separation of DB and ORM functionality request

There is some functionality which does not need table(s), for example drop_table, user_version. These operate above ORM functionality. Creating ORM storage object to use this functionality is not always possible and logical. For example, checking existence of table, checking version of database.
I see there are two alternatives.

  1. Creation of separate class which deals with database functionality and adding make_db equivalent to make_storage without tables. (preferred)
  2. extending make_storage to make tables optional.

statement for alter table with string default value has double single quotes

I found bug with default value and sync schema.
First revision of schema:

make_storage("test_db.sqlite",
    make_table("User",
        make_column("Id",
            &User::userId,
            primary_key()),
        make_column("Name",
            &User::name),
        make_column("Age",
            &User::age)));

Second revision(with std::string field 'email'):

make_storage("test_db.sqlite",
    make_table("User",
        make_column("Id",
            &User::userId,
            primary_key()),
        make_column("Name",
            &User::name),
        make_column("Age",
            &User::age),
        make_column("Email",
            &User::email,
            default_value("[email protected]"))));

Single qoutes was added in default_value_extractor and get_table_info.
Generated statement: ALTER TABLE User ADD COLUMN Email TEXT NOT NULL DEFAULT ''[email protected]''

string primary key

Hello!

I tried to create a table with string (text) primary key. I always get

terminating with uncaught exception of type std::runtime_error: NOT NULL constraint failed: counters.key

error when I try to insert an element. I tested this on the command line of sqlite3 and it worked without error.
I printed out the sql querry from insert function. I get the next:

INSERT INTO 'counters' ("value") VALUES (?)

looks like it drops the key part of the insert statement.

And here is my example code:

#include <sqlite_orm.h>

using namespace sqlite_orm;

struct Counter {
    std::string key;
    uint32_t    value;
};

int main()
{
    auto storage = make_storage("./db.sqlite",
                                make_table("counters",
                                           make_column("key", &Counter::key, primary_key()),
                                           make_column("value", &Counter::value)));
    storage.sync_schema(true);

    Counter c = {"/hardware/a4printer/printed_pages", 3};
    std::cout << "key: " << c.key << ": " << c.value << std::endl;
    storage.insert(c);

    return 0;
}

The output of command line test:

sqlite> .schema counters
CREATE TABLE IF NOT EXISTS 'counters' ( 'key' TEXT PRIMARY KEY NOT NULL , 'value' INTEGER NOT NULL );
sqlite> insert into counters VALUES("/hardware/a4printer/printed_pages", 3);
sqlite> select * from counters;
/hardware/a4printer/printed_pages|3

If I just simple use unique for key, I have the next table:

CREATE TABLE IF NOT EXISTS 'counters' ( 'key' TEXT UNIQUE NOT NULL , 'value' INTEGER NOT NULL );

and the program works. The insert statement will be:

INSERT INTO 'counters' ("key", "value") VALUES (?, ?)

Foreign Key usage unable to complete build

Attached is a rough example where I cannot build using the makefile. There is nothing special about these tables, one merely points to another with a foreign key. The 2 tables both have primary keys, and the second points to the first with a foreign key. The ORM allows it to generate the schema fine, but when you call update on the db object with the second table with the foreign key, it breaks. Commenting out the update call will build.

sqlite_orm_bug.tar.gz

Here is the error output:

In file included from main.cpp:4:0: sqlite_orm.h: In instantiation of ‘std::vector<std::__cxx11::basic_string<char> > sqlite_orm::table_impl<H, T ...>::column_names_with() [with Op = {sqlite_orm::constraints::primary_key_t<>}; H = sqlite_orm::constraints::foreign_key_t<int testnamespace::test2::*, int testnamespace::test1::*>; T = {}]’: sqlite_orm.h:1809:64: recursively required from ‘std::vector<std::__cxx11::basic_string<char> > sqlite_orm::table_impl<H, T ...>::column_names_with() [with Op = {sqlite_orm::constraints::primary_key_t<>}; H = sqlite_orm::internal::column_t<testnamespace::test2, int>; T = {sqlite_orm::internal::column_t<testnamespace::test2, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, sqlite_orm::internal::column_t<testnamespace::test2, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, sqlite_orm::constraints::foreign_key_t<int testnamespace::test2::*, int testnamespace::test1::*>}]’ sqlite_orm.h:1809:64: required from ‘std::vector<std::__cxx11::basic_string<char> > sqlite_orm::table_impl<H, T ...>::column_names_with() [with Op = {sqlite_orm::constraints::primary_key_t<>}; H = sqlite_orm::internal::column_t<testnamespace::test2, int, sqlite_orm::constraints::primary_key_t<> >; T = {sqlite_orm::internal::column_t<testnamespace::test2, int>, sqlite_orm::internal::column_t<testnamespace::test2, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, sqlite_orm::internal::column_t<testnamespace::test2, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, sqlite_orm::constraints::foreign_key_t<int testnamespace::test2::*, int testnamespace::test1::*>}]’ sqlite_orm.h:1994:69: required from ‘std::vector<std::__cxx11::basic_string<char> > sqlite_orm::table_t<Cs>::column_names_with() [with Op = {sqlite_orm::constraints::primary_key_t<>}; Cs = {sqlite_orm::internal::column_t<testnamespace::test2, int, sqlite_orm::constraints::primary_key_t<> >, sqlite_orm::internal::column_t<testnamespace::test2, int>, sqlite_orm::internal::column_t<testnamespace::test2, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, sqlite_orm::internal::column_t<testnamespace::test2, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, sqlite_orm::constraints::foreign_key_t<int testnamespace::test2::*, int testnamespace::test1::*>}]’ sqlite_orm.h:4230:110: required from ‘void sqlite_orm::storage_t<Ts>::update(const O&) [with O = testnamespace::test2; Ts = {sqlite_orm::table_t<sqlite_orm::internal::column_t<testnamespace::test1, int, sqlite_orm::constraints::primary_key_t<> >, sqlite_orm::internal::column_t<testnamespace::test1, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, sqlite_orm::internal::column_t<testnamespace::test1, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, sqlite_orm::table_t<sqlite_orm::internal::column_t<testnamespace::test2, int, sqlite_orm::constraints::primary_key_t<> >, sqlite_orm::internal::column_t<testnamespace::test2, int>, sqlite_orm::internal::column_t<testnamespace::test2, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, sqlite_orm::internal::column_t<testnamespace::test2, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, sqlite_orm::constraints::foreign_key_t<int testnamespace::test2::*, int testnamespace::test1::*> >}]’ main.cpp:78:22: required from here sqlite_orm.h:1810:13: error: ‘sqlite_orm::table_impl<sqlite_orm::constraints::foreign_key_t<int testnamespace::test2::*, int testnamespace::test1::*> >::column_type {aka struct sqlite_orm::constraints::foreign_key_t<int testnamespace::test2::*, int testnamespace::test1::*>}’ has no member named ‘has_every’ if(this->col.template has_every<Op...>()) { ^ sqlite_orm.h:1811:17: error: ‘sqlite_orm::table_impl<sqlite_orm::constraints::foreign_key_t<int testnamespace::test2::*, int testnamespace::test1::*> >::column_type {aka struct sqlite_orm::constraints::foreign_key_t<int testnamespace::test2::*, int testnamespace::test1::*>}’ has no member named ‘name’ res.emplace_back(this->col.name); ^ makefile:4: recipe for target 'cpp' failed make: *** [cpp] Error 1

Autoincrement error

I experimenting with this ORM and for now i can tell that is very nice documented and provided with lots of examples. I'm heaving some issue with "unique.cpp" example. Compiling example works fine but when run program i got error:

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
terminate called after throwing an instance of 'std::runtime_error'
what(): near "AUTOINCREMENT": syntax error

"unique.sqlite" was created but without any table and column. It is completely empty.

I'm using Eclipse CDT Oxygen.1a Release (4.7.1a) on windows 7, with mingw64
gcc version 6.3.0 (x86_64-posix-seh-rev1, Built by MinGW-W64 project)

Donations

Thanks for providing the great libraries.
I really appreciate your work and consultation.

As recognition to your work, I would like to donate some money. Do you accept donations?`

how to read db from this library?

Hi, I'm a newbie to c++. This orm c++ library is aswsome.
I have a stupid question. how to use function make_storage("exists.db",null) to read a exists sqlite db file.
I don't want to use make_storage("exists.db", make_table("blabla..")) on this format. I just want to use:
auto storage = read_storage("exists.db");
storage.select(...) to query my data.
How to do it. Thanks.

Saving and retrieving image(blob) in DB

Good morning,
Do you have any example for saving and retrieving image(blob) in DB? I do not know corresponding data type of blob in C++.

It will be great if you provide some information.

foreign key fails

with the current sqlite, I see the following error when running the foreign_key.cpp example

replace query: REPLACE INTO 'artist' ("artistid", "artistname") VALUES(?, ?)
replace query: REPLACE INTO 'artist' ("artistid", "artistname") VALUES(?, ?)
sending query: INSERT INTO 'track' ("trackname", "trackartist") VALUES (?, ?)
terminate called after throwing an instance of 'std::runtime_error'
what(): no such table: main.artistid
Aborted
I believe its because its getting main. as part of the foreign key name. not sure where the bug is - still debugging. any help?

Primary key in column does not work

I have following code.
struct Data
{
int mDefault; /**< 0=User or 1=Default*/
std::string mLang1;
};

return make_storage(DBPath,
                    make_table("UserPreferences",
                               make_column("IsDefault", &Data::mDefault, primary_key()),
                               make_column("Lang1", &Data::mLang1)
                               )
                                    );
  • The created database does not create primary key.
  • Is there any way to get Data object for specific primary key without using getall function like storage.get(insertedId);? Does storage.get(insertedId); work in this case?

Split the header file.

Hello,

What do you think about splitting the 6600 lines file into smaller headers and provide sqlite_orm.h which just includes all internal headers?

Conan package

Hello,

Can yuo create Conan package for your library?

Escaping string values

String quotes are not escaped and generate wrong sql queries. Taking the employee examples, making a query like:

storage.get_all<Employee>(where(is_equal(&Employee::name, "Paul'l")));

Generates query:
SELECT COMPANY."ID", COMPANY."NAME", COMPANY."AGE", COMPANY."ADDRESS", COMPANY."SALARY" FROM 'COMPANY' WHERE ( 'COMPANY'."NAME" = 'Paul'l')

Throwing an error:

terminate called after throwing an instance of 'std::runtime_error' what(): near "l": syntax error zsh: abort (core dumped) ./a.out

Error compling in Vs2015 update 3

Hi
Awesome library. Got this error when compile sample in Visual Studio 2015, is it possible to fix? Or it's vs2015 unsupport some c++14 feature? I don't fugurate for do to that. Also 2017 compile pretty well.

Thank you.

Error C2440 'return': cannot convert from 'initializer list' to 'sqlite_orm::constraints::primary_key_t<>'

template<class ...Cs>
inline constraints::primary_key_t<Cs...> primary_key(Cs ...cs) {
	return{ std::make_tuple(cs...) }; // << Error
}

wstring support

I found that the code does not support wstrings. I otherwise like what you have done. I modified the code to support it if you would like it.
`
#ifndef sqlite_orm_h
#define sqlite_orm_h

#include
#include
#include <type_traits>
#include "..\sqlite3.h"
#include
#include // std::vector
#include // std::stringstream
#include // std::find
#include // std::shared_ptr, std::unique_ptr
#include
#include
#include <initializer_list>
#include // std::set
#include // std::function
#include // std::ostream
#include // std::iterator_traits
#include
#include

#if defined(_MSC_VER)

if defined(min)

__pragma(push_macro("min"))

undef min

define RESTORE_MIN

endif

if defined(max)

__pragma(push_macro("max"))

undef max

define RESTORE_MAX

endif

#endif // defined(_MSC_VER)

namespace sqlite_orm {

typedef sqlite_int64 int64;
typedef sqlite_uint64 uint64;

//  got from here http://stackoverflow.com/questions/25958259/how-do-i-find-out-if-a-tuple-contains-a-type
namespace tuple_helper {

    template <typename T, typename Tuple>
    struct has_type;

    template <typename T>
    struct has_type<T, std::tuple<>> : std::false_type {};

    template <typename T, typename U, typename... Ts>
    struct has_type<T, std::tuple<U, Ts...>> : has_type<T, std::tuple<Ts...>> {};

    template <typename T, typename... Ts>
    struct has_type<T, std::tuple<T, Ts...>> : std::true_type {};

    template <typename T, typename Tuple>
    using tuple_contains_type = typename has_type<T, Tuple>::type;

    template<size_t N, class ...Args>
    struct iterator {

        template<class L>
        void operator()(std::tuple<Args...> &t, L l) {
            l(std::get<N>(t));
            iterator<N - 1, Args...>()(t, l);
        }
    };

    template<class ...Args>
    struct iterator<0, Args...>{
        template<class L>
        void operator()(std::tuple<Args...> &t, L l) {
            l(std::get<0>(t));
        }
    };

    template<size_t N>
    struct iterator<N> {

        template<class L>
        void operator()(std::tuple<> &, L) {
            //..
        }
    };

    template <class F, typename T, std::size_t... I>
    void tuple_for_each_impl(F&& f, const T& t, std::index_sequence<I...>){
        int _[] = { (f(std::get<I>(t)), int{}) ... };
        (void)_;
    }

    template <typename F, typename ...Args>
    void tuple_for_each(const std::tuple<Args...>& t, F&& f){
        tuple_for_each_impl(std::forward<F>(f), t, std::index_sequence_for<Args...>{});
    }
}

/**
 *  This class accepts c++ type and transfers it to sqlite name (int -> INTEGER, std::string -> TEXT)
 */
template<class T>
struct type_printer;

struct integer_printer {
    inline const std::string& print() {
        static const std::string res = "INTEGER";
        return res;
    }
};

struct text_printer {
    inline const std::string& print() {
        static const std::string res = "TEXT";
        return res;
    }
};

struct real_printer {
    inline const std::string& print() {
        static const std::string res = "REAL";
        return res;
    }
};

struct blob_printer {
    inline const std::string& print() {
        static const std::string res = "BLOB";
        return res;
    }
};

//Note unsigned/signed char and simple char used for storing integer values, not char values.
template<>
struct type_printer<unsigned char> : public integer_printer {};

template<>
struct type_printer<signed char> : public integer_printer {};

template<>
struct type_printer<char> : public integer_printer {};

template<>
struct type_printer<unsigned short int> : public integer_printer {};

template<>
struct type_printer<short> : public integer_printer {};

template<>
struct type_printer<unsigned int> : public integer_printer {};

template<>
struct type_printer<int> : public integer_printer {};

template<>
struct type_printer<unsigned long> : public integer_printer {};

template<>
struct type_printer<long> : public integer_printer {};

template<>
struct type_printer<unsigned long long> : public integer_printer {};

template<>
struct type_printer<long long> : public integer_printer {};

template<>
struct type_printer<bool> : public integer_printer {};

template<>
struct type_printer<std::string> : public text_printer {};

template<>
struct type_printer<std::wstring> : public text_printer {};

template<>
struct type_printer<const char*> : public text_printer {};

template<>
struct type_printer<double> : public real_printer {};

template<class T>
struct type_printer<std::shared_ptr<T>> : public type_printer<T> {};

template<class T>
struct type_printer<std::unique_ptr<T>> : public type_printer<T> {};

template<>
struct type_printer<std::vector<char>> : public blob_printer {};

namespace internal {

    enum class collate_argument {
        binary,
        nocase,
        rtrim,
    };
}

namespace constraints {

    /**
     *  AUTOINCREMENT constraint class.
     */
    struct autoincrement_t {

        operator std::string() const {
            return "AUTOINCREMENT";
        }
    };

    /**
     *  PRIMARY KEY constraint class.
     *  Cs is parameter pack which contains columns (member pointer and/or function pointers). Can be empty when used withen `make_column` function.
     */
    template<class ...Cs>
    struct primary_key_t {
        std::tuple<Cs...> columns;
        enum class order_by {
            unspecified,
            ascending,
            descending,
        };
        order_by asc_option = order_by::unspecified;

        primary_key_t(decltype(columns) c):columns(std::move(c)){}

        typedef void field_type;    //  for column iteration. Better be deleted
        typedef std::tuple<> constraints_type;

        operator std::string() const {
            std::string res = "PRIMARY KEY";
            switch(this->asc_option){
                case order_by::ascending:
                    res += " ASC";
                    break;
                case order_by::descending:
                    res += " DESC";
                    break;
                default:
                    break;
            }
            return res;
        }

        primary_key_t<Cs...> asc() const {
            auto res = *this;
            res.asc_option = order_by::ascending;
            return res;
        }

        primary_key_t<Cs...> desc() const {
            auto res = *this;
            res.asc_option = order_by::descending;
            return res;
        }
    };

    /**
     *  UNIQUE constraint class.
     */
    struct unique_t {

        operator std::string() const {
            return "UNIQUE";
        }
    };

    /**
     *  DEFAULT constraint class.
     *  T is a value type.
     */
    template<class T>
    struct default_t {
        typedef T value_type;

        value_type value;

        operator std::string() const {
            std::stringstream ss;
            ss << "DEFAULT ";
            auto needQuotes = std::is_base_of<text_printer, type_printer<T>>::value;
            if(needQuotes){
                ss << "'";
            }
            ss << this->value;
            if(needQuotes){
                ss << "'";
            }
            return ss.str();
        }
    };

#if SQLITE_VERSION_NUMBER >= 3006019

    /**
     *  FOREIGN KEY constraint class.
     *  C is column which has foreign key
     *  R is column which C references to
     *  Available in SQLite 3.6.19 or higher
     */
    template<class C, class R>
    struct foreign_key_t {
        C m = nullptr;
        R r = nullptr;

        foreign_key_t(C m_, R r_):m(m_),r(r_){}

        typedef void field_type;    //  for column iteration. Better be deleted
        typedef std::tuple<> constraints_type;

        template<class L>
        void for_each_column(L) {}

        template<class ...Opts>
        constexpr bool has_every() const  {
            return false;
        }
    };

    /**
     *  C can be a class member pointer, a getter function member pointer or setter
     *  func member pointer
     *  Available in SQLite 3.6.19 or higher
     */
    template<class C>
    struct foreign_key_intermediate_t {
        C m = nullptr;

        foreign_key_intermediate_t(C m_):m(m_){}

        template<class O, class F>
        foreign_key_t<C, F O::*> references(F O::*r) {
            typedef foreign_key_t<C, F O::*> ret_type;
            return ret_type(this->m, r);
        }

        template<class O, class F>
        foreign_key_t<C, const F& (O::*)() const> references(const F& (O::*getter)() const) {
            typedef foreign_key_t<C, const F& (O::*)() const> ret_type;
            return ret_type(this->m, getter);
        }

        template<class O, class F>
        foreign_key_t<C, void (O::*)(F)> references(void (O::*setter)(F)) {
            typedef foreign_key_t<C, void (O::*)(F)> ret_type;
            return ret_type(this->m, setter);
        }
    };

#endif

    struct collate_t {
        internal::collate_argument argument;

        collate_t(internal::collate_argument argument_):argument(argument_){}

        operator std::string() const {
            std::string res = "COLLATE ";
            switch(this->argument){
                case decltype(this->argument)::binary:
                    res += "BINARY";
                    break;
                case decltype(this->argument)::nocase:
                    res += "NOCASE";
                    break;
                case decltype(this->argument)::rtrim:
                    res += "RTRIM";
                    break;
            }
            return res;
        }
    };
}

#if SQLITE_VERSION_NUMBER >= 3006019

/**
 *  FOREIGN KEY constraint construction function that takes member pointer as argument
 *  Available in SQLite 3.6.19 or higher
 */
template<class O, class F>
constraints::foreign_key_intermediate_t<F O::*> foreign_key(F O::*m) {
    return {m};
}

/**
 *  FOREIGN KEY constraint construction function that takes getter function pointer as argument
 *  Available in SQLite 3.6.19 or higher
 */
template<class O, class F>
constraints::foreign_key_intermediate_t<const F& (O::*)() const> foreign_key(const F& (O::*getter)() const) {
    typedef constraints::foreign_key_intermediate_t<const F& (O::*)() const> ret_type;
    return ret_type(getter);
}

/**
 *  FOREIGN KEY constraint construction function that takes setter function pointer as argument
 *  Available in SQLite 3.6.19 or higher
 */
template<class O, class F>
constraints::foreign_key_intermediate_t<void (O::*)(F)> foreign_key(void (O::*setter)(F)) {
    return {setter};
}

#endif

/**
 *  UNIQUE constraint builder function.
 */
inline constraints::unique_t unique() {
    return {};
}

inline constraints::autoincrement_t autoincrement() {
    return {};
}

template<class ...Cs>
inline constraints::primary_key_t<Cs...> primary_key(Cs ...cs) {
    typedef constraints::primary_key_t<Cs...> ret_type;
    return ret_type(std::make_tuple(cs...));
}

template<class T>
constraints::default_t<T> default_value(T t) {
    return {t};
}

inline constraints::collate_t collate_nocase() {
    return {internal::collate_argument::nocase};
}

inline constraints::collate_t collate_binary() {
    return {internal::collate_argument::binary};
}

inline constraints::collate_t collate_rtrim() {
    return {internal::collate_argument::rtrim};
}

enum class sqlite_type {
    INTEGER,
    TEXT,
    BLOB,
    REAL,

// NUMERIC, // numeric and real are the same for c++
};

/**
 *  @param str case doesn't matter - it is uppercased before comparing.
 */
inline std::shared_ptr<sqlite_type> to_sqlite_type(const std::string &str) {
    auto asciiStringToUpper = [](std::string &s){
        std::transform(s.begin(),
                       s.end(),
                       s.begin(),
                       [](char c){
                           return std::toupper(c);
                       });
    };
    auto upperStr = str;
    asciiStringToUpper(upperStr);

    static std::map<sqlite_type, std::vector<std::regex>> typeMap = {
        { sqlite_type::INTEGER, {
            std::regex("INT"),
            std::regex("INT.*"),

// std::regex("INTEGER"),
std::regex("TINYINT"),
std::regex("SMALLINT"),
std::regex("MEDIUMINT"),
std::regex("BIGINT"),
std::regex("UNSIGNED BIG INT"),
std::regex("INT2"),
std::regex("INT8"),
} }, { sqlite_type::TEXT, {
std::regex("CHARACTER\([[:digit:]]+\)"),
std::regex("VARCHAR\([[:digit:]]+\)"),
std::regex("VARYING CHARACTER\([[:digit:]]+\)"),
std::regex("NCHAR\([[:digit:]]+\)"),
std::regex("NATIVE CHARACTER\([[:digit:]]+\)"),
std::regex("NVARCHAR\([[:digit:]]+\)"),
std::regex("CLOB"),
std::regex("TEXT"),
} }, { sqlite_type::BLOB, {
std::regex("BLOB"),
} }, { sqlite_type::REAL, {
std::regex("REAL"),
std::regex("DOUBLE"),
std::regex("DOUBLE PRECISION"),
std::regex("FLOAT"),
std::regex("NUMERIC"),
std::regex("DECIMAL\([[:digit:]]+,[[:digit:]]+\)"),
std::regex("BOOLEAN"),
std::regex("DATE"),
std::regex("DATETIME"),
} },
};
for(auto &p : typeMap) {
for(auto &r : p.second) {
if(std::regex_match(upperStr, r)){
return std::make_shared<sqlite_type>(p.first);
}
}
}

    return {};
}

/**
 *  This is class that tells `sqlite_orm` that type is nullable. Nullable types
 *  are mapped to sqlite database as `NULL` and not-nullable are mapped as `NOT NULL`.
 *  Default nullability status for all types is `NOT NULL`. So if you want to map
 *  custom type as `NULL` (for example: boost::optional) you have to create a specialiation
 *  of type_is_nullable for your type and derive from `std::true_type`.
 */
template<class T>
struct type_is_nullable : public std::false_type {
    bool operator()(const T &) const {
        return true;
    }
};

/**
 *  This is a specialization for std::shared_ptr. std::shared_ptr is nullable in sqlite_orm.
 */
template<class T>
struct type_is_nullable<std::shared_ptr<T>> : public std::true_type {
    bool operator()(const std::shared_ptr<T> &t) const {
        return static_cast<bool>(t);
    }
};

/**
 *  This is a specialization for std::unique_ptr. std::unique_ptr is nullable too.
 */
template<class T>
struct type_is_nullable<std::unique_ptr<T>> : public std::true_type {
    bool operator()(const std::unique_ptr<T> &t) const {
        return static_cast<bool>(t);
    }
};

namespace internal {

    /**
     *  This class is used in tuple interation to know whether tuple constains `default_value_t`
     *  constraint class and what it's value if it is
     */
    struct default_value_extractor {

        template<class A>
        std::shared_ptr<std::string> operator() (const A &) {
            return {};
        }

        template<class T>
        std::shared_ptr<std::string> operator() (const constraints::default_t<T> &t) {
            std::stringstream ss;
            ss << t.value;
            return std::make_shared<std::string>(ss.str());
        }
    };

    /**
     *  Result of concatenation || operator
     */
    template<class L, class R>
    struct conc_t {
        L l;
        R r;
    };

    /**
     *  This class stores single column info. column_t is a pair of [column_name:member_pointer] mapped to a storage
     *  O is a mapped class, e.g. User
     *  T is a mapped class'es field type, e.g. &User::name
     *  Op... is a constraints pack, e.g. primary_key_t, autoincrement_t etc
     */
    template<class O, class T, class ...Op>
    struct column_t {
        typedef O object_type;
        typedef T field_type;
        typedef std::tuple<Op...> constraints_type;
        typedef field_type object_type::*member_pointer_t;
        typedef const field_type& (object_type::*getter_type)() const;
        typedef void (object_type::*setter_type)(field_type);

        /**
         *  Column name. Specified during construction in `make_column`.
         */
        const std::string name;

        /**
         *  Member pointer used to read/write member
         */
        member_pointer_t member_pointer/* = nullptr*/;

        /**
         *  Getter member function pointer to get a value. If member_pointer is null than
         *  `getter` and `setter` must be not null
         */
        getter_type getter/* = nullptr*/;

        /**
         *  Setter member function.
         */
        setter_type setter/* = nullptr*/;

        /**
         *  Constraints tuple
         */
        constraints_type constraints;

        bool not_null() const {
            return !type_is_nullable<field_type>::value;
        }

        template<class Opt>
        constexpr bool has() const {
            return tuple_helper::tuple_contains_type<Opt, constraints_type>::value;
        }

        template<class O1, class O2, class ...Opts>
        constexpr bool has_every() const  {
            if(has<O1>() && has<O2>()) {
                return true;
            }else{
                return has_every<Opts...>();
            }
        }

        template<class O1>
        constexpr bool has_every() const {
            return has<O1>();
        }

        std::shared_ptr<std::string> default_value() {
            std::shared_ptr<std::string> res;
            tuple_helper::iterator<std::tuple_size<constraints_type>::value - 1, Op...>()(constraints, [&res](auto &v){
                auto dft = internal::default_value_extractor()(v);
                if(dft){
                    res = dft;
                }
            });
            return res;
        }
    };

    template<class T>
    struct is_column : public std::false_type {};

    template<class O, class T, class ...Op>
    struct is_column<column_t<O, T, Op...>> : public std::true_type {};

    template<class T>
    struct is_foreign_key : std::false_type{};

    template<class C, class R>
    struct is_foreign_key<constraints::foreign_key_t<C, R>> : std::true_type{};

    template<class T>
    struct is_primary_key : public std::false_type {};

    template<class ...Cs>
    struct is_primary_key<constraints::primary_key_t<Cs...>> : public std::true_type {};

    /*template<class R, class F, class T>
    struct case_after_when_t;

    template<class R, class F, class ...Args>
    struct case_unclosed_t {
        typedef R result_type;
        typedef std::tuple<Args...> args_type;

        F field;    //  case field
        args_type args;

        case_unclosed_t(F f):field(std::move(f)){}

        template<class T>
        case_after_when_t<R, F, T> when(T t) {
            typedef case_after_when_t<R, F, T> ret_type;
            return ret_type(t);
        }
    };

    template<class R, class F, class T>
    struct case_after_when_t {
        T when;

        case_after_when_t(T t):when(std::move(t)){}
    };*/

    template<class T>
    struct expression_t {
        T t;

        expression_t(T t_):t(t_){}
    };

    template<class F, class R, class ...Whens>
    struct case_t {
        F field;
        typedef R result_type;
        typedef std::tuple<Whens...> whens_t;
        typedef case_t<F, R, Whens...> self_type;

        case_t(F f):field(std::move(f)){}

        self_type end() {
            return *this;
        }

        self_type end(const std::string &str) {
            this->column_name = str;
            return this->end();
        }

    protected:
        std::string column_name;
    };

}

template<class T>
internal::expression_t<T> c(T t) {
    typedef internal::expression_t<T> result_type;
    return result_type(t);
}

template<class R, class F>
internal::case_t<R, F> case_(F f) {
    typedef typename internal::case_t<R, F> ret_type;
    return ret_type(f);
}

template<class L, class R>
internal::conc_t<L, R> conc(L l, R r) {
    return {l, r};
}

/**
 *  Is used to print members mapped to objects in storage_t::dump member function.
 */
template<class T>
struct field_printer {
    std::string operator()(const T &t) const {
        std::stringstream stream;
        stream << t;
        return stream.str();
    }
};

//upgrade to integer is required when using unsigned char(uint8_t)
template<>
struct field_printer<unsigned char> {
    std::string operator()(const unsigned char &t) const {
        std::stringstream stream;
        stream << +t;
        return stream.str();
    }
};

//upgrade to integer is required when using signed char(int8_t)
template<>
struct field_printer<signed char> {
    std::string operator()(const signed char &t) const {
        std::stringstream stream;
        stream << +t;
        return stream.str();
    }
};

//  char is neigher signer char nor unsigned char so it has its own specialization
template<>
struct field_printer<char> {
    std::string operator()(const char &t) const {
        std::stringstream stream;
        stream << +t;
        return stream.str();
    }
};

template<>
struct field_printer<std::string> {
    std::string operator()(const std::string &t) const {
        return t;
    }
};

template<>
struct field_printer<std::vector<char>> {
    std::string operator()(const std::vector<char> &t) const {
        std::stringstream ss;
        ss << std::hex;
        for(auto c : t) {
            ss << c;
        }
        return ss.str();
    }
};

template<>
struct field_printer<std::nullptr_t> {
    std::string operator()(const std::nullptr_t &) const {
        return "null";
    }
};

template<class T>
struct field_printer<std::shared_ptr<T>> {
    std::string operator()(const std::shared_ptr<T> &t) const {
        if(t){
            return field_printer<T>()(*t);
        }else{
            return field_printer<std::nullptr_t>()(nullptr);
        }
    }
};

template<class T>
struct field_printer<std::unique_ptr<T>> {
    std::string operator()(const std::unique_ptr<T> &t) const {
        if(t){
            return field_printer<T>()(*t);
        }else{
            return field_printer<std::nullptr_t>()(nullptr);
        }
    }
};

/**
 *  Column builder function. You should use it to create columns and not constructor.
 */
template<class O, class T, class ...Op>
internal::column_t<O, T, Op...> make_column(const std::string &name, T O::*m, Op ...constraints){
    return {name, m, nullptr, nullptr, std::make_tuple(constraints...)};
}

template<class O, class T, class ...Op>
internal::column_t<O, T, Op...> make_column(const std::string &name, void (O::*setter)(T), const T& (O::*getter)() const, Op ...constraints) {
    return {name, nullptr, getter, setter, std::make_tuple(constraints...)};
}

template<class O, class T, class ...Op>
internal::column_t<O, T, Op...> make_column(const std::string &name, const T& (O::*getter)() const, void (O::*setter)(T), Op ...constraints) {
    return {name, nullptr, getter, setter, std::make_tuple(constraints...)};
}

namespace conditions {

    struct limit_t {
        int lim;
        bool has_offset = false;
        bool offset_is_implicit = false;
        int off = 0;

        limit_t(decltype(lim) lim_):lim(lim_){}

        limit_t(decltype(lim) lim_,
                decltype(has_offset) has_offset_,
                decltype(offset_is_implicit) offset_is_implicit_,
                decltype(off) off_):
        lim(lim_),
        has_offset(has_offset_),
        offset_is_implicit(offset_is_implicit_),
        off(off_){}

        operator std::string () const {
            return "LIMIT";
        }
    };

    struct offset_t {
        int off;
    };

    /**
     *  Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators
     */
    struct condition_t {};

    template<class T>
    struct collate_t : public condition_t {
        T expr;
        internal::collate_argument argument;

        collate_t(T expr_, internal::collate_argument argument_):expr(expr_),argument(argument_){}

        operator std::string () const {
            return constraints::collate_t{this->argument};
        }
    };

    template<class C>
    struct negated_condition_t : public condition_t {
        C c;

        negated_condition_t(){}

        negated_condition_t(C c_):c(c_){}

        operator std::string () const {
            return "NOT";
        }
    };

    template<class L, class R>
    struct and_condition_t : public condition_t {
        L l;
        R r;

        and_condition_t(){}

        and_condition_t(L l_, R r_):l(l_),r(r_){}

        operator std::string () const {
            return "AND";
        }
    };

    template<class L, class R>
    struct or_condition_t : public condition_t {
        L l;
        R r;

        or_condition_t(){}

        or_condition_t(L l_, R r_):l(l_),r(r_){}

        operator std::string () const {
            return "OR";
        }
    };

    template<class L, class R>
    struct binary_condition : public condition_t {
        L l;
        R r;

        binary_condition(){}

        binary_condition(L l_, R r_):l(l_),r(r_){}
    };

    /**
     *  = and == operators object.
     */
    template<class L, class R>
    struct is_equal_t : public binary_condition<L, R> {

        typedef is_equal_t<L, R> Self;

        using binary_condition<L, R>::binary_condition;

        operator std::string () const {
            return "=";
        }

        negated_condition_t<is_equal_t<L, R>> operator!() const {
            return {*this};
        }

        collate_t<Self> collate_binary() const {
            return {*this, internal::collate_argument::binary};
        }

        collate_t<Self> collate_nocase() const {
            return {*this, internal::collate_argument::nocase};
        }

        collate_t<Self> collate_rtrim() const {
            return {*this, internal::collate_argument::rtrim};
        }

    };

    /**
     *  != operator object.
     */
    template<class L, class R>
    struct is_not_equal_t : public binary_condition<L, R> {

        typedef is_not_equal_t<L, R> Self;

        using binary_condition<L, R>::binary_condition;

        operator std::string () const {
            return "!=";
        }

        negated_condition_t<is_not_equal_t<L, R>> operator!() const {
            return {*this};
        }

        collate_t<Self> collate_binary() const {
            return {*this, internal::collate_argument::binary};
        }

        collate_t<Self> collate_nocase() const {
            return {*this, internal::collate_argument::nocase};
        }

        collate_t<Self> collate_rtrim() const {
            return {*this, internal::collate_argument::rtrim};
        }
    };

    /**
     *  > operator object.
     */
    template<class L, class R>
    struct greater_than_t : public binary_condition<L, R> {

        typedef greater_than_t<L, R> Self;

        using binary_condition<L, R>::binary_condition;

        operator std::string () const {
            return ">";
        }

        negated_condition_t<greater_than_t<L, R>> operator!() const {
            return {*this};
        }

        collate_t<Self> collate_binary() const {
            return {*this, internal::collate_argument::binary};
        }

        collate_t<Self> collate_nocase() const {
            return {*this, internal::collate_argument::nocase};
        }

        collate_t<Self> collate_rtrim() const {
            return {*this, internal::collate_argument::rtrim};
        }
    };

    /**
     *  >= operator object.
     */
    template<class L, class R>
    struct greater_or_equal_t : public binary_condition<L, R> {

        typedef greater_or_equal_t<L, R> Self;

        using binary_condition<L, R>::binary_condition;

        operator std::string () const {
            return ">=";
        }

        negated_condition_t<greater_or_equal_t<L, R>> operator!() const {
            return {*this};
        }

        collate_t<Self> collate_binary() const {
            return {*this, internal::collate_argument::binary};
        }

        collate_t<Self> collate_nocase() const {
            return {*this, internal::collate_argument::nocase};
        }

        collate_t<Self> collate_rtrim() const {
            return {*this, internal::collate_argument::rtrim};
        }
    };

    /**
     *  < operator object.
     */
    template<class L, class R>
    struct lesser_than_t : public binary_condition<L, R> {

        typedef lesser_than_t<L, R> Self;

        using binary_condition<L, R>::binary_condition;

        operator std::string () const {
            return "<";
        }

        negated_condition_t<lesser_than_t<L, R>> operator!() const {
            return {*this};
        }

        collate_t<Self> collate_binary() const {
            return {*this, internal::collate_argument::binary};
        }

        collate_t<Self> collate_nocase() const {
            return {*this, internal::collate_argument::nocase};
        }

        collate_t<Self> collate_rtrim() const {
            return {*this, internal::collate_argument::rtrim};
        }
    };

    /**
     *  <= operator object.
     */
    template<class L, class R>
    struct lesser_or_equal_t : public binary_condition<L, R> {

        typedef lesser_or_equal_t<L, R> Self;

        using binary_condition<L, R>::binary_condition;

        operator std::string () const {
            return "<=";
        }

        negated_condition_t<lesser_or_equal_t<L, R>> operator!() const {
            return {*this};
        }

        collate_t<Self> collate_binary() const {
            return {*this, internal::collate_argument::binary};
        }

        collate_t<Self> collate_nocase() const {
            return {*this, internal::collate_argument::nocase};
        }

        collate_t<Self> collate_rtrim() const {
            return {*this, internal::collate_argument::rtrim};
        }
    };

    template<class L, class E>
    struct in_t : public condition_t {
        L l;    //  left expression..
        std::vector<E> values;       //  values..

        in_t(L l_, std::vector<E> values_):l(l_), values(values_){}

        negated_condition_t<in_t<L, E>> operator!() const {
            return {*this};
        }

        operator std::string () const {
            return "IN";
        }
    };

    template<class T>
    struct is_null_t {
        T t;

        negated_condition_t<is_null_t<T>> operator!() const {
            return {*this};
        }

        operator std::string () const {
            return "IS NULL";
        }
    };

    template<class T>
    struct is_not_null_t {
        T t;

        negated_condition_t<is_not_null_t<T>> operator!() const {
            return {*this};
        }

        operator std::string () const {
            return "IS NOT NULL";
        }
    };

    template<class C>
    struct where_t {
        C c;

        operator std::string() const {
            return "WHERE";
        }
    };

    template<class O>
    struct order_by_t {
        using self_type = order_by_t<O>;

        O o;
        int asc_desc = 0;   //  1: asc, -1: desc
        std::shared_ptr<internal::collate_argument> _collate_argument;

        order_by_t():o(nullptr){}

        order_by_t(O o_):o(o_){}

        operator std::string() const {
            return "ORDER BY";
        }

        order_by_t<O> asc() {
            auto res = *this;
            res.asc_desc = 1;
            return res;
        }

        order_by_t<O> desc() {
            auto res = *this;
            res.asc_desc = -1;
            return res;
        }

        self_type collate_binary() const {
            auto res = *this;
            res._collate_argument = std::make_unique<internal::collate_argument>(internal::collate_argument::binary);
            return res;
        }

        self_type collate_nocase() const {

// return {*this, internal::collate_argument::nocase};
auto res = *this;
res._collate_argument = std::make_uniqueinternal::collate_argument(internal::collate_argument::nocase);
return res;
}

        self_type collate_rtrim() const {

// return {*this, internal::collate_argument::rtrim};
auto res = *this;
res._collate_argument = std::make_uniqueinternal::collate_argument(internal::collate_argument::rtrim);
return res;
}
};

    template<class ...Args>
    struct group_by_t {
        std::tuple<Args...> args;

        operator std::string() const {
            return "GROUP BY";
        }
    };

    template<class A, class T>
    struct between_t : public condition_t {
        A expr;
        T b1;
        T b2;

        between_t(A expr_, T b1_, T b2_):expr(expr_),b1(b1_),b2(b2_){}

        operator std::string() const {
            return "BETWEEN";
        }
    };

    template<class A, class T>
    struct like_t : public condition_t {
        A a;
        T t;

        like_t(A a_, T t_):a(a_), t(t_){}

        operator std::string() const {
            return "LIKE";
        }
    };

    template<class T>
    struct cross_join_t {
        typedef T type;

        operator std::string() const {
            return "CROSS JOIN";
        }
    };

    template<class T, class O>
    struct left_join_t {
        typedef T type;
        typedef O on_type;

        on_type constraint;

        operator std::string() const {
            return "LEFT JOIN";
        }
    };

    /*template<class T>
    struct left_join_t<T, void> {
        typedef T type;

        operator std::string() const {
            return "LEFT JOIN";
        }
    };*/

    template<class T, class O>
    struct join_t {

        typedef T type;
        typedef O on_type;

        on_type constraint;

        operator std::string() const {
            return "JOIN";
        }
    };

    /*template<class T>
    struct natural_join_t {
        typedef T type;

        operator std::string() const {
            return "NATURAL JOIN";
        }
    };*/

    template<class T, class O>
    struct left_outer_join_t {
        typedef T type;
        typedef O on_type;

        on_type constraint;

        operator std::string() const {
            return "LEFT OUTER JOIN";
        }
    };

    template<class T>
    struct on_t {
        T t;

        operator std::string() const {
            return "ON";
        }
    };

    template<class F, class O>
    struct using_t {
        F O::*column;

        operator std::string() const {
            return "USING";
        }
    };

    template<class T, class O>
    struct inner_join_t {
        typedef T type;
        typedef O on_type;

        on_type constraint;

        operator std::string() const {
            return "INNER JOIN";
        }
    };

}

//  cute operators for columns

template<class T, class R>
conditions::lesser_than_t<T, R> operator<(internal::expression_t<T> expr, R r) {
    return {expr.t, r};
}

template<class L, class T>
conditions::lesser_than_t<L, T> operator<(L l, internal::expression_t<T> expr) {
    return {l, expr.t};
}

template<class T, class R>
conditions::lesser_or_equal_t<T, R> operator<=(internal::expression_t<T> expr, R r) {
    return {expr.t, r};
}

template<class L, class T>
conditions::lesser_or_equal_t<L, T> operator<=(L l, internal::expression_t<T> expr) {
    return {l, expr.t};
}

template<class T, class R>
conditions::greater_than_t<T, R> operator>(internal::expression_t<T> expr, R r) {
    return {expr.t, r};
}

template<class L, class T>
conditions::greater_than_t<L, T> operator>(L l, internal::expression_t<T> expr) {
    return {l, expr.t};
}

template<class T, class R>
conditions::greater_or_equal_t<T, R> operator>=(internal::expression_t<T> expr, R r) {
    return {expr.t, r};
}

template<class L, class T>
conditions::greater_or_equal_t<L, T> operator>=(L l, internal::expression_t<T> expr) {
    return {l, expr.t};
}

template<class T, class R>
conditions::is_equal_t<T, R> operator==(internal::expression_t<T> expr, R r) {
    return {expr.t, r};
}

template<class L, class T>
conditions::is_equal_t<L, T> operator==(L l, internal::expression_t<T> expr) {
    return {l, expr.t};
}

template<class T, class R>
conditions::is_not_equal_t<T, R> operator!=(internal::expression_t<T> expr, R r) {
    return {expr.t, r};
}

template<class L, class T>
conditions::is_not_equal_t<L, T> operator!=(L l, internal::expression_t<T> expr) {
    return {l, expr.t};
}

namespace internal {

    template<class ...Args>
    struct join_iterator {

        template<class L>
        void operator()(L) {
            //..
        }
    };

    template<>
    struct join_iterator<> {

        template<class L>
        void operator()(L) {
            //..
        }
    };

    template<class H, class ...Tail>
    struct join_iterator<H, Tail...> : public join_iterator<Tail...>{
        H h;

        typedef join_iterator<Tail...> super;

        template<class L>
        void operator()(L l) {
            this->super::operator()(l);
        }

    };

    template<class T, class ...Tail>
    struct join_iterator<conditions::cross_join_t<T>, Tail...> : public join_iterator<Tail...>{
        conditions::cross_join_t<T> h;

        typedef join_iterator<Tail...> super;

        template<class L>
        void operator()(L l) {
            l(h);
            this->super::operator()(l);
        }
    };

    template<class T, class O, class ...Tail>
    struct join_iterator<conditions::left_join_t<T, O>, Tail...> : public join_iterator<Tail...> {
        conditions::left_join_t<T, O> h;

        typedef join_iterator<Tail...> super;

        template<class L>
        void operator()(L l) {
            l(h);
            this->super::operator()(l);
        }
    };

    template<class T, class O, class ...Tail>
    struct join_iterator<conditions::join_t<T, O>, Tail...> : public join_iterator<Tail...> {
        conditions::join_t<T, O> h;

        typedef join_iterator<Tail...> super;

        template<class L>
        void operator()(L l) {
            l(h);
            this->super::operator()(l);
        }
    };

    template<class T, class O, class ...Tail>
    struct join_iterator<conditions::left_outer_join_t<T, O>, Tail...> : public join_iterator<Tail...> {
        conditions::left_outer_join_t<T, O> h;

        typedef join_iterator<Tail...> super;

        template<class L>
        void operator()(L l) {
            l(h);
            this->super::operator()(l);
        }
    };

    template<class T, class O, class ...Tail>
    struct join_iterator<conditions::inner_join_t<T, O>, Tail...> : public join_iterator<Tail...> {
        conditions::inner_join_t<T, O> h;

        typedef join_iterator<Tail...> super;

        template<class L>
        void operator()(L l) {
            l(h);
            this->super::operator()(l);
        }
    };
}

template<class F, class O>
conditions::using_t<F, O> using_(F O::*p) {
    return {p};
}

template<class T>
conditions::on_t<T> on(T t) {
    return {t};
}

template<class T>
conditions::cross_join_t<T> cross_join() {
    return {};
}

template<class T, class O>
conditions::left_join_t<T, O> left_join(O o) {
    return {o};
}

template<class T, class O>
conditions::join_t<T, O> join(O o) {
    return {o};
}

/*template<class T>
conditions::natural_join_t<T> natural_join() {
    return {};
}*/

template<class T, class O>
conditions::left_outer_join_t<T, O> left_outer_join(O o) {
    return {o};
}

template<class T, class O>
conditions::inner_join_t<T, O> inner_join(O o) {
    return {o};
}

inline conditions::offset_t offset(int off) {
    return {off};
}

inline conditions::limit_t limit(int lim) {
    return {lim};
}

inline conditions::limit_t limit(int off, int lim) {
    return {lim, true, true, off};
}

inline conditions::limit_t limit(int lim, conditions::offset_t offt) {
    return {lim, true, false, offt.off };
}

template<
    class L,
    class R,
    typename = typename std::enable_if<std::is_base_of<conditions::condition_t, L>::value && std::is_base_of<conditions::condition_t, R>::value>::type
>
conditions::and_condition_t<L, R> operator &&(const L &l, const R &r) {
    return {l, r};
}

template<
    class L,
    class R,
    typename = typename std::enable_if<std::is_base_of<conditions::condition_t, L>::value && std::is_base_of<conditions::condition_t, R>::value>::type
>
conditions::or_condition_t<L, R> operator ||(const L &l, const R &r) {
    return {l, r};
}

template<class T>
conditions::is_not_null_t<T> is_not_null(T t) {
    return {t};
}

template<class T>
conditions::is_null_t<T> is_null(T t) {
    return {t};
}

template<class L, class E>
conditions::in_t<L, E> in(L l, std::vector<E> values) {
    return {std::move(l), std::move(values)};
}

template<class L, class E>
conditions::in_t<L, E> in(L l, std::initializer_list<E> values) {
    return {std::move(l), std::move(values)};
}

template<class L, class R>
conditions::is_equal_t<L, R> is_equal(L l, R r) {
    return {l, r};
}

template<class L, class R>
conditions::is_equal_t<L, R> eq(L l, R r) {
    return {l, r};
}

template<class L, class R>
conditions::is_not_equal_t<L, R> is_not_equal(L l, R r) {
    return {l, r};
}

template<class L, class R>
conditions::is_not_equal_t<L, R> ne(L l, R r) {
    return {l, r};
}

template<class L, class R>
conditions::greater_than_t<L, R> greater_than(L l, R r) {
    return {l, r};
}

template<class L, class R>
conditions::greater_than_t<L, R> gt(L l, R r) {
    return {l, r};
}

template<class L, class R>
conditions::greater_or_equal_t<L, R> greater_or_equal(L l, R r) {
    return {l, r};
}

template<class L, class R>
conditions::greater_or_equal_t<L, R> ge(L l, R r) {
    return {l, r};
}

template<class L, class R>
conditions::lesser_than_t<L, R> lesser_than(L l, R r) {
    return {l, r};
}

template<class L, class R>
conditions::lesser_than_t<L, R> lt(L l, R r) {
    return {l, r};
}

template<class L, class R>
conditions::lesser_or_equal_t<L, R> lesser_or_equal(L l, R r) {
    return {l, r};
}

template<class L, class R>
conditions::lesser_or_equal_t<L, R> le(L l, R r) {
    return {l, r};
}

template<class C>
conditions::where_t<C> where(C c) {
    return {c};
}

template<class O>
conditions::order_by_t<O> order_by(O o) {
    return {o};
}

template<class ...Args>
conditions::group_by_t<Args...> group_by(Args&& ...args) {
    return {std::make_tuple(std::forward<Args>(args)...)};
}

template<class A, class T>
conditions::between_t<A, T> between(A expr, T b1, T b2) {
    return {expr, b1, b2};
}

template<class A, class T>
conditions::like_t<A, T> like(A a, T t) {
    return {a, t};
}

namespace core_functions {

    //  base class for operator overloading
    struct core_function_t {};

    /**
     *  LENGTH(x) function https://sqlite.org/lang_corefunc.html#length
     */
    template<class T>
    struct length_t : public core_function_t {
        T t;

        length_t(T t_):t(t_){}

        operator std::string() const {
            return "LENGTH";
        }
    };

    /**
     *  ABS(x) function https://sqlite.org/lang_corefunc.html#abs
     */
    template<class T>
    struct abs_t : public core_function_t {
        T t;

        abs_t(T t_):t(t_){}

        operator std::string() const {
            return "ABS";
        }
    };

    /**
     *  LOWER(x) function https://sqlite.org/lang_corefunc.html#lower
     */
    template<class T>
    struct lower_t : public core_function_t {
        T t;

        lower_t(T t_):t(t_){}

        operator std::string() const {
            return "LOWER";
        }
    };

    /**
     *  UPPER(x) function https://sqlite.org/lang_corefunc.html#upper
     */
    template<class T>
    struct upper_t : public core_function_t {
        T t;

        upper_t(T t_):t(t_){}

        operator std::string() const {
            return "UPPER";
        }
    };

    /**
     *  CHANGES() function https://sqlite.org/lang_corefunc.html#changes
     */
    struct changes_t : public core_function_t {

        operator std::string() const {
            return "CHANGES";
        }
    };

    /**
     *  TRIM(X) function https://sqlite.org/lang_corefunc.html#trim
     */
    template<class X>
    struct trim_single_t : public core_function_t {
        X x;

        trim_single_t(X x_):x(x_){}

        operator std::string() const {
            return "TRIM";
        }
    };

    /**
     *  TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim
     */
    template<class X, class Y>
    struct trim_double_t : public core_function_t {
        X x;
        Y y;

        trim_double_t(X x_, Y y_):x(x_), y(y_){}

        operator std::string() const {
            return static_cast<std::string>(trim_single_t<X>(0));
        }
    };

    /**
     *  LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim
     */
    template<class X>
    struct ltrim_single_t : public core_function_t {
        X x;

        ltrim_single_t(X x_):x(x_){}

        operator std::string() const {
            return "LTRIM";
        }
    };

    /**
     *  LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim
     */
    template<class X, class Y>
    struct ltrim_double_t : public core_function_t {
        X x;
        Y y;

        ltrim_double_t(X x_, Y y_):x(x_), y(y_){}

        operator std::string() const {
            return static_cast<std::string>(ltrim_single_t<X>(0));
        }
    };

    /**
     *  RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim
     */
    template<class X>
    struct rtrim_single_t : public core_function_t {
        X x;

        rtrim_single_t(X x_):x(x_){}

        operator std::string() const {
            return "RTRIM";
        }
    };

    /**
     *  RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim
     */
    template<class X, class Y>
    struct rtrim_double_t : public core_function_t {
        X x;
        Y y;

        rtrim_double_t(X x_, Y y_):x(x_), y(y_){}

        operator std::string() const {
            return static_cast<std::string>(rtrim_single_t<X>(0));
        }
    };

#if SQLITE_VERSION_NUMBER >= 3007016

    /**
     *  CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char
     */
    template<class ...Args>
    struct char_t_ : public core_function_t {
        typedef std::tuple<Args...> args_type;
        args_type args;

        char_t_(args_type args_):args(args_){}

        operator std::string() const {
            return "CHAR";
        }
    };

    struct random_t : public core_function_t {

        operator std::string() const {
            return "RANDOM";
        }
    };

#endif
template<class T, class ...Args>
struct date_t : public core_function_t {
typedef std::tuple<Args...> modifiers_type;
T timestring;
modifiers_type modifiers;

        date_t(T timestring_, modifiers_type modifiers_):timestring(timestring_),modifiers(modifiers_){}

        operator std::string() const {
            return "DATE";
        }
    };

    template<class T, class ...Args>
    struct datetime_t : public core_function_t {
        typedef std::tuple<Args...> modifiers_type;

        T timestring;
        modifiers_type modifiers;

        datetime_t(T timestring_, modifiers_type modifiers_):timestring(timestring_), modifiers(modifiers_){}

        operator std::string() const {
            return "DATETIME";
        }
    };
}

//  cute operators for core functions

template<
class F,
class R,
typename = typename std::enable_if<std::is_base_of<core_functions::core_function_t, F>::value>::type>
conditions::lesser_than_t<F, R> operator<(F f, R r) {
    return {f, r};
}

template<
class F,
class R,
typename = typename std::enable_if<std::is_base_of<core_functions::core_function_t, F>::value>::type>
conditions::lesser_or_equal_t<F, R> operator<=(F f, R r) {
    return {f, r};
}

template<
class F,
class R,
typename = typename std::enable_if<std::is_base_of<core_functions::core_function_t, F>::value>::type>
conditions::greater_than_t<F, R> operator>(F f, R r) {
    return {f, r};
}

template<
class F,
class R,
typename = typename std::enable_if<std::is_base_of<core_functions::core_function_t, F>::value>::type>
conditions::greater_or_equal_t<F, R> operator>=(F f, R r) {
    return {f, r};
}

template<
class F,
class R,
typename = typename std::enable_if<std::is_base_of<core_functions::core_function_t, F>::value>::type>
conditions::is_equal_t<F, R> operator==(F f, R r) {
    return {f, r};
}

template<
class F,
class R,
typename = typename std::enable_if<std::is_base_of<core_functions::core_function_t, F>::value>::type>
conditions::is_not_equal_t<F, R> operator!=(F f, R r) {
    return {f, r};
}

inline core_functions::random_t random() {
    return {};
}

template<class T, class ...Args, class Res = core_functions::date_t<T, Args...>>
Res date(T timestring, Args ...modifiers) {
    return Res(timestring, std::make_tuple(modifiers...));
}

template<class T, class ...Args, class Res = core_functions::datetime_t<T, Args...>>
Res datetime(T timestring, Args ...modifiers) {
    return Res(timestring, std::make_tuple(modifiers...));
}

#if SQLITE_VERSION_NUMBER >= 3007016

template<class ...Args>
core_functions::char_t_<Args...> char_(Args&& ...args) {
    typedef core_functions::char_t_<Args...> result_type;
    return result_type(std::make_tuple(std::forward<Args>(args)...));
}

#endif

template<class X, class Res = core_functions::trim_single_t<X>>
Res trim(X x) {
    return Res(x);
}

template<class X, class Y, class Res = core_functions::trim_double_t<X, Y>>
Res trim(X x, Y y) {
    return Res(x, y);
}

template<class X, class Res = core_functions::ltrim_single_t<X>>
Res ltrim(X x) {
    return Res(x);
}

template<class X, class Y, class Res = core_functions::ltrim_double_t<X, Y>>
Res ltrim(X x, Y y) {
    return Res(x, y);
}

template<class X, class Res = core_functions::rtrim_single_t<X>>
Res rtrim(X x) {
    return Res(x);
}

template<class X, class Y, class Res = core_functions::rtrim_double_t<X, Y>>
Res rtrim(X x, Y y) {
    return Res(x, y);
}

inline core_functions::changes_t changes() {
    return {};
}

template<class T>
core_functions::length_t<T> length(T t) {
    typedef core_functions::length_t<T> result_type;
    return result_type(t);
}

template<class T>
core_functions::abs_t<T> abs(T t) {
    typedef core_functions::abs_t<T> result_type;
    return result_type(t);
}

template<class T, class Res = core_functions::lower_t<T>>
Res lower(T t) {
    return Res(t);
}

template<class T, class Res = core_functions::upper_t<T>>
Res upper(T t) {
    return Res(t);
}

namespace aggregate_functions {

    template<class T>
    struct avg_t {
        T t;

        operator std::string() const {
            return "AVG";
        }
    };

    template<class T>
    struct count_t {
        T t;

        operator std::string() const {
            return "COUNT";
        }
    };

    struct count_asterisk_t {

        operator std::string() const {
            return "COUNT";
        }

    };

    template<class T>
    struct sum_t {
        T t;

        operator std::string() const {
            return "SUM";
        }
    };

    template<class T>
    struct total_t {
        T t;

        operator std::string() const {
            return "TOTAL";
        }
    };

    template<class T>
    struct max_t {
        T t;

        operator std::string() const {
            return "MAX";
        }
    };

    template<class T>
    struct min_t {
        T t;

        operator std::string() const {
            return "MIN";
        }
    };

    template<class T>
    struct group_concat_single_t {
        T t;

        operator std::string() const {
            return "GROUP_CONCAT";
        }
    };

    template<class T>
    struct group_concat_double_t {
        T t;
        std::string y;

        operator std::string() const {
            return "GROUP_CONCAT";
        }
    };

}

template<class T>
aggregate_functions::avg_t<T> avg(T t) {
    return {t};
}

template<class T>
aggregate_functions::count_t<T> count(T t) {
    return {t};
}

inline aggregate_functions::count_asterisk_t count() {
    return {};
}

template<class T>
aggregate_functions::sum_t<T> sum(T t) {
    return {t};
}

template<class T>
aggregate_functions::max_t<T> max(T t) {
    return {t};
}

template<class T>
aggregate_functions::min_t<T> min(T t) {
    return {t};
}

template<class T>
aggregate_functions::total_t<T> total(T t) {
    return {t};
}

template<class T>
aggregate_functions::group_concat_single_t<T> group_concat(T t) {
    return {t};
}

template<class T, class Y>
aggregate_functions::group_concat_double_t<T> group_concat(T t, Y y) {
    return {t, y};
}

namespace internal {

    template<class T>
    struct distinct_t {
        T t;

        operator std::string() const {
            return "DISTINCT";
        }
    };
    
    template<class T>
    struct all_t {
        T t;
        
        operator std::string() const {
            return "ALL";
        }
    };

    template<class ...Args>
    struct columns_t {
        bool distinct = false;

        template<class L>
        void for_each(L ) {
            //..
        }

        int count() {
            return 0;
        }
    };

    template<class T, class ...Args>
    struct columns_t<T, Args...> : public columns_t<Args...> {
        T m;

        columns_t(decltype(m) m_, Args&& ...args): Super(std::forward<Args>(args)...), m(m_) {}

        template<class L>
        void for_each(L l) {
            l(this->m);
            Super::for_each(l);
        }

        int count() {
            return 1 + Super::count();
        }
    private:
        typedef columns_t<Args...> Super;
    };

    template<class ...Args>
    struct set_t {

        operator std::string() const {
            return "SET";
        }

        template<class F>
        void for_each(F) {
            //..
        }
    };

    template<class L, class R, class ...Args>
    struct set_t<L, R, Args...> : public set_t<Args...> {
        L l;
        R r;

        typedef set_t<Args...> Super;

        set_t(L l_, R r_, Args&& ...args) : Super(std::forward<Args>(args)...), l(std::move(l_)), r(std::move(r_)) {}

        template<class F>
        void for_each(F f) {
            f(l, r);
            Super::for_each(f);
        }
    };

    struct database_connection {

        database_connection(const std::string &filename) {
            auto rc = sqlite3_open(filename.c_str(), &this->db);
            if(rc != SQLITE_OK){
                auto msg = sqlite3_errmsg(this->db);
                throw std::runtime_error(msg);
            }
        }

        ~database_connection() {
            sqlite3_close(this->db);
        }

        sqlite3* get_db() {
            return this->db;
        }

        protected:
        sqlite3 *db = nullptr;
    };

    /**
     *  Trait class used to define table mapped type by setter/getter/member
     */
    template<class T>
    struct table_type;

    template<class O, class F>
    struct table_type<F O::*> {
        typedef O type;
    };

    template<class O, class F>
    struct table_type<const F& (O::*)() const> {
        typedef O type;
    };

    template<class O, class F>
    struct table_type<void (O::*)(F)> {
        typedef O type;
    };
}

template<class T>
internal::distinct_t<T> distinct(T t) {
    return {t};
}

template<class T>
internal::all_t<T> all(T t) {
    return {t};
}

template<class ...Args>
internal::columns_t<Args...> distinct(internal::columns_t<Args...> cols) {
    cols.distinct = true;
    return cols;
}

template<class ...Args>
internal::set_t<Args...> set(Args&& ...args) {
    return {std::forward<Args>(args)...};
}

template<class ...Args>
internal::columns_t<Args...> columns(Args&& ...args) {
    return {std::forward<Args>(args)...};
}

struct table_info {
    int cid;
    std::string name;
    std::string type;
    bool notnull;
    std::string dflt_value;
    int pk;
};

/**
 *  Common case for table_impl class.
 */
template<typename... Args>
struct table_impl {

    std::vector<std::string> column_names() { return {}; }

    template<class ...Op>
    std::vector<std::string> column_names_exept() { return {}; }

    template<class ...Op>
    std::vector<std::string> column_names_with() { return{}; }

    template<class L>
    void for_each_column(L) {}

    template<class L>
    void for_each_column_with_constraints(L) {}

    template<class F, class L>
    void for_each_column_with_field_type(L) {}

    template<class Op, class L>
    void for_each_column_exept(L){}

    template<class Op, class L>
    void for_each_column_with(L) {}

    template<class L>
    void for_each_primary_key(L) {}

    int columns_count() const {
        return 0;
    }

};

template<typename H, typename... T>
struct table_impl<H, T...> : private table_impl<T...> {
    typedef H column_type;
    typedef std::tuple<T...> tail_types;

    table_impl(H h, T ...t) : Super(t...), col(h) {}

    column_type col;

    int columns_count() const {
        return 1 + Super::columns_count();
    }

    /**
     *  column_names_with implementation. Notice that result will be reversed.
     *  It is reversed back in `table` class.
     *  @return vector of column names that have specified Op... conditions.
     */
    template<class ...Op>
    std::vector<std::string> column_names_with() {
        auto res = Super::template column_names_with<Op...>();
        if(this->col.template has_every<Op...>()) {
            res.emplace_back(this->col.name);
        }
        return res;
    }

    /**
     *  For each implementation. Calls templated lambda with its column
     *  and passed call to superclass.
     */
    template<class L>
    void for_each_column(L l){
        this->apply_to_col_if(l, internal::is_column<column_type>{});
        Super::for_each_column(l);
    }

    /**
     *  For each implementation. Calls templated lambda with its column
     *  and passed call to superclass.
     */
    template<class L>
    void for_each_column_with_constraints(L l){
        l(this->col);

// this->apply_to_col_if(l, internal::is_column<column_type>{});
Super::for_each_column_with_constraints(l);
}

    template<class F, class L>
    void for_each_column_with_field_type(L l) {
        this->apply_to_col_if(l, std::is_same<F, typename column_type::field_type>{});
        Super::template for_each_column_with_field_type<F, L>(l);
    }

    /**
     *  Working version of `for_each_column_exept`. Calls lambda if column has no option and fire super's function.
     */
    template<class Op, class L>
    void for_each_column_exept(L l) {
        using has_opt = tuple_helper::tuple_contains_type<Op, typename column_type::constraints_type>;
        this->apply_to_col_if(l, std::integral_constant<bool, !has_opt::value>{});
        Super::template for_each_column_exept<Op, L>(l);
    }

    /**
     *  Working version of `for_each_column_with`. Calls lambda if column has option and fire super's function.
     */
    template<class Op, class L>
    void for_each_column_with(L l) {
        this->apply_to_col_if(l, tuple_helper::tuple_contains_type<Op, typename column_type::constraints_type>{});
        Super::template for_each_column_with<Op, L>(l);
    }

    /**
     *  Calls l(this->col) if H is primary_key_t
     */
    template<class L>
    void for_each_primary_key(L l) {
        this->apply_to_col_if(l, internal::is_primary_key<H>{});
        Super::for_each_primary_key(l);
    }

protected:

    template<class L>
    void apply_to_col_if(L& l, std::true_type) {
        l(this->col);
    }

    template<class L>
    void apply_to_col_if(L&, std::false_type) {}

private:
    typedef table_impl<T...> Super;
};

/**
 *  Table interface class. Implementation is hidden in `table_impl` class.
 */
template<class ...Cs>
struct table_t {
    typedef table_impl<Cs...> impl_type;
    typedef typename std::tuple_element<0, std::tuple<Cs...>>::type::object_type object_type;

    /**
     *  Table name.
     */
    const std::string name;

    /**
     *  Implementation that stores columns information.
     */
    impl_type impl;

    table_t(decltype(name) name_, decltype(impl) impl_):name(std::move(name_)), impl(std::move(impl_)){}

    bool _without_rowid = false;

    table_t<Cs...> without_rowid() const {
        auto res = *this;
        res._without_rowid = true;
        return res;
    }

    /**
     *  @return vector of column names of table.
     */
    std::vector<std::string> column_names() {
        std::vector<std::string> res;
        this->impl.for_each_column([&res](auto &c){
            res.push_back(c.name);
        });
        return res;
    }

    std::vector<std::string> composite_key_columns_names() {
        std::vector<std::string> res;
        this->impl.for_each_primary_key([this, &res](auto c){
            res = this->composite_key_columns_names(c);
        });
        return res;
    }

    std::vector<std::string> primary_key_column_names() {
        std::vector<std::string> res;
        this->impl.template for_each_column_with<constraints::primary_key_t<>>([&res](auto &c){
            res.push_back(c.name);
        });
        if(!res.size()){
            res = this->composite_key_columns_names();
        }
        return res;
    }

    template<class ...Args>
    std::vector<std::string> composite_key_columns_names(constraints::primary_key_t<Args...> pk) {
        std::vector<std::string> res;
        typedef decltype(pk.columns) pk_columns_tuple;
        res.reserve(std::tuple_size<pk_columns_tuple>::value);
        tuple_helper::iterator<std::tuple_size<pk_columns_tuple>::value - 1, Args...>()(pk.columns, [this, &res](auto &v){
            res.push_back(this->find_column_name(v));
        });
        return res;
    }

    int columns_count() const {
        return this->impl.columns_count();
    }

    /**
     *  Searches column name by class member pointer passed as first argument.
     *  @return column name or empty string if nothing found.
     */
    template<class F, class O>
    std::string find_column_name(F O::*m) {
        std::string res;
        this->template for_each_column_with_field_type<F>([&](auto c) {
            if(c.member_pointer == m) {
                res = c.name;
            }
        });
        return res;
    }

    template<class F, class O>
    std::string find_column_name(const F& (O::*getter)() const) {
        std::string res;
        this->template for_each_column_with_field_type<F>([&](auto c) {
            if(c.getter == getter) {
                res = c.name;
            }
        });
        return res;
    }

    template<class F, class O>
    std::string find_column_name(void (O::*setter)(F)) {
        std::string res;
        this->template for_each_column_with_field_type<F>([&](auto c) {
            if(c.setter == setter) {
                res = c.name;
            }
        });
        return res;
    }

    /**
     *  @return vector of column names that have constraints provided as template arguments (not_null, autoincrement).
     */
    template<class ...Op>
    std::vector<std::string> column_names_with() {
        auto res = this->impl.template column_names_with<Op...>();
        std::reverse(res.begin(),
                     res.end());
        return res;
    }

    /**
     *  Iterates all columns and fires passed lambda. Lambda must have one and only templated argument Otherwise code will
     *  not compile. Excludes table constraints (e.g. foreign_key_t) at the end of the columns list. To iterate columns with
     *  table constraints use for_each_column_with_constraints instead.
     *  L is lambda type. Do not specify it explicitly.
     *  @param l Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {}
     */
    template<class L>
    void for_each_column(L l) {
        this->impl.for_each_column(l);
    }

    template<class L>
    void for_each_column_with_constraints(L l) {
        this->impl.for_each_column_with_constraints(l);
    }

    template<class F, class L>
    void for_each_column_with_field_type(L l) {
        this->impl.template for_each_column_with_field_type<F, L>(l);
    }

    /**
     *  Iterates all columns exept ones that have specified constraints and fires passed lambda.
     *  Lambda must have one and only templated argument Otherwise code will not compile.
     *  L is lambda type. Do not specify it explicitly.
     *  @param l Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {}
     */
    template<class Op, class L>
    void for_each_column_exept(L l) {
        this->impl.template for_each_column_exept<Op>(l);
    }

    /**
     *  Iterates all columns that have specified constraints and fires passed lambda.
     *  Lambda must have one and only templated argument Otherwise code will not compile.
     *  L is lambda type. Do not specify it explicitly.
     *  @param l Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {}
     */
    template<class Op, class L>
    void for_each_column_with(L l) {
        this->impl.template for_each_column_with<Op>(l);
    }

    std::vector<table_info> get_table_info() {
        std::vector<table_info> res;
        res.reserve(size_t(this->columns_count()));
        this->for_each_column([&res](auto col){
            std::string dft;
            if(auto d = col.default_value()) {
                typedef typename decltype(col)::field_type field_type;
                auto needQuotes = std::is_base_of<text_printer, type_printer<field_type>>::value;
                if(needQuotes){
                    dft = "'" + *d + "'";
                }else{
                    dft = *d;
                }
            }
            table_info i{
                -1,
                col.name,
                type_printer<typename decltype(col)::field_type>().print(),
                col.not_null(),
                dft,
                col.template has<constraints::primary_key_t<>>(),
            };
            res.emplace_back(i);
        });
        std::vector<std::string> compositeKeyColumnNames;
        this->impl.for_each_primary_key([this, &compositeKeyColumnNames](auto c){
            compositeKeyColumnNames = this->composite_key_columns_names(c);
        });
        for(size_t i = 0; i < compositeKeyColumnNames.size(); ++i) {
            auto &columnName = compositeKeyColumnNames[i];
            auto it = std::find_if(res.begin(),
                                   res.end(),
                                   [&columnName](const table_info &ti) {
                                       return ti.name == columnName;
                                   });
            if(it != res.end()){
                it->pk = static_cast<int>(i + 1);
            }
        }
        return res;
    }

};

/**
 *  Function used for table creation. Do not use table constructor - use this function
 *  cause table class is templated and its constructing too (just like std::make_shared or std::make_pair).
 */
template<class ...Cs>
table_t<Cs...> make_table(const std::string &name, Cs&& ...args) {
    return {name, table_impl<Cs...>(std::forward<Cs>(args)...)};
}

struct statement_finalizer {
    sqlite3_stmt *stmt = nullptr;

    statement_finalizer(decltype(stmt) stmt_):stmt(stmt_){}

    inline ~statement_finalizer() {
        sqlite3_finalize(this->stmt);
    }

};

/**
 *  Helper classes used by statement_binder and row_extractor.
 */
struct int_or_smaller_tag{};
struct bigint_tag{};
struct real_tag{};

template<class V>
struct arithmetic_tag
{
    using type = std::conditional_t<
        std::is_integral<V>::value,
        // Integer class
        std::conditional_t<
            sizeof(V) <= sizeof(int),
            int_or_smaller_tag,
            bigint_tag
        >,
        // Floating-point class
        real_tag
    >;
};

template<class V>
using arithmetic_tag_t = typename arithmetic_tag<V>::type;

/**
 *  Helper class used for binding fields to sqlite3 statements.
 */
template<class V, typename Enable = void>
struct statement_binder
{
    int bind(sqlite3_stmt *stmt, int index, const V &value);
};

/**
 *  Specialization for arithmetic types.
 */
template<class V>
struct statement_binder<
    V,
    std::enable_if_t<std::is_arithmetic<V>::value>
>
{
    int bind(sqlite3_stmt *stmt, int index, const V &value) {
        return bind(stmt, index, value, tag());
    }

private:
    using tag = arithmetic_tag_t<V>;

    int bind(
        sqlite3_stmt *stmt, int index, const V &value,
        const int_or_smaller_tag&
    ) {
        return sqlite3_bind_int(stmt, index, static_cast<int>(value));
    }

    int bind(
        sqlite3_stmt *stmt, int index, const V &value,
        const bigint_tag&
    ) {
        return sqlite3_bind_int64(stmt, index, static_cast<sqlite3_int64>(value));
    }

    int bind(
        sqlite3_stmt *stmt, int index, const V &value,
        const real_tag&
    ) {
        return sqlite3_bind_double(stmt, index, static_cast<double>(value));
    }
};


/**
 *  Specialization for std::string and C-string.
 */
template<class V>
struct statement_binder<
    V,
    std::enable_if_t<
        std::is_same<V, std::string>::value
        ||
        std::is_same<V, const char*>::value
    >
>
{
    int bind(sqlite3_stmt *stmt, int index, const V &value) {
        return sqlite3_bind_text(stmt, index, string_data(value), -1, SQLITE_TRANSIENT);
    }

private:
    const char* string_data(const std::string& s) const
    { return s.c_str(); }

    const char* string_data(const char* s) const
    { return s; }
};
template<class V>
struct statement_binder<
	V,
	std::enable_if_t<
	std::is_same<V, std::wstring>::value
	||
	std::is_same<V, const wchar_t*>::value
	>
>
{
	int bind(sqlite3_stmt *stmt, int index, const V &value) {
		return sqlite3_bind_text16(stmt, index, string_data(value), -1, SQLITE_TRANSIENT);
	}

private:
	const wchar_t* string_data(const std::wstring& s) const
	{
		return s.c_str();
	}

	const wchar_t* string_data(const wchar_t* s) const
	{
		return s;
	}
};

/**
 *  Specialization for std::nullptr_t.
 */
template<class V>
struct statement_binder<
    V,
    std::enable_if_t<std::is_same<V, std::nullptr_t>::value>
>
{
    int bind(sqlite3_stmt *stmt, int index, const V &) {
        return sqlite3_bind_null(stmt, index);
    }
};

/**
 *  Specialization for optional type (std::shared_ptr / std::unique_ptr).
 */
template <typename T>
struct is_std_ptr : std::false_type
{};

template <typename T>
struct is_std_ptr<std::shared_ptr<T>> : std::true_type
{
    static
    std::shared_ptr<T> make(const T& v)
    { return std::make_shared<T>(v); }
};

template <typename T>
struct is_std_ptr<std::unique_ptr<T>> : std::true_type
{
    static
    std::unique_ptr<T> make(const T& v)
    { return std::make_unique<T>(v); }
};

template<class V>
struct statement_binder<
    V,
    std::enable_if_t<is_std_ptr<V>::value>
>
{
    using value_type = typename V::element_type;

    int bind(sqlite3_stmt *stmt, int index, const V &value) {
        if(value){
            return statement_binder<value_type>().bind(stmt, index, *value);
        }else{
            return statement_binder<std::nullptr_t>().bind(stmt, index, nullptr);
        }
    }
};

/**
 *  Specialization for optional type (std::vector<char>).
 */
template<class V>
struct statement_binder<
    V,
    std::enable_if_t<std::is_same<V, std::vector<char>>::value>
>
{
    int bind(sqlite3_stmt *stmt, int index, const V &value) {
        if (value.size()) {
            return sqlite3_bind_blob(stmt, index, (const void *)&value.front(), int(value.size()), SQLITE_TRANSIENT);
        }else{
            return sqlite3_bind_blob(stmt, index, "", 0, SQLITE_TRANSIENT);
        }
    }
};

/**
 *  Helper class used to cast values from argv to V class
 *  which depends from column type.
 *
 */
template<class V, typename Enable = void>
struct row_extractor
{
    //  used in sqlite3_exec (select)
    V extract(const char *row_value);

    //  used in sqlite_column (iteration, get_all)
    V extract(sqlite3_stmt *stmt, int columnIndex);
};

/**
 *  Specialization for arithmetic types.
 */
template<class V>
struct row_extractor<
    V,
    std::enable_if_t<std::is_arithmetic<V>::value>
>
{
    V extract(const char *row_value) {
        return extract(row_value, tag());
    }

    V extract(sqlite3_stmt *stmt, int columnIndex) {
        return extract(stmt, columnIndex, tag());
    }

private:
    using tag = arithmetic_tag_t<V>;

    V extract(
        const char *row_value,
        const int_or_smaller_tag& trait
    )
    { return static_cast<V>(atoi(row_value)); }

    V extract(
        sqlite3_stmt *stmt, int columnIndex,
        const int_or_smaller_tag& trait
    )
    { return static_cast<V>(sqlite3_column_int(stmt, columnIndex)); }

    V extract(
        const char *row_value,
        const bigint_tag& trait
    )
    { return static_cast<V>(atoll(row_value)); }

    V extract(
        sqlite3_stmt *stmt, int columnIndex,
        const bigint_tag& trait
    )
    { return static_cast<V>(sqlite3_column_int64(stmt, columnIndex)); }

    V extract(
        const char *row_value,
        const real_tag& trait
    )
    { return static_cast<V>(atof(row_value)); }

    V extract(
        sqlite3_stmt *stmt, int columnIndex,
        const real_tag& trait
    )
    { return static_cast<V>(sqlite3_column_double(stmt, columnIndex)); }
};

/**
 *  Specialization for std::string.
 */
template<class V>
struct row_extractor<
    V,
    std::enable_if_t<std::is_same<V, std::string>::value>
>
{
    std::string extract(const char *row_value) {
        if(row_value){
            return row_value;
        }else{
            return {};
        }
    }

    std::string extract(sqlite3_stmt *stmt, int columnIndex) {
        auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex);
        if(cStr){
            return cStr;
        }else{
            return {};
        }
    }
};

/**
 *  Specialization for std::vector<char>.
 */
template<class V>
struct row_extractor<
    V,
    std::enable_if_t<std::is_same<V, std::vector<char>>::value>
>
{
    std::vector<char> extract(const char *row_value) {
        if(row_value){
            auto len = ::strlen(row_value);
            return this->go(row_value, static_cast<int>(len));
        }else{
            return {};
        }
    }

    std::vector<char> extract(sqlite3_stmt *stmt, int columnIndex) {
        auto bytes = static_cast<const char *>(sqlite3_column_blob(stmt, columnIndex));
        auto len = sqlite3_column_bytes(stmt, columnIndex);
        return this->go(bytes, len);
    }

protected:

    std::vector<char> go(const char *bytes, int len) {
        if(len){
            std::vector<char> res;
            res.reserve(len);
            std::copy(bytes,
                      bytes + len,
                      std::back_inserter(res));
            return res;
        }else{
            return {};
        }
    }
};

template<class V>
struct row_extractor<
    V,
    std::enable_if_t<is_std_ptr<V>::value>
>
{
    using value_type = typename V::element_type;

    V extract(const char *row_value) {
        if(row_value){
            return is_std_ptr<V>::make(row_extractor<value_type>().extract(row_value));
        }else{
            return {};
        }
    }

    V extract(sqlite3_stmt *stmt, int columnIndex) {
        auto type = sqlite3_column_type(stmt, columnIndex);
        if(type != SQLITE_NULL){
            return is_std_ptr<V>::make(row_extractor<value_type>().extract(stmt, columnIndex));
        }else{
            return {};
        }
    }
};

/**
 *  Specialization for std::vector<char>.
 */
template<>
struct row_extractor<std::vector<char>> {
    std::vector<char> extract(const char *row_value) {
        if(row_value){
            auto len = ::strlen(row_value);
            return this->go(row_value, static_cast<int>(len));
        }else{
            return {};
        }
    }

    std::vector<char> extract(sqlite3_stmt *stmt, int columnIndex) {
        auto bytes = static_cast<const char *>(sqlite3_column_blob(stmt, columnIndex));
        auto len = sqlite3_column_bytes(stmt, columnIndex);
        return this->go(bytes, len);
    }

protected:

    std::vector<char> go(const char *bytes, int len) {
        if(len){
            std::vector<char> res;
            res.reserve(len);
            std::copy(bytes,
                      bytes + len,
                      std::back_inserter(res));
            return res;
        }else{
            return {};
        }
    }
};

template<class ...Args>
struct row_extractor<std::tuple<Args...>> {

    std::tuple<Args...> extract(char **argv) {
        std::tuple<Args...> res;
        this->extract<std::tuple_size<decltype(res)>::value>(res, argv);
        return res;
    }

    std::tuple<Args...> extract(sqlite3_stmt *stmt, int columnIndex) {
        (void)columnIndex;
        std::tuple<Args...> res;
        this->extract<std::tuple_size<decltype(res)>::value>(res, stmt);
        return res;
    }

protected:

    template<size_t I, typename std::enable_if<I != 0>::type * = nullptr>
    void extract(std::tuple<Args...> &t, sqlite3_stmt *stmt) {
        typedef typename std::tuple_element<I - 1, typename std::tuple<Args...>>::type tuple_type;
        std::get<I - 1>(t) = row_extractor<tuple_type>().extract(stmt, I - 1);
        this->extract<I - 1>(t, stmt);
    }

    template<size_t I, typename std::enable_if<I == 0>::type * = nullptr>
    void extract(std::tuple<Args...> &, sqlite3_stmt *) {
        //..
    }

    template<size_t I, typename std::enable_if<I != 0>::type * = nullptr>
    void extract(std::tuple<Args...> &t, char **argv) {
        typedef typename std::tuple_element<I - 1, typename std::tuple<Args...>>::type tuple_type;
        std::get<I - 1>(t) = row_extractor<tuple_type>().extract(argv[I - 1]);
        this->extract<I - 1>(t, argv);
    }

    template<size_t I, typename std::enable_if<I == 0>::type * = nullptr>
    void extract(std::tuple<Args...> &, char **) {
        //..
    }
};

/**
 *  Exeption thrown if nothing was found in database with specified id.
 */
struct not_found_exception : public std::exception {

    virtual const char* what() const throw() override {
        return "Not found";
    };
};


enum class sync_schema_result {
    /**
     *  created new table, table with the same tablename did not exist
     */
    new_table_created,

    /**
     *  table schema is the same as storage, nothing to be done
     */
    already_in_sync,

    /**
     *  removed excess columns in table (than storage) without dropping a table
     */
    old_columns_removed,

    /**
     *  lacking columns in table (than storage) added without dropping a table
     */
    new_columns_added,

    /**
     *  both old_columns_removed and new_columns_added
     */
    new_columns_added_and_old_columns_removed,

    /**
     *  old table is dropped and new is recreated. Reasons :
     *      1. delete excess columns in the table than storage if preseve = false
     *      2. Lacking columns in the table cannot be added due to NULL and DEFAULT constraint
     *      3. Reasons 1 and 2 both together
     *      4. data_type mismatch between table and storage.
     */
    dropped_and_recreated,
};


inline std::ostream& operator<<(std::ostream &os, sync_schema_result value) {
    switch(value){
        case sync_schema_result::new_table_created: return os << "new table created";
        case sync_schema_result::already_in_sync: return os << "table and storage is already in sync.";
        case sync_schema_result::old_columns_removed: return os << "old excess columns removed";
        case sync_schema_result::new_columns_added: return os << "new columns added";
        case sync_schema_result::new_columns_added_and_old_columns_removed: return os << "old excess columns removed and new columns added";
        case sync_schema_result::dropped_and_recreated: return os << "old table dropped and recreated";
    }
}

namespace internal {

    template<class ...Cols>
    struct index_t {
        typedef std::tuple<Cols...> columns_type;
        typedef void object_type;

        std::string name;
        bool unique;
        columns_type columns;

        template<class L>
        void for_each_column_with_constraints(L) {}
    };
}

template<class ...Cols>
internal::index_t<Cols...> make_index(const std::string &name, Cols ...cols) {
    return {name, false, std::make_tuple(cols...)};
}

template<class ...Cols>
internal::index_t<Cols...> make_unique_index(const std::string &name, Cols ...cols) {
    return {name, true, std::make_tuple(cols...)};
}

namespace internal {
    
    /**
     *  This is a generic implementation. Used as a tail in storage_impl inheritance chain
     */
    template<class ...Ts>
    struct storage_impl {
        
        template<class L>
        void for_each(L) {}
        
        /*template<class T>
         constexpr bool type_is_mapped() const {
         return std::integral_constant<bool, false>::value;
         }*/
        
        int foreign_keys_count() {
            return 0;
        }
        
        template<class O>
        std::string dump(const O &, sqlite3 *, std::nullptr_t) {
            throw std::runtime_error("type " + std::string(typeid(O).name()) + " is not mapped to storage in max");
        }
        
        bool table_exists(const std::string &tableName, sqlite3 *db) {
            auto res = false;
            std::stringstream ss;
            ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = '" << "table" << "' AND name = '" << tableName << "'";
            auto query = ss.str();
            auto rc = sqlite3_exec(db,
                                   query.c_str(),
                                   [](void *data, int argc, char **argv,char **/*azColName*/) -> int {
                                       auto &res = *(bool*)data;
                                       if(argc){
                                           res = !!std::atoi(argv[0]);
                                       }
                                       return 0;
                                   }, &res, nullptr);
            if(rc != SQLITE_OK) {
                auto msg = sqlite3_errmsg(db);
                throw std::runtime_error(msg);
            }
            return res;
        }
        
        void begin_transaction(sqlite3 *db) {
            std::stringstream ss;
            ss << "BEGIN TRANSACTION";
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    //  done..
                }else{
                    auto msg = sqlite3_errmsg(db);
                    throw std::runtime_error(msg);
                }
            }else {
                auto msg = sqlite3_errmsg(db);
                throw std::runtime_error(msg);
            }
        }
        
        void commit(sqlite3 *db) {
            std::stringstream ss;
            ss << "COMMIT";
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    //  done..
                }else{
                    auto msg = sqlite3_errmsg(db);
                    throw std::runtime_error(msg);
                }
            }else {
                auto msg = sqlite3_errmsg(db);
                throw std::runtime_error(msg);
            }
        }
        
        void rollback(sqlite3 *db) {
            std::stringstream ss;
            ss << "ROLLBACK";
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    //  done..
                }else{
                    auto msg = sqlite3_errmsg(db);
                    throw std::runtime_error(msg);
                }
            }else {
                auto msg = sqlite3_errmsg(db);
                throw std::runtime_error(msg);
            }
        }
        
        /*void drop_table(const std::string &tableName, sqlite3 *db) {
         
         }*/
        
        void rename_table(sqlite3 *db, const std::string &oldName, const std::string &newName) {
            std::stringstream ss;
            ss << "ALTER TABLE " << oldName << " RENAME TO " << newName;
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    //  done..
                }else{
                    auto msg = sqlite3_errmsg(db);
                    throw std::runtime_error(msg);
                }
            }else {
                auto msg = sqlite3_errmsg(db);
                throw std::runtime_error(msg);
            }
        }
        
        std::string current_timestamp(sqlite3 *db) {
            std::string res;
            std::stringstream ss;
            ss << "SELECT CURRENT_TIMESTAMP";
            auto query = ss.str();
            auto rc = sqlite3_exec(db,
                                   query.c_str(),
                                   [](void *data, int argc, char **argv, char **) -> int {
                                       auto &res = *(std::string*)data;
                                       if(argc){
                                           if(argv[0]){
                                               res = row_extractor<std::string>().extract(argv[0]);
                                           }
                                       }
                                       return 0;
                                   }, &res, nullptr);
            if(rc != SQLITE_OK) {
                auto msg = sqlite3_errmsg(db);
                throw std::runtime_error(msg);
            }
            return res;
        }
    };
    
    template<class H, class ...Ts>
    struct storage_impl<H, Ts...> : public storage_impl<Ts...> {
        typedef H table_type;
        
        storage_impl(H h, Ts ...ts) : Super(ts...), table(h) {}
        
        table_type table;
        
        template<class L>
        void for_each(L l) {
            Super::for_each(l);
            l(this);
        }

#if SQLITE_VERSION_NUMBER >= 3006019

        //  returns foreign keys count in table definition
        int foreign_keys_count(){
            auto res = 0;
            this->table.for_each_column_with_constraints([&res](auto c){
                if(internal::is_foreign_key<decltype(c)>::value) {
                    ++res;
                }
            });
            return res;
        }

#endif

        template<class O, class F, class HH = typename H::object_type>
        std::string column_name(F O::*m, typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) {
            return this->table.find_column_name(m);
        }
        
        template<class O, class F, class HH = typename H::object_type>
        std::string column_name(F O::*m, typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) {
            return Super::column_name(m);
        }
        
        template<class O, class F, class HH = typename H::object_type>
        std::string column_name(const F& (O::*g)() const, typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) {
            return this->table.find_column_name(g);
        }
        
        template<class O, class F, class HH = typename H::object_type>
        std::string column_name(const F& (O::*g)() const, typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) {
            return Super::column_name(g);
        }
        
        template<class O, class F, class HH = typename H::object_type>
        std::string column_name(void (O::*s)(F), typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) {
            return this->table.find_column_name(s);
        }
        
        template<class O, class F, class HH = typename H::object_type>
        std::string column_name(void (O::*s)(F), typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) {
            return Super::column_name(s);
        }
        
        template<class O, class HH = typename H::object_type>
        auto& get_impl(typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) {
            return *this;
        }
        
        template<class O, class HH = typename H::object_type>
        auto& get_impl(typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) {
            return Super::template get_impl<O>();
        }
        
        template<class O, class HH = typename H::object_type>
        std::string find_table_name(typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) {
            return this->table.name;
        }
        
        template<class O, class HH = typename H::object_type>
        std::string find_table_name(typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) {
            return this->Super::template find_table_name<O>();
        }
        
        template<class O, class HH = typename H::object_type>
        std::string dump(const O &o, sqlite3 *db, typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) {
            return Super::dump(o, db, nullptr);
        }
        
        template<class O, class HH = typename H::object_type>
        std::string dump(const O &o, sqlite3 *, typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) {
            std::stringstream ss;
            ss << "{ ";
            std::vector<std::pair<std::string, std::string>> pairs;
            this->table.for_each_column([&pairs, &o] (auto c) {
                typedef typename decltype(c)::field_type field_type;
                const field_type *value = nullptr;
                if(c.member_pointer){
                    value = &(o.*c.member_pointer);
                }else{
                    value = &((o).*(c.getter))();
                }
                pairs.push_back(std::make_pair(c.name, field_printer<field_type>()(*value)));
            });
            for(size_t i = 0; i < pairs.size(); ++i) {
                auto &p = pairs[i];
                ss << p.first << " : '" << p.second << "'";
                if(i < pairs.size() - 1) {
                    ss << ", ";
                }else{
                    ss << " }";
                }
            }
            return ss.str();
        }
        
        std::vector<table_info> get_table_info(const std::string &tableName, sqlite3 *db) {
            std::vector<table_info> res;
            auto query = "PRAGMA table_info('" + tableName + "')";
            auto rc = sqlite3_exec(db,
                                   query.c_str(),
                                   [](void *data, int argc, char **argv,char **) -> int {
                                       auto &res = *(std::vector<table_info>*)data;
                                       if(argc){
                                           auto index = 0;
                                           auto cid = std::atoi(argv[index++]);
                                           std::string name = argv[index++];
                                           std::string type = argv[index++];
                                           bool notnull = !!std::atoi(argv[index++]);
                                           std::string dflt_value = argv[index] ? argv[index] : "";
                                           index++;
                                           auto pk = std::atoi(argv[index++]);
                                           res.push_back(table_info{cid, name, type, notnull, dflt_value, pk});
                                       }
                                       return 0;
                                   }, &res, nullptr);
            if(rc != SQLITE_OK) {
                auto msg = sqlite3_errmsg(db);
                throw std::runtime_error(msg);
            }
            return res;
        }
        
        void add_column(table_info &ti, sqlite3 *db) {
            std::stringstream ss;
            ss << "ALTER TABLE " << this->table.name << " ADD COLUMN " << ti.name << " ";
            ss << ti.type << " ";
            if(ti.pk){
                ss << "PRIMARY KEY ";
            }
            if(ti.notnull){
                ss << "NOT NULL ";
            }
            if(ti.dflt_value.length()) {
                ss << "DEFAULT " << ti.dflt_value << " ";
            }
            auto query = ss.str();
            sqlite3_stmt *stmt;
            auto prepareResult = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr);
            if (prepareResult == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    return;
                }else{
                    auto msg = sqlite3_errmsg(db);
                    throw std::runtime_error(msg);
                }
            }else{
                auto msg = sqlite3_errmsg(db);
                throw std::runtime_error(msg);
            }
        }
        
        /**
         *  Copies current table to another table with a given **name**.
         *  Performs CREATE TABLE %name% AS SELECT %this->table.columns_names()% FROM &this->table.name%;
         */
        void copy_table(sqlite3 *db, const std::string &name) {
            std::stringstream ss;
            std::vector<std::string> columnNames;
            this->table.for_each_column([&] (auto c) {
                columnNames.emplace_back(c.name);
            });
            auto columnNamesCount = columnNames.size();
            ss << "INSERT INTO " << name << " (";
            for(size_t i = 0; i < columnNamesCount; ++i) {
                ss << columnNames[i];
                if(i < columnNamesCount - 1) {
                    ss << ", ";
                }else{
                    ss << " ";
                }
            }
            ss << ") ";
            ss << "SELECT ";
            for(size_t i = 0; i < columnNamesCount; ++i) {
                ss << columnNames[i];
                if(i < columnNamesCount - 1) {
                    ss << ", ";
                }else{
                    ss << " ";
                }
            }
            ss << " FROM '" << this->table.name << "' ";
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    return;
                }else{
                    auto msg = sqlite3_errmsg(db);
                    throw std::runtime_error(msg);
                }
            }else{
                auto msg = sqlite3_errmsg(db);
                throw std::runtime_error(msg);
            }
        }
        
        sync_schema_result schema_status(sqlite3 *db, bool preserve) {
            
            auto res = sync_schema_result::already_in_sync;
            
            //  first let's see if table with such name exists..
            auto gottaCreateTable = !this->table_exists(this->table.name, db);
            if(!gottaCreateTable){
                
                //  get table info provided in `make_table` call..
                auto storageTableInfo = this->table.get_table_info();
                
                //  now get current table info from db using `PRAGMA table_info` query..
                auto dbTableInfo = get_table_info(this->table.name, db);
                
                //  this vector will contain pointers to columns that gotta be added..
                std::vector<table_info*> columnsToAdd;
                
                if(get_remove_add_columns(columnsToAdd,
                                          storageTableInfo,
                                          dbTableInfo)) {
                    gottaCreateTable = true;
                }
                
                if(!gottaCreateTable){  //  if all storage columns are equal to actual db columns but there are excess columns at the db..
                    if(dbTableInfo.size() > 0){
                        //extra table columns than storage columns
                        if(!preserve){
                            gottaCreateTable = true;
                        }else{
                            res = decltype(res)::old_columns_removed;
                        }
                    }
                }
                if(gottaCreateTable){
                    res = decltype(res)::dropped_and_recreated;
                }else{
                    if(columnsToAdd.size()){
                        //extra storage columns than table columns
                        for(auto columnPointer : columnsToAdd) {
                            if(columnPointer->notnull && columnPointer->dflt_value.empty()){
                                gottaCreateTable = true;
                                break;
                            }
                        }
                        if(!gottaCreateTable){
                            if(res == decltype(res)::old_columns_removed) {
                                res = decltype(res)::new_columns_added_and_old_columns_removed;
                            }else{
                                res = decltype(res)::new_columns_added;
                            }
                        }else{
                            res = decltype(res)::dropped_and_recreated;
                        }
                    }else{
                        if(res != decltype(res)::old_columns_removed){
                            res = decltype(res)::already_in_sync;
                        }
                    }
                }
            }else{
                res = decltype(res)::new_table_created;
            }
            return res;
        }
        
        static bool get_remove_add_columns(std::vector<table_info*>& columnsToAdd,
                                           std::vector<table_info>& storageTableInfo,
                                           std::vector<table_info>& dbTableInfo)
        {
            bool notEqual = false;
            
            //  iterate through storage columns
            for(size_t storageColumnInfoIndex = 0; storageColumnInfoIndex < storageTableInfo.size(); ++storageColumnInfoIndex) {
                
                //  get storage's column info
                auto &storageColumnInfo = storageTableInfo[storageColumnInfoIndex];
                auto &columnName = storageColumnInfo.name;
                
                //  search for a column in db eith the same name
                auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(),
                                                   dbTableInfo.end(),
                                                   [&](auto &ti){
                                                       return ti.name == columnName;
                                                   });
                if(dbColumnInfoIt != dbTableInfo.end()){
                    auto &dbColumnInfo = *dbColumnInfoIt;
                    auto dbColumnInfoType = to_sqlite_type(dbColumnInfo.type);
                    auto storageColumnInfoType = to_sqlite_type(storageColumnInfo.type);
                    if(dbColumnInfoType && storageColumnInfoType) {
                        auto columnsAreEqual = dbColumnInfo.name == storageColumnInfo.name &&
                        *dbColumnInfoType == *storageColumnInfoType &&
                        dbColumnInfo.notnull == storageColumnInfo.notnull &&
                        bool(dbColumnInfo.dflt_value.length()) == bool(storageColumnInfo.dflt_value.length()) &&
                        dbColumnInfo.pk == storageColumnInfo.pk;
                        if(!columnsAreEqual){
                            notEqual = true;
                            break;
                        }
                        dbTableInfo.erase(dbColumnInfoIt);
                        storageTableInfo.erase(storageTableInfo.begin() + storageColumnInfoIndex);
                        --storageColumnInfoIndex;
                    }else{
                        
                        //  undefined type/types
                        notEqual = true;
                        break;
                    }
                }else{
                    columnsToAdd.push_back(&storageColumnInfo);
                }
            }
            return notEqual;
        }
        
        
    private:
        typedef storage_impl<Ts...> Super;
        typedef storage_impl<H, Ts...> Self;
    };

    /**
     *  This is a proxy class used to define what type must have result type depending on select
     *  arguments (member pointer, aggregate functions, etc). Below you can see specializations
     *  for different types. E.g. specialization for core_functions::length_t has `type` int cause
     *  LENGTH returns INTEGER in sqlite. Every column_result_t must have `type` type that equals
     *  c++ SELECT return type for T
     *  T - C++ type
     *  Ts - tables pack from storage. Rarely used. Required in asterisk to define columns mapped for a type
     */
    template<class T, class ...Ts>
    struct column_result_t;

    template<class O, class F, class ...Ts>
    struct column_result_t<F O::*, Ts...> {
        typedef F type;
    };

    template<class O, class F, class ...Ts>
    struct column_result_t<const F& (O::*)() const, Ts...> {
        typedef F type;
    };

    template<class O, class F, class ...Ts>
    struct column_result_t<void (O::*)(F), Ts...> {
        typedef F type;
    };

    template<class T, class ...Ts>
    struct column_result_t<core_functions::length_t<T>, Ts...> {
        typedef int type;
    };

#if SQLITE_VERSION_NUMBER >= 3007016

    template<class ...Args, class ...Ts>
    struct column_result_t<core_functions::char_t_<Args...>, Ts...> {
        typedef std::string type;
    };

#endif

    template<class ...Ts>
    struct column_result_t<core_functions::random_t, Ts...> {
        typedef int type;
    };

    template<class ...Ts>
    struct column_result_t<core_functions::changes_t, Ts...> {
        typedef int type;
    };

    template<class T, class ...Ts>
    struct column_result_t<core_functions::abs_t<T>, Ts...> {
        typedef std::shared_ptr<double> type;
    };

    template<class T, class ...Ts>
    struct column_result_t<core_functions::lower_t<T>, Ts...> {
        typedef std::string type;
    };

    template<class T, class ...Ts>
    struct column_result_t<core_functions::upper_t<T>, Ts...> {
        typedef std::string type;
    };

    template<class X, class ...Ts>
    struct column_result_t<core_functions::trim_single_t<X>, Ts...> {
        typedef std::string type;
    };

    template<class X, class Y, class ...Ts>
    struct column_result_t<core_functions::trim_double_t<X, Y>, Ts...> {
        typedef std::string type;
    };

    template<class X, class ...Ts>
    struct column_result_t<core_functions::ltrim_single_t<X>, Ts...> {
        typedef std::string type;
    };

    template<class X, class Y, class ...Ts>
    struct column_result_t<core_functions::ltrim_double_t<X, Y>, Ts...> {
        typedef std::string type;
    };

    template<class X, class ...Ts>
    struct column_result_t<core_functions::rtrim_single_t<X>, Ts...> {
        typedef std::string type;
    };

    template<class X, class Y, class ...Ts>
    struct column_result_t<core_functions::rtrim_double_t<X, Y>, Ts...> {
        typedef std::string type;
    };

    template<class T, class ...Args, class ...Ts>
    struct column_result_t<core_functions::date_t<T, Args...>, Ts...> {
        typedef std::string type;
    };

    template<class T, class ...Args, class ...Ts>
    struct column_result_t<core_functions::datetime_t<T, Args...>, Ts...> {
        typedef std::string type;
    };

    template<class T, class ...Ts>
    struct column_result_t<aggregate_functions::avg_t<T>, Ts...> {
        typedef double type;
    };

    template<class T, class ...Ts>
    struct column_result_t<aggregate_functions::count_t<T>, Ts...> {
        typedef int type;
    };

    template<class ...Ts>
    struct column_result_t<aggregate_functions::count_asterisk_t, Ts...> {
        typedef int type;
    };

    template<class T, class ...Ts>
    struct column_result_t<aggregate_functions::sum_t<T>, Ts...> {
        typedef std::shared_ptr<double> type;
    };

    template<class T, class ...Ts>
    struct column_result_t<aggregate_functions::total_t<T>, Ts...> {
        typedef double type;
    };

    template<class T, class ...Ts>
    struct column_result_t<aggregate_functions::group_concat_single_t<T>, Ts...> {
        typedef std::string type;
    };

    template<class T, class ...Ts>
    struct column_result_t<aggregate_functions::group_concat_double_t<T>, Ts...> {
        typedef std::string type;
    };

    template<class T, class ...Ts>
    struct column_result_t<aggregate_functions::max_t<T>, Ts...> {
        typedef std::shared_ptr<typename column_result_t<T>::type> type;
    };

    template<class T, class ...Ts>
    struct column_result_t<aggregate_functions::min_t<T>, Ts...> {
        typedef std::shared_ptr<typename column_result_t<T>::type> type;
    };

    template<class T, class ...Ts>
    struct column_result_t<internal::distinct_t<T>, Ts...> {
        typedef typename column_result_t<T>::type type;
    };
    
    template<class T, class ...Ts>
    struct column_result_t<internal::all_t<T>, Ts...> {
        typedef typename column_result_t<T>::type type;
    };

    template<class L, class R, class ...Ts>
    struct column_result_t<internal::conc_t<L, R>, Ts...> {
        typedef std::string type;
    };
    
    /**
     *  Storage class itself. Create an instanse to use it as an interfacto to sqlite db by calling `make_storage` function.
     */
    template<class ...Ts>
    struct storage_t {
        using storage_type = storage_t<Ts...>;
        using impl_type = storage_impl<Ts...>;
        
        template<class T, class ...Args>
        struct view_t {
            using mapped_type = T;
            
            storage_t &storage;
            std::shared_ptr<internal::database_connection> connection;
            
            const std::string query;
            
            view_t(storage_t &stor, decltype(connection) conn, Args&& ...args):
            storage(stor),
            connection(conn),
            query([&]{
                std::string q;
                stor.template generate_select_asterisk<T>(&q, args...);
                return q;
            }()){}
            
            struct iterator_t {
            protected:
                std::shared_ptr<sqlite3_stmt *> stmt;
                view_t<T, Args...> &view;
                std::shared_ptr<T> temp;
                
                void extract_value(decltype(temp) &temp) {
                    temp = std::make_shared<T>();
                    auto &storage = this->view.storage;
                    auto &impl = storage.template get_impl<T>();
                    auto index = 0;
                    impl.table.for_each_column([&index, &temp, this] (auto c) {
                        auto value = row_extractor<typename decltype(c)::field_type>().extract(*this->stmt, index++);
                        if(c.member_pointer){
                            auto member_pointer = c.member_pointer;
                            (*temp).*member_pointer = value;
                        }else{
                            ((*temp).*(c.setter))(std::move(value));
                        }
                    });
                }
                
            public:
                iterator_t(sqlite3_stmt * stmt_, view_t<T, Args...> &view_):stmt(std::make_shared<sqlite3_stmt *>(stmt_)),view(view_){
                    this->operator++();
                }
                
                ~iterator_t() {
                    if(this->stmt){
                        statement_finalizer f{*this->stmt};
                    }
                }
                
                T& operator*() {
                    if(!this->stmt) throw std::runtime_error("trying to dereference null iterator");
                    if(!this->temp){
                        this->extract_value(this->temp);
                    }
                    return *this->temp;
                }
                
                T* operator->() {
                    if(!this->stmt) throw std::runtime_error("trying to dereference null iterator");
                    if(!this->temp){
                        this->extract_value(this->temp);
                    }
                    return &*this->temp;
                }
                
                void operator++() {
                    if(this->stmt && *this->stmt){
                        auto ret = sqlite3_step(*this->stmt);
                        switch(ret){
                            case SQLITE_ROW:
                                this->temp = nullptr;
                                break;
                            case SQLITE_DONE:{
                                statement_finalizer f{*this->stmt};
                                *this->stmt = nullptr;
                            }break;
                            default:{
                                auto db = this->view.connection->get_db();
                                auto msg = sqlite3_errmsg(db);
                                throw std::runtime_error(msg);
                            }
                        }
                    }
                }
                
                void operator++(int) {
                    this->operator++();
                }
                
                bool operator==(const iterator_t &other) const {
                    if(this->stmt && other.stmt){
                        return *this->stmt == *other.stmt;
                    }else{
                        if(!this->stmt && !other.stmt){
                            return true;
                        }else{
                            return false;
                        }
                    }
                }
                
                bool operator!=(const iterator_t &other) const {
                    return !(*this == other);
                }
            };
            
            size_t size() {
                return this->storage.template count<T>();
            }
            
            bool empty() {
                return !this->size();
            }
            
            iterator_t end() {
                return {nullptr, *this};
            }
            
            iterator_t begin() {
                sqlite3_stmt *stmt = nullptr;
                auto db = this->connection->get_db();
                auto ret = sqlite3_prepare_v2(db, this->query.c_str(), -1, &stmt, nullptr);
                if(ret == SQLITE_OK){
                    return {stmt, *this};
                }else{
                    auto msg = sqlite3_errmsg(db);
                    throw std::runtime_error(msg);
                }
            }
        };
        
        struct transaction_guard_t {
            typedef storage_t<Ts...> storage_type;
            
            /**
             *  This is a public lever to tell a guard what it must do in its destructor
             *  if `gotta_fire` is true
             */
            bool commit_on_destroy = false;
            
            transaction_guard_t(storage_type &s):storage(s){}
            
            ~transaction_guard_t() {
                if(this->gotta_fire){
                    if(!this->commit_on_destroy){
                        this->storage.rollback();
                    }else{
                        this->storage.commit();
                    }
                }
            }
            
            /**
             *  Call `COMMIT` explicitly. After this call
             *  guard will not call `COMMIT` or `ROLLBACK`
             *  in its destructor.
             */
            void commit() {
                this->storage.commit();
                this->gotta_fire = false;
            }
            
            /**
             *  Call `ROLLBACK` explicitly. After this call
             *  guard will not call `COMMIT` or `ROLLBACK`
             *  in its destructor.
             */
            void rollback() {
                this->storage.rollback();
                this->gotta_fire = false;
            }
            
        protected:
            storage_type &storage;
            bool gotta_fire = true;
        };
        
        std::function<void(sqlite3*)> on_open;
        
        transaction_guard_t transaction_guard() {
            this->begin_transaction();
            return {*this};
        }
        
        struct pragma_t {
            
            pragma_t(storage_type &storage_):storage(storage_){}
            
            int synchronous() {
                return this->get_pragma<int>("synchronous");
            }
            
            void synchronous(int value) {
                this->_synchronous = -1;
                this->set_pragma("synchronous", value);
                this->_synchronous = value;
            }
            
            int user_version() {
                return this->get_pragma<int>("user_version");
            }
            
            void user_version(int value) {
                this->set_pragma("user_version", value);
            }
            
            friend class storage_t<Ts...>;
            
        protected:
            storage_type &storage;
            int _synchronous = -1;
            
            template<class T>
            T get_pragma(const std::string &name) {
                auto connection = this->storage.get_or_create_connection();
                std::string query = "PRAGMA " + name;
                int res = -1;
                auto rc = sqlite3_exec(connection->get_db(),
                                       query.c_str(),
                                       [](void *data, int argc, char **argv,char **) -> int {
                                           auto &res = *(T*)data;
                                           if(argc){
                                               res = row_extractor<T>().extract(argv[0]);
                                           }
                                           return 0;
                                       }, &res, nullptr);
                if(rc != SQLITE_OK) {
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
                return res;
            }
            
            template<class T>
            void set_pragma(const std::string &name, const T &value) {
                auto connection = this->storage.get_or_create_connection();
                std::stringstream ss;
                ss << "PRAGMA " << name << " = " << this->storage.string_from_expression(value);
                auto query = ss.str();
                auto rc = sqlite3_exec(connection->get_db(), query.c_str(), nullptr, nullptr, nullptr);
                if(rc != SQLITE_OK) {
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }
        };
        
        /**
         *  @param filename_ database filename.
         */
        storage_t(const std::string &filename_, impl_type impl_):
        filename(filename_),
        impl(impl_),
        inMemory(filename_.empty() || filename_ == ":memory:"),
        pragma(*this){
            if(inMemory){
                this->currentTransaction = std::make_shared<internal::database_connection>(this->filename);
                this->on_open_internal(this->currentTransaction->get_db());
            }
        }
        
    protected:
        
        /**
         *  Check whether connection exists and returns it if yes or creates a new one
         *  and returns it.
         */
        std::shared_ptr<internal::database_connection> get_or_create_connection() {
            decltype(this->currentTransaction) connection;
            if(!this->currentTransaction){
                connection = std::make_shared<internal::database_connection>(this->filename);
                this->on_open_internal(connection->get_db());
            }else{
                connection = this->currentTransaction;
            }
            return connection;
        }
        
        template<class O, class T, class ...Op>
        std::string serialize_column_schema(internal::column_t<O, T, Op...> c) {
            std::stringstream ss;
            ss << "'" << c.name << "' ";
            typedef typename decltype(c)::field_type field_type;
            typedef typename decltype(c)::constraints_type constraints_type;
            ss << type_printer<field_type>().print() << " ";
            tuple_helper::iterator<std::tuple_size<constraints_type>::value - 1, Op...>()(c.constraints, [&](auto &v){
                ss << static_cast<std::string>(v) << ' ';
            });
            if(c.not_null()){
                ss << "NOT NULL ";
            }
            return ss.str();
        }
        
        template<class ...Cs>
        std::string serialize_column_schema(constraints::primary_key_t<Cs...> fk) {
            std::stringstream ss;
            ss << static_cast<std::string>(fk) << " (";
            std::vector<std::string> columnNames;
            columnNames.reserve(std::tuple_size<decltype(fk.columns)>::value);
            tuple_helper::iterator<std::tuple_size<decltype(fk.columns)>::value - 1, Cs...>()(fk.columns, [&](auto &c){
                columnNames.push_back(this->impl.column_name(c));
            });
            for(size_t i = 0; i < columnNames.size(); ++i) {
                ss << columnNames[i];
                if(i < columnNames.size() - 1) {
                    ss << ", ";
                }
            }
            ss << ") ";
            return ss.str();
        }

#if SQLITE_VERSION_NUMBER >= 3006019

        template<class C, class R>
        std::string serialize_column_schema(constraints::foreign_key_t<C, R> fk) {
            std::stringstream ss;
            typedef typename internal::table_type<decltype(fk.r)>::type ref_type;
            auto refTableName = this->impl.template find_table_name<ref_type>();
            auto refColumnName = this->impl.column_name(fk.r);
            ss << "FOREIGN KEY(" << this->impl.column_name(fk.m) << ") REFERENCES ";
            ss << refTableName << "(" << refColumnName << ") ";
            return ss.str();
        }

#endif

        template<class I>
        void create_table(sqlite3 *db, const std::string &tableName, I *impl) {
            std::stringstream ss;
            ss << "CREATE TABLE '" << tableName << "' ( ";
            auto columnsCount = impl->table.columns_count();
            auto index = 0;
            impl->table.for_each_column_with_constraints([columnsCount, &index, &ss, this] (auto c) {
                ss << this->serialize_column_schema(c);
                if(index < columnsCount - 1) {
                    ss << ", ";
                }
                index++;
            });
            ss << ") ";
            if(impl->table._without_rowid) {
                ss << "WITHOUT ROWID ";
            }
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    //  done..
                }else{
                    auto msg = sqlite3_errmsg(db);
                    throw std::runtime_error(msg);
                }
            }else {
                auto msg = sqlite3_errmsg(db);
                throw std::runtime_error(msg);
            }
        }
        
        template<class I>
        void backup_table(sqlite3 *db, I *impl) {
            
            //  here we copy source table to another with a name with '_backup' suffix, but in case table with such
            //  a name already exists we append suffix 1, then 2, etc until we find a free name..
            auto backupTableName = impl->table.name + "_backup";
            if(impl->table_exists(backupTableName, db)){
                int suffix = 1;
                do{
                    std::stringstream stream;
                    stream << suffix;
                    auto anotherBackupTableName = backupTableName + stream.str();
                    if(!impl->table_exists(anotherBackupTableName, db)){
                        backupTableName = anotherBackupTableName;
                        break;
                    }
                    ++suffix;
                }while(true);
            }
            
            this->create_table(db, backupTableName, impl);
            
            impl->copy_table(db, backupTableName);
            
            this->drop_table_internal(impl->table.name, db);
            
            impl->rename_table(db, backupTableName, impl->table.name);
        }
        
        template<class O>
        void assert_mapped_type() {
            typedef std::tuple<typename Ts::object_type...> mapped_types_tuples;
            static_assert(tuple_helper::has_type<O, mapped_types_tuples>::value, "type is not mapped to a storage");
        }
        
        template<class O>
        auto& get_impl() {
            return this->impl.template get_impl<O>();
        }
        
        std::string escape(std::string text) {
            for(size_t i = 0; i < text.length(); ) {
                if(text[i] == '\''){
                    text.insert(text.begin() + i, '\'');
                    i += 2;
                }
                else
                    ++i;
            }
            return text;
        }
        
        template<class T>
        std::string string_from_expression(T t, bool /*noTableName*/ = false, bool escape = false) {
            auto isNullable = type_is_nullable<T>::value;
            if(isNullable && !type_is_nullable<T>()(t)){
                return "NULL";
            }else{
                auto needQuotes = std::is_base_of<text_printer, type_printer<T>>::value;
                std::stringstream ss;
                if(needQuotes){
                    ss << "'";
                }
                std::string text = field_printer<T>()(t);
                if(escape){
                    text = this->escape(text);
                }
                ss << text;
                if(needQuotes){
                    ss << "'";
                }
                return ss.str();
            }
        }
        
        std::string string_from_expression(const std::string &t, bool /*noTableName*/ = false, bool escape = false) {
            std::stringstream ss;
            std::string text = t;
            if(escape){
                text = this->escape(text);
            }
            ss << "'" << text << "'";
            return ss.str();
        }
        
        std::string string_from_expression(const char *t, bool /*noTableName*/ = false, bool escape = false) {
            std::stringstream ss;
            std::string text = t;
            if(escape){
                text = this->escape(text);
            }
            ss << "'" << text << "'";
            return ss.str();
        }
        
        template<class F, class O>
        std::string string_from_expression(F O::*m, bool noTableName = false, bool /*escape*/ = false) {
            std::stringstream ss;
            if(!noTableName){
                ss << " '" << this->impl.template find_table_name<O>() << "'.";
            }
            ss << "\"" << this->impl.column_name(m) << "\"";
            return ss.str();
        }
        
        template<class F, class O>
        std::string string_from_expression(const F* (O::*g)() const, bool noTableName = false, bool /*escape*/ = false) {
            std::stringstream ss;
            if(!noTableName){
                ss << " '" << this->impl.template find_table_name<O>() << "'.";
            }
            ss << "\"" << this->impl.column_name(g) << "\"";
            return ss.str();
        }
        
        template<class F, class O>
        std::string string_from_expression(void (O::*s)(F), bool noTableName = false, bool /*escape*/ = false) {
            std::stringstream ss;
            if(!noTableName){
                ss << " '" << this->impl.template find_table_name<O>() << "'.";
            }
            ss << "\"" << this->impl.column_name(s) << "\"";
            return ss.str();
        }
        
        template<class T>
        std::string string_from_expression(aggregate_functions::group_concat_double_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(f.t);
            auto expr2 = this->string_from_expression(f.y);
            ss << static_cast<std::string>(f) << "(" << expr << ", " << expr2 << ") ";
            return ss.str();
        }
        
        template<class T>
        std::string string_from_expression(aggregate_functions::group_concat_single_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(f.t);
            ss << static_cast<std::string>(f) << "(" << expr << ") ";
            return ss.str();
        }
        
        template<class L, class R>
        std::string string_from_expression(internal::conc_t<L, R> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto lhs = this->string_from_expression(f.l);
            auto rhs = this->string_from_expression(f.r);
            ss << "(" << lhs << " || " << rhs << ") ";
            return ss.str();
        }
        
        template<class T>
        std::string string_from_expression(aggregate_functions::min_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(f.t);
            ss << static_cast<std::string>(f) << "(" << expr << ") ";
            return ss.str();
        }
        
        template<class T>
        std::string string_from_expression(aggregate_functions::max_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(f.t);
            ss << static_cast<std::string>(f) << "(" << expr << ") ";
            return ss.str();
        }
        
        template<class T>
        std::string string_from_expression(aggregate_functions::total_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(f.t);
            ss << static_cast<std::string>(f) << "(" << expr << ") ";
            return ss.str();
        }
        
        template<class T>
        std::string string_from_expression(aggregate_functions::sum_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(f.t);
            ss << static_cast<std::string>(f) << "(" << expr << ") ";
            return ss.str();
        }
        
        std::string string_from_expression(aggregate_functions::count_asterisk_t &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            ss << static_cast<std::string>(f) << "(*) ";
            return ss.str();
        }
        
        template<class T>
        std::string string_from_expression(aggregate_functions::count_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(f.t);
            ss << static_cast<std::string>(f) << "(" << expr << ") ";
            return ss.str();
        }
        
        template<class T>
        std::string string_from_expression(aggregate_functions::avg_t<T> &a, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(a.t);
            ss << static_cast<std::string>(a) << "(" << expr << ") ";
            return ss.str();
        }
        
        template<class T>
        std::string string_from_expression(internal::distinct_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(f.t);
            ss << static_cast<std::string>(f) << "(" << expr << ") ";
            return ss.str();
        }
        
        template<class T>
        std::string string_from_expression(internal::all_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(f.t);
            ss << static_cast<std::string>(f) << "(" << expr << ") ";
            return ss.str();
        }
        
        template<class X, class Y>
        std::string string_from_expression(core_functions::rtrim_double_t<X, Y> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(f.x);
            auto expr2 = this->string_from_expression(f.y);
            ss << static_cast<std::string>(f) << "(" << expr << ", " << expr2 << ") ";
            return ss.str();
        }
        
        template<class X>
        std::string string_from_expression(core_functions::rtrim_single_t<X> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(f.x);
            ss << static_cast<std::string>(f) << "(" << expr << ") ";
            return ss.str();
        }
        
        template<class X, class Y>
        std::string string_from_expression(core_functions::ltrim_double_t<X, Y> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(f.x);
            auto expr2 = this->string_from_expression(f.y);
            ss << static_cast<std::string>(f) << "(" << expr << ", " << expr2 << ") ";
            return ss.str();
        }
        
        template<class X>
        std::string string_from_expression(core_functions::ltrim_single_t<X> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(f.x);
            ss << static_cast<std::string>(f) << "(" << expr << ") ";
            return ss.str();
        }
        
        template<class X, class Y>
        std::string string_from_expression(core_functions::trim_double_t<X, Y> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(f.x);
            auto expr2 = this->string_from_expression(f.y);
            ss << static_cast<std::string>(f) << "(" << expr << ", " << expr2 << ") ";
            return ss.str();
        }
        
        template<class X>
        std::string string_from_expression(core_functions::trim_single_t<X> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(f.x);
            ss << static_cast<std::string>(f) << "(" << expr << ") ";
            return ss.str();
        }
        
        std::string string_from_expression(core_functions::changes_t &ch, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            ss << static_cast<std::string>(ch) << "() ";
            return ss.str();
        }
        
        template<class T>
        std::string string_from_expression(core_functions::length_t<T> &len, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(len.t);
            ss << static_cast<std::string>(len) << "(" << expr << ") ";
            return ss.str();
        }
        
        template<class T, class ...Args>
        std::string string_from_expression(core_functions::datetime_t<T, Args...> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            ss << static_cast<std::string>(f) << "(" << this->string_from_expression(f.timestring);
            typedef std::tuple<Args...> tuple_t;
            tuple_helper::iterator<std::tuple_size<tuple_t>::value - 1, Args...>()(f.modifiers, [&](auto &v){
                ss << ", " << this->string_from_expression(v);
            });
            ss << ") ";
            return ss.str();
        }
        
        template<class T, class ...Args>
        std::string string_from_expression(core_functions::date_t<T, Args...> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            ss << static_cast<std::string>(f) << "(" << this->string_from_expression(f.timestring);
            typedef std::tuple<Args...> tuple_t;
            tuple_helper::iterator<std::tuple_size<tuple_t>::value - 1, Args...>()(f.modifiers, [&](auto &v){
                ss << ", " << this->string_from_expression(v);
            });
            ss << ") ";
            return ss.str();
        }
        
        std::string string_from_expression(core_functions::random_t &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            ss << static_cast<std::string>(f) << "() ";
            return ss.str();
        }

#if SQLITE_VERSION_NUMBER >= 3007016

        template<class ...Args>
        std::string string_from_expression(core_functions::char_t_<Args...> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            typedef decltype(f.args) tuple_t;
            std::vector<std::string> args;
            args.reserve(std::tuple_size<tuple_t>::value);
            tuple_helper::tuple_for_each(f.args, [&](auto &v){
                auto expression = this->string_from_expression(v);
                args.emplace_back(std::move(expression));
            });
            ss << static_cast<std::string>(f) << "(";
            auto lim = int(args.size());
            for(auto i = 0; i < lim; ++i) {
                ss << args[i];
                if(i < lim - 1) {
                    ss << ", ";
                }else{
                    ss << " ";
                }
            }
            ss << ") ";
            return ss.str();
        }

#endif

        template<class T>
        std::string string_from_expression(core_functions::upper_t<T> &a, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(a.t);
            ss << static_cast<std::string>(a) << "(" << expr << ") ";
            return ss.str();
        }
        
        template<class T>
        std::string string_from_expression(core_functions::lower_t<T> &a, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(a.t);
            ss << static_cast<std::string>(a) << "(" << expr << ") ";
            return ss.str();
        }
        
        template<class T>
        std::string string_from_expression(core_functions::abs_t<T> &a, bool /*noTableName*/ = false, bool /*escape*/ = false) {
            std::stringstream ss;
            auto expr = this->string_from_expression(a.t);
            ss << static_cast<std::string>(a) << "(" << expr << ") ";
            return ss.str();
        }
        
        template<class T>
        std::string process_where(conditions::is_null_t<T> &c) {
            std::stringstream ss;
            ss << this->string_from_expression(c.t) << " " << static_cast<std::string>(c) << " ";
            return ss.str();
        }
        
        template<class T>
        std::string process_where(conditions::is_not_null_t<T> &c) {
            std::stringstream ss;
            ss << this->string_from_expression(c.t) << " " << static_cast<std::string>(c) << " ";
            return ss.str();
        }
        
        template<class C>
        std::string process_where(conditions::negated_condition_t<C> &c) {
            std::stringstream ss;
            ss << " " << static_cast<std::string>(c) << " ";
            auto cString = this->process_where(c.c);
            ss << " (" << cString << " ) ";
            return ss.str();
        }
        
        template<class L, class R>
        std::string process_where(conditions::and_condition_t<L, R> &c) {
            std::stringstream ss;
            ss << " (" << this->process_where(c.l) << ") " << static_cast<std::string>(c) << " (" << this->process_where(c.r) << ") ";
            return ss.str();
        }
        
        template<class L, class R>
        std::string process_where(conditions::or_condition_t<L, R> &c) {
            std::stringstream ss;
            ss << " (" << this->process_where(c.l) << ") " << static_cast<std::string>(c) << " (" << this->process_where(c.r) << ") ";
            return ss.str();
        }
        
        /**
         *  Common case. Is used to process binary conditions like is_equal, not_equal
         */
        template<class C>
        std::string process_where(C c) {
            auto leftString = this->string_from_expression(c.l, false, true);
            auto rightString = this->string_from_expression(c.r, false, true);
            std::stringstream ss;
            ss << leftString << " " << static_cast<std::string>(c) << " " << rightString;
            return ss.str();
        }
        
        template<class T>
        std::string process_where(conditions::collate_t<T> &col) {
            auto res = this->process_where(col.expr);
            return res + " " + static_cast<std::string>(col);
        }
        
        template<class L, class E>
        std::string process_where(conditions::in_t<L, E> &inCondition) {
            std::stringstream ss;
            auto leftString = this->string_from_expression(inCondition.l);
            ss << leftString << " " << static_cast<std::string>(inCondition) << " (";
            for(size_t index = 0; index < inCondition.values.size(); ++index) {
                auto &value = inCondition.values[index];
                ss << " " << this->string_from_expression(value);
                if(index < inCondition.values.size() - 1) {
                    ss << ", ";
                }
            }
            ss << " )";
            return ss.str();
        }
        
        template<class A, class T>
        std::string process_where(conditions::like_t<A, T> &l) {
            std::stringstream ss;
            ss << this->string_from_expression(l.a) << " " << static_cast<std::string>(l) << " " << this->string_from_expression(l.t) << " ";
            return ss.str();
        }
        
        template<class A, class T>
        std::string process_where(conditions::between_t<A, T> &bw) {
            std::stringstream ss;
            auto expr = this->string_from_expression(bw.expr);
            ss << expr << " " << static_cast<std::string>(bw) << " " << this->string_from_expression(bw.b1) << " AND " << this->string_from_expression(bw.b2) << " ";
            return ss.str();
        }
        
        template<class O>
        std::string process_order_by(conditions::order_by_t<O> &orderBy) {
            std::stringstream ss;
            auto columnName = this->string_from_expression(orderBy.o);
            ss << columnName << " ";
            if(orderBy._collate_argument){
                constraints::collate_t col(*orderBy._collate_argument);
                ss << static_cast<std::string>(col) << " ";
            }
            switch(orderBy.asc_desc){
                case 1:
                    ss << "ASC ";
                    break;
                case -1:
                    ss << "DESC ";
                    break;
            }
            return ss.str();
        }
        
        template<class T>
        void process_join_constraint(std::stringstream &ss, conditions::on_t<T> &t) {
            ss << static_cast<std::string>(t) << " " << this->process_where(t.t) << " ";
        }
        
        template<class F, class O>
        void process_join_constraint(std::stringstream &ss, conditions::using_t<F, O> &u) {
            ss << static_cast<std::string>(u) << " (" << this->string_from_expression(u.column, true) << " ) ";
        }
        
        void process_single_condition(std::stringstream &ss, conditions::limit_t limt) {
            ss << static_cast<std::string>(limt) << " ";
            if(limt.has_offset) {
                if(limt.offset_is_implicit){
                    ss << limt.off << ", " << limt.lim;
                }else{
                    ss << limt.lim << " OFFSET " << limt.off;
                }
            }else{
                ss << limt.lim;
            }
            ss << " ";
        }
        
        template<class O>
        void process_single_condition(std::stringstream &ss, conditions::cross_join_t<O> c) {
            ss << static_cast<std::string>(c) << " ";
            ss << " '" << this->impl.template find_table_name<O>() << "' ";
        }
        
        template<class T, class O>
        void process_single_condition(std::stringstream &ss, conditions::inner_join_t<T, O> l) {
            ss << static_cast<std::string>(l) << " ";
            ss << " '" << this->impl.template find_table_name<T>() << "' ";
            this->process_join_constraint(ss, l.constraint);
        }
        
        template<class T, class O>
        void process_single_condition(std::stringstream &ss, conditions::left_outer_join_t<T, O> l) {
            ss << static_cast<std::string>(l) << " ";
            ss << " '" << this->impl.template find_table_name<T>() << "' ";
            this->process_join_constraint(ss, l.constraint);
        }
        
        template<class T, class O>
        void process_single_condition(std::stringstream &ss, conditions::left_join_t<T, O> l) {
            ss << static_cast<std::string>(l) << " ";
            ss << " '" << this->impl.template find_table_name<T>() << "' ";
            this->process_join_constraint(ss, l.constraint);
        }
        
        template<class T, class O>
        void process_single_condition(std::stringstream &ss, conditions::join_t<T, O> l) {
            ss << static_cast<std::string>(l) << " ";
            ss << " '" << this->impl.template find_table_name<T>() << "' ";
            this->process_join_constraint(ss, l.constraint);
        }
        
        /*template<class T>
         void process_single_condition(std::stringstream &ss, conditions::natural_join_t<T> l) {
         ss << static_cast<std::string>(l) << " ";
         ss << " '" << this->impl.template find_table_name<T>() << "' ";
         //            this->process_join_constraint(ss, l.constraint);
         }*/
        
        template<class C>
        void process_single_condition(std::stringstream &ss, conditions::where_t<C> w) {
            ss << static_cast<std::string>(w) << " ";
            auto whereString = this->process_where(w.c);
            ss << "( " << whereString << ") ";
        }
        
        template<class O>
        void process_single_condition(std::stringstream &ss, conditions::order_by_t<O> orderBy) {
            ss << static_cast<std::string>(orderBy) << " ";
            auto orderByString = this->process_order_by(orderBy);
            ss << orderByString << " ";
        }
        
        template<class ...Args>
        void process_single_condition(std::stringstream &ss, conditions::group_by_t<Args...> groupBy) {
            std::vector<std::string> expressions;
            typedef std::tuple<Args...> typle_t;
            tuple_helper::iterator<std::tuple_size<typle_t>::value - 1, Args...>()(groupBy.args, [&](auto &v){
                auto expression = this->string_from_expression(v);
                expressions.push_back(expression);
            });
            ss << static_cast<std::string>(groupBy) << " ";
            for(size_t i = 0; i < expressions.size(); ++i) {
                ss << expressions[i];
                if(i < expressions.size() - 1) {
                    ss << ", ";
                }
            }
            ss << " ";
        }
        
        /**
         *  Recursion end.
         */
        template<class ...Args>
        void process_conditions(std::stringstream &, Args .../*args*/) {
            //..
        }
        
        template<class C, class ...Args>
        void process_conditions(std::stringstream &ss, C c, Args&& ...args) {
            this->process_single_condition(ss, std::move(c));
            this->process_conditions(ss, std::forward<Args>(args)...);
        }
        
        void on_open_internal(sqlite3 *db) {

#if SQLITE_VERSION_NUMBER >= 3006019
if(this->foreign_keys_count()){
this->foreign_keys(db, true);
}
#endif
if(this->pragma._synchronous != -1) {
this->pragma.synchronous(this->pragma._synchronous);
}

            if(this->on_open){
                this->on_open(db);
            }
            
        }

#if SQLITE_VERSION_NUMBER >= 3006019

        //  returns foreign keys count in storage definition
        int foreign_keys_count() {
            auto res = 0;
            this->impl.for_each([&res](auto impl){
                res += impl->foreign_keys_count();
            });
            return res;
        }

#endif

    public:
        
        template<class T, class ...Args>
        view_t<T, Args...> iterate(Args&& ...args) {
            this->assert_mapped_type<T>();
            
            auto connection = this->get_or_create_connection();
            return {*this, connection, std::forward<Args>(args)...};
        }
        
        template<class O, class ...Args>
        void remove_all(Args&& ...args) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            auto &impl = this->get_impl<O>();
            std::stringstream ss;
            ss << "DELETE FROM '" << impl.table.name << "' ";
            this->process_conditions(ss, std::forward<Args>(args)...);
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    return;
                }else{
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else {
                auto msg = sqlite3_errmsg(connection->get_db());
                throw std::runtime_error(msg);
            }
        }
        
        /**
         *  Delete routine.
         *  O is an object's type. Must be specified explicitly.
         *  @param id id of object to be removed.
         */
        template<class O, class I>
        void remove(I id) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            auto &impl = this->get_impl<O>();
            std::stringstream ss;
            ss << "DELETE FROM '" << impl.table.name << "' ";
            ss << "WHERE ";
            std::vector<std::string> primaryKeyColumnNames;
            impl.table.for_each_column([&] (auto c) {
                if(c.template has<constraints::primary_key_t<>>()) {
                    primaryKeyColumnNames.emplace_back(c.name);
                }
            });
            for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) {
                ss << "\"" << primaryKeyColumnNames[i] << "\"" << " =  ?";
                if(i < primaryKeyColumnNames.size() - 1) {
                    ss << " AND ";
                }else{
                    ss << " ";
                }
            }
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                auto index = 1;
                impl.table.template for_each_column_with<constraints::primary_key_t<>>([&] (auto c) {
                    typedef typename decltype(c)::field_type field_type;
                    statement_binder<field_type>().bind(stmt, index++, id);
                });
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    return;
                }else{
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else{
                auto msg = sqlite3_errmsg(connection->get_db());
                throw std::runtime_error(msg);
            }
        }
        
        /**
         *  Update routine. Sets all non primary key fields where primary key is equal.
         *  O is an object type. May be not specified explicitly cause it can be deduced by
         *      compiler from first parameter.
         *  @param o object to be updated.
         */
        template<class O>
        void update(const O &o) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            auto &impl = this->get_impl<O>();
            std::stringstream ss;
            ss << "UPDATE '" << impl.table.name << "' SET ";
            std::vector<std::string> setColumnNames;
            impl.table.for_each_column([&](auto c) {
                if(!c.template has<constraints::primary_key_t<>>()) {
                    setColumnNames.emplace_back(c.name);
                }
            });
            for(size_t i = 0; i < setColumnNames.size(); ++i) {
                ss << "\"" << setColumnNames[i] << "\"" << " = ?";
                if(i < setColumnNames.size() - 1) {
                    ss << ", ";
                }else{
                    ss << " ";
                }
            }
            ss << "WHERE ";
            auto primaryKeyColumnNames = impl.table.primary_key_column_names();
            for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) {
                ss << "\"" << primaryKeyColumnNames[i] << "\"" << " = ?";
                if(i < primaryKeyColumnNames.size() - 1) {
                    ss << " AND ";
                }else{
                    ss << " ";
                }
            }
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                auto index = 1;
                impl.table.for_each_column([&o, stmt, &index] (auto c) {
                    if(!c.template has<constraints::primary_key_t<>>()) {
                        typedef typename decltype(c)::field_type field_type;
                        const field_type *value = nullptr;
                        if(c.member_pointer){
                            value = &(o.*c.member_pointer);
                        }else{
                            value = &((o).*(c.getter))();
                        }
                        statement_binder<field_type>().bind(stmt, index++, *value);
                    }
                });
                impl.table.for_each_column([&o, stmt, &index] (auto c) {
                    if(c.template has<constraints::primary_key_t<>>()) {
                        typedef typename decltype(c)::field_type field_type;
                        const field_type *value = nullptr;
                        if(c.member_pointer){
                            value = &(o.*c.member_pointer);
                        }else{
                            value = &((o).*(c.getter))();
                        }
                        statement_binder<field_type>().bind(stmt, index++, *value);
                    }
                });
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    return;
                }else{
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else {
                auto msg = sqlite3_errmsg(connection->get_db());
                throw std::runtime_error(msg);
            }
        }
        
        template<class ...Args, class ...Wargs>
        void update_all(internal::set_t<Args...> set, Wargs ...wh) {
            auto connection = this->get_or_create_connection();
            
            std::stringstream ss;
            ss << "UPDATE ";
            std::set<std::string> tableNamesSet;
            set.for_each([this, &tableNamesSet](auto &lhs, auto &/*rhs*/){
                auto tableName = this->parse_table_name(lhs);
                tableNamesSet.insert(tableName.begin(), tableName.end());
            });
            if(tableNamesSet.size()){
                if(tableNamesSet.size() == 1){
                    ss << " '" << *tableNamesSet.begin() << "' ";
                    ss << static_cast<std::string>(set) << " ";
                    std::vector<std::string> setPairs;
                    set.for_each([this, &setPairs](auto &lhs, auto &rhs){
                        std::stringstream sss;
                        sss << this->string_from_expression(lhs, true) << " = " << this->string_from_expression(rhs) << " ";
                        setPairs.push_back(sss.str());
                    });
                    auto setPairsCount = setPairs.size();
                    for(size_t i = 0; i < setPairsCount; ++i) {
                        ss << setPairs[i] << " ";
                        if(i < setPairsCount - 1) {
                            ss << ", ";
                        }
                    }
                    this->process_conditions(ss, wh...);
                    auto query = ss.str();
                    sqlite3_stmt *stmt;
                    if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                        statement_finalizer finalizer{stmt};
                        if (sqlite3_step(stmt) == SQLITE_DONE) {
                            return;
                        }else{
                            auto msg = sqlite3_errmsg(connection->get_db());
                            throw std::runtime_error(msg);
                        }
                    }else {
                        auto msg = sqlite3_errmsg(connection->get_db());
                        throw std::runtime_error(msg);
                    }
                }else{
                    throw std::runtime_error("too many tables specified - UPDATE can be performed only for a single table");
                }
            }else{
                throw std::runtime_error("incorrect SET fields specified");
            }
        }
        
    protected:
        
        /**
         *  O - mapped type
         *  Args - conditions
         *  @param query - result query string
         *  @return impl for O
         */
        template<class O, class ...Args>
        auto& generate_select_asterisk(std::string *query, Args&& ...args) {
            std::stringstream ss;
            ss << "SELECT ";
            auto &impl = this->get_impl<O>();
            auto columnNames = impl.table.column_names();
            for(size_t i = 0; i < columnNames.size(); ++i) {
                ss
                << "'" << impl.table.name << "'."
                << "\""
                << columnNames[i]
                << "\""
                ;
                if(i < columnNames.size() - 1) {
                    ss << ", ";
                }else{
                    ss << " ";
                }
            }
            ss << "FROM '" << impl.table.name << "' ";
            this->process_conditions(ss, std::forward<Args>(args)...);
            if(query){
                *query = ss.str();
            }
            return impl;
        }
        
        template<class T>
        std::set<std::string> parse_table_name(T &) {
            return {};
        }
        
        template<class F, class O>
        std::set<std::string> parse_table_name(F O::*) {
            return {this->impl.template find_table_name<O>()};
        }
        
        template<class T>
        std::set<std::string> parse_table_name(aggregate_functions::min_t<T> &f) {
            return this->parse_table_name(f.t);
        }
        
        template<class T>
        std::set<std::string> parse_table_name(aggregate_functions::max_t<T> &f) {
            return this->parse_table_name(f.t);
        }
        
        template<class T>
        std::set<std::string> parse_table_name(aggregate_functions::sum_t<T> &f) {
            return this->parse_table_name(f.t);
        }
        
        template<class T>
        std::set<std::string> parse_table_name(aggregate_functions::total_t<T> &f) {
            return this->parse_table_name(f.t);
        }
        
        template<class T>
        std::set<std::string> parse_table_name(aggregate_functions::group_concat_double_t<T> &f) {
            auto res = this->parse_table_name(f.t);
            auto secondSet = this->parse_table_name(f.y);
            res.insert(secondSet.begin(), secondSet.end());
            return res;
        }
        
        template<class T>
        std::set<std::string> parse_table_name(aggregate_functions::group_concat_single_t<T> &f) {
            return this->parse_table_name(f.t);
        }
        
        template<class T>
        std::set<std::string> parse_table_name(aggregate_functions::count_t<T> &f) {
            return this->parse_table_name(f.t);
        }
        
        template<class T>
        std::set<std::string> parse_table_name(aggregate_functions::avg_t<T> &a) {
            return this->parse_table_name(a.t);
        }
        
        template<class T>
        std::set<std::string> parse_table_name(core_functions::length_t<T> &len) {
            return this->parse_table_name(len.t);
        }
        
        template<class T, class ...Args>
        std::set<std::string> parse_table_name(core_functions::date_t<T, Args...> &f) {
            auto res = this->parse_table_name(f.timestring);
            typedef decltype(f.modifiers) tuple_t;
            tuple_helper::iterator<std::tuple_size<tuple_t>::value - 1, Args...>()(f.modifiers, [&](auto &v){
                auto tableNames = this->parse_table_name(v);
                res.insert(tableNames.begin(), tableNames.end());
            });
            return res;
        }
        
        template<class T, class ...Args>
        std::set<std::string> parse_table_name(core_functions::datetime_t<T, Args...> &f) {
            auto res = this->parse_table_name(f.timestring);
            typedef decltype(f.modifiers) tuple_t;
            tuple_helper::iterator<std::tuple_size<tuple_t>::value - 1, Args...>()(f.modifiers, [&](auto &v){
                auto tableNames = this->parse_table_name(v);
                res.insert(tableNames.begin(), tableNames.end());
            });
            return res;
        }
        
        template<class X>
        std::set<std::string> parse_table_name(core_functions::trim_single_t<X> &f) {
            return this->parse_table_name(f.x);
        }
        
        template<class X, class Y>
        std::set<std::string> parse_table_name(core_functions::trim_double_t<X, Y> &f) {
            auto res = this->parse_table_name(f.x);
            auto res2 = this->parse_table_name(f.y);
            res.insert(res2.begin(), res2.end());
            return res;
        }
        
        template<class X>
        std::set<std::string> parse_table_name(core_functions::rtrim_single_t<X> &f) {
            return this->parse_table_name(f.x);
        }
        
        template<class X, class Y>
        std::set<std::string> parse_table_name(core_functions::rtrim_double_t<X, Y> &f) {
            auto res = this->parse_table_name(f.x);
            auto res2 = this->parse_table_name(f.y);
            res.insert(res2.begin(), res2.end());
            return res;
        }
        
        template<class X>
        std::set<std::string> parse_table_name(core_functions::ltrim_single_t<X> &f) {
            return this->parse_table_name(f.x);
        }
        
        template<class X, class Y>
        std::set<std::string> parse_table_name(core_functions::ltrim_double_t<X, Y> &f) {
            auto res = this->parse_table_name(f.x);
            auto res2 = this->parse_table_name(f.y);
            res.insert(res2.begin(), res2.end());
            return res;
        }

#if SQLITE_VERSION_NUMBER >= 3007016

        template<class ...Args>
        std::set<std::string> parse_table_name(core_functions::char_t_<Args...> &f) {
            std::set<std::string> res;
            typedef decltype(f.args) tuple_t;
            tuple_helper::iterator<std::tuple_size<tuple_t>::value - 1, Args...>()(f.args, [&](auto &v){
                auto tableNames = this->parse_table_name(v);
                res.insert(tableNames.begin(), tableNames.end());
            });
            return res;
        }

#endif

        std::set<std::string> parse_table_name(core_functions::random_t &f) {
            return {};
        }
        
        template<class T>
        std::set<std::string> parse_table_name(core_functions::upper_t<T> &a) {
            return this->parse_table_name(a.t);
        }
        
        template<class T>
        std::set<std::string> parse_table_name(core_functions::lower_t<T> &a) {
            return this->parse_table_name(a.t);
        }
        
        template<class T>
        std::set<std::string> parse_table_name(core_functions::abs_t<T> &a) {
            return this->parse_table_name(a.t);
        }
        
        template<class T>
        std::set<std::string> parse_table_name(internal::distinct_t<T> &f) {
            return this->parse_table_name(f.t);
        }
        
        template<class T>
        std::set<std::string> parse_table_name(internal::all_t<T> &f) {
            return this->parse_table_name(f.t);
        }
        
        template<class ...Args>
        std::set<std::string> parse_table_names(Args...) {
            return {};
        }
        
        template<class H, class ...Args>
        std::set<std::string> parse_table_names(H h, Args&& ...args) {
            auto res = this->parse_table_names(std::forward<Args>(args)...);
            auto tableName = this->parse_table_name(h);
            res.insert(tableName.begin(),
                       tableName.end());
            return res;
        }
        
        template<class ...Args>
        std::set<std::string> parse_table_names(internal::columns_t<Args...> &cols) {
            std::set<std::string> res;
            cols.for_each([&](auto &m){
                auto tableName = this->parse_table_name(m);
                res.insert(tableName.begin(),
                           tableName.end());
            });
            return res;
        }
        
        template<class F, class O, class ...Args>
        std::string group_concat_internal(F O::*m, std::shared_ptr<const std::string> y, Args&& ...args) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            auto &impl = this->get_impl<O>();
            std::string res;
            std::stringstream ss;
            ss << "SELECT " << static_cast<std::string>(sqlite_orm::group_concat(0)) << "(";
            auto columnName = this->string_from_expression(m);
            if(columnName.length()){
                ss << columnName;
                if(y){
                    ss << ",\"" << *y << "\"";
                }
                ss << ") FROM '"<< impl.table.name << "' ";
                this->process_conditions(ss, std::forward<Args>(args)...);
                auto query = ss.str();
                auto rc = sqlite3_exec(connection->get_db(),
                                       query.c_str(),
                                       [](void *data, int argc, char **argv,char **) -> int {
                                           auto &res = *(std::string*)data;
                                           if(argc){
                                               res = row_extractor<std::string>().extract(argv[0]);
                                           }
                                           return 0;
                                       }, &res, nullptr);
                if(rc != SQLITE_OK) {
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else{
                throw std::runtime_error("column not found");
            }
            return res;
        }
        
    public:
        
        /**
         *  Select * with no conditions routine.
         *  O is an object type to be extracted. Must be specified explicitly.
         *  @return All objects of type O stored in database at the moment.
         */
        template<class O, class C = std::vector<O>, class ...Args>
        C get_all(Args&& ...args) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            C res;
            std::string query;
            auto &impl = this->generate_select_asterisk<O>(&query, std::forward<Args>(args)...);
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                int stepRes;
                do{
                    stepRes = sqlite3_step(stmt);
                    switch(stepRes){
                        case SQLITE_ROW:{
                            O obj;
                            auto index = 0;
                            impl.table.for_each_column([&index, &obj, stmt] (auto c) {
                                typedef typename decltype(c)::field_type field_type;
                                auto value = row_extractor<field_type>().extract(stmt, index++);
                                if(c.member_pointer){
                                    obj.*c.member_pointer = value;
                                }else{
                                    ((obj).*(c.setter))(std::move(value));
                                }
                            });
                            res.push_back(std::move(obj));
                        }break;
                        case SQLITE_DONE: break;
                        default:{
                            auto msg = sqlite3_errmsg(connection->get_db());
                            throw std::runtime_error(msg);
                        }
                    }
                }while(stepRes != SQLITE_DONE);
                return res;
            }else{
                auto msg = sqlite3_errmsg(connection->get_db());
                throw std::runtime_error(msg);
            }
        }
        
        /**
         *  Select * by id routine.
         *  throws sqlite_orm::not_found_exeption if object not found with given id.
         *  throws std::runtime_error in case of db error.
         *  O is an object type to be extracted. Must be specified explicitly.
         *  @return Object of type O where id is equal parameter passed or throws `not_found_exception`
         *  if there is no object with such id.
         */
        template<class O, class ...Ids>
        O get(Ids ...ids) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            auto &impl = this->get_impl<O>();
            std::shared_ptr<O> res;
            std::stringstream ss;
            ss << "SELECT ";
            auto columnNames = impl.table.column_names();
            for(size_t i = 0; i < columnNames.size(); ++i) {
                ss << "\"" << columnNames[i] << "\"";
                if(i < columnNames.size() - 1) {
                    ss << ", ";
                }else{
                    ss << " ";
                }
            }
            ss << "FROM '" << impl.table.name << "' WHERE ";
            auto primaryKeyColumnNames = impl.table.primary_key_column_names();
            if(primaryKeyColumnNames.size()){
                for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) {
                    ss << "\"" << primaryKeyColumnNames[i] << "\"" << " = ? ";
                    if(i < primaryKeyColumnNames.size() - 1) {
                        ss << "AND ";
                    }
                    ss << ' ';
                }
                auto query = ss.str();
                sqlite3_stmt *stmt;
                if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                    statement_finalizer finalizer{stmt};
                    auto index = 1;
                    auto idsTuple = std::make_tuple(std::forward<Ids>(ids)...);
                    constexpr const auto idsCount = std::tuple_size<decltype(idsTuple)>::value;
                    tuple_helper::iterator<idsCount - 1, Ids...>()(idsTuple, [stmt, &index](auto &v){
                        typedef typename std::remove_reference<decltype(v)>::type field_type;
                        statement_binder<field_type>().bind(stmt, index++, v);
                    });
                    auto stepRes = sqlite3_step(stmt);
                    switch(stepRes){
                        case SQLITE_ROW:{
                            O res;
                            index = 0;
                            impl.table.for_each_column([&index, &res, stmt] (auto c) {
                                typedef typename decltype(c)::field_type field_type;
                                auto value = row_extractor<field_type>().extract(stmt, index++);
                                if(c.member_pointer){
                                    res.*c.member_pointer = value;
                                }else{
                                    ((res).*(c.setter))(std::move(value));
                                }
                            });
                            return res;
                        }break;
                        case SQLITE_DONE:{
                            throw not_found_exception{};
                        }break;
                        default:{
                            auto msg = sqlite3_errmsg(connection->get_db());
                            throw std::runtime_error(msg);
                        }
                    }
                }else{
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else{
                throw std::runtime_error("table " + impl.table.name + " has no primary key column");
            }
        }
        
        /**
         *  The same as `get` function but doesn't throw an exeption if noting found but returns std::shared_ptr with null value.
         *  throws std::runtime_error iin case of db error.
         */
        template<class O, class ...Ids>
        std::shared_ptr<O> get_no_throw(Ids ...ids) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            auto &impl = this->get_impl<O>();
            std::shared_ptr<O> res;
            std::stringstream ss;
            ss << "SELECT ";
            auto columnNames = impl.table.column_names();
            for(size_t i = 0; i < columnNames.size(); ++i) {
                ss << "\"" << columnNames[i] << "\"";
                if(i < columnNames.size() - 1) {
                    ss << ", ";
                }else{
                    ss << " ";
                }
            }
            ss << "FROM '" << impl.table.name << "' WHERE ";
            auto primaryKeyColumnNames = impl.table.primary_key_column_names();
            if(primaryKeyColumnNames.size() && primaryKeyColumnNames.front().length()){
                for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) {
                    ss << "\"" << primaryKeyColumnNames[i] << "\"" << " = ? ";
                    if(i < primaryKeyColumnNames.size() - 1) {
                        ss << "AND ";
                    }
                    ss << ' ';
                }
                auto query = ss.str();
                sqlite3_stmt *stmt;
                if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                    statement_finalizer finalizer{stmt};
                    auto index = 1;
                    auto idsTuple = std::make_tuple(std::forward<Ids>(ids)...);
                    constexpr const auto idsCount = std::tuple_size<decltype(idsTuple)>::value;
                    tuple_helper::iterator<idsCount - 1, Ids...>()(idsTuple, [stmt, &index](auto &v){
                        typedef typename std::remove_reference<decltype(v)>::type field_type;
                        statement_binder<field_type>().bind(stmt, index++, v);
                    });
                    auto stepRes = sqlite3_step(stmt);
                    switch(stepRes){
                        case SQLITE_ROW:{
                            O res;
                            index = 0;
                            impl.table.for_each_column([&index, &res, stmt] (auto c) {
                                typedef typename decltype(c)::field_type field_type;
                                auto value = row_extractor<field_type>().extract(stmt, index++);
                                if(c.member_pointer){
                                    res.*c.member_pointer = value;
                                }else{
                                    ((res).*(c.setter))(std::move(value));
                                }
                            });
                            return std::make_shared<O>(std::move(res));
                        }break;
                        case SQLITE_DONE:{
                            return {};
                        }break;
                        default:{
                            auto msg = sqlite3_errmsg(connection->get_db());
                            throw std::runtime_error(msg);
                        }
                    }
                }else{
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else{
                throw std::runtime_error("table " + impl.table.name + " has no primary key column");
            }
        }
        
        /**
         *  SELECT COUNT(*) with no conditions routine. https://www.sqlite.org/lang_aggfunc.html#count
         *  @return Number of O object in table.
         */
        template<class O, class ...Args>
        int count(Args&& ...args) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            auto &impl = this->get_impl<O>();
            int res = 0;
            std::stringstream ss;
            ss << "SELECT " << static_cast<std::string>(sqlite_orm::count()) << "(*) FROM '" << impl.table.name << "' ";
            this->process_conditions(ss, args...);
            auto query = ss.str();
            auto rc = sqlite3_exec(connection->get_db(),
                                   query.c_str(),
                                   [](void *data, int argc, char **argv,char **) -> int {
                                       auto &res = *(int*)data;
                                       if(argc){
                                           res = row_extractor<int>().extract(argv[0]);
                                       }
                                       return 0;
                                   }, &res, nullptr);
            if(rc != SQLITE_OK) {
                auto msg = sqlite3_errmsg(connection->get_db());
                throw std::runtime_error(msg);
            }
            return res;
        }
        
        /**
         *  SELECT COUNT(X) https://www.sqlite.org/lang_aggfunc.html#count
         *  @param m member pointer to class mapped to the storage.
         */
        template<class F, class O, class ...Args>
        int count(F O::*m, Args&& ...args) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            auto &impl = this->get_impl<O>();
            int res = 0;
            std::stringstream ss;
            ss << "SELECT " << static_cast<std::string>(sqlite_orm::count(0)) << "(";
            auto columnName = this->string_from_expression(m);
            if(columnName.length()){
                ss << columnName << ") FROM '"<< impl.table.name << "' ";
                this->process_conditions(ss, std::forward<Args>(args)...);
                auto query = ss.str();
                auto rc = sqlite3_exec(connection->get_db(),
                                       query.c_str(),
                                       [](void *data, int argc, char **argv,char **) -> int {
                                           auto &res = *(int*)data;
                                           if(argc){
                                               res = row_extractor<int>().extract(argv[0]);
                                           }
                                           return 0;
                                       }, &res, nullptr);
                if(rc != SQLITE_OK) {
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else{
                throw std::runtime_error("column not found");
            }
            return res;
        }
        
        /**
         *  AVG(X) query.   https://www.sqlite.org/lang_aggfunc.html#avg
         *  @param m is a class member pointer (the same you passed into make_column).
         *  @return average value from db.
         */
        template<class F, class O, class ...Args>
        double avg(F O::*m, Args&& ...args) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            auto &impl = this->get_impl<O>();
            double res = 0;
            std::stringstream ss;
            ss << "SELECT " << static_cast<std::string>(sqlite_orm::avg(0)) << "(";
            auto columnName = this->string_from_expression(m);
            if(columnName.length()){
                ss << columnName << ") FROM '"<< impl.table.name << "' ";
                this->process_conditions(ss, std::forward<Args>(args)...);
                auto query = ss.str();
                auto rc = sqlite3_exec(connection->get_db(),
                                       query.c_str(),
                                       [](void *data, int argc, char **argv,char **)->int{
                                           auto &res = *(double*)data;
                                           if(argc){
                                               res = row_extractor<double>().extract(argv[0]);
                                           }
                                           return 0;
                                       }, &res, nullptr);
                if(rc != SQLITE_OK) {
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else{
                throw std::runtime_error("column not found");
            }
            return res;
        }
        
        template<class F, class O>
        std::string group_concat(F O::*m) {
            return this->group_concat_internal(m, {});
        }
        
        /**
         *  GROUP_CONCAT(X) query.  https://www.sqlite.org/lang_aggfunc.html#groupconcat
         *  @param m is a class member pointer (the same you passed into make_column).
         *  @return group_concat query result.
         */
        template<class F, class O, class ...Args,
        class Tuple = std::tuple<Args...>,
        typename sfinae = typename std::enable_if<std::tuple_size<std::tuple<Args...>>::value >= 1>::type
        >
        std::string group_concat(F O::*m, Args&& ...args) {
            return this->group_concat_internal(m, {}, std::forward<Args>(args)...);
        }
        
        /**
         *  GROUP_CONCAT(X, Y) query.   https://www.sqlite.org/lang_aggfunc.html#groupconcat
         *  @param m is a class member pointer (the same you passed into make_column).
         *  @return group_concat query result.
         */
        template<class F, class O, class ...Args>
        std::string group_concat(F O::*m, const std::string &y, Args&& ...args) {
            return this->group_concat_internal(m, std::make_shared<std::string>(y), std::forward<Args>(args)...);
        }
        
        template<class F, class O, class ...Args>
        std::string group_concat(F O::*m, const char *y, Args&& ...args) {
            return this->group_concat_internal(m, std::make_shared<std::string>(y), std::forward<Args>(args)...);
        }
        
        /**
         *  MAX(x) query.
         *  @param m is a class member pointer (the same you passed into make_column).
         *  @return std::shared_ptr with max value or null if sqlite engine returned null.
         */
        template<class F, class O, class ...Args>
        std::shared_ptr<F> max(F O::*m, Args&& ...args) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            auto &impl = this->get_impl<O>();
            std::shared_ptr<F> res;
            std::stringstream ss;
            ss << "SELECT " << static_cast<std::string>(sqlite_orm::max(0)) << "(";
            auto columnName = this->string_from_expression(m);
            if(columnName.length()){
                ss << columnName << ") FROM '" << impl.table.name << "' ";
                this->process_conditions(ss, std::forward<Args>(args)...);
                auto query = ss.str();
                auto rc = sqlite3_exec(connection->get_db(),
                                       query.c_str(),
                                       [](void *data, int argc, char **argv,char **)->int{
                                           auto &res = *(std::shared_ptr<F>*)data;
                                           if(argc){
                                               if(argv[0]){
                                                   res = std::make_shared<F>(row_extractor<F>().extract(argv[0]));
                                               }
                                           }
                                           return 0;
                                       }, &res, nullptr);
                if(rc != SQLITE_OK) {
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else{
                throw std::runtime_error("column not found");
            }
            return res;
        }
        
        /**
         *  MIN(x) query.
         *  @param m is a class member pointer (the same you passed into make_column).
         *  @return std::shared_ptr with min value or null if sqlite engine returned null.
         */
        template<class F, class O, class ...Args>
        std::shared_ptr<F> min(F O::*m, Args&& ...args) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            auto &impl = this->get_impl<O>();
            std::shared_ptr<F> res;
            std::stringstream ss;
            ss << "SELECT " << static_cast<std::string>(sqlite_orm::min(0)) << "(";
            auto columnName = this->string_from_expression(m);
            if(columnName.length()){
                ss << columnName << ") FROM '" << impl.table.name << "' ";
                this->process_conditions(ss, std::forward<Args>(args)...);
                auto query = ss.str();
                auto rc = sqlite3_exec(connection->get_db(),
                                       query.c_str(),
                                       [](void *data, int argc, char **argv,char **)->int{
                                           auto &res = *(std::shared_ptr<F>*)data;
                                           if(argc){
                                               if(argv[0]){
                                                   res = std::make_shared<F>(row_extractor<F>().extract(argv[0]));
                                               }
                                           }
                                           return 0;
                                       }, &res, nullptr);
                if(rc != SQLITE_OK) {
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else{
                throw std::runtime_error("column not found");
            }
            return res;
        }
        
        /**
         *  SUM(x) query.
         *  @param m is a class member pointer (the same you passed into make_column).
         *  @return std::shared_ptr with sum value or null if sqlite engine returned null.
         */
        template<class F, class O, class ...Args>
        std::shared_ptr<F> sum(F O::*m, Args&& ...args) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            auto &impl = this->get_impl<O>();
            std::shared_ptr<F> res;
            std::stringstream ss;
            ss << "SELECT " << static_cast<std::string>(sqlite_orm::sum(0)) << "(";
            auto columnName = this->string_from_expression(m);
            if(columnName.length()){
                ss << columnName << ") FROM '"<< impl.table.name << "' ";
                this->process_conditions(ss, std::forward<Args>(args)...);
                auto query = ss.str();
                auto rc = sqlite3_exec(connection->get_db(),
                                       query.c_str(),
                                       [](void *data, int argc, char **argv,char **)->int{
                                           auto &res = *(std::shared_ptr<F>*)data;
                                           if(argc){
                                               res = std::make_shared<F>(row_extractor<F>().extract(argv[0]));
                                           }
                                           return 0;
                                       }, &res, nullptr);
                if(rc != SQLITE_OK) {
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else{
                throw std::runtime_error("column not found");
            }
            return res;
        }
        
        /**
         *  TOTAL(x) query.
         *  @param m is a class member pointer (the same you passed into make_column).
         *  @return total value (the same as SUM but not nullable. More details here https://www.sqlite.org/lang_aggfunc.html)
         */
        template<class F, class O, class ...Args>
        double total(F O::*m, Args&& ...args) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            double res;
            std::stringstream ss;
            ss << "SELECT " << static_cast<std::string>(sqlite_orm::total(0)) << "(";
            auto columnName = this->string_from_expression(m);
            if(columnName.length()){
                ss << columnName << ") ";
                auto tableNamesSet = this->parse_table_names(m);
                if(tableNamesSet.size()){
                    ss << "FROM " ;
                    std::vector<std::string> tableNames(tableNamesSet.begin(), tableNamesSet.end());
                    for(size_t i = 0; i < tableNames.size(); ++i) {
                        ss << " '" << tableNames[i] << "' ";
                        if(i < tableNames.size() - 1) {
                            ss << ",";
                        }
                        ss << " ";
                    }
                }
                this->process_conditions(ss, std::forward<Args>(args)...);
                auto query = ss.str();
                auto rc = sqlite3_exec(connection->get_db(),
                                       query.c_str(),
                                       [](void *data, int argc, char **argv,char **)->int{
                                           auto &res = *(double*)data;
                                           if(argc){
                                               res = row_extractor<double>().extract(argv[0]);
                                           }
                                           return 0;
                                       }, &res, nullptr);
                if(rc != SQLITE_OK) {
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else{
                throw std::runtime_error("column not found");
            }
            return res;
        }
        
        /**
         *  Select a single column into std::vector<T>.
         */
        template<class T, class ...Args, class R = typename internal::column_result_t<T, Ts...>::type>
        std::vector<R> select(T m, Args&& ...args) {
            auto connection = this->get_or_create_connection();
            std::stringstream ss;
            ss << "SELECT ";
            auto columnName = this->string_from_expression(m);
            if(columnName.length()){
                ss << columnName << " ";
                auto tableNamesSet = this->parse_table_names(m);
                if(tableNamesSet.size()){
                    ss << "FROM " ;
                    std::vector<std::string> tableNames(tableNamesSet.begin(), tableNamesSet.end());
                    for(size_t i = 0; i < tableNames.size(); ++i) {
                        ss << " '" << tableNames[i] << "' ";
                        if(i < tableNames.size() - 1) {
                            ss << ",";
                        }
                        ss << " ";
                    }
                }
                this->process_conditions(ss, std::forward<Args>(args)...);
                auto query = ss.str();
                sqlite3_stmt *stmt;
                if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                    statement_finalizer finalizer{stmt};
                    std::vector<R> res;
                    int stepRes;
                    do{
                        stepRes = sqlite3_step(stmt);
                        switch(stepRes){
                            case SQLITE_ROW:{
                                res.push_back(row_extractor<R>().extract(stmt, 0));
                            }break;
                            case SQLITE_DONE: break;
                            default:{
                                auto msg = sqlite3_errmsg(connection->get_db());
                                throw std::runtime_error(msg);
                            }
                        }
                    }while(stepRes != SQLITE_DONE);
                    return res;
                }else{
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else{
                throw std::runtime_error("column not found");
            }
        }
        
        /**
         *  Select several columns into std::vector<std::tuple<...>>.
         */
        template<class ...Args,
        class R = std::tuple<typename internal::column_result_t<Args, Ts...>::type...>,
        class ...Conds
        >
        std::vector<R> select(internal::columns_t<Args...> cols, Conds ...conds) {
            auto connection = this->get_or_create_connection();
            std::vector<R> res;
            std::stringstream ss;
            ss << "SELECT ";
            if(cols.distinct) {
                ss << static_cast<std::string>(distinct(0)) << " ";
            }
            std::vector<std::string> columnNames;
            columnNames.reserve(cols.count());
            cols.for_each([&](auto &m) {
                auto columnName = this->string_from_expression(m);
                if(columnName.length()){
                    columnNames.push_back(columnName);
                }else{
                    throw std::runtime_error("column not found");
                }
            });
            for(size_t i = 0; i < columnNames.size(); ++i) {
                ss << columnNames[i];
                if(i < columnNames.size() - 1) {
                    ss << ", ";
                }else{
                    ss << " ";
                }
            }
            auto tableNamesSet = this->parse_table_names(cols);
            internal::join_iterator<Conds...>()([&](auto c){
                typedef typename decltype(c)::type crossJoinType;
                auto crossJoinedTableName = this->impl.template find_table_name<crossJoinType>();
                tableNamesSet.erase(crossJoinedTableName);
            });
            if(tableNamesSet.size()){
                ss << " FROM ";
                std::vector<std::string> tableNames(tableNamesSet.begin(), tableNamesSet.end());
                for(size_t i = 0; i < tableNames.size(); ++i) {
                    ss << " '" << tableNames[i] << "' ";
                    if(int(i) < int(tableNames.size()) - 1) {
                        ss << ",";
                    }
                    ss << " ";
                }
            }
            this->process_conditions(ss, conds...);
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                std::vector<R> res;
                int stepRes;
                do{
                    stepRes = sqlite3_step(stmt);
                    switch(stepRes){
                        case SQLITE_ROW:{
                            res.push_back(row_extractor<R>().extract(stmt, 0));
                        }break;
                        case SQLITE_DONE: break;
                        default:{
                            auto msg = sqlite3_errmsg(connection->get_db());
                            throw std::runtime_error(msg);
                        }
                    }
                }while(stepRes != SQLITE_DONE);
                return res;
            }else{
                auto msg = sqlite3_errmsg(connection->get_db());
                throw std::runtime_error(msg);
            }
            return res;
        }
        
        /**
         *  Returns a string representation of object of a class mapped to the storage.
         *  Type of string has json-like style.
         */
        template<class O>
        std::string dump(const O &o) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            return this->impl.dump(o, connection->get_db());
        }
        
        /**
         *  This is REPLACE (INSERT OR REPLACE) function.
         *  Also if you need to insert value with knows id you should
         *  also you this function instead of insert cause inserts ignores
         *  id and creates own one.
         */
        template<class O>
        void replace(const O &o) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            auto &impl = get_impl<O>();
            std::stringstream ss;
            ss << "REPLACE INTO '" << impl.table.name << "' (";
            auto columnNames = impl.table.column_names();
            auto columnNamesCount = columnNames.size();
            for(size_t i = 0; i < columnNamesCount; ++i) {
                ss << "\"" << columnNames[i] << "\"";
                if(i < columnNamesCount - 1) {
                    ss << ", ";
                }else{
                    ss << ") ";
                }
            }
            ss << "VALUES(";
            for(size_t i = 0; i < columnNamesCount; ++i) {
                ss << "?";
                if(i < columnNamesCount - 1) {
                    ss << ", ";
                }else{
                    ss << ")";
                }
            }
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                auto index = 1;
                impl.table.for_each_column([&o, &index, &stmt] (auto c) {
                    typedef typename decltype(c)::field_type field_type;
                    const field_type *value = nullptr;
                    if(c.member_pointer){
                        value = &(o.*c.member_pointer);
                    }else{
                        value = &((o).*(c.getter))();
                    }
                    statement_binder<field_type>().bind(stmt, index++, *value);
                });
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    //..
                }else{
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else {
                auto msg = sqlite3_errmsg(connection->get_db());
                throw std::runtime_error(msg);
            }
        }
        
        template<class It>
        void replace_range(It from, It to) {
            typedef typename std::iterator_traits<It>::value_type O;
            this->assert_mapped_type<O>();
            if(from == to) {
                return;
            }
            
            auto connection = this->get_or_create_connection();
            auto &impl = get_impl<O>();
            std::stringstream ss;
            ss << "REPLACE INTO '" << impl.table.name << "' (";
            auto columnNames = impl.table.column_names();
            auto columnNamesCount = columnNames.size();
            for(size_t i = 0; i < columnNamesCount; ++i) {
                ss << "\"" << columnNames[i] << "\"";
                if(i < columnNamesCount - 1) {
                    ss << ", ";
                }else{
                    ss << ") ";
                }
            }
            ss << "VALUES ";
            auto valuesString = [columnNamesCount]{
                std::stringstream ss;
                ss << "(";
                for(size_t i = 0; i < columnNamesCount; ++i) {
                    ss << "?";
                    if(i < columnNamesCount - 1) {
                        ss << ", ";
                    }else{
                        ss << ")";
                    }
                }
                return ss.str();
            }();
            auto valuesCount = static_cast<int>(std::distance(from, to));
            for(auto i = 0; i < valuesCount; ++i) {
                ss << valuesString;
                if(i < valuesCount - 1) {
                    ss << ",";
                }
                ss << " ";
            }
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                auto index = 1;
                for(auto it = from; it != to; ++it) {
                    auto &o = *it;
                    impl.table.for_each_column([&o, &index, &stmt] (auto c) {
                        typedef typename decltype(c)::field_type field_type;
                        const field_type *value = nullptr;
                        if(c.member_pointer){
                            value = &(o.*c.member_pointer);
                        }else{
                            value = &((o).*(c.getter))();
                        }
                        statement_binder<field_type>().bind(stmt, index++, *value);
                    });
                }
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    //..
                }else{
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else {
                auto msg = sqlite3_errmsg(connection->get_db());
                throw std::runtime_error(msg);
            }
        }
        
        /**
         *  Insert routine. Inserts object with all non primary key fields in passed object. Id of passed
         *  object doesn't matter.
         *  @return id of just created object.
         */
        template<class O>
        int insert(const O &o) {
            this->assert_mapped_type<O>();
            
            auto connection = this->get_or_create_connection();
            auto &impl = get_impl<O>();
            int res = 0;
            std::stringstream ss;
            ss << "INSERT INTO '" << impl.table.name << "' (";
            std::vector<std::string> columnNames;
            auto compositeKeyColumnNames = impl.table.composite_key_columns_names();
            impl.table.for_each_column([&impl, &columnNames, &compositeKeyColumnNames] (auto c) {
                if(impl.table._without_rowid || !c.template has<constraints::primary_key_t<>>()) {
                    auto it = std::find(compositeKeyColumnNames.begin(),
                                        compositeKeyColumnNames.end(),
                                        c.name);
                    if(it == compositeKeyColumnNames.end()){
                        columnNames.emplace_back(c.name);
                    }
                }
            });
            
            auto columnNamesCount = columnNames.size();
            for(size_t i = 0; i < columnNamesCount; ++i) {
                ss << "\"" << columnNames[i] << "\"";
                if(i < columnNamesCount - 1) {
                    ss << ", ";
                }else{
                    ss << ") ";
                }
            }
            ss << "VALUES (";
            for(size_t i = 0; i < columnNamesCount; ++i) {
                ss << "?";
                if(i < columnNamesCount - 1) {
                    ss << ", ";
                }else{
                    ss << ")";
                }
            }
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                auto index = 1;
                impl.table.for_each_column([&o, &index, &stmt, &impl, &compositeKeyColumnNames] (auto c) {
                    if(impl.table._without_rowid || !c.template has<constraints::primary_key_t<>>()){
                        auto it = std::find(compositeKeyColumnNames.begin(),
                                            compositeKeyColumnNames.end(),
                                            c.name);
                        if(it == compositeKeyColumnNames.end()){
                            typedef typename decltype(c)::field_type field_type;
                            const field_type *value = nullptr;
                            if(c.member_pointer){
                                value = &(o.*c.member_pointer);
                            }else{
                                value = &((o).*(c.getter))();
                            }
                            statement_binder<field_type>().bind(stmt, index++, *value);
                        }
                    }
                });
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    res = int(sqlite3_last_insert_rowid(connection->get_db()));
                }else{
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else {
                auto msg = sqlite3_errmsg(connection->get_db());
                throw std::runtime_error(msg);
            }
            return res;
        }
        
        template<class It>
        void insert_range(It from, It to) {
            typedef typename std::iterator_traits<It>::value_type O;
            this->assert_mapped_type<O>();
            if(from == to) {
                return;
            }
            
            auto connection = this->get_or_create_connection();
            auto &impl = get_impl<O>();
            
            std::stringstream ss;
            ss << "INSERT INTO '" << impl.table.name << "' (";
            std::vector<std::string> columnNames;
            impl.table.for_each_column([&] (auto c) {
                if(!c.template has<constraints::primary_key_t<>>()) {
                    columnNames.emplace_back(c.name);
                }
            });
            
            auto columnNamesCount = columnNames.size();
            for(size_t i = 0; i < columnNamesCount; ++i) {
                ss << "\"" << columnNames[i] << "\"";
                if(i < columnNamesCount - 1) {
                    ss << ", ";
                }else{
                    ss << ") ";
                }
            }
            ss << "VALUES ";
            auto valuesString = [columnNamesCount]{
                std::stringstream ss;
                ss << "(";
                for(size_t i = 0; i < columnNamesCount; ++i) {
                    ss << "?";
                    if(i < columnNamesCount - 1) {
                        ss << ", ";
                    }else{
                        ss << ")";
                    }
                }
                return ss.str();
            }();
            auto valuesCount = static_cast<int>(std::distance(from, to));
            for(auto i = 0; i < valuesCount; ++i) {
                ss << valuesString;
                if(i < valuesCount - 1) {
                    ss << ",";
                }
                ss << " ";
            }
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                auto index = 1;
                for(auto it = from; it != to; ++it) {
                    auto &o = *it;
                    impl.table.for_each_column([&o, &index, &stmt] (auto c) {
                        if(!c.template has<constraints::primary_key_t<>>()){
                            typedef typename decltype(c)::field_type field_type;
                            const field_type *value = nullptr;
                            if(c.member_pointer){
                                value = &(o.*c.member_pointer);
                            }else{
                                value = &((o).*(c.getter))();
                            }
                            statement_binder<field_type>().bind(stmt, index++, *value);
                        }
                    });
                }
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    //..
                }else{
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else {
                auto msg = sqlite3_errmsg(connection->get_db());
                throw std::runtime_error(msg);
            }
        }
        
        void drop_index(const std::string &indexName) {
            auto connection = this->get_or_create_connection();
            std::stringstream ss;
            ss << "DROP INDEX '" << indexName + "'";
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    //  done..
                }else{
                    auto msg = sqlite3_errmsg(connection->get_db());
                    throw std::runtime_error(msg);
                }
            }else {
                auto msg = sqlite3_errmsg(connection->get_db());
                throw std::runtime_error(msg);
            }
        }
        
    protected:
        
        void drop_table_internal(const std::string &tableName, sqlite3 *db) {
            std::stringstream ss;
            ss << "DROP TABLE '" << tableName + "'";
            auto query = ss.str();
            sqlite3_stmt *stmt;
            if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
                statement_finalizer finalizer{stmt};
                if (sqlite3_step(stmt) == SQLITE_DONE) {
                    //  done..
                }else{
                    auto msg = sqlite3_errmsg(db);
                    throw std::runtime_error(msg);
                }
            }else {
                auto msg = sqlite3_errmsg(db);
                throw std::runtime_error(msg);
            }
        }
        
    public:
        
        /**
         *  Drops table with given name.
         */
        void drop_table(const std::string &tableName) {
            auto connection = this->get_or_create_connection();
            this->drop_table_internal(tableName, connection->get_db());
        }
        
        /**
         *  sqlite3_changes function.
         */
        int changes() {
            auto connection = this->get_or_create_connection();
            return sqlite3_changes(connection->get_db());
        }
        
        /**
         *  sqlite3_total_changes function.
         */
        int total_changes() {
            auto connection = this->get_or_create_connection();
            return sqlite3_total_changes(connection->get_db());
        }
        
        int64 last_insert_rowid() {
            auto connection = this->get_or_create_connection();
            return sqlite3_last_insert_rowid(connection->get_db());
        }
        
        /**
         *  Returns libsqltie3 lib version, not sqlite_orm
         */
        std::string libversion() {
            return sqlite3_libversion();
        }
        
    protected:
        
        template<class ...Tss, class ...Cols>
        sync_schema_result sync_table(storage_impl<internal::index_t<Cols...>, Tss...> *impl, sqlite3 *db, bool) {
            auto res = sync_schema_result::already_in_sync;
            std::stringstream ss;
            ss << "CREATE ";
            if(impl->table.unique){
                ss << "UNIQUE ";
            }
            typedef typename decltype(impl->table)::columns_type columns_type;
            typedef typename std::tuple_element<0, columns_type>::type head_t;
            typedef typename internal::table_type<head_t>::type indexed_type;
            ss << "INDEX IF NOT EXISTS " << impl->table.name << " ON '" << this->impl.template find_table_name<indexed_type>() << "' ( ";
            std::vector<std::string> columnNames;
            tuple_helper::iterator<std::tuple_size<columns_type>::value - 1, Cols...>()(impl->table.columns, [&columnNames, this](auto &v){
                columnNames.push_back(this->impl.column_name(v));
            });
            for(size_t i = 0; i < columnNames.size(); ++i) {
                ss << columnNames[i];
                if(i < columnNames.size() - 1) {
                    ss << ",";
                }
                ss << " ";
            }
            ss << ") ";
            auto query = ss.str();
            auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr);
            if(rc != SQLITE_OK) {
                auto msg = sqlite3_errmsg(db);
                throw std::runtime_error(msg);
            }
            return res;
        }
        
        template<class ...Tss, class ...Cs>
        sync_schema_result sync_table(storage_impl<table_t<Cs...>, Tss...> *impl, sqlite3 *db, bool preserve) {
            auto res = sync_schema_result::already_in_sync;
            
            auto schema_stat = impl->schema_status(db, preserve);
            if(schema_stat != decltype(schema_stat)::already_in_sync) {
                if(schema_stat == decltype(schema_stat)::new_table_created) {
                    this->create_table(db, impl->table.name, impl);
                    res = decltype(res)::new_table_created;
                }else{
                    if(schema_stat == sync_schema_result::old_columns_removed ||
                       schema_stat == sync_schema_result::new_columns_added ||
                       schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) {
                        
                        //  get table info provided in `make_table` call..
                        auto storageTableInfo = impl->table.get_table_info();
                        
                        //  now get current table info from db using `PRAGMA table_info` query..
                        auto dbTableInfo = impl->get_table_info(impl->table.name, db);
                        
                        //  this vector will contain pointers to columns that gotta be added..
                        std::vector<table_info*> columnsToAdd;
                        
                        impl->get_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo);
                        
                        
                        if(schema_stat == sync_schema_result::old_columns_removed) {
                            
                            //  extra table columns than storage columns
                            this->backup_table(db, impl);
                            res = decltype(res)::old_columns_removed;
                        }
                        
                        if(schema_stat == sync_schema_result::new_columns_added) {
                            for(auto columnPointer : columnsToAdd) {
                                impl->add_column(*columnPointer, db);
                            }
                            res = decltype(res)::new_columns_added;
                        }
                        
                        if(schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) {
                            
                            //remove extra columns
                            this->backup_table(db, impl);
                            for(auto columnPointer : columnsToAdd) {
                                impl->add_column(*columnPointer, db);
                            }
                            res = decltype(res)::new_columns_added_and_old_columns_removed;
                        }
                    } else if(schema_stat == sync_schema_result::dropped_and_recreated) {
                        this->drop_table_internal(impl->table.name, db);
                        this->create_table(db, impl->table.name, impl);
                        res = decltype(res)::dropped_and_recreated;
                    }
                }
            }
            return res;
        }
        
    public:
        
        /**
         *  This is a cute function used to replace migration up/down functionality.
         *  It performs check storage schema with actual db schema and:
         *  * if there are excess tables exist in db they are ignored (not dropped)
         *  * every table from storage is compared with it's db analog and
         *      * if table doesn't exist it is being created
         *      * if table exists its colums are being compared with table_info from db and
         *          * if there are columns in db that do not exist in storage (excess) table will be dropped and recreated
         *          * if there are columns in storage that do not exist in db they will be added using `ALTER TABLE ... ADD COLUMN ...' command
         *          * if there is any column existing in both db and storage but differs by any of properties/constraints (type, pk, notnull, dflt_value) table will be dropped and recreated
         *  Be aware that `sync_schema` doesn't guarantee that data will not be dropped. It guarantees only that it will make db schema the same
         *  as you specified in `make_storage` function call. A good point is that if you have no db file at all it will be created and
         *  all tables also will be created with exact tables and columns you specified in `make_storage`, `make_table` and `make_column` call.
         *  The best practice is to call this function right after storage creation.
         *  @param preserve affects on function behaviour in case it is needed to remove a column. If it is `false` so table will be dropped
         *  if there is column to remove, if `true` -  table is being copied into another table, dropped and copied table is renamed with source table name.
         *  Warning: sync_schema doesn't check foreign keys cause it is unable to do so in sqlite3. If you know how to get foreign key info
         *  please submit an issue https://github.com/fnc12/sqlite_orm/issues
         *  @return std::map with std::string key equal table name and `sync_schema_result` as value. `sync_schema_result` is a enum value that stores
         *  table state after syncing a schema. `sync_schema_result` can be printed out on std::ostream with `operator<<`.
         */
        std::map<std::string, sync_schema_result> sync_schema(bool preserve = false) {
            auto connection = this->get_or_create_connection();
            std::map<std::string, sync_schema_result> result;
            auto db = connection->get_db();
            this->impl.for_each([&result, db, preserve, this](auto impl){
                auto res = this->sync_table(impl, db, preserve);
                result.insert({impl->table.name, res});
            });
            return result;
        }
        
        /**
         *  This function returns the same map that `sync_schema` returns but it
         *  doesn't perform `sync_schema` actually - just simulates it in case you want to know
         *  what will happen if you sync your schema.
         */
        std::map<std::string, sync_schema_result> sync_schema_simulate(bool preserve = false) {
            auto connection = this->get_or_create_connection();
            std::map<std::string, sync_schema_result> result;
            auto db = connection->get_db();
            this->impl.for_each([&result, db, preserve](auto impl){
                result.insert({impl->table.name, impl->schema_status(db, preserve)});
            });
            return result;
        }
        
        bool transaction(std::function<bool()> f) {
            this->begin_transaction();
            auto db = this->currentTransaction->get_db();
            auto shouldCommit = f();
            if(shouldCommit){
                this->impl.commit(db);
            }else{
                this->impl.rollback(db);
            }
            if(!this->inMemory && !this->isOpenedForever){
                this->currentTransaction = nullptr;
            }
            return shouldCommit;
        }
        
        void begin_transaction() {
            if(!this->inMemory){
                if(!this->isOpenedForever){
                    if(this->currentTransaction) throw std::runtime_error("cannot start a transaction within a transaction");
                    this->currentTransaction = std::make_shared<internal::database_connection>(this->filename);
                    this->on_open_internal(this->currentTransaction->get_db());
                }
            }
            auto db = this->currentTransaction->get_db();
            this->impl.begin_transaction(db);
        }
        
        void commit() {
            if(!this->inMemory){
                if(!this->currentTransaction) throw std::runtime_error("cannot commit - no transaction is active");
            }
            auto db = this->currentTransaction->get_db();
            this->impl.commit(db);
            if(!this->inMemory && !this->isOpenedForever){
                this->currentTransaction = nullptr;
            }
        }
        
        void rollback() {
            if(!this->inMemory){
                if(!this->currentTransaction) throw std::runtime_error("cannot rollback - no transaction is active");
            }
            auto db = this->currentTransaction->get_db();
            this->impl.rollback(db);
            if(!this->inMemory && !this->isOpenedForever){
                this->currentTransaction = nullptr;
            }
        }
        
        std::string current_timestamp() {
            auto connection = this->get_or_create_connection();
            return this->impl.current_timestamp(connection->get_db());
        }
        
    protected:

#if SQLITE_VERSION_NUMBER >= 3006019

        void foreign_keys(sqlite3 *db, bool value) {
            std::stringstream ss;
            ss << "PRAGMA foreign_keys = " << value;
            auto query = ss.str();
            auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr);
            if(rc != SQLITE_OK) {
                auto msg = sqlite3_errmsg(db);
                throw std::runtime_error(msg);
            }
        }
        
        bool foreign_keys(sqlite3 *db) {
            std::string query = "PRAGMA foreign_keys";
            auto res = false;
            auto rc = sqlite3_exec(db,
                                   query.c_str(),
                                   [](void *data, int argc, char **argv,char **) -> int {
                                       auto &res = *(bool*)data;
                                       if(argc){
                                           res = row_extractor<bool>().extract(argv[0]);
                                       }
                                       return 0;
                                   }, &res, nullptr);
            if(rc != SQLITE_OK) {
                auto msg = sqlite3_errmsg(db);
                throw std::runtime_error(msg);
            }
            return res;
        }

#endif

    public:

#if SQLITE_VERSION_NUMBER >= 3007010
/**
* \fn db_release_memory
* \brief Releases freeable memory of database. It is function can/should be called periodically by application,
* if application has less memory usage constraint.
* \note sqlite3_db_release_memory added in 3.7.10 https://sqlite.org/changes.html
*/
int db_release_memory() {
auto connection = this->get_or_create_connection();
return sqlite3_db_release_memory(connection->get_db());
}
#endif

        /**
         *  Checks whether table exists in db. Doesn't check storage itself - works only with actual database.
         *  Note: table can be not mapped to a storage
         *  @return true if table with a given name exists in db, false otherwise.
         */
        bool table_exists(const std::string &tableName) {
            auto connection = this->get_or_create_connection();
            return this->impl.table_exists(tableName, connection->get_db());
        }
        
        /**
         *  Returns existing permanent table names in database. Doesn't check storage itself - works only with actual database.
         *  @return Returns list of tables in database.
         */
        std::vector<std::string> table_names() {
            auto connection = this->get_or_create_connection();
            std::vector<std::string> tableNames;
            std::string sql = std::string("SELECT name FROM sqlite_master WHERE type='table'");
            typedef std::vector<std::string> Data;
            int res = sqlite3_exec(connection->get_db(), sql.c_str(),
                                   [] (void *data, int argc, char **argv, char **/*columnName*/) -> int {
                                       auto& tableNames = *(Data*)data;
                                       for(int i = 0; i < argc; i++) {
                                           if(argv[i]){
                                               tableNames.push_back(argv[i]);
                                           }
                                       }
                                       return 0;
                                   }, &tableNames,nullptr);
            
            if(res != SQLITE_OK) {
                auto msg = sqlite3_errmsg(connection->get_db());
                throw std::runtime_error(msg);
            }
            return tableNames;
        }
        
        void open_forever() {
            this->isOpenedForever = true;
            if(!this->currentTransaction){
                this->currentTransaction = std::make_shared<internal::database_connection>(this->filename);
                this->on_open_internal(this->currentTransaction->get_db());
            }
        }
        
        
    protected:
        std::string filename;
        impl_type impl;
        std::shared_ptr<internal::database_connection> currentTransaction;
        const bool inMemory;
        bool isOpenedForever = false;
    public:
        pragma_t pragma;
    };
}

template<class ...Ts>
internal::storage_t<Ts...> make_storage(const std::string &filename, Ts ...tables) {
    return {filename, internal::storage_impl<Ts...>(tables...)};
}

}

#if defined(_MSC_VER)

if defined(RESTORE_MIN)

__pragma(pop_macro("min"))

undef RESTORE_MIN

endif

if defined(RESTORE_MAX)

__pragma(pop_macro("max"))

undef RESTORE_MAX

endif

#endif // defined(_MSC_VER)

#endif /* sqlite_orm_h */
`

Transaction commit and rollback

Thanks for providing commit and rollback of transactions.
I found some deficiencies in the features. They may keep database in perpetually locked state.

  1. Possibilty of no excecution of either rollback or commit after begin_transaction(e.g. due to exception thrown after begin_transaction and before rollback or commit). Is it possible for you to create small Transaction class will rollback transaction in its destructor if it is not commited?
  2. commit can fail (https://sqlite.org/lang_transaction.html). There is no way to know if commit was successful. Can you return success or failure from commit method?

Functionality request in sync_schema

Hello,

I am looking for strict migration procedure. sync_schema is not best option for me. If the database table schema and application schema do not match then, it is error condition. The error condition can be caused by unauthorised user manipulation or mismatch between application version and DB version(e.g. due to application update/upgrade).

  • Incase of unauthorised user access should abort application without modifying anything. User should take care of it.
  • Incase of mismatch between application version and DB version, sync_schema should backup old table and create new table from old content. The newly added database columns should be filled with specific values(not default values.)
    Current sync_schema does not give any feedback about its operations carried out.
  1. schema version for each table or at least database. int Storage.GetVersion(), int DB.GetTableVersion(std::string TableName). https://stackoverflow.com/questions/989558/best-practices-for-in-app-database-migration-for-sqlite. Will you consider making a system table which can store database properties like schema version of table(s)(and owner application name, description etc if possible). There are two options, either I should create a table and give these values or you can create a table and version number & other information can be given in make_storage. What is your opinion?
  2. boolean Storage.IsTablePresent(std::string TableName). This table name is one of from makestorage.
  3. boolean Storage.IsTableSchemaSameAsCPP(std::string TableName). This table name is one of from makestorage. This function will compare table schema and schema given in makestorage function.

Composite key

I want to use composite key which consists of 2 columns. Is there any way to specify in table creation?
If yes, please give me syntax of the command.

I wish you happy weekend.

post-increment operator for statement_binder<T>

First off, thanks for the work you've put in so far! I'm having a stab at using this project as the lib for implementing sqlite into a side project.

The statement_binder struct templates bind functions do a post-increment on int index that's passed by value:

https://github.com/fnc12/sqlite_orm/tree/9fd5032713d94dc0f58458578659b014f721486c/include/sqlite_orm/sqlite_orm.h#L1358-#L1423

So aren't all these post-incremented values of index not used, or is it late and am i seeing things not clearly?

General improvements & tweaks for MSVC

This is almost a copy/paste from reddit, as requested.

General improvements:

  • Don't use exception specification anymore, it's deprecated.
  • Your use of std::enable_if in template arg is wrong - it should be class = std::enable_if_t<cond> or std::enable_if_t<cond, bool> = true, I suggest the latter.
  • Don't use template keyword if the method does not follow by <..>, e.g. obj.f(...) instead of obj.template f(...).
  • Mark method const if it is, even if it's constexpr.
  • You forgot to include <cctype>.

Tweaks for MSVC:

  • Don't name a variable with the something that's already declared. e.g. you have template table and local variable table, this confuses MSVC.
  • Don't use decltype(this), use the type directly.
  • Explicitly capture the variables in lambda expr, otherwise MSVC will have problem in finding the variables.

handling of busy/lock

Hello,

I got std::runtime_error when on other process also uses the database (read only) and I want to commit my changes. This is absolutely OK according the sqlite3 specification. But I would like to get an interface that gives a way to retry the commit in this case.

I write it and send you a pull request, just I don't know what should be the good solution(s)?
Currently sqlite_orm starts deferred transactions (all lock get as late as possible). So you will get exception at commit.

As a first draft, I intoroduced a new exception (database_locked_exception) and commit throws this exception if the error code is SQLITE_LOCKED or SQLITE_BUSY. In this case I get the next user code:

auto guard = storage.transaction_guard();
...
for(;;) {
    try {
        guard.commit();
        break;
    } catch(const sqlite_orm::database_locked_exception& ex) {
        LOG("Database busy, wait a bit and retry");
        //sleep or usleep here
    }
}

This is very ugly in the view of users, furthermore you use std::runtime_error everywhere so probably in this case would be nice to define a root exception for sqlite_orm and the locked can inherit from that.

An alternative solution for this (and I think it is better) that commit can get a timeout parameter, and this retrying is done by commit itself. If time spent and the commit failed all times then we send the exception.

My problem with the two solutions above that I don't see what helps to avoid the starvation (||: reader arrives, we try -> error, sleep, reader gone :||). I think nothing.

An other solution for this (but I don't deeply understand the description of sqlite3), that sqlite_orm enable to set the type of the transaction (deferred, Immediate, exclusive). But I'm not sure that we will blocked until we can acquire the lock or we get busy in the same way.

Maybe you have better general idea for proper handling of this locking. But I think using specialized exceptions makes the library better since there can be situation when you would like to do other things in case of sql error.

No apparent way to issue PRAGMA commands

Thank you so much for sqlite_orm, it is a LOT of fun! Using it for some analysis now that I would otherwise never have been able to do safely.
So question, I need to insert millions of objects quickly, and I'd like to issue things like "PRAGMA synchronous = OFF".
I have looked for ways to get_db() the handle out of sqlite_orm, but it is not obvious if you can do that from storage_t. I have also looked for something like ::raw_query() but can't find it.
I can also implement this feature, but would like your guidance on where this should 'live'.

I added a simple pragma() above libversion():

        void pragma(const std::string& str) {
            std::shared_ptr<internal::database_connection> connection;
            sqlite3 *db;
            if(!this->currentTransaction){
                connection = std::make_shared<internal::database_connection>(this->filename);
                db = connection->get_db();
                this->on_open_internal(db);
            }else{
                db = this->currentTransaction->get_db();
            }
            int rc = sqlite3_exec(db, ("PRAGMA "+str).c_str(), nullptr, nullptr, nullptr);
            if(rc != SQLITE_OK) {
                auto msg = sqlite3_errmsg(db);
                throw std::runtime_error(msg);
            }
        }

Is this an idea?

Indexes and foreign keys

Hi,

Thanks for creating this library! It is intuitive and easy to use.
Just want to find out if there are any plans to add support for foreign keys and indexing?

MSVC Compile error

hi,

First of all, I apologize for my poor English ability.

I have some problem in the process of integrating into my tbag project using this source code.
MSVC encountered a compiler error.

I modified it as follows:

diff --git a/libtbag/3rd/sqlite_orm/sqlite_orm.h b/libtbag/3rd/sqlite_orm/sqlite_orm.h
index cf319196..1f9d46f0 100644
--- a/libtbag/3rd/sqlite_orm/sqlite_orm.h
+++ b/libtbag/3rd/sqlite_orm/sqlite_orm.h
@@ -21,6 +21,19 @@
 #include <ostream>  //  std::ostream
 #include <iterator> //  std::iterator_traits
 
+#if defined(_MSC_VER)
+# if defined(min)
+__pragma(push_macro("min"))
+# undef min
+# define __RESTORE_MIN__
+# endif
+# if defined(max)
+__pragma(push_macro("max"))
+# undef max
+# define __RESTORE_MAX__
+# endif
+#endif // defined(_MSC_VER)
+
 namespace sqlite_orm {
     
     typedef sqlite_int64 int64;
@@ -3742,7 +3755,7 @@ namespace sqlite_orm {
                 }
                 
                 void operator++() {
-                    if(this->stmt and *this->stmt){
+                    if(this->stmt && *this->stmt){
                         auto ret = sqlite3_step(*this->stmt);
                         switch(ret){
                             case SQLITE_ROW:
@@ -3767,10 +3780,10 @@ namespace sqlite_orm {
                 
 				//removed const return type to remove complier warning
                 bool operator==(const iterator_t &other) const {
-                    if(this->stmt and other.stmt){
+                    if(this->stmt && other.stmt){
                         return *this->stmt == *other.stmt;
                     }else{
-                        if(!this->stmt and !other.stmt){
+                        if(!this->stmt && !other.stmt){
                             return true;
                         }else{
                             return false;
@@ -3868,7 +3881,7 @@ namespace sqlite_orm {
         storage_t(const std::string &filename_, impl_type impl_):
         filename(filename_),
         impl(impl_),
-        inMemory(filename_.empty() or filename_ == ":memory:"){
+        inMemory(filename_.empty() || filename_ == ":memory:"){
             if(inMemory){
                 this->currentTransaction = std::make_shared<internal::database_connection>(this->filename);
                 this->on_open_internal(this->currentTransaction->get_db());
@@ -4012,7 +4025,7 @@ namespace sqlite_orm {
         template<class T>
         std::string string_from_expression(T t, bool /*noTableName*/ = false, bool escape = false) {
             auto isNullable = type_is_nullable<T>::value;
-            if(isNullable and !type_is_nullable<T>()(t)){
+            if(isNullable && !type_is_nullable<T>()(t)){
                 return "NULL";
             }else{
                 auto needQuotes = std::is_base_of<text_printer, type_printer<T>>::value;
@@ -5197,7 +5210,7 @@ namespace sqlite_orm {
             }
             ss << "FROM '" << impl.table.name << "' WHERE ";
             auto primaryKeyColumnNames = impl.table.primary_key_column_names();
-            if(primaryKeyColumnNames.size() and primaryKeyColumnNames.front().length()){
+            if(primaryKeyColumnNames.size() && primaryKeyColumnNames.front().length()){
 //                ss << "\"" << primaryKeyColumnNames.front() << "\"" << " = " << string_from_expression(id);
                 for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) {
                     ss << "\"" << primaryKeyColumnNames[i] << "\"" << " = ? ";
@@ -6090,7 +6103,7 @@ namespace sqlite_orm {
             std::vector<std::string> columnNames;
             auto compositeKeyColumnNames = impl.table.composite_key_columns_names();
             impl.table.for_each_column([&impl, &columnNames, &compositeKeyColumnNames] (auto c) {
-                if(impl.table._without_rowid or !c.template has<constraints::primary_key_t<>>()) {
+                if(impl.table._without_rowid || !c.template has<constraints::primary_key_t<>>()) {
                     auto it = std::find(compositeKeyColumnNames.begin(),
                                         compositeKeyColumnNames.end(),
                                         c.name);
@@ -6124,7 +6137,7 @@ namespace sqlite_orm {
                 statement_finalizer finalizer{stmt};
                 auto index = 1;
                 impl.table.for_each_column([&o, &index, &stmt, &impl, &compositeKeyColumnNames] (auto c) {
-                    if(impl.table._without_rowid or !c.template has<constraints::primary_key_t<>>()){
+                    if(impl.table._without_rowid || !c.template has<constraints::primary_key_t<>>()){
                         auto it = std::find(compositeKeyColumnNames.begin(),
                                             compositeKeyColumnNames.end(),
                                             c.name);
@@ -6758,4 +6771,15 @@ namespace sqlite_orm {
     
 }
 
+#if defined(_MSC_VER)
+# if defined(__RESTORE_MIN__)
+__pragma(pop_macro("min"))
+# undef __RESTORE_MIN__
+# endif
+# if defined(__RESTORE_MAX__)
+__pragma(pop_macro("max"))
+# undef __RESTORE_MAX__
+# endif
+#endif // defined(_MSC_VER)
+
 #endif /* sqlite_orm_h */

What do you think?

see also:
https://ci.appveyor.com/project/osom8979/tbag/build/1.0.392
osom8979/tbag@2bf0fc0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.