{"id":13418816,"url":"https://github.com/fnc12/sqlite_orm","last_synced_at":"2025-05-14T01:10:08.498Z","repository":{"id":37561809,"uuid":"76951323","full_name":"fnc12/sqlite_orm","owner":"fnc12","description":"❤️ SQLite ORM light header only library for modern C++","archived":false,"fork":false,"pushed_at":"2024-10-02T14:55:03.000Z","size":9139,"stargazers_count":2282,"open_issues_count":28,"forks_count":316,"subscribers_count":66,"default_branch":"master","last_synced_at":"2024-10-29T15:09:16.655Z","etag":null,"topics":["cplusplus","cplusplus-14","cpp","crud","modern-cpp","orm","sql","sqlite","sqlite-orm","sqlite3","sqlite3-database","sqliteorm"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fnc12.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"fnc12","custom":"https://paypal.me/fnc12"}},"created_at":"2016-12-20T11:35:20.000Z","updated_at":"2024-10-24T08:26:03.000Z","dependencies_parsed_at":"2023-09-24T21:56:28.445Z","dependency_job_id":"cdb1a000-1e18-4389-9f9f-bbac2f1aff54","html_url":"https://github.com/fnc12/sqlite_orm","commit_stats":{"total_commits":1898,"total_committers":46,"mean_commits":41.26086956521739,"dds":0.6296101159114857,"last_synced_commit":"ff7c878af872f7987b62d825284fb25d6043af10"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnc12%2Fsqlite_orm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnc12%2Fsqlite_orm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnc12%2Fsqlite_orm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnc12%2Fsqlite_orm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fnc12","download_url":"https://codeload.github.com/fnc12/sqlite_orm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248353135,"owners_count":21089562,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cplusplus","cplusplus-14","cpp","crud","modern-cpp","orm","sql","sqlite","sqlite-orm","sqlite3","sqlite3-database","sqliteorm"],"created_at":"2024-07-30T22:01:07.466Z","updated_at":"2025-04-11T06:25:05.105Z","avatar_url":"https://github.com/fnc12.png","language":"C++","funding_links":["https://github.com/sponsors/fnc12","https://paypal.me/fnc12","https://patreon.com/fnc12"],"categories":["TODO scan for Android support in followings","Database","Networking","C++","Libraries","\u003ca name=\"C%2B%2B\"\u003e\u003c/a\u003eC++","\u003e 100 ⭐️","wrapper/ORM"],"sub_categories":["Database"],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/fnc12/sqlite_orm/blob/master/logo.png\" alt=\"Sublime's custom image\" width=\"557\"/\u003e\n\u003c/p\u003e\n\n[![C++](https://img.shields.io/badge/c++-%2300599C.svg?style=for-the-badge\u0026logo=c%2B%2B\u0026logoColor=white)](https://en.cppreference.com/w/)\n[![SQLite](https://img.shields.io/badge/sqlite-%2307405e.svg?style=for-the-badge\u0026logo=sqlite\u0026logoColor=white)](https://www.sqlite.org/index.html)\n[![](https://dcbadge.limes.pink/api/server/https://discord.gg/kBM4cPZx6a)](https://discord.gg/https://discord.gg/kBM4cPZx6a)\n[![GitHub Actions](https://img.shields.io/badge/githubactions-%232671E5.svg?style=for-the-badge\u0026logo=githubactions\u0026logoColor=white)](https://github.com/fnc12/sqlite_orm/actions)\n[![CMake](https://img.shields.io/badge/CMake-%23008FBA.svg?style=for-the-badge\u0026logo=cmake\u0026logoColor=white)](https://github.com/fnc12/sqlite_orm/blob/dev/CMakeLists.txt)\n[![Stack Overflow](https://img.shields.io/badge/-Stackoverflow-FE7A16?style=for-the-badge\u0026logo=stack-overflow\u0026logoColor=white)](https://stackoverflow.com/search?q=sqlite_orm)\n[![PayPal](https://img.shields.io/badge/PayPal-00457C?style=for-the-badge\u0026logo=paypal\u0026logoColor=white)](https://paypal.me/fnc12)\n[![Twitter](https://img.shields.io/badge/sqlite_orm-%231DA1F2.svg?style=for-the-badge\u0026logo=Twitter\u0026logoColor=white)](https://twitter.com/sqlite_orm)\n[![Patreon](https://img.shields.io/badge/Patreon-F96854?style=for-the-badge\u0026logo=patreon\u0026logoColor=white)](https://patreon.com/fnc12)\n\n# SQLite ORM\nSQLite 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](https://paypal.me/fnc12) or any different way (contact using email fnc12@me.com)).\n\n# Status\n| Branch | Travis | Appveyor |\n| :----- | :----- | :------- |\n| [`master`](https://github.com/fnc12/sqlite_orm/tree/master) | [![Build Status](https://travis-ci.org/fnc12/sqlite_orm.svg?branch=master)](https://travis-ci.org/fnc12/sqlite_orm) | [![Build status](https://ci.appveyor.com/api/projects/status/github/fnc12/sqlite_orm?branch=master\u0026svg=true)](https://ci.appveyor.com/project/fnc12/sqlite-orm/history) | | | [![Website](https://img.shields.io/badge/official-website-brightgreen.svg)](https://github.com/fnc12/sqlite_orm/) |\n| [`dev`](https://github.com/fnc12/sqlite_orm/tree/dev) | [![Build Status](https://travis-ci.org/fnc12/sqlite_orm.svg?branch=dev)](https://travis-ci.org/fnc12/sqlite_orm) | [![Build status](https://ci.appveyor.com/api/projects/status/github/fnc12/sqlite_orm?branch=dev\u0026svg=true)](https://ci.appveyor.com/project/fnc12/sqlite-orm/history) | | | [![Website](https://img.shields.io/badge/official-website-brightgreen.svg)](https://github.com/fnc12/sqlite_orm/tree/dev) |\n\n# Advantages\n\n* **No raw string queries**\n* **Intuitive syntax**\n* **Comfortable interface - one code line per single query**\n* **Built with modern C++14/C++17/C++20 features (no macros and external scripts)**\n* **CRUD support**\n* **Pure select query support**\n* **Prepared statements support**\n* **UNION, EXCEPT and INTERSECT support**\n* **STL compatible**\n* **Custom types binding support**\n* **BLOB support** - maps to `std::vector\u003cchar\u003e` or one can bind your custom type\n* **FOREIGN KEY support**\n* **Composite key support**\n* **JOIN support**\n* **Transactions support**\n* **Migrations functionality**\n* **Powerful conditions**\n* **ORDER BY and LIMIT, OFFSET support**\n* **GROUP BY / DISTINCT support**\n* **INDEX support**\n* **Follows single responsibility principle** - no need write code inside your data model classes\n* **Easy integration** - single header only lib.\n* **The only dependency** - libsqlite3\n* **C++ standard code style**\n* **In memory database support** - provide `:memory:` or empty filename\n* **COLLATE support**\n* **Limits setting/getting support**\n* **User defined functions support**\n\n`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:\n\n```c++\n\nstruct User{\n    int id;\n    std::string firstName;\n    std::string lastName;\n    int birthDate;\n    std::unique_ptr\u003cstd::string\u003e imageUrl;\n    int typeId;\n};\n\nstruct UserType {\n    int id;\n    std::string name;\n};\n\n```\n\nSo we have database with predefined schema like \n\n`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)`\n\n`CREATE TABLE user_types (id integer primary key autoincrement, name text not null DEFAULT 'name_placeholder')`\n\nNow 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.\n\n```c++\n\nusing namespace sqlite_orm;\nauto storage = make_storage(\"db.sqlite\",\n                            make_table(\"users\",\n                                       make_column(\"id\", \u0026User::id, primary_key().autoincrement()),\n                                       make_column(\"first_name\", \u0026User::firstName),\n                                       make_column(\"last_name\", \u0026User::lastName),\n                                       make_column(\"birth_date\", \u0026User::birthDate),\n                                       make_column(\"image_url\", \u0026User::imageUrl),\n                                       make_column(\"type_id\", \u0026User::typeId)),\n                            make_table(\"user_types\",\n                                       make_column(\"id\", \u0026UserType::id, primary_key().autoincrement()),\n                                       make_column(\"name\", \u0026UserType::name, default_value(\"name_placeholder\"))));\n```\n\nToo 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: `\u0026User::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`/`null` are deduced from type automatically but can be added manually if you wish with `null()` and `not_null()`).\n\nMore details about making storage can be found in [tutorial](https://github.com/fnc12/sqlite_orm/wiki/Making-a-storage).\n\nIf 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](https://github.com/fnc12/sqlite_orm/blob/master/examples/private_class_members.cpp).\n\n# CRUD\n\nLet'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.\n\n```c++\nUser user{-1, \"Jonh\", \"Doe\", 664416000, std::make_unique\u003cstd::string\u003e(\"url_to_heaven\"), 3 };\n    \nauto insertedId = storage.insert(user);\ncout \u003c\u003c \"insertedId = \" \u003c\u003c insertedId \u003c\u003c endl;      //  insertedId = 8\nuser.id = insertedId;\n\nUser secondUser{-1, \"Alice\", \"Inwonder\", 831168000, {} , 2};\ninsertedId = storage.insert(secondUser);\nsecondUser.id = insertedId;\n\n```\n\nNote: if we need to insert a new user with specified id call `storage.replace(user);` instead of `insert`.\n\nNext let's get our user by id.\n\n```c++\ntry{\n    auto user = storage.get\u003cUser\u003e(insertedId);\n    cout \u003c\u003c \"user = \" \u003c\u003c user.firstName \u003c\u003c \" \" \u003c\u003c user.lastName \u003c\u003c endl;\n}catch(std::system_error e) {\n    cout \u003c\u003c e.what() \u003c\u003c endl;\n}catch(...){\n    cout \u003c\u003c \"unknown exeption\" \u003c\u003c endl;\n}\n```\n\nProbably 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`.\n\n```c++\nif(auto user = storage.get_pointer\u003cUser\u003e(insertedId)){\n    cout \u003c\u003c \"user = \" \u003c\u003c user-\u003efirstName \u003c\u003c \" \" \u003c\u003c user-\u003elastName \u003c\u003c endl;\n}else{\n    cout \u003c\u003c \"no user with id \" \u003c\u003c insertedId \u003c\u003c endl;\n}\n```\n\n`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`.\n\nWe 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`\n\n```c++\nuser.firstName = \"Nicholas\";\nuser.imageUrl = \"https://cdn1.iconfinder.com/data/icons/man-icon-set/100/man_icon-21-512.png\"\nstorage.update(user);\n```\n\nAlso there is a non-CRUD update version `update_all`:\n\n```c++\nstorage.update_all(set(c(\u0026User::lastName) = \"Hardey\",\n                       c(\u0026User::typeId) = 2),\n                   where(c(\u0026User::firstName) == \"Tom\"));\n```\n\nAnd 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++.\n\n```c++\nstorage.remove\u003cUser\u003e(insertedId)\n```\n\nAlso we can extract all objects into `std::vector`.\n\n```c++\nauto allUsers = storage.get_all\u003cUser\u003e();\ncout \u003c\u003c \"allUsers (\" \u003c\u003c allUsers.size() \u003c\u003c \"):\" \u003c\u003c endl;\nfor(auto \u0026user : allUsers) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c 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' }\n}\n```\n\nAnd one can specify return container type explicitly: let's get all users in `std::list`, not `std::vector`:\n\n```c++\nauto allUsersList = storage.get_all\u003cUser, std::list\u003cUser\u003e\u003e();\n```\n\nContainer must be STL compatible (must have `push_back(T\u0026\u0026)` function in this case).\n\n`get_all` can be too heavy for memory so you can iterate row by row (i.e. object by object):\n\n```c++\nfor(auto \u0026user : storage.iterate\u003cUser\u003e()) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n```\n\n`iterate` member function returns adapter object that has `begin` and `end` member functions returning iterators that fetch object on dereference operator call.\n\nCRUD 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.\n\n# Prepared statements\n\nPrepared statements are strongly typed.\n\n```c++\n//  SELECT doctor_id\n//  FROM visits\n//  WHERE LENGTH(patient_name) \u003e 8\nauto selectStatement = storage.prepare(select(\u0026Visit::doctor_id, where(length(\u0026Visit::patient_name) \u003e 8)));\ncout \u003c\u003c \"selectStatement = \" \u003c\u003c selectStatement.sql() \u003c\u003c endl;  //  prints \"SELECT doctor_id FROM ...\"\nauto rows = storage.execute(selectStatement); //  rows is std::vector\u003cdecltype(Visit::doctor_id)\u003e\n\n//  SELECT doctor_id\n//  FROM visits\n//  WHERE LENGTH(patient_name) \u003e 11\nget\u003c0\u003e(selectStatement) = 11;\nauto rows2 = storage.execute(selectStatement);\n```\n`get\u003cN\u003e(statement)` function call allows you to access fields to bind them to your statement.\n\n# Aggregate Functions\n\n```c++\n//  SELECT AVG(id) FROM users\nauto averageId = storage.avg(\u0026User::id);    \ncout \u003c\u003c \"averageId = \" \u003c\u003c averageId \u003c\u003c endl;        //  averageId = 4.5\n    \n//  SELECT AVG(birth_date) FROM users\nauto averageBirthDate = storage.avg(\u0026User::birthDate);  \ncout \u003c\u003c \"averageBirthDate = \" \u003c\u003c averageBirthDate \u003c\u003c endl;      //  averageBirthDate = 6.64416e+08\n  \n//  SELECT COUNT(*) FROM users\nauto usersCount = storage.count\u003cUser\u003e();    \ncout \u003c\u003c \"users count = \" \u003c\u003c usersCount \u003c\u003c endl;     //  users count = 8\n\n//  SELECT COUNT(id) FROM users\nauto countId = storage.count(\u0026User::id);    \ncout \u003c\u003c \"countId = \" \u003c\u003c countId \u003c\u003c endl;        //  countId = 8\n\n//  SELECT COUNT(image_url) FROM users\nauto countImageUrl = storage.count(\u0026User::imageUrl);   \ncout \u003c\u003c \"countImageUrl = \" \u003c\u003c countImageUrl \u003c\u003c endl;      //  countImageUrl = 5\n\n//  SELECT GROUP_CONCAT(id) FROM users\nauto concatedUserId = storage.group_concat(\u0026User::id);      \ncout \u003c\u003c \"concatedUserId = \" \u003c\u003c concatedUserId \u003c\u003c endl;      //  concatedUserId = 1,2,3,4,5,6,7,8\n\n//  SELECT GROUP_CONCAT(id, \"---\") FROM users\nauto concatedUserIdWithDashes = storage.group_concat(\u0026User::id, \"---\");     \ncout \u003c\u003c \"concatedUserIdWithDashes = \" \u003c\u003c concatedUserIdWithDashes \u003c\u003c endl;      //  concatedUserIdWithDashes = 1---2---3---4---5---6---7---8\n\n//  SELECT MAX(id) FROM users\nif(auto maxId = storage.max(\u0026User::id)){    \n    cout \u003c\u003c \"maxId = \" \u003c\u003c *maxId \u003c\u003cendl;    //  maxId = 12  (maxId is std::unique_ptr\u003cint\u003e)\n}else{\n    cout \u003c\u003c \"maxId is null\" \u003c\u003c endl;\n}\n    \n//  SELECT MAX(first_name) FROM users\nif(auto maxFirstName = storage.max(\u0026User::firstName)){ \n    cout \u003c\u003c \"maxFirstName = \" \u003c\u003c *maxFirstName \u003c\u003c endl; //  maxFirstName = Jonh (maxFirstName is std::unique_ptr\u003cstd::string\u003e)\n}else{\n    cout \u003c\u003c \"maxFirstName is null\" \u003c\u003c endl;\n}\n\n//  SELECT MIN(id) FROM users\nif(auto minId = storage.min(\u0026User::id)){    \n    cout \u003c\u003c \"minId = \" \u003c\u003c *minId \u003c\u003c endl;   //  minId = 1 (minId is std::unique_ptr\u003cint\u003e)\n}else{\n    cout \u003c\u003c \"minId is null\" \u003c\u003c endl;\n}\n\n//  SELECT MIN(last_name) FROM users\nif(auto minLastName = storage.min(\u0026User::lastName)){\n    cout \u003c\u003c \"minLastName = \" \u003c\u003c *minLastName \u003c\u003c endl;   //  minLastName = Doe\n}else{\n    cout \u003c\u003c \"minLastName is null\" \u003c\u003c endl;\n}\n\n//  SELECT SUM(id) FROM users\nif(auto sumId = storage.sum(\u0026User::id)){    //  sumId is std::unique_ptr\u003cint\u003e\n    cout \u003c\u003c \"sumId = \" \u003c\u003c *sumId \u003c\u003c endl;\n}else{\n    cout \u003c\u003c \"sumId is null\" \u003c\u003c endl;\n}\n\n//  SELECT TOTAL(id) FROM users\nauto totalId = storage.total(\u0026User::id);\ncout \u003c\u003c \"totalId = \" \u003c\u003c totalId \u003c\u003c endl;    //  totalId is double (always)\n```\n\n# Where conditions\n\nYou also can select objects with custom where conditions with `=`, `!=`, `\u003e`, `\u003e=`, `\u003c`, `\u003c=`, `IN`, `BETWEEN` and `LIKE`.\n\nFor example: let's select users with id lesser than 10:\n\n```c++\n//  SELECT * FROM users WHERE id \u003c 10\nauto idLesserThan10 = storage.get_all\u003cUser\u003e(where(c(\u0026User::id) \u003c 10));\ncout \u003c\u003c \"idLesserThan10 count = \" \u003c\u003c idLesserThan10.size() \u003c\u003c endl;\nfor(auto \u0026user : idLesserThan10) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n```\n\nOr select all users who's first name is not equal \"John\":\n\n```c++\n//  SELECT * FROM users WHERE first_name != 'John'\nauto notJohn = storage.get_all\u003cUser\u003e(where(c(\u0026User::firstName) != \"John\"));\ncout \u003c\u003c \"notJohn count = \" \u003c\u003c notJohn.size() \u003c\u003c endl;\nfor(auto \u0026user : notJohn) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n```\n\nBy the way one can implement not equal in a different way using C++ negation operator:\n\n```c++\nauto notJohn2 = storage.get_all\u003cUser\u003e(where(not (c(\u0026User::firstName) == \"John\")));\n```\n\nYou 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 \u003e= 5 and id \u003c= 7 and not id = 6`:\n\n```c++\nauto id5and7 = storage.get_all\u003cUser\u003e(where(c(\u0026User::id) \u003c= 7 and c(\u0026User::id) \u003e= 5 and not (c(\u0026User::id) == 6)));\ncout \u003c\u003c \"id5and7 count = \" \u003c\u003c id5and7.size() \u003c\u003c endl;\nfor(auto \u0026user : id5and7) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n```\n\nOr let's just export two users with id 10 or id 16 (of course if these users exist):\n\n```c++\nauto id10or16 = storage.get_all\u003cUser\u003e(where(c(\u0026User::id) == 10 or c(\u0026User::id) == 16));\ncout \u003c\u003c \"id10or16 count = \" \u003c\u003c id10or16.size() \u003c\u003c endl;\nfor(auto \u0026user : id10or16) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n```\n\nIn 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!\n\nMoreover you can use parentheses to set the priority of query conditions:\n\n```c++\nauto cuteConditions = storage.get_all\u003cUser\u003e(where((c(\u0026User::firstName) == \"John\" or c(\u0026User::firstName) == \"Alex\") and c(\u0026User::id) == 4));  //  where (first_name = 'John' or first_name = 'Alex') and id = 4\ncout \u003c\u003c \"cuteConditions count = \" \u003c\u003c cuteConditions.size() \u003c\u003c endl; //  cuteConditions count = 1\ncuteConditions = storage.get_all\u003cUser\u003e(where(c(\u0026User::firstName) == \"John\" or (c(\u0026User::firstName) == \"Alex\" and c(\u0026User::id) == 4)));   //  where first_name = 'John' or (first_name = 'Alex' and id = 4)\ncout \u003c\u003c \"cuteConditions count = \" \u003c\u003c cuteConditions.size() \u003c\u003c endl; //  cuteConditions count = 2\n```\n\nAlso we can implement `get` by id with `get_all` and `where` like this:\n\n```c++\n//  SELECT * FROM users WHERE ( 2 = id )\nauto idEquals2 = storage.get_all\u003cUser\u003e(where(2 == c(\u0026User::id)));\ncout \u003c\u003c \"idEquals2 count = \" \u003c\u003c idEquals2.size() \u003c\u003c endl;\nif(idEquals2.size()){\n    cout \u003c\u003c storage.dump(idEquals2.front()) \u003c\u003c endl;\n}else{\n    cout \u003c\u003c \"user with id 2 doesn't exist\" \u003c\u003c endl;\n}\n```\n\nLets try the `IN` operator:\n\n```c++\n//  SELECT * FROM users WHERE id IN (2, 4, 6, 8, 10)\nauto evenLesserTen10 = storage.get_all\u003cUser\u003e(where(in(\u0026User::id, {2, 4, 6, 8, 10})));\ncout \u003c\u003c \"evenLesserTen10 count = \" \u003c\u003c evenLesserTen10.size() \u003c\u003c endl;\nfor(auto \u0026user : evenLesserTen10) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n\n//  SELECT * FROM users WHERE last_name IN (\"Doe\", \"White\")\nauto doesAndWhites = storage.get_all\u003cUser\u003e(where(in(\u0026User::lastName, {\"Doe\", \"White\"})));\ncout \u003c\u003c \"doesAndWhites count = \" \u003c\u003c doesAndWhites.size() \u003c\u003c endl;\nfor(auto \u0026user : doesAndWhites) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n```\n\nAnd `BETWEEN`:\n\n```c++\n//  SELECT * FROM users WHERE id BETWEEN 66 AND 68\nauto betweenId = storage.get_all\u003cUser\u003e(where(between(\u0026User::id, 66, 68)));\ncout \u003c\u003c \"betweenId = \" \u003c\u003c betweenId.size() \u003c\u003c endl;\nfor(auto \u0026user : betweenId) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n```\n\nAnd even `LIKE`:\n\n```c++\n//  SELECT * FROM users WHERE last_name LIKE 'D%'\nauto whereNameLike = storage.get_all\u003cUser\u003e(where(like(\u0026User::lastName, \"D%\")));\ncout \u003c\u003c \"whereNameLike = \" \u003c\u003c whereNameLike.size() \u003c\u003c endl;\nfor(auto \u0026user : whereNameLike) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n```\n\nLooks 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\n\n* is_equal\n* is_not_equal\n* greater_than\n* greater_or_equal\n* lesser_than\n* lesser_or_equal\n* is_null\n* is_not_null\n\nthat 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.\n\nIf you omit `where` function in `get_all` it will return all objects from a table:\n\n```c++\nauto allUsers = storage.get_all\u003cUser\u003e();\n```\n\nAlso you can use `remove_all` function to perform `DELETE FROM ... WHERE` query with the same type of conditions.\n\n```c++\nstorage.remove_all\u003cUser\u003e(where(c(\u0026User::id) \u003c 100));\n```\n\n# Raw select\n\nIf you need to extract only a single column (`SELECT %column_name% FROM %table_name% WHERE %conditions%`) you can use a non-CRUD `select` function:\n\n```c++\n\n//  SELECT id FROM users\nauto allIds = storage.select(\u0026User::id);    \ncout \u003c\u003c \"allIds count = \" \u003c\u003c allIds.size() \u003c\u003c endl; //  allIds is std::vector\u003cint\u003e\nfor(auto \u0026id : allIds) {\n    cout \u003c\u003c id \u003c\u003c \" \";\n}\ncout \u003c\u003c endl;\n\n//  SELECT id FROM users WHERE last_name = 'Doe'\nauto doeIds = storage.select(\u0026User::id, where(c(\u0026User::lastName) == \"Doe\"));\ncout \u003c\u003c \"doeIds count = \" \u003c\u003c doeIds.size() \u003c\u003c endl; //  doeIds is std::vector\u003cint\u003e\nfor(auto \u0026doeId : doeIds) {\n    cout \u003c\u003c doeId \u003c\u003c \" \";\n}\ncout \u003c\u003c endl;\n\n//  SELECT last_name FROM users WHERE id \u003c 300\nauto allLastNames = storage.select(\u0026User::lastName, where(c(\u0026User::id) \u003c 300));    \ncout \u003c\u003c \"allLastNames count = \" \u003c\u003c allLastNames.size() \u003c\u003c endl; //  allLastNames is std::vector\u003cstd::string\u003e\nfor(auto \u0026lastName : allLastNames) {\n    cout \u003c\u003c lastName \u003c\u003c \" \";\n}\ncout \u003c\u003c endl;\n\n//  SELECT id FROM users WHERE image_url IS NULL\nauto idsWithoutUrls = storage.select(\u0026User::id, where(is_null(\u0026User::imageUrl)));\nfor(auto id : idsWithoutUrls) {\n    cout \u003c\u003c \"id without image url \" \u003c\u003c id \u003c\u003c endl;\n}\n\n//  SELECT id FROM users WHERE image_url IS NOT NULL\nauto idsWithUrl = storage.select(\u0026User::id, where(is_not_null(\u0026User::imageUrl)));\nfor(auto id : idsWithUrl) {\n    cout \u003c\u003c \"id with image url \" \u003c\u003c id \u003c\u003c endl;\n}\nauto idsWithUrl2 = storage.select(\u0026User::id, where(not is_null(\u0026User::imageUrl)));\nassert(std::equal(idsWithUrl2.begin(),\n                  idsWithUrl2.end(),\n                  idsWithUrl.begin()));\n```\n\nAlso you're able to select several column in a vector of tuples. Example:\n\n```c++\n//  `SELECT first_name, last_name FROM users WHERE id \u003e 250 ORDER BY id`\nauto partialSelect = storage.select(columns(\u0026User::firstName, \u0026User::lastName),\n                                    where(c(\u0026User::id) \u003e 250),\n                                    order_by(\u0026User::id));\ncout \u003c\u003c \"partialSelect count = \" \u003c\u003c partialSelect.size() \u003c\u003c endl;\nfor(auto \u0026t : partialSelect) {\n    auto \u0026firstName = std::get\u003c0\u003e(t);\n    auto \u0026lastName = std::get\u003c1\u003e(t);\n    cout \u003c\u003c firstName \u003c\u003c \" \" \u003c\u003c lastName \u003c\u003c endl;\n}\n```\n\n# ORDER BY support\n\nORDER 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:\n\n```c++\n//  `SELECT * FROM users ORDER BY id`\nauto orderedUsers = storage.get_all\u003cUser\u003e(order_by(\u0026User::id));\ncout \u003c\u003c \"orderedUsers count = \" \u003c\u003c orderedUsers.size() \u003c\u003c endl;\nfor(auto \u0026user : orderedUsers) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n\n//  `SELECT * FROM users WHERE id \u003c 250 ORDER BY first_name`\nauto orderedUsers2 = storage.get_all\u003cUser\u003e(where(c(\u0026User::id) \u003c 250), order_by(\u0026User::firstName));\ncout \u003c\u003c \"orderedUsers2 count = \" \u003c\u003c orderedUsers2.size() \u003c\u003c endl;\nfor(auto \u0026user : orderedUsers2) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n\n//  `SELECT * FROM users WHERE id \u003e 100 ORDER BY first_name ASC`\nauto orderedUsers3 = storage.get_all\u003cUser\u003e(where(c(\u0026User::id) \u003e 100), order_by(\u0026User::firstName).asc());\ncout \u003c\u003c \"orderedUsers3 count = \" \u003c\u003c orderedUsers3.size() \u003c\u003c endl;\nfor(auto \u0026user : orderedUsers3) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n\n//  `SELECT * FROM users ORDER BY id DESC`\nauto orderedUsers4 = storage.get_all\u003cUser\u003e(order_by(\u0026User::id).desc());\ncout \u003c\u003c \"orderedUsers4 count = \" \u003c\u003c orderedUsers4.size() \u003c\u003c endl;\nfor(auto \u0026user : orderedUsers4) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n\n//  `SELECT first_name FROM users ORDER BY ID DESC`\nauto orderedFirstNames = storage.select(\u0026User::firstName, order_by(\u0026User::id).desc());\ncout \u003c\u003c \"orderedFirstNames count = \" \u003c\u003c orderedFirstNames.size() \u003c\u003c endl;\nfor(auto \u0026firstName : orderedFirstNames) {\n    cout \u003c\u003c \"firstName = \" \u003c\u003c firstName \u003c\u003c endl;\n}\n```\n\n# LIMIT and OFFSET\n\nThere are three available versions of `LIMIT`/`OFFSET` options:\n\n- LIMIT %limit%\n- LIMIT %limit% OFFSET %offset%\n- LIMIT %offset%, %limit%\n\nAll these versions available with the same interface:\n\n```c++\n//  `SELECT * FROM users WHERE id \u003e 250 ORDER BY id LIMIT 5`\nauto limited5 = storage.get_all\u003cUser\u003e(where(c(\u0026User::id) \u003e 250),\n                                      order_by(\u0026User::id),\n                                      limit(5));\ncout \u003c\u003c \"limited5 count = \" \u003c\u003c limited5.size() \u003c\u003c endl;\nfor(auto \u0026user : limited5) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n\n//  `SELECT * FROM users WHERE id \u003e 250 ORDER BY id LIMIT 5, 10`\nauto limited5comma10 = storage.get_all\u003cUser\u003e(where(c(\u0026User::id) \u003e 250),\n                                             order_by(\u0026User::id),\n                                             limit(5, 10));\ncout \u003c\u003c \"limited5comma10 count = \" \u003c\u003c limited5comma10.size() \u003c\u003c endl;\nfor(auto \u0026user : limited5comma10) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n\n//  `SELECT * FROM users WHERE id \u003e 250 ORDER BY id LIMIT 5 OFFSET 10`\nauto limit5offset10 = storage.get_all\u003cUser\u003e(where(c(\u0026User::id) \u003e 250),\n                                            order_by(\u0026User::id),\n                                            limit(5, offset(10)));\ncout \u003c\u003c \"limit5offset10 count = \" \u003c\u003c limit5offset10.size() \u003c\u003c endl;\nfor(auto \u0026user : limit5offset10) {\n    cout \u003c\u003c storage.dump(user) \u003c\u003c endl;\n}\n```\n\nPlease beware that queries `LIMIT 5, 10` and `LIMIT 5 OFFSET 10` mean different. `LIMIT 5, 10` means `LIMIT 10 OFFSET 5`.\n\n# JOIN support\n\nYou 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:\n\n```c++\n//  SELECT a.doctor_id, a.doctor_name,\n//      c.patient_name, c.vdate\n//  FROM doctors a\n//  LEFT JOIN visits c\n//  ON a.doctor_id=c.doctor_id;\nauto rows = storage2.select(columns(\u0026Doctor::id, \u0026Doctor::name, \u0026Visit::patientName, \u0026Visit::vdate),\n                            left_join\u003cVisit\u003e(on(c(\u0026Doctor::id) == \u0026Visit::doctorId)));  //  one `c` call is enough cause operator overloads are templated\nfor(auto \u0026row : rows) {\n    cout \u003c\u003c std::get\u003c0\u003e(row) \u003c\u003c '\\t' \u003c\u003c std::get\u003c1\u003e(row) \u003c\u003c '\\t' \u003c\u003c std::get\u003c2\u003e(row) \u003c\u003c '\\t' \u003c\u003c std::get\u003c3\u003e(row) \u003c\u003c endl;\n}\ncout \u003c\u003c endl;\n```\n\nSimple `JOIN`:\n\n```c++\n//  SELECT a.doctor_id,a.doctor_name,\n//      c.patient_name,c.vdate\n//  FROM doctors a\n//  JOIN visits c\n//  ON a.doctor_id=c.doctor_id;\nrows = storage2.select(columns(\u0026Doctor::id, \u0026Doctor::name, \u0026Visit::patientName, \u0026Visit::vdate),\n                       join\u003cVisit\u003e(on(c(\u0026Doctor::id) == \u0026Visit::doctorId)));\nfor(auto \u0026row : rows) {\n    cout \u003c\u003c std::get\u003c0\u003e(row) \u003c\u003c '\\t' \u003c\u003c std::get\u003c1\u003e(row) \u003c\u003c '\\t' \u003c\u003c std::get\u003c2\u003e(row) \u003c\u003c '\\t' \u003c\u003c std::get\u003c3\u003e(row) \u003c\u003c endl;\n}\ncout \u003c\u003c endl;\n```\n\nTwo `INNER JOIN`s in one query:\n\n```c++\n//  SELECT\n//      trackid,\n//      tracks.name AS Track,\n//      albums.title AS Album,\n//      artists.name AS Artist\n//  FROM\n//      tracks\n//  INNER JOIN albums ON albums.albumid = tracks.albumid\n//  INNER JOIN artists ON artists.artistid = albums.artistid;\nauto innerJoinRows2 = storage.select(columns(\u0026Track::trackId, \u0026Track::name, \u0026Album::title, \u0026Artist::name),\n                                     inner_join\u003cAlbum\u003e(on(c(\u0026Album::albumId) == \u0026Track::albumId)),\n                                     inner_join\u003cArtist\u003e(on(c(\u0026Artist::artistId) == \u0026Album::artistId)));\n//  innerJoinRows2 is std::vector\u003cstd::tuple\u003cdecltype(Track::trackId), decltype(Track::name), decltype(Album::title), decltype(Artist::name)\u003e\u003e\n```\n\nMore join examples can be found in [examples folder](https://github.com/fnc12/sqlite_orm/blob/master/examples/left_and_inner_join.cpp).\n\n# Migrations functionality\n\nThere 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.\n\n```c++\nstorage.sync_schema();\n//  or\nstorage.sync_schema(true);\n```\n\nPlease 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:\n* if there are excess tables exist in db they are ignored (not dropped)\n* every table from storage is compared with it's db analog and \n    * if table doesn't exist it is created\n    * if table exists its colums are being compared with table_info from db and\n        * 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.\n        * 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\n        * 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).\n\nThe best practice is to call this function right after storage creation.\n\n# Transactions\n\nThere are three ways to begin and commit/rollback transactions:\n* explicitly call `begin_transaction();`, `rollback();` or `commit();` functions\n* 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`.\n* use `transaction_guard` function which returns a guard object which works just like `lock_guard` for `std::mutex`.\n\nExample for explicit call:\n\n```c++\nauto secondUser = storage.get\u003cUser\u003e(2);\n\nstorage.begin_transaction();\nsecondUser.typeId = 3;\nstorage.update(secondUser);\nstorage.rollback(); //  or storage.commit();\n\nsecondUser = storage.get\u003cdecltype(secondUser)\u003e(secondUser.id);\nassert(secondUser.typeId != 3);\n```\n\nExample for implicit call:\n\n```c++\nstorage.transaction([\u0026] () mutable {    //  mutable keyword allows make non-const function calls\n    auto secondUser = storage.get\u003cUser\u003e(2);\n    secondUser.typeId = 1;\n    storage.update(secondUser);\n    auto gottaRollback = bool(rand() % 2);\n    if(gottaRollback){  //  dummy condition for test\n        return false;   //  exits lambda and calls ROLLBACK\n    }\n    return true;        //  exits lambda and calls COMMIT\n});\n```\n\nThe second way guarantees that `commit` or `rollback` will be called. You can use either way.\n\nTransactions are useful with `changes` sqlite function that returns number of rows modified.\n\n```c++\nstorage.transaction([\u0026] () mutable {\n    storage.remove_all\u003cUser\u003e(where(c(\u0026User::id) \u003c 100));\n    auto usersRemoved = storage.changes();\n    cout \u003c\u003c \"usersRemoved = \" \u003c\u003c usersRemoved \u003c\u003c endl;\n    return true;\n});\n```\n\nIt 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.\n\nAlso 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:\n\n```c++\nauto commited = storage.transaction([\u0026] () mutable {    \n    auto secondUser = storage.get\u003cUser\u003e(2);\n    secondUser.typeId = 1;\n    storage.update(secondUser);\n    auto gottaRollback = bool(rand() % 2);\n    if(gottaRollback){  //  dummy condition for test\n        return false;   //  exits lambda and calls ROLLBACK\n    }\n    return true;        //  exits lambda and calls COMMIT\n});\nif(commited){\n    cout \u003c\u003c \"Commited successfully, go on.\" \u003c\u003c endl;\n}else{\n    cerr \u003c\u003c \"Commit failed, process an error\" \u003c\u003c endl;\n}\n```\n\nExample for `transaction_guard` function:\n\n```c++\ntry{\n  auto guard = storage.transaction_guard(); //  calls BEGIN TRANSACTION and returns guard object\n  user.name = \"Paul\";\n  auto notExisting = storage.get\u003cUser\u003e(-1); //  exception is thrown here, guard calls ROLLBACK in its destructor\n  guard.commit();\n}catch(...){\n  cerr \u003c\u003c \"exception\" \u003c\u003c endl;\n}\n```\n\n# In memory database\n\nTo manage in memory database just provide `:memory:` or `\"\"` instead as filename to `make_storage`.\n\n# Comparison with other C++ libs\n\n|   |sqlite_orm|[SQLiteCpp](https://github.com/SRombauts/SQLiteCpp)|[hiberlite](https://github.com/paulftw/hiberlite)|[ODB](https://www.codesynthesis.com/products/odb/)|\n|---|:---:|:---:|:---:|:---:|\n|Schema sync|yes|no|yes|no|\n|Single responsibility principle|yes|yes|no|no|\n|STL compatible|yes|no|no|no|\n|No raw string queries|yes|no|yes|yes|\n|Transactions|yes|yes|no|yes|\n|Custom types binding|yes|no|yes|yes|\n|Doesn't use macros and/or external codegen scripts|yes|yes|no|no|\n|Aggregate functions|yes|yes|no|yes|\n|Prepared statements|yes|yes|no|no|\n\n# Notes\n\nTo 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.\n\nFor more details please check the project [wiki](https://github.com/fnc12/sqlite_orm/wiki).\n\n# Installation\n\n**Note**: Installation is not necessary if you plan to use the fetchContent method, see below in Usage.\n\nUse a popular package manager like [vcpkg](https://github.com/Microsoft/vcpkg) and just install it with the `vcpkg install sqlite-orm` command.\n\nOr you build it from source:\n\n```bash\ngit clone https://github.com/fnc12/sqlite_orm.git sqlite_orm\ncd sqlite_orm\ncmake -B build\ncmake --build build --target install\n```\nYou might need admin rights for the last command.\n\n# Usage\n\n## CMake\n\nIf 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). \n\nEither 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)\n\n## Find Package\n\nIf 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.\n\n```cmake\nfind_package(SqliteOrm REQUIRED)\n\ntarget_link_libraries(main PRIVATE sqlite_orm::sqlite_orm)\n```\n\n## Fetch Content (Recommended)\n\nAlternatively, cmake can download the project directly from github during configure stage and therefore you don't need to install the lib before.\nAgaint 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.\n\n## No CMake\n\nIf 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.\n\n# Requirements\n\n* C++14 compatible compiler (not C++11 cause of templated lambdas in the lib).\n* 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)\n\n# Video from conference\n\n[![Video from conference](https://img.youtube.com/vi/ngsilquWgpo/0.jpg)](https://www.youtube.com/watch?v=ngsilquWgpo)\n\n# SqliteMan\n\nIn 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.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffnc12%2Fsqlite_orm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffnc12%2Fsqlite_orm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffnc12%2Fsqlite_orm/lists"}