{"id":13316335,"url":"https://github.com/cyyever/mariadb_modern_cpp","last_synced_at":"2026-05-06T18:33:33.770Z","repository":{"id":109078985,"uuid":"130674288","full_name":"cyyever/mariadb_modern_cpp","owner":"cyyever","description":null,"archived":false,"fork":false,"pushed_at":"2018-10-08T03:47:53.000Z","size":60,"stargazers_count":2,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-16T02:27:19.102Z","etag":null,"topics":["cpp17","mariadb","mysql"],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cyyever.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2018-04-23T09:39:48.000Z","updated_at":"2024-02-18T08:40:17.000Z","dependencies_parsed_at":"2023-04-21T13:54:59.082Z","dependency_job_id":null,"html_url":"https://github.com/cyyever/mariadb_modern_cpp","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cyyever/mariadb_modern_cpp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyyever%2Fmariadb_modern_cpp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyyever%2Fmariadb_modern_cpp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyyever%2Fmariadb_modern_cpp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyyever%2Fmariadb_modern_cpp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cyyever","download_url":"https://codeload.github.com/cyyever/mariadb_modern_cpp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyyever%2Fmariadb_modern_cpp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271620597,"owners_count":24791569,"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","status":"online","status_checked_at":"2025-08-22T02:00:08.480Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["cpp17","mariadb","mysql"],"created_at":"2024-07-29T18:21:32.067Z","updated_at":"2026-05-06T18:33:33.743Z","avatar_url":"https://github.com/cyyever.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"mariadb modern cpp wrapper\n====\n\nThis library is a lightweight modern wrapper around mariadb/mysql C api inspired by [sqlite_modern_cpp](https://github.com/SqliteModernCpp/sqlite_modern_cpp) and uses C++17.\n\n```c++\n#include \u003ciostream\u003e\n#include \u003cmariadb_modern_cpp.hpp\u003e\nusing namespace mariadb;\nusing namespace std;\n\nint main() {\n\n   try {\n      // connects to server\n      mariadb::mariadb_config config;\n      config.host = \"xxx\";\n      config.port = 3306;\n      config.user = \"xxx\";\n      config.passwd = \"xxx\";\n      config.default_database = \"my_db\";\n      database db(config);\n\n      // executes the query and creates a 'user' table\n      db \u003c\u003c\n         \"create table if not exists user (\"\n         \"   _id int primary key autoincrement not null,\"\n         \"   age int,\"\n         \"   name text,\"\n         \"   weight real\"\n         \");\";\n\n      // inserts a new user record.\n      // binds the fields to '?' .\n      // note that only types allowed for bindings are :\n      //      integer types(int, long, long long, etc)\n      //      float types(float , double , etc)\n      //      string (for CHAR,VARCHAR,TEXT columns)\n      //      std::vector\u003cstd::byte\u003e (for BLOB columns)\n      //      std::optional (for NULL columns)\n      db \u003c\u003c \"insert into user (age,name,weight) values (?,?,?);\"\n         \u003c\u003c 20\n         \u003c\u003c \"bob\"\n         \u003c\u003c 83.25;\n\n      int age = 21;\n      float weight = 68.5;\n      string name = \"jack\";\n      db \u003c\u003c \"insert into user (age,name,weight) values (?,?,?);\"\n         \u003c\u003c age\n         \u003c\u003c name\n         \u003c\u003c weight;\n\n      //  gets the value generated for an AUTO_INCREMENT column by the previous INSERT or UPDATE statement.\n      cout \u003c\u003c \"The new record got assigned id \" \u003c\u003c db.insert_id() \u003c\u003c endl;\n\n      // selects from user table on a condition ( age \u003e 18 ) and executes\n      // the lambda for each row returned .\n      db \u003c\u003c \"select age,name,weight from user where age \u003e ? ;\"\n         \u003c\u003c 18\n         \u003e\u003e [\u0026](int age, string name, double weight) {\n            cout \u003c\u003c age \u003c\u003c ' ' \u003c\u003c name \u003c\u003c ' ' \u003c\u003c weight \u003c\u003c endl;\n         };\n\n      // selects the count(*) from user table\n      // note that you can extract a single culumn single row result only to : int,long,long,float,double,string,u16string\n      int count = 0;\n      db \u003c\u003c \"select count(*) from user\" \u003e\u003e count;\n      cout \u003c\u003c \"cout : \" \u003c\u003c count \u003c\u003c endl;\n\n      // you can also extract multiple column rows\n      db \u003c\u003c \"select age, name from user where _id=1;\" \u003e\u003e tie(age, name);\n      cout \u003c\u003c \"Age = \" \u003c\u003c age \u003c\u003c \", name = \" \u003c\u003c name \u003c\u003c endl;\n\n      // this also works and the returned value will be automatically converted to string\n      string str_count;\n      db \u003c\u003c \"select count(*) from user\" \u003e\u003e str_count;\n      cout \u003c\u003c \"scount : \" \u003c\u003c str_count \u003c\u003c endl;\n   }\n   catch (exception\u0026 e) {\n      cout \u003c\u003c e.what() \u003c\u003c endl;\n   }\n}\n```\n\nNOTES\n----\nCurrently multi-statement execution is not supported.\n\nPrepared Statements\n----\nIt is possible to retain and reuse statments this will keep the query plan and in case of an complex query or many uses might increase the performance significantly.\n\n```c++\ndatabase db(config);\n\n// if you use \u003c\u003c on a mariadb::database you get a prepared statment back\n// this will not be executed till it gets destroyed or you execute it explicitly\nauto ps = db \u003c\u003c \"select a,b from table where something = ? and anotherthing = ?\"; // get a prepared parsed and ready statment\n\n// first if needed bind values to it\nps \u003c\u003c 5;\nint tmp = 8;\nps \u003c\u003c tmp;\n\n// now you can execute it with `operator\u003e\u003e` or `execute()`.\n// If the statement was executed once it will not be executed again when it goes out of scope.\n// But beware that it will execute on destruction if it wasn't executed!\nps \u003e\u003e [\u0026](int a,int b){ ... };\n\n// after a successfull execution the statment can be executed again, but the bound values are resetted.\n// If you don't need the returned values you can execute it like this\nps.execute();\n\n// To disable the execution of a statment when it goes out of scope and wasn't used\nps.used(true); // or false if you want it to execute even if it was used\n\n// Usage Example:\n\nauto ps = db \u003c\u003c \"insert into complex_table_with_lots_of_indices values (?,?,?)\";\nint i = 0;\nwhile( i \u003c 100000 ){\n   ps \u003c\u003c long_list[i++] \u003c\u003c long_list[i++] \u003c\u003c long_list[i++];\n   ps.execute();\n}\n```\n\nShared Connections\n----\nIf you need the handle to the database connection to execute mariadb commands directly you can get a managed shared_ptr to it, so it will not close as long as you have a referenc to it.\n\nTake this example on how to deal with a database backup using mariadbs own functions in a save and modern way.\n```c++\ntry {\n   database db(config);\n\n   auto mysql_handle = db.connection();   // get a handle to the DB we want to backup in our scope\n                                 // this way we are sure the DB is open and ok while we backup\n\n   mysql_ping(mysql_handle.get());\n} // Release allocated resources.\n```\n\nTransactions\n----\nAll sql statements executed in a transaction context are commited as a transaction when the context is destructed,but if an exception is thrown,the transaction is canceled.\n\n```c++\n{\n  auto ctx = db.get_transaction_context();\n  db \u003c\u003c \"insert into user (age,name,weight) values (?,?,?);\"\n     \u003c\u003c 20\n     \u003c\u003c \"bob\"\n     \u003c\u003c 83.25f;\n  db \u003c\u003c \"insert into user (age,name,weight) values (?,?,?);\"\n     \u003c\u003c 19\n     \u003c\u003c \"chirs\"\n     \u003c\u003c 82.7;\n}\n\n{\n  auto ctx = db.get_transaction_context();\n  db \u003c\u003c \"insert into user (age,name,weight) values (?,?,?);\"\n     \u003c\u003c 20\n     \u003c\u003c \"bob\"\n     \u003c\u003c 83.25f;\n  db \u003c\u003c \"insert into user (age,name,weight) values (?,?,?);\"\n     \u003c\u003c 19\n     \u003c\u003c \"chirs\"\n     \u003c\u003c 82.7;\n  throw std::runtime_error(\"Transaction will be canceled\");\n}\n\n```\n\nBlob\n----\nUse `std::vector\u003cstd::byte\u003e` to store and retrieve blob data.  \n\nYou can also Use `std::vector\u003cT\u003e` to store and retrieve blob data where `T` is a integer type,we will treat the vector as an continuous memeory block like `std::vector\u003cstd::byte\u003e`.\n\n```c++\ndb \u003c\u003c \"CREATE TABLE person (name TEXT, numbers BLOB);\";\ndb \u003c\u003c \"INSERT INTO person VALUES (?, ?)\" \u003c\u003c \"bob\" \u003c\u003c vector\u003cstd::byte\u003e { 1, 2, 3, 4};\n\nvector\u003cstd::byte\u003e numbers_bob;\ndb \u003c\u003c \"SELECT numbers from person where name = ?;\" \u003c\u003c \"bob\" \u003e\u003e numbers_bob;\n\ndb \u003c\u003c \"SELECT numbers from person where name = ?;\" \u003c\u003c \"sara\" \u003e\u003e [](vector\u003cstd::byte\u003e numbers_sara){\n    for(auto e : numbers_sara) cout \u003c\u003c (int)e \u003c\u003c ' '; cout \u003c\u003c endl;\n};\n```\n\nNULL values\n----\nIf you have databases where some rows may be null, you can use `std::unique_ptr\u003cT\u003e` to retain the NULL values between C++ variables and the database.\n\n```c++\ndb \u003c\u003c \"CREATE TABLE tbl (id integer,age integer, name string, img blob);\";\ndb \u003c\u003c \"INSERT INTO tbl VALUES (?, ?, ?, ?);\" \u003c\u003c 1 \u003c\u003c 24 \u003c\u003c \"bob\" \u003c\u003c vector\u003cint\u003e { 1, 2 , 3};\nunique_ptr\u003cstring\u003e ptr_null; // you can even bind empty unique_ptr\u003cT\u003e\ndb \u003c\u003c \"INSERT INTO tbl VALUES (?, ?, ?, ?);\" \u003c\u003c 2 \u003c\u003c nullptr \u003c\u003c ptr_null \u003c\u003c nullptr;\n\ndb \u003c\u003c \"select age,name,img from tbl where id = 1\"\n\t\t\u003e\u003e [](unique_ptr\u003cint\u003e age_p, unique_ptr\u003cstring\u003e name_p, unique_ptr\u003cvector\u003cint\u003e\u003e img_p) {\n\t\t\tif(age_p == nullptr || name_p == nullptr || img_p == nullptr) {\n\t\t\t\tcerr \u003c\u003c \"ERROR: values should not be null\" \u003c\u003c std::endl;\n\t\t\t}\n\n\t\t\tcout \u003c\u003c \"age:\" \u003c\u003c *age_p \u003c\u003c \" name:\" \u003c\u003c *name_p \u003c\u003c \" img:\";\n\t\t\tfor(auto i : *img_p) cout \u003c\u003c i \u003c\u003c \",\"; cout \u003c\u003c endl;\n\t\t};\n\ndb \u003c\u003c \"select age,name,img from tbl where id = 2\"\n\t\t\u003e\u003e [](unique_ptr\u003cint\u003e age_p, unique_ptr\u003cstring\u003e name_p, unique_ptr\u003cvector\u003cint\u003e\u003e img_p) {\n\t\t\tif(age_p != nullptr || name_p != nullptr || img_p != nullptr) {\n\t\t\t\tcerr \u003c\u003c \"ERROR: values should be nullptr\" \u003c\u003c std::endl;\n\t\t\t\texit(EXIT_FAILURE);\n\t\t\t}\n\n\t\t\tcout \u003c\u003c \"OK all three values are nullptr\" \u003c\u003c endl;\n\t\t};\n```\n\nYou can use `std::optional\u003cT\u003e` as an alternative for `std::unique_ptr\u003cT\u003e` to work with NULL values.\n\n```c++\n#include \u003cmariadb_modern_cpp.hpp\u003e\n\nstruct User {\n   long long _id;\n   std::optional\u003cint\u003e age;\n   std::optional\u003cstring\u003e name;\n   std::optional\u003creal\u003e weight;\n};\n\nint main() {\n   User user;\n   user.name = \"bob\";\n\n   // Same database as above\n   database db(config);\n\n   // Here, age and weight will be inserted as NULL in the database.\n   db \u003c\u003c \"insert into user (age,name,weight) values (?,?,?);\"\n      \u003c\u003c user.age\n      \u003c\u003c user.name\n      \u003c\u003c user.weight;\n   user._id = db.last_insert_rowid();\n\n   // Here, the User instance will retain the NULL value(s) from the database.\n   db \u003c\u003c \"select _id,age,name,weight from user where age \u003e ? ;\"\n      \u003c\u003c 18\n      \u003e\u003e [\u0026](long long id,\n         std::optional\u003cint\u003e age,\n         std::optional\u003cstring\u003e name\n         std::optional\u003creal\u003e weight) {\n\n      cout \u003c\u003c \"id=\" \u003c\u003c _id\n         \u003c\u003c \" age = \" \u003c\u003c (age ? to_string(*age) ? string(\"NULL\"))\n         \u003c\u003c \" name = \" \u003c\u003c (name ? *name : string(\"NULL\"))\n         \u003c\u003c \" weight = \" \u003c\u003c (weight ? to_string(*weight) : string(NULL))\n         \u003c\u003c endl;\n   };\n}\n```\n\nErrors\n----\n\nOn error, the library throws an error class indicating the type of error. The error classes are derived from the mariadb::mariadb_exception class.\nyou can use `get_sql()` to see the SQL statement leading to the error.\n\n```c++\ndatabase db(\":memory:\");\ndb \u003c\u003c \"create table person (id integer primary key not null, name text);\";\n\ntry {\n   db \u003c\u003c \"insert into person (id, name) values (?,?)\" \u003c\u003c 1 \u003c\u003c \"jack\";\n   // inserting again to produce error\n   db \u003c\u003c \"insert into person (id, name) values (?,?)\" \u003c\u003c 1 \u003c\u003c \"jack\";\n}\n/* if you are trying to catch all mariadb related exceptions\n * make sure to catch them by reference */\ncatch (mariadb_exception\u0026 e) {\n   cerr  \u003c\u003c e.what() \u003c\u003c \" during \"\n         \u003c\u003c e.get_sql() \u003c\u003c endl;\n}\n```\n\nBuilding and Installing\n----\n\nThe usual way works for installing:\n\n```bash\nmkdir build \u0026\u0026 cmake .. \u0026\u0026 make \u0026\u0026 sudo make install\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyyever%2Fmariadb_modern_cpp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcyyever%2Fmariadb_modern_cpp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyyever%2Fmariadb_modern_cpp/lists"}