From ba9e9731d25eb6dc5b5c74abc222dcce9f582c07 Mon Sep 17 00:00:00 2001 From: Tanglong3bf Date: Sat, 16 Dec 2023 14:07:27 +0800 Subject: [PATCH] Fix ORM: The original way did not handle exceptions correctly. (#1872) --- drogon_ctl/templates/model_cc.csp | 235 ++--- orm_lib/tests/CMakeLists.txt | 15 + orm_lib/tests/db_test.cc | 1328 +++++++++++++++++++++++++- orm_lib/tests/mysql/Blog.cc | 790 +++++++++++++++ orm_lib/tests/mysql/Blog.h | 260 +++++ orm_lib/tests/mysql/BlogTag.cc | 564 +++++++++++ orm_lib/tests/mysql/BlogTag.h | 228 +++++ orm_lib/tests/mysql/Category.cc | 563 +++++++++++ orm_lib/tests/mysql/Category.h | 231 +++++ orm_lib/tests/mysql/Tag.cc | 571 +++++++++++ orm_lib/tests/mysql/Tag.h | 233 +++++ orm_lib/tests/mysql/Users.cc | 449 +++++---- orm_lib/tests/mysql/Users.h | 34 +- orm_lib/tests/mysql/Wallets.cc | 749 +++++++++++++++ orm_lib/tests/mysql/Wallets.h | 254 +++++ orm_lib/tests/mysql/model.json | 59 +- orm_lib/tests/postgresql/Blog.cc | 789 +++++++++++++++ orm_lib/tests/postgresql/Blog.h | 283 ++++++ orm_lib/tests/postgresql/BlogTag.cc | 564 +++++++++++ orm_lib/tests/postgresql/BlogTag.h | 246 +++++ orm_lib/tests/postgresql/Category.cc | 562 +++++++++++ orm_lib/tests/postgresql/Category.h | 250 +++++ orm_lib/tests/postgresql/Tag.cc | 570 +++++++++++ orm_lib/tests/postgresql/Tag.h | 252 +++++ orm_lib/tests/postgresql/Users.cc | 449 +++++---- orm_lib/tests/postgresql/Users.h | 34 +- orm_lib/tests/postgresql/Wallets.cc | 748 +++++++++++++++ orm_lib/tests/postgresql/Wallets.h | 282 ++++++ orm_lib/tests/postgresql/model.json | 49 +- orm_lib/tests/sqlite3/Blog.cc | 809 ++++++++++++++++ orm_lib/tests/sqlite3/Blog.h | 274 ++++++ orm_lib/tests/sqlite3/BlogTag.cc | 564 +++++++++++ orm_lib/tests/sqlite3/BlogTag.h | 228 +++++ orm_lib/tests/sqlite3/Category.cc | 579 +++++++++++ orm_lib/tests/sqlite3/Category.h | 241 +++++ orm_lib/tests/sqlite3/Tag.cc | 587 ++++++++++++ orm_lib/tests/sqlite3/Tag.h | 243 +++++ orm_lib/tests/sqlite3/Users.cc | 82 +- orm_lib/tests/sqlite3/Users.h | 18 +- orm_lib/tests/sqlite3/Wallets.cc | 745 +++++++++++++++ orm_lib/tests/sqlite3/Wallets.h | 259 +++++ orm_lib/tests/sqlite3/model.json | 53 +- 42 files changed, 15717 insertions(+), 606 deletions(-) create mode 100644 orm_lib/tests/mysql/Blog.cc create mode 100644 orm_lib/tests/mysql/Blog.h create mode 100644 orm_lib/tests/mysql/BlogTag.cc create mode 100644 orm_lib/tests/mysql/BlogTag.h create mode 100644 orm_lib/tests/mysql/Category.cc create mode 100644 orm_lib/tests/mysql/Category.h create mode 100644 orm_lib/tests/mysql/Tag.cc create mode 100644 orm_lib/tests/mysql/Tag.h create mode 100644 orm_lib/tests/mysql/Wallets.cc create mode 100644 orm_lib/tests/mysql/Wallets.h create mode 100644 orm_lib/tests/postgresql/Blog.cc create mode 100644 orm_lib/tests/postgresql/Blog.h create mode 100644 orm_lib/tests/postgresql/BlogTag.cc create mode 100644 orm_lib/tests/postgresql/BlogTag.h create mode 100644 orm_lib/tests/postgresql/Category.cc create mode 100644 orm_lib/tests/postgresql/Category.h create mode 100644 orm_lib/tests/postgresql/Tag.cc create mode 100644 orm_lib/tests/postgresql/Tag.h create mode 100644 orm_lib/tests/postgresql/Wallets.cc create mode 100644 orm_lib/tests/postgresql/Wallets.h create mode 100644 orm_lib/tests/sqlite3/Blog.cc create mode 100644 orm_lib/tests/sqlite3/Blog.h create mode 100644 orm_lib/tests/sqlite3/BlogTag.cc create mode 100644 orm_lib/tests/sqlite3/BlogTag.h create mode 100644 orm_lib/tests/sqlite3/Category.cc create mode 100644 orm_lib/tests/sqlite3/Category.h create mode 100644 orm_lib/tests/sqlite3/Tag.cc create mode 100644 orm_lib/tests/sqlite3/Tag.h create mode 100644 orm_lib/tests/sqlite3/Wallets.cc create mode 100644 orm_lib/tests/sqlite3/Wallets.h diff --git a/drogon_ctl/templates/model_cc.csp b/drogon_ctl/templates/model_cc.csp index fd90fc80..cbfc082a 100644 --- a/drogon_ctl/templates/model_cc.csp +++ b/drogon_ctl/templates/model_cc.csp @@ -1568,62 +1568,52 @@ for(auto &relationship : relationships) auto relationshipValName=nameTransform(name, false); auto alias=relationship.targetTableAlias(); auto aliasValName=nameTransform(alias, false); + if(!alias.empty()) + { + if(alias[0] <= 'z' && alias[0] >= 'a') + { + alias[0] += ('A' - 'a'); + } + } + else + { + alias = relationshipClassName; + } + std::string alind(alias.length(), ' '); if(relationship.type() == Relationship::Type::HasOne) { - if(!alias.empty()) - { - if(alias[0] <= 'z' && alias[0] >= 'a') - { - alias[0] += ('A' - 'a'); - } - std::string alind(alias.length(), ' '); %> - -{%relationshipClassName%} [[className]]::get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const { - std::shared_ptr> pro(new std::promise<{%relationshipClassName%}>); - std::future<{%relationshipClassName%}> f = pro->get_future(); - get{%alias%}(clientPtr, [&pro] ({%relationshipClassName%} result) { - try { - pro->set_value(result); - } - catch (...) { - pro->set_exception(std::current_exception()); - } - }, [&pro] (const DrogonDbException &err) { - pro->set_exception(std::make_exception_ptr(err)); - }); - return f.get(); -} -void [[className]]::get{%alias%}(const DbClientPtr &clientPtr, - {%indentStr%} {%alind%} const std::function &rcb, - {%indentStr%} {%alind%} const ExceptionCallback &ecb) const -<%c++ +{%relationshipClassName%} [[className]]::get{%alias%}(const DbClientPtr &clientPtr) const { + const static std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++ + if(rdbms=="postgresql") + { + $$<<"$1"; } else { - std::string relationshipClassInde(relationshipClassName.length(), ' '); -%> -{%relationshipClassName%} [[className]]::get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const { - std::shared_ptr> pro(new std::promise<{%relationshipClassName%}>); - std::future<{%relationshipClassName%}> f = pro->get_future(); - get{%relationshipClassName%}(clientPtr, [&pro] ({%relationshipClassName%} result) { - try { - pro->set_value(result); - } - catch (...) { - pro->set_exception(std::current_exception()); - } - }, [&pro] (const DrogonDbException &err) { - pro->set_exception(std::make_exception_ptr(err)); - }); - return f.get(); + $$<<"?"; + }%>"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *{%nameTransform(relationship.originalKey(), false)%}_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + if (r.size() == 0) + { + throw UnexpectedRows("0 rows found"); + } + else if (r.size() > 1) + { + throw UnexpectedRows("Found more than one row"); + } + return {%relationshipClassName%}(r[0]); } -void [[className]]::get{%relationshipClassName%}(const DbClientPtr &clientPtr, - {%indentStr%} {%relationshipClassInde%} const std::function &rcb, - {%indentStr%} {%relationshipClassInde%} const ExceptionCallback &ecb) const -<%c++ - } -%> + +void [[className]]::get{%alias%}(const DbClientPtr &clientPtr, + {%indentStr%} {%alind%} const std::function &rcb, + {%indentStr%} {%alind%} const ExceptionCallback &ecb) const { const static std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++ if(rdbms=="postgresql") @@ -1656,59 +1646,36 @@ void [[className]]::get{%relationshipClassName%}(const DbClientPtr &clientPtr, } else if(relationship.type() == Relationship::Type::HasMany) { - if(!alias.empty()) - { - if(alias[0] <= 'z' && alias[0] >= 'a') - { - alias[0] += ('A' - 'a'); - } - std::string alind(alias.length(), ' '); %> -std::vector<{%relationshipClassName%}> [[className]]::get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const { - std::shared_ptr>> pro(new std::promise>); - std::future> f = pro->get_future(); - get{%alias%}(clientPtr, [&pro] (std::vector<{%relationshipClassName%}> result) { - try { - pro->set_value(result); - } - catch (...) { - pro->set_exception(std::current_exception()); - } - }, [&pro] (const DrogonDbException &err) { - pro->set_exception(std::make_exception_ptr(err)); - }); - return f.get(); -} -void [[className]]::get{%alias%}(const DbClientPtr &clientPtr, - {%indentStr%} {%alind%} const std::function)> &rcb, - {%indentStr%} {%alind%} const ExceptionCallback &ecb) const -<%c++ +std::vector<{%relationshipClassName%}> [[className]]::get{%alias%}(const DbClientPtr &clientPtr) const { + const static std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++ + if(rdbms=="postgresql") + { + $$<<"$1"; } else { - std::string relationshipClassInde(relationshipClassName.length(), ' '); -%> -std::vector<{%relationshipClassName%}> [[className]]::get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const { - std::shared_ptr>> pro(new std::promise>); - std::future> f = pro->get_future(); - get{%relationshipClassName%}(clientPtr, [&pro] (std::vector<{%relationshipClassName%}> result) { - try { - pro->set_value(result); - } - catch (...) { - pro->set_exception(std::current_exception()); - } - }, [&pro] (const DrogonDbException &err) { - pro->set_exception(std::make_exception_ptr(err)); - }); - return f.get(); + $$<<"?"; + }%>"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *{%nameTransform(relationship.originalKey(), false)%}_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + std::vector<{%relationshipClassName%}> ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back({%relationshipClassName%}(row)); + } + return ret; } -void [[className]]::get{%relationshipClassName%}(const DbClientPtr &clientPtr, - {%indentStr%} {%relationshipClassInde%} const std::function)> &rcb, - {%indentStr%} {%relationshipClassInde%} const ExceptionCallback &ecb) const -<%c++ - } -%> + +void [[className]]::get{%alias%}(const DbClientPtr &clientPtr, + {%indentStr%} {%alind%} const std::function)> &rcb, + {%indentStr%} {%alind%} const ExceptionCallback &ecb) const { const static std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++ if(rdbms=="postgresql") @@ -1740,59 +1707,37 @@ void [[className]]::get{%relationshipClassName%}(const DbClientPtr &clientPtr, auto pivotTableClassName=nameTransform(pivotTableName, true); auto &pivotOriginalKey=relationship.pivotTable().originalKey(); auto &pivotTargetKey=relationship.pivotTable().targetKey(); - if(!alias.empty()) - { - if(alias[0] <= 'z' && alias[0] >= 'a') - { - alias[0] += ('A' - 'a'); - } - std::string alind(alias.length(), ' '); %> -std::vector> [[className]]::get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const { - std::shared_ptr>>> pro(new std::promise>>); - std::future>> f = pro->get_future(); - get{%alias%}(clientPtr, [&pro] (std::vector> result) { - try { - pro->set_value(result); - } - catch (...) { - pro->set_exception(std::current_exception()); - } - }, [&pro] (const DrogonDbException &err) { - pro->set_exception(std::make_exception_ptr(err)); - }); - return f.get(); -} -void [[className]]::get{%alias%}(const DbClientPtr &clientPtr, - {%indentStr%} {%alind%} const std::function>)> &rcb, - {%indentStr%} {%alind%} const ExceptionCallback &ecb) const -<%c++ +std::vector> [[className]]::get{%alias%}(const DbClientPtr &clientPtr) const { + const static std::string sql = "select * from {%name%},{%pivotTableName%} where {%pivotTableName%}.{%pivotOriginalKey%} = <%c++ + if(rdbms=="postgresql") + { + $$<<"$1"; } else { - std::string relationshipClassInde(relationshipClassName.length(), ' '); -%> -std::vector> [[className]]::get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const { - std::shared_ptr>>> pro(new std::promise>>); - std::future>> f = pro->get_future(); - get{%relationshipClassName%}(clientPtr, [&pro] (std::vector> result) { - try { - pro->set_value(result); - } - catch (...) { - pro->set_exception(std::current_exception()); - } - }, [&pro] (const DrogonDbException &err) { - pro->set_exception(std::make_exception_ptr(err)); - }); - return f.get(); + $$<<"?"; + }%> and {%pivotTableName%}.{%pivotTargetKey%} = {%name%}.{%relationship.targetKey()%}"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *{%nameTransform(relationship.originalKey(), false)%}_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + std::vector> ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back(std::pair<{%relationshipClassName%},{%pivotTableClassName%}>( + {%relationshipClassName%}(row),{%pivotTableClassName%}(row,{%relationshipClassName%}::getColumnNumber()))); + } + return ret; } -void [[className]]::get{%relationshipClassName%}(const DbClientPtr &clientPtr, - {%indentStr%} {%relationshipClassInde%} const std::function>)> &rcb, - {%indentStr%} {%relationshipClassInde%} const ExceptionCallback &ecb) const -<%c++ - } -%> + +void [[className]]::get{%alias%}(const DbClientPtr &clientPtr, + {%indentStr%} {%alind%} const std::function>)> &rcb, + {%indentStr%} {%alind%} const ExceptionCallback &ecb) const { const static std::string sql = "select * from {%name%},{%pivotTableName%} where {%pivotTableName%}.{%pivotOriginalKey%} = <%c++ if(rdbms=="postgresql") diff --git a/orm_lib/tests/CMakeLists.txt b/orm_lib/tests/CMakeLists.txt index 06114061..ec896a3a 100644 --- a/orm_lib/tests/CMakeLists.txt +++ b/orm_lib/tests/CMakeLists.txt @@ -6,8 +6,23 @@ endif (WIN32) add_executable(db_test db_test.cc postgresql/Users.cc + postgresql/Wallets.cc + postgresql/Blog.cc + postgresql/Category.cc + postgresql/BlogTag.cc + postgresql/Tag.cc mysql/Users.cc + mysql/Wallets.cc + mysql/Blog.cc + mysql/Category.cc + mysql/BlogTag.cc + mysql/Tag.cc sqlite3/Users.cc + sqlite3/Wallets.cc + sqlite3/Blog.cc + sqlite3/Category.cc + sqlite3/BlogTag.cc + sqlite3/Tag.cc ) if (WIN32) diff --git a/orm_lib/tests/db_test.cc b/orm_lib/tests/db_test.cc index 9323b4d2..086a8cca 100644 --- a/orm_lib/tests/db_test.cc +++ b/orm_lib/tests/db_test.cc @@ -31,8 +31,25 @@ #include #include "mysql/Users.h" +#include "mysql/Wallets.h" +#include "mysql/Blog.h" +#include "mysql/Category.h" +#include "mysql/BlogTag.h" +#include "mysql/Tag.h" + #include "postgresql/Users.h" +#include "postgresql/Wallets.h" +#include "postgresql/Blog.h" +#include "postgresql/Category.h" +#include "postgresql/BlogTag.h" +#include "postgresql/Tag.h" + #include "sqlite3/Users.h" +#include "sqlite3/Wallets.h" +#include "sqlite3/Blog.h" +#include "sqlite3/Category.h" +#include "sqlite3/BlogTag.h" +#include "sqlite3/Tag.h" using namespace std::chrono_literals; using namespace drogon::orm; @@ -88,6 +105,89 @@ DROGON_TEST(PostgreTest) FAULT("postgresql - Prepare the test environment(1) what():", e.base().what()); }; + // wallets table one-to-one with users table + *clientPtr << "DROP TABLE IF EXISTS wallets" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("postgresql - Prepare the test environment(2) what():", + e.base().what()); + }; + *clientPtr << "CREATE TABLE wallets (" + " id serial PRIMARY KEY," + " user_id character varying(32) DEFAULT NULL," + " amount numeric(16,2) DEFAULT NULL" + ")" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("postgresql - Prepare the test environment(3) what():", + e.base().what()); + }; + // blog + *clientPtr << "DROP TABLE IF EXISTS blog" >> [TEST_CTX](const Result &r) { + SUCCESS(); + } >> [TEST_CTX](const DrogonDbException &e) { + FAULT("postgresql - Prepare the test environment(4) what():", + e.base().what()); + }; + *clientPtr << "CREATE TABLE blog (" + " id serial PRIMARY KEY," + " title character varying(30) DEFAULT NULL," + " category_id int4 DEFAULT NULL" + ")" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("postgresql - Prepare the test environment(5) what():", + e.base().what()); + }; + // category table one-to-many with blog table + *clientPtr << "DROP TABLE IF EXISTS category" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("postgresql - Prepare the test environment(6) what():", + e.base().what()); + }; + *clientPtr << "CREATE TABLE category (" + " id serial PRIMARY KEY," + " name character varying(30) DEFAULT NULL" + ")" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("postgresql - Prepare the test environment(7) what():", + e.base().what()); + }; + // tag table many-to-many with blog table + *clientPtr << "DROP TABLE IF EXISTS tag" >> [TEST_CTX](const Result &r) { + SUCCESS(); + } >> [TEST_CTX](const DrogonDbException &e) { + FAULT("postgresql - Prepare the test environment(8) what():", + e.base().what()); + }; + *clientPtr << "CREATE TABLE tag (" + " id serial PRIMARY KEY," + " name character varying(30) DEFAULT NULL" + ")" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("postgresql - Prepare the test environment(9) what():", + e.base().what()); + }; + // blog_tag table is an intermediate table + *clientPtr << "DROP TABLE IF EXISTS blog_tag" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("postgresql - Prepare the test environment(10) what():", + e.base().what()); + }; + *clientPtr << "CREATE TABLE blog_tag (" + " blog_id int4 NOT NULL," + " tag_id int4 NOT NULL," + " CONSTRAINT blog_tag_pkey PRIMARY KEY (blog_id, tag_id)" + ")" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("postgresql - Prepare the test environment(11) what():", + e.base().what()); + }; /// Test1:DbClient streaming-type interface /// 1.1 insert,non-blocking *clientPtr << "insert into users (user_id,user_name,password,org_name) " @@ -981,6 +1081,364 @@ DROGON_TEST(PostgreTest) drogon::sync_wait(coro_test()); #endif + + /// 8 Test ORM related query + /// 8.1 async + /// 8.1.1 one-to-one + Mapper walletsMapper(clientPtr); + + /// prepare + { + Wallets wallet; + wallet.setUserId("pg"); + wallet.setAmount("2000.00"); + try + { + walletsMapper.insert(wallet); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("postgresql - ORM mapper related prepare(0) what():", + e.base().what()); + } + } + + /// users to wallets + { + Users user; + user.setUserId("pg"); + user.getWallet( + clientPtr, + [TEST_CTX](Wallets r) { + MANDATE(r.getValueOfAmount() == "2000.00"); + }, + [TEST_CTX](const DrogonDbException &e) { + FAULT( + "postgresql - ORM mapper related async one-to-one(0) " + "what():", + e.base().what()); + }); + } + /// users to wallets without data + { + Users user; + user.setUserId("pg1"); + user.getWallet( + clientPtr, + [TEST_CTX](Wallets w) { + FAULT("postgresql - ORM mapper related async one-to-one(1)"); + }, + [TEST_CTX](const DrogonDbException &e) { SUCCESS(); }); + } + + /// 8.1.2 one-to-many + Mapper categoryMapper(clientPtr); + Mapper blogMapper(clientPtr); + + /// prepare + { + Category category; + category.setName("category1"); + try + { + categoryMapper.insert(category); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("postgresql - ORM mapper related prepare(1) what():", + e.base().what()); + } + category.setName("category2"); + try + { + categoryMapper.insert(category); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("postgresql - ORM mapper related prepare(2) what():", + e.base().what()); + } + Blog blog; + blog.setTitle("title1"); + blog.setCategoryId(1); + try + { + blogMapper.insert(blog); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("postgresql - ORM mapper related prepare(3) what():", + e.base().what()); + } + blog.setTitle("title2"); + blog.setCategoryId(1); + try + { + blogMapper.insert(blog); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("postgresql - ORM mapper related prepare(4) what():", + e.base().what()); + } + blog.setTitle("title3"); + blog.setCategoryId(3); + try + { + blogMapper.insert(blog); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("postgresql - ORM mapper related prepare(5) what():", + e.base().what()); + } + } + + /// categories to blogs + { + Category category; + category.setId(1); + category.getBlogs( + clientPtr, + [TEST_CTX](std::vector r) { MANDATE(r.size() == 2); }, + [TEST_CTX](const DrogonDbException &e) { + FAULT( + "postgresql - ORM mapper related async one-to-many(0) " + "what():", + e.base().what()); + }); + } + /// categories to blogs without data + { + Category category; + category.setId(2); + category.getBlogs( + clientPtr, + [TEST_CTX](std::vector r) { MANDATE(r.size() == 0); }, + [TEST_CTX](const DrogonDbException &e) { + FAULT( + "postgresql - ORM mapper related async one-to-many(1) " + "what():", + e.base().what()); + }); + } + /// blogs to categories + { + Blog blog; + blog.setCategoryId(1); + blog.getCategory( + clientPtr, + [TEST_CTX](Category r) { + MANDATE(r.getValueOfName() == "category1"); + }, + [TEST_CTX](const DrogonDbException &e) { + FAULT( + "postgresql - ORM mapper related async one-to-many(2) " + "what():", + e.base().what()); + }); + } + /// blogs to categories without data + { + Blog blog; + blog.setCategoryId(3); + blog.getCategory( + clientPtr, + [TEST_CTX](Category r) { + FAULT("postgresql - ORM mapper related async one-to-many(3)"); + }, + [TEST_CTX](const DrogonDbException &e) { SUCCESS(); }); + } + + /// 8.1.3 many-to-many + Mapper blogTagMapper(clientPtr); + Mapper tagMapper(clientPtr); + + /// prepare + { + BlogTag blogTag; + blogTag.setBlogId(1); + blogTag.setTagId(1); + try + { + blogTagMapper.insert(blogTag); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("postgresql - ORM mapper related prepare(6) what():", + e.base().what()); + } + blogTag.setBlogId(1); + blogTag.setTagId(2); + try + { + blogTagMapper.insert(blogTag); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("postgresql - ORM mapper related prepare(7) what():", + e.base().what()); + } + Tag tag; + tag.setName("tag1"); + try + { + tagMapper.insert(tag); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("postgresql - ORM mapper related prepare(8) what():", + e.base().what()); + } + tag.setName("tag2"); + try + { + tagMapper.insert(tag); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("postgresql - ORM mapper related prepare(9) what():", + e.base().what()); + } + } + + /// blogs to tags + { + Blog blog; + blog.setId(1); + blog.getTags( + clientPtr, + [TEST_CTX](std::vector> r) { + MANDATE(r.size() == 2); + }, + [TEST_CTX](const DrogonDbException &e) { + FAULT( + "postgresql - ORM mapper related async many-to-many(0) " + "what():", + e.base().what()); + }); + } + + /// 8.2 async + /// 8.2.1 one-to-one + /// users to wallets + { + Users user; + user.setUserId("pg"); + try + { + auto r = user.getWallet(clientPtr); + MANDATE(r.getValueOfAmount() == "2000.00"); + } + catch (const DrogonDbException &e) + { + FAULT("postgresql - ORM mapper related sync one-to-one(0) what():", + e.base().what()); + } + } + /// users to wallets without data + { + Users user; + user.setUserId("pg1"); + try + { + auto r = user.getWallet(clientPtr); + FAULT("postgresql - ORM mapper related sync one-to-one(1)"); + } + catch (const DrogonDbException &e) + { + SUCCESS(); + } + } + + /// 8.2.2 one-to-many + /// categories to blogs + { + Category category; + category.setId(1); + try + { + auto r = category.getBlogs(clientPtr); + MANDATE(r.size() == 2); + } + catch (const DrogonDbException &e) + { + FAULT("postgresql - ORM mapper related sync one-to-many(0) what():", + e.base().what()); + } + } + /// categories to blogs without data + { + Category category; + category.setId(2); + try + { + auto r = category.getBlogs(clientPtr); + MANDATE(r.size() == 0); + } + catch (const DrogonDbException &e) + { + FAULT("postgresql - ORM mapper related sync one-to-many(1) what():", + e.base().what()); + } + } + /// blogs to categories + { + Blog blog; + blog.setCategoryId(1); + try + { + auto r = blog.getCategory(clientPtr); + MANDATE(r.getValueOfName() == "category1"); + } + catch (const DrogonDbException &e) + { + FAULT("postgresql - ORM mapper related sync one-to-many(2) what():", + e.base().what()); + } + } + /// blogs to categories without data + { + Blog blog; + blog.setCategoryId(3); + try + { + auto r = blog.getCategory(clientPtr); + FAULT("postgresql - ORM mapper related sync one-to-many(3)"); + } + catch (const DrogonDbException &e) + { + SUCCESS(); + } + } + + /// 8.2.3 many-to-many + /// blogs to tags + { + Blog blog; + blog.setId(1); + try + { + auto r = blog.getTags(clientPtr); + MANDATE(r.size() == 2); + } + catch (const DrogonDbException &e) + { + FAULT( + "postgresql - ORM mapper related sync many-to-many(0) what():", + e.base().what()); + } + } } #endif @@ -1035,6 +1493,89 @@ DROGON_TEST(MySQLTest) FAULT("mysql - Prepare the test environment(2) what():", e.base().what()); }; + // wallets table one-to-one with users table + *clientPtr << "DROP TABLE IF EXISTS wallets" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("mysql - Prepare the test environment(3) what():", + e.base().what()); + }; + *clientPtr << "CREATE TABLE `wallets` (" + " `id` int(11) AUTO_INCREMENT PRIMARY KEY," + " `user_id` varchar(32) DEFAULT NULL," + " `amount` decimal(16,2) DEFAULT NULL" + ")" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("mysql - Prepare the test environment(4) what():", + e.base().what()); + }; + // blog + *clientPtr << "DROP TABLE IF EXISTS blog" >> [TEST_CTX](const Result &r) { + SUCCESS(); + } >> [TEST_CTX](const DrogonDbException &e) { + FAULT("mysql - Prepare the test environment(5) what():", + e.base().what()); + }; + *clientPtr << "CREATE TABLE `blog` (" + " `id` int(11) AUTO_INCREMENT PRIMARY KEY," + " `title` varchar(30) DEFAULT NULL," + " `category_id` int(11) DEFAULT NULL" + ")" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("mysql - Prepare the test environment(6) what():", + e.base().what()); + }; + // category table one-to-many with blog table + *clientPtr << "DROP TABLE IF EXISTS category" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("mysql - Prepare the test environment(7) what():", + e.base().what()); + }; + *clientPtr << "CREATE TABLE `category` (" + " `id` int(11) AUTO_INCREMENT PRIMARY KEY," + " `name` varchar(30) DEFAULT NULL" + ")" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("mysql - Prepare the test environment(8) what():", + e.base().what()); + }; + // tag table many-to-many with blog table + *clientPtr << "DROP TABLE IF EXISTS tag" >> [TEST_CTX](const Result &r) { + SUCCESS(); + } >> [TEST_CTX](const DrogonDbException &e) { + FAULT("mysql - Prepare the test environment(9) what():", + e.base().what()); + }; + *clientPtr << "CREATE TABLE `tag` (" + " `id` int(11) AUTO_INCREMENT PRIMARY KEY," + " `name` varchar(30) DEFAULT NULL" + ")" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("mysql - Prepare the test environment(10) what():", + e.base().what()); + }; + // blog_tag table is an intermediate table + *clientPtr << "DROP TABLE IF EXISTS blog_tag" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("mysql - Prepare the test environment(11) what():", + e.base().what()); + }; + *clientPtr << "CREATE TABLE `blog_tag` (" + " `blog_id` int(11) NOT NULL," + " `tag_id` int(11) NOT NULL," + " PRIMARY KEY (`blog_id`,`tag_id`)" + ")" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("mysql - Prepare the test environment(12) what():", + e.base().what()); + }; /// Test1:DbClient streaming-type interface /// 1.1 insert,non-blocking *clientPtr @@ -1751,6 +2292,354 @@ DROGON_TEST(MySQLTest) drogon::sync_wait(coro_test()); #endif + + /// 8 Test ORM related query + /// 8.1 async + /// 8.1.1 one-to-one + Mapper walletsMapper(clientPtr); + + /// prepare + { + Wallets wallet; + wallet.setUserId("pg"); + wallet.setAmount("2000.00"); + try + { + walletsMapper.insert(wallet); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("mysql - ORM mapper related prepare(0) what():", + e.base().what()); + } + } + + /// users to wallets + { + Users user; + user.setUserId("pg"); + user.getWallet( + clientPtr, + [TEST_CTX](Wallets r) { + MANDATE(r.getValueOfAmount() == "2000.00"); + }, + [TEST_CTX](const DrogonDbException &e) { + FAULT("mysql - ORM mapper related async one-to-one(0) what():", + e.base().what()); + }); + } + /// users to wallets without data + { + Users user; + user.setUserId("pg1"); + user.getWallet( + clientPtr, + [TEST_CTX](Wallets w) { + FAULT("mysql - ORM mapper related async one-to-one(1)"); + }, + [TEST_CTX](const DrogonDbException &e) { SUCCESS(); }); + } + + /// 8.1.2 one-to-many + Mapper categoryMapper(clientPtr); + Mapper blogMapper(clientPtr); + + /// prepare + { + Category category; + category.setName("category1"); + try + { + categoryMapper.insert(category); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("mysql - ORM mapper related prepare(1) what():", + e.base().what()); + } + category.setName("category2"); + try + { + categoryMapper.insert(category); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("mysql - ORM mapper related prepare(2) what():", + e.base().what()); + } + Blog blog; + blog.setTitle("title1"); + blog.setCategoryId(1); + try + { + blogMapper.insert(blog); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("mysql - ORM mapper related prepare(3) what():", + e.base().what()); + } + blog.setTitle("title2"); + blog.setCategoryId(1); + try + { + blogMapper.insert(blog); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("mysql - ORM mapper related prepare(4) what():", + e.base().what()); + } + blog.setTitle("title3"); + blog.setCategoryId(3); + try + { + blogMapper.insert(blog); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("mysql - ORM mapper related prepare(5) what():", + e.base().what()); + } + } + + /// categories to blogs + { + Category category; + category.setId(1); + category.getBlogs( + clientPtr, + [TEST_CTX](std::vector r) { MANDATE(r.size() == 2); }, + [TEST_CTX](const DrogonDbException &e) { + FAULT("mysql - ORM mapper related async one-to-many(0) what():", + e.base().what()); + }); + } + /// categories to blogs without data + { + Category category; + category.setId(2); + category.getBlogs( + clientPtr, + [TEST_CTX](std::vector r) { MANDATE(r.size() == 0); }, + [TEST_CTX](const DrogonDbException &e) { + FAULT("mysql - ORM mapper related async one-to-many(1) what():", + e.base().what()); + }); + } + /// blogs to categories + { + Blog blog; + blog.setCategoryId(1); + blog.getCategory( + clientPtr, + [TEST_CTX](Category r) { + MANDATE(r.getValueOfName() == "category1"); + }, + [TEST_CTX](const DrogonDbException &e) { + FAULT("mysql - ORM mapper related async one-to-many(2) what():", + e.base().what()); + }); + } + /// blogs to categories without data + { + Blog blog; + blog.setCategoryId(3); + blog.getCategory( + clientPtr, + [TEST_CTX](Category r) { + FAULT("mysql - ORM mapper related async one-to-many(3)"); + }, + [TEST_CTX](const DrogonDbException &e) { SUCCESS(); }); + } + + /// 8.1.3 many-to-many + Mapper blogTagMapper(clientPtr); + Mapper tagMapper(clientPtr); + + /// prepare + { + BlogTag blogTag; + blogTag.setBlogId(1); + blogTag.setTagId(1); + try + { + blogTagMapper.insert(blogTag); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("mysql - ORM mapper related prepare(6) what():", + e.base().what()); + } + blogTag.setBlogId(1); + blogTag.setTagId(2); + try + { + blogTagMapper.insert(blogTag); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("mysql - ORM mapper related prepare(7) what():", + e.base().what()); + } + Tag tag; + tag.setName("tag1"); + try + { + tagMapper.insert(tag); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("mysql - ORM mapper related prepare(8) what():", + e.base().what()); + } + tag.setName("tag2"); + try + { + tagMapper.insert(tag); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("mysql - ORM mapper related prepare(9) what():", + e.base().what()); + } + } + + /// blogs to tags + { + Blog blog; + blog.setId(1); + blog.getTags( + clientPtr, + [TEST_CTX](std::vector> r) { + MANDATE(r.size() == 2); + }, + [TEST_CTX](const DrogonDbException &e) { + FAULT( + "mysql - ORM mapper related async many-to-many(0) what():", + e.base().what()); + }); + } + + /// 8.2 async + /// 8.2.1 one-to-one + /// users to wallets + { + Users user; + user.setUserId("pg"); + try + { + auto r = user.getWallet(clientPtr); + MANDATE(r.getValueOfAmount() == "2000.00"); + } + catch (const DrogonDbException &e) + { + FAULT("mysql - ORM mapper related sync one-to-one(0) what():", + e.base().what()); + } + } + /// users to wallets without data + { + Users user; + user.setUserId("pg1"); + try + { + auto r = user.getWallet(clientPtr); + FAULT("mysql - ORM mapper related sync one-to-one(1)"); + } + catch (const DrogonDbException &e) + { + SUCCESS(); + } + } + + /// 8.2.2 one-to-many + /// categories to blogs + { + Category category; + category.setId(1); + try + { + auto r = category.getBlogs(clientPtr); + MANDATE(r.size() == 2); + } + catch (const DrogonDbException &e) + { + FAULT("mysql - ORM mapper related sync one-to-many(0) what():", + e.base().what()); + } + } + /// categories to blogs without data + { + Category category; + category.setId(2); + try + { + auto r = category.getBlogs(clientPtr); + MANDATE(r.size() == 0); + } + catch (const DrogonDbException &e) + { + FAULT("mysql - ORM mapper related sync one-to-many(1) what():", + e.base().what()); + } + } + /// blogs to categories + { + Blog blog; + blog.setCategoryId(1); + try + { + auto r = blog.getCategory(clientPtr); + MANDATE(r.getValueOfName() == "category1"); + } + catch (const DrogonDbException &e) + { + FAULT("mysql - ORM mapper related sync one-to-many(2) what():", + e.base().what()); + } + } + /// blogs to categories without data + { + Blog blog; + blog.setCategoryId(3); + try + { + auto r = blog.getCategory(clientPtr); + FAULT("mysql - ORM mapper related sync one-to-many(3)"); + } + catch (const DrogonDbException &e) + { + SUCCESS(); + } + } + + /// 8.2.3 many-to-many + /// blogs to tags + { + Blog blog; + blog.setId(1); + try + { + auto r = blog.getTags(clientPtr); + MANDATE(r.size() == 2); + } + catch (const DrogonDbException &e) + { + FAULT("mysql - ORM mapper related sync many-to-many(0) what():", + e.base().what()); + } + } } #endif @@ -1794,6 +2683,89 @@ DROGON_TEST(SQLite3Test) FAULT("sqlite3 - Prepare the test environment(1) what():", e.base().what()); }; + // wallets table one-to-one with users table + *clientPtr << "DROP TABLE IF EXISTS wallets" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("sqlite3 - Prepare the test environment(2) what():", + e.base().what()); + }; + *clientPtr << "CREATE TABLE `wallets` (" + " `id` INTEGER PRIMARY KEY autoincrement," + " `user_id` varchar(32) DEFAULT NULL," + " `amount` decimal(16,2) DEFAULT NULL" + ")" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("sqlite3 - Prepare the test environment(3) what():", + e.base().what()); + }; + // blog + *clientPtr << "DROP TABLE IF EXISTS blog" >> [TEST_CTX](const Result &r) { + SUCCESS(); + } >> [TEST_CTX](const DrogonDbException &e) { + FAULT("sqlite3 - Prepare the test environment(4) what():", + e.base().what()); + }; + *clientPtr << "CREATE TABLE `blog` (" + " `id` INTEGER PRIMARY KEY autoincrement," + " `title` varchar(30) DEFAULT NULL," + " `category_id` INTEGER DEFAULT NULL" + ")" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("sqlite3 - Prepare the test environment(5) what():", + e.base().what()); + }; + // category table one-to-many with blog table + *clientPtr << "DROP TABLE IF EXISTS category" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("sqlite3 - Prepare the test environment(6) what():", + e.base().what()); + }; + *clientPtr << "CREATE TABLE `category` (" + " `id` INTEGER PRIMARY KEY autoincrement," + " `name` varchar(30) DEFAULT NULL" + ")" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("sqlite3 - Prepare the test environment(7) what():", + e.base().what()); + }; + // tag table many-to-many with blog table + *clientPtr << "DROP TABLE IF EXISTS tag" >> [TEST_CTX](const Result &r) { + SUCCESS(); + } >> [TEST_CTX](const DrogonDbException &e) { + FAULT("sqlite3 - Prepare the test environment(8) what():", + e.base().what()); + }; + *clientPtr << "CREATE TABLE `tag` (" + " `id` INTEGER PRIMARY KEY autoincrement," + " `name` varchar(30) DEFAULT NULL" + ")" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("sqlite3 - Prepare the test environment(9) what():", + e.base().what()); + }; + // blog_tag table is an intermediate table + *clientPtr << "DROP TABLE IF EXISTS blog_tag" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("sqlite3 - Prepare the test environment(10) what():", + e.base().what()); + }; + *clientPtr << "CREATE TABLE `blog_tag` (" + " `blog_id` INTEGER NOT NULL," + " `tag_id` INTEGER NOT NULL," + " PRIMARY KEY (`blog_id`,`tag_id`)" + ")" >> + [TEST_CTX](const Result &r) { SUCCESS(); } >> + [TEST_CTX](const DrogonDbException &e) { + FAULT("sqlite3 - Prepare the test environment(11) what():", + e.base().what()); + }; /// Test1:DbClient streaming-type interface /// 1.1 insert,non-blocking *clientPtr << "insert into users " @@ -2591,6 +3563,360 @@ DROGON_TEST(SQLite3Test) drogon::sync_wait(coro_test()); #endif + + /// 8 Test ORM related query + /// 8.1 async + /// 8.1.1 one-to-one + Mapper walletsMapper(clientPtr); + + /// prepare + { + Wallets wallet; + wallet.setUserId("pg"); + wallet.setAmount("2000.00"); + try + { + walletsMapper.insert(wallet); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("sqlite3 - ORM mapper related prepare(0) what():", + e.base().what()); + } + } + + /// users to wallets + { + Users user; + user.setUserId("pg"); + user.getWallet( + clientPtr, + [TEST_CTX](Wallets r) { MANDATE(r.getValueOfAmount() == "2000"); }, + [TEST_CTX](const DrogonDbException &e) { + FAULT( + "sqlite3 - ORM mapper related async one-to-one(0) what(): ", + e.base().what()); + }); + } + /// users to wallets without data + { + Users user; + user.setUserId("pg1"); + user.getWallet( + clientPtr, + [TEST_CTX](Wallets w) { + FAULT("sqlite3 - ORM mapper related async one-to-one(1)"); + }, + [TEST_CTX](const DrogonDbException &e) { SUCCESS(); }); + } + + /// 8.1.2 one-to-many + Mapper categoryMapper(clientPtr); + Mapper blogMapper(clientPtr); + + /// prepare + { + Category category; + category.setName("category1"); + try + { + categoryMapper.insert(category); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("sqlite3 - ORM mapper related prepare(1) what():", + e.base().what()); + } + category.setName("category2"); + try + { + categoryMapper.insert(category); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("sqlite3 - ORM mapper related prepare(2) what():", + e.base().what()); + } + Blog blog; + blog.setTitle("title1"); + blog.setCategoryId(1); + try + { + blogMapper.insert(blog); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("sqlite3 - ORM mapper related prepare(3) what():", + e.base().what()); + } + blog.setTitle("title2"); + blog.setCategoryId(1); + try + { + blogMapper.insert(blog); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("sqlite3 - ORM mapper related prepare(4) what():", + e.base().what()); + } + blog.setTitle("title3"); + blog.setCategoryId(3); + try + { + blogMapper.insert(blog); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("sqlite3 - ORM mapper related prepare(5) what():", + e.base().what()); + } + } + + /// categories to blogs + { + Category category; + category.setId(1); + category.getBlogs( + clientPtr, + [TEST_CTX](std::vector r) { MANDATE(r.size() == 2); }, + [TEST_CTX](const DrogonDbException &e) { + FAULT( + "sqlite3 - ORM mapper related async one-to-many(0) " + "what(): ", + e.base().what()); + }); + } + /// categories to blogs without data + { + Category category; + category.setId(2); + category.getBlogs( + clientPtr, + [TEST_CTX](std::vector r) { MANDATE(r.size() == 0); }, + [TEST_CTX](const DrogonDbException &e) { + FAULT( + "sqlite3 - ORM mapper related async one-to-many(1) " + "what(): ", + e.base().what()); + }); + } + /// blogs to categories + { + Blog blog; + blog.setCategoryId(1); + blog.getCategory( + clientPtr, + [TEST_CTX](Category r) { + MANDATE(r.getValueOfName() == "category1"); + }, + [TEST_CTX](const DrogonDbException &e) { + FAULT( + "sqlite3 - ORM mapper related async one-to-many(2) " + "what(): ", + e.base().what()); + }); + } + /// blogs to categories without data + { + Blog blog; + blog.setCategoryId(3); + blog.getCategory( + clientPtr, + [TEST_CTX](Category r) { + FAULT("sqlite3 - ORM mapper related async one-to-many(3)"); + }, + [TEST_CTX](const DrogonDbException &e) { SUCCESS(); }); + } + + /// 8.1.3 many-to-many + Mapper blogTagMapper(clientPtr); + Mapper tagMapper(clientPtr); + + /// prepare + { + BlogTag blogTag; + blogTag.setBlogId(1); + blogTag.setTagId(1); + try + { + blogTagMapper.insert(blogTag); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("sqlite3 - ORM mapper related prepare(6) what():", + e.base().what()); + } + blogTag.setBlogId(1); + blogTag.setTagId(2); + try + { + blogTagMapper.insert(blogTag); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("sqlite3 - ORM mapper related prepare(7) what():", + e.base().what()); + } + Tag tag; + tag.setName("tag1"); + try + { + tagMapper.insert(tag); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("sqlite3 - ORM mapper related prepare(8) what():", + e.base().what()); + } + tag.setName("tag2"); + try + { + tagMapper.insert(tag); + SUCCESS(); + } + catch (const DrogonDbException &e) + { + FAULT("sqlite3 - ORM mapper related prepare(9) what():", + e.base().what()); + } + } + + /// blogs to tags + { + Blog blog; + blog.setId(1); + blog.getTags( + clientPtr, + [TEST_CTX](std::vector> r) { + MANDATE(r.size() == 2); + }, + [TEST_CTX](const DrogonDbException &e) { + FAULT( + "sqlite3 - ORM mapper related async many-to-many(0) " + "what():", + e.base().what()); + }); + } + + /// 8.2 async + /// 8.2.1 one-to-one + /// users to wallets + { + Users user; + user.setUserId("pg"); + try + { + auto r = user.getWallet(clientPtr); + MANDATE(r.getValueOfAmount() == "2000"); + } + catch (const DrogonDbException &e) + { + FAULT("sqlite3 - ORM mapper related sync one-to-one(0) what():", + e.base().what()); + } + } + /// users to wallets without data + { + Users user; + user.setUserId("pg1"); + try + { + auto r = user.getWallet(clientPtr); + FAULT("sqlite3 - ORM mapper related sync one-to-one(1)"); + } + catch (const DrogonDbException &e) + { + SUCCESS(); + } + } + + /// 8.2.2 one-to-many + /// categories to blogs + { + Category category; + category.setId(1); + try + { + auto r = category.getBlogs(clientPtr); + MANDATE(r.size() == 2); + } + catch (const DrogonDbException &e) + { + FAULT("sqlite3 - ORM mapper related sync one-to-many(0) what():", + e.base().what()); + } + } + /// categories to blogs without data + { + Category category; + category.setId(2); + try + { + auto r = category.getBlogs(clientPtr); + MANDATE(r.size() == 0); + } + catch (const DrogonDbException &e) + { + FAULT("sqlite3 - ORM mapper related sync one-to-many(1) what():", + e.base().what()); + } + } + /// blogs to categories + { + Blog blog; + blog.setCategoryId(1); + try + { + auto r = blog.getCategory(clientPtr); + MANDATE(r.getValueOfName() == "category1"); + } + catch (const DrogonDbException &e) + { + FAULT("sqlite3 - ORM mapper related sync one-to-many(2) what():", + e.base().what()); + } + } + /// blogs to categories without data + { + Blog blog; + blog.setCategoryId(3); + try + { + auto r = blog.getCategory(clientPtr); + FAULT("sqlite3 - ORM mapper related sync one-to-many(3)"); + } + catch (const DrogonDbException &e) + { + SUCCESS(); + } + } + + /// 8.2.3 many-to-many + /// blogs to tags + { + Blog blog; + blog.setId(1); + try + { + auto r = blog.getTags(clientPtr); + MANDATE(r.size() == 2); + } + catch (const DrogonDbException &e) + { + FAULT("sqlite3 - ORM mapper related sync many-to-many(0) what():", + e.base().what()); + } + } } #endif @@ -2602,7 +3928,7 @@ int main(int argc, char **argv) #if USE_MYSQL mysqlClient = DbClient::newMysqlClient( - "host=localhost port=3306 user=root client_encoding=utf8mb4", 1); + "host=127.0.0.1 port=3306 user=root client_encoding=utf8mb4", 1); #endif #if USE_POSTGRESQL postgreClient = DbClient::newPgClient( diff --git a/orm_lib/tests/mysql/Blog.cc b/orm_lib/tests/mysql/Blog.cc new file mode 100644 index 00000000..0bde571c --- /dev/null +++ b/orm_lib/tests/mysql/Blog.cc @@ -0,0 +1,790 @@ +/** + * + * Blog.cc + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#include "Blog.h" +#include "BlogTag.h" +#include "Category.h" +#include "Tag.h" +#include +#include + +using namespace drogon; +using namespace drogon::orm; +using namespace drogon_model::drogonTestMysql; + +const std::string Blog::Cols::_id = "id"; +const std::string Blog::Cols::_title = "title"; +const std::string Blog::Cols::_category_id = "category_id"; +const std::string Blog::primaryKeyName = "id"; +const bool Blog::hasPrimaryKey = true; +const std::string Blog::tableName = "blog"; + +const std::vector Blog::metaData_ = { + {"id", "int32_t", "int(11)", 4, 1, 1, 1}, + {"title", "std::string", "varchar(30)", 30, 0, 0, 0}, + {"category_id", "int32_t", "int(11)", 4, 0, 0, 0}}; + +const std::string &Blog::getColumnName(size_t index) noexcept(false) +{ + assert(index < metaData_.size()); + return metaData_[index].colName_; +} + +Blog::Blog(const Row &r, const ssize_t indexOffset) noexcept +{ + if (indexOffset < 0) + { + if (!r["id"].isNull()) + { + id_ = std::make_shared(r["id"].as()); + } + if (!r["title"].isNull()) + { + title_ = + std::make_shared(r["title"].as()); + } + if (!r["category_id"].isNull()) + { + categoryId_ = + std::make_shared(r["category_id"].as()); + } + } + else + { + size_t offset = (size_t)indexOffset; + if (offset + 3 > r.size()) + { + LOG_FATAL << "Invalid SQL result for this model"; + return; + } + size_t index; + index = offset + 0; + if (!r[index].isNull()) + { + id_ = std::make_shared(r[index].as()); + } + index = offset + 1; + if (!r[index].isNull()) + { + title_ = std::make_shared(r[index].as()); + } + index = offset + 2; + if (!r[index].isNull()) + { + categoryId_ = std::make_shared(r[index].as()); + } + } +} + +Blog::Blog(const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 3) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + dirtyFlag_[0] = true; + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + title_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + dirtyFlag_[2] = true; + if (!pJson[pMasqueradingVector[2]].isNull()) + { + categoryId_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[2]].asInt64()); + } + } +} + +Blog::Blog(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + dirtyFlag_[0] = true; + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("title")) + { + dirtyFlag_[1] = true; + if (!pJson["title"].isNull()) + { + title_ = std::make_shared(pJson["title"].asString()); + } + } + if (pJson.isMember("category_id")) + { + dirtyFlag_[2] = true; + if (!pJson["category_id"].isNull()) + { + categoryId_ = std::make_shared( + (int32_t)pJson["category_id"].asInt64()); + } + } +} + +void Blog::updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 3) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + title_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + dirtyFlag_[2] = true; + if (!pJson[pMasqueradingVector[2]].isNull()) + { + categoryId_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[2]].asInt64()); + } + } +} + +void Blog::updateByJson(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("title")) + { + dirtyFlag_[1] = true; + if (!pJson["title"].isNull()) + { + title_ = std::make_shared(pJson["title"].asString()); + } + } + if (pJson.isMember("category_id")) + { + dirtyFlag_[2] = true; + if (!pJson["category_id"].isNull()) + { + categoryId_ = std::make_shared( + (int32_t)pJson["category_id"].asInt64()); + } + } +} + +const int32_t &Blog::getValueOfId() const noexcept +{ + const static int32_t defaultValue = int32_t(); + if (id_) + return *id_; + return defaultValue; +} + +const std::shared_ptr &Blog::getId() const noexcept +{ + return id_; +} + +void Blog::setId(const int32_t &pId) noexcept +{ + id_ = std::make_shared(pId); + dirtyFlag_[0] = true; +} + +const typename Blog::PrimaryKeyType &Blog::getPrimaryKey() const +{ + assert(id_); + return *id_; +} + +const std::string &Blog::getValueOfTitle() const noexcept +{ + const static std::string defaultValue = std::string(); + if (title_) + return *title_; + return defaultValue; +} + +const std::shared_ptr &Blog::getTitle() const noexcept +{ + return title_; +} + +void Blog::setTitle(const std::string &pTitle) noexcept +{ + title_ = std::make_shared(pTitle); + dirtyFlag_[1] = true; +} + +void Blog::setTitle(std::string &&pTitle) noexcept +{ + title_ = std::make_shared(std::move(pTitle)); + dirtyFlag_[1] = true; +} + +void Blog::setTitleToNull() noexcept +{ + title_.reset(); + dirtyFlag_[1] = true; +} + +const int32_t &Blog::getValueOfCategoryId() const noexcept +{ + const static int32_t defaultValue = int32_t(); + if (categoryId_) + return *categoryId_; + return defaultValue; +} + +const std::shared_ptr &Blog::getCategoryId() const noexcept +{ + return categoryId_; +} + +void Blog::setCategoryId(const int32_t &pCategoryId) noexcept +{ + categoryId_ = std::make_shared(pCategoryId); + dirtyFlag_[2] = true; +} + +void Blog::setCategoryIdToNull() noexcept +{ + categoryId_.reset(); + dirtyFlag_[2] = true; +} + +void Blog::updateId(const uint64_t id) +{ + id_ = std::make_shared(static_cast(id)); +} + +const std::vector &Blog::insertColumns() noexcept +{ + static const std::vector inCols = {"title", "category_id"}; + return inCols; +} + +void Blog::outputArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getTitle()) + { + binder << getValueOfTitle(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[2]) + { + if (getCategoryId()) + { + binder << getValueOfCategoryId(); + } + else + { + binder << nullptr; + } + } +} + +const std::vector Blog::updateColumns() const +{ + std::vector ret; + if (dirtyFlag_[1]) + { + ret.push_back(getColumnName(1)); + } + if (dirtyFlag_[2]) + { + ret.push_back(getColumnName(2)); + } + return ret; +} + +void Blog::updateArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getTitle()) + { + binder << getValueOfTitle(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[2]) + { + if (getCategoryId()) + { + binder << getValueOfCategoryId(); + } + else + { + binder << nullptr; + } + } +} + +Json::Value Blog::toJson() const +{ + Json::Value ret; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getTitle()) + { + ret["title"] = getValueOfTitle(); + } + else + { + ret["title"] = Json::Value(); + } + if (getCategoryId()) + { + ret["category_id"] = getValueOfCategoryId(); + } + else + { + ret["category_id"] = Json::Value(); + } + return ret; +} + +Json::Value Blog::toMasqueradedJson( + const std::vector &pMasqueradingVector) const +{ + Json::Value ret; + if (pMasqueradingVector.size() == 3) + { + if (!pMasqueradingVector[0].empty()) + { + if (getId()) + { + ret[pMasqueradingVector[0]] = getValueOfId(); + } + else + { + ret[pMasqueradingVector[0]] = Json::Value(); + } + } + if (!pMasqueradingVector[1].empty()) + { + if (getTitle()) + { + ret[pMasqueradingVector[1]] = getValueOfTitle(); + } + else + { + ret[pMasqueradingVector[1]] = Json::Value(); + } + } + if (!pMasqueradingVector[2].empty()) + { + if (getCategoryId()) + { + ret[pMasqueradingVector[2]] = getValueOfCategoryId(); + } + else + { + ret[pMasqueradingVector[2]] = Json::Value(); + } + } + return ret; + } + LOG_ERROR << "Masquerade failed"; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getTitle()) + { + ret["title"] = getValueOfTitle(); + } + else + { + ret["title"] = Json::Value(); + } + if (getCategoryId()) + { + ret["category_id"] = getValueOfCategoryId(); + } + else + { + ret["category_id"] = Json::Value(); + } + return ret; +} + +bool Blog::validateJsonForCreation(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, true)) + return false; + } + if (pJson.isMember("title")) + { + if (!validJsonOfField(1, "title", pJson["title"], err, true)) + return false; + } + if (pJson.isMember("category_id")) + { + if (!validJsonOfField( + 2, "category_id", pJson["category_id"], err, true)) + return false; + } + return true; +} + +bool Blog::validateMasqueradedJsonForCreation( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 3) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty()) + { + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[2].empty()) + { + if (pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + true)) + return false; + } + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Blog::validateJsonForUpdate(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (pJson.isMember("title")) + { + if (!validJsonOfField(1, "title", pJson["title"], err, false)) + return false; + } + if (pJson.isMember("category_id")) + { + if (!validJsonOfField( + 2, "category_id", pJson["category_id"], err, false)) + return false; + } + return true; +} + +bool Blog::validateMasqueradedJsonForUpdate( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 3) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + false)) + return false; + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Blog::validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation) +{ + switch (index) + { + case 0: + if (pJson.isNull()) + { + err = "The " + fieldName + " column cannot be null"; + return false; + } + if (isForCreation) + { + err = "The automatic primary key cannot be set"; + return false; + } + if (!pJson.isInt()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 1: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isString()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + // asString().length() creates a string object, is there any better + // way to validate the length? + if (pJson.isString() && pJson.asString().length() > 30) + { + err = "String length exceeds limit for the " + fieldName + + " field (the maximum value is 30)"; + return false; + } + + break; + case 2: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isInt()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + default: + err = "Internal error in the server"; + return false; + } + return true; +} + +Category Blog::getCategory(const DbClientPtr &clientPtr) const +{ + const static std::string sql = "select * from category where id = ?"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *categoryId_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + if (r.size() == 0) + { + throw UnexpectedRows("0 rows found"); + } + else if (r.size() > 1) + { + throw UnexpectedRows("Found more than one row"); + } + return Category(r[0]); +} + +void Blog::getCategory(const DbClientPtr &clientPtr, + const std::function &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = "select * from category where id = ?"; + *clientPtr << sql << *categoryId_ >> [rcb = std::move(rcb), + ecb](const Result &r) { + if (r.size() == 0) + { + ecb(UnexpectedRows("0 rows found")); + } + else if (r.size() > 1) + { + ecb(UnexpectedRows("Found more than one row")); + } + else + { + rcb(Category(r[0])); + } + } >> ecb; +} + +std::vector> Blog::getTags( + const DbClientPtr &clientPtr) const +{ + const static std::string sql = + "select * from tag,blog_tag where blog_tag.blog_id = ? and " + "blog_tag.tag_id = tag.id"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *id_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + std::vector> ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back( + std::pair(Tag(row), + BlogTag(row, Tag::getColumnNumber()))); + } + return ret; +} + +void Blog::getTags( + const DbClientPtr &clientPtr, + const std::function>)> &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = + "select * from tag,blog_tag where blog_tag.blog_id = ? and " + "blog_tag.tag_id = tag.id"; + *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) { + std::vector> ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back( + std::pair(Tag(row), + BlogTag(row, Tag::getColumnNumber()))); + } + rcb(ret); + } >> ecb; +} diff --git a/orm_lib/tests/mysql/Blog.h b/orm_lib/tests/mysql/Blog.h new file mode 100644 index 00000000..72d31309 --- /dev/null +++ b/orm_lib/tests/mysql/Blog.h @@ -0,0 +1,260 @@ +/** + * + * Blog.h + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#ifdef __cpp_impl_coroutine +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClient; +using DbClientPtr = std::shared_ptr; +} // namespace orm +} // namespace drogon + +namespace drogon_model +{ +namespace drogonTestMysql +{ +class BlogTag; +class Category; +class Tag; + +class Blog +{ + public: + struct Cols + { + static const std::string _id; + static const std::string _title; + static const std::string _category_id; + }; + + const static int primaryKeyNumber; + const static std::string tableName; + const static bool hasPrimaryKey; + const static std::string primaryKeyName; + using PrimaryKeyType = int32_t; + const PrimaryKeyType &getPrimaryKey() const; + + /** + * @brief constructor + * @param r One row of records in the SQL query result. + * @param indexOffset Set the offset to -1 to access all columns by column + * names, otherwise access all columns by offsets. + * @note If the SQL is not a style of 'select * from table_name ...' (select + * all columns by an asterisk), please set the offset to -1. + */ + explicit Blog(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + */ + explicit Blog(const Json::Value &pJson) noexcept(false); + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + * @param pMasqueradingVector The aliases of table columns. + */ + Blog(const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + + Blog() = default; + + void updateByJson(const Json::Value &pJson) noexcept(false); + void updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + static bool validateJsonForCreation(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForCreation( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validateJsonForUpdate(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForUpdate( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation); + + /** For column id */ + /// Get the value of the column id, returns the default value if the column + /// is null + const int32_t &getValueOfId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getId() const noexcept; + /// Set the value of the column id + void setId(const int32_t &pId) noexcept; + + /** For column title */ + /// Get the value of the column title, returns the default value if the + /// column is null + const std::string &getValueOfTitle() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getTitle() const noexcept; + /// Set the value of the column title + void setTitle(const std::string &pTitle) noexcept; + void setTitle(std::string &&pTitle) noexcept; + void setTitleToNull() noexcept; + + /** For column category_id */ + /// Get the value of the column category_id, returns the default value if + /// the column is null + const int32_t &getValueOfCategoryId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getCategoryId() const noexcept; + /// Set the value of the column category_id + void setCategoryId(const int32_t &pCategoryId) noexcept; + void setCategoryIdToNull() noexcept; + + static size_t getColumnNumber() noexcept + { + return 3; + } + + static const std::string &getColumnName(size_t index) noexcept(false); + + Json::Value toJson() const; + Json::Value toMasqueradedJson( + const std::vector &pMasqueradingVector) const; + /// Relationship interfaces + Category getCategory(const drogon::orm::DbClientPtr &clientPtr) const; + void getCategory(const drogon::orm::DbClientPtr &clientPtr, + const std::function &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + std::vector> getTags( + const drogon::orm::DbClientPtr &clientPtr) const; + void getTags( + const drogon::orm::DbClientPtr &clientPtr, + const std::function>)> &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + + private: + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; +#ifdef __cpp_impl_coroutine + friend drogon::orm::CoroMapper; +#endif + static const std::vector &insertColumns() noexcept; + void outputArgs(drogon::orm::internal::SqlBinder &binder) const; + const std::vector updateColumns() const; + void updateArgs(drogon::orm::internal::SqlBinder &binder) const; + /// For mysql or sqlite3 + void updateId(const uint64_t id); + std::shared_ptr id_; + std::shared_ptr title_; + std::shared_ptr categoryId_; + + struct MetaData + { + const std::string colName_; + const std::string colType_; + const std::string colDatabaseType_; + const ssize_t colLength_; + const bool isAutoVal_; + const bool isPrimaryKey_; + const bool notNull_; + }; + + static const std::vector metaData_; + bool dirtyFlag_[3] = {false}; + + public: + static const std::string &sqlForFindingByPrimaryKey() + { + static const std::string sql = + "select * from " + tableName + " where id = ?"; + return sql; + } + + static const std::string &sqlForDeletingByPrimaryKey() + { + static const std::string sql = + "delete from " + tableName + " where id = ?"; + return sql; + } + + std::string sqlForInserting(bool &needSelection) const + { + std::string sql = "insert into " + tableName + " ("; + size_t parametersCount = 0; + needSelection = false; + sql += "id,"; + ++parametersCount; + if (dirtyFlag_[1]) + { + sql += "title,"; + ++parametersCount; + } + if (dirtyFlag_[2]) + { + sql += "category_id,"; + ++parametersCount; + } + needSelection = true; + if (parametersCount > 0) + { + sql[sql.length() - 1] = ')'; + sql += " values ("; + } + else + sql += ") values ("; + + sql += "default,"; + if (dirtyFlag_[1]) + { + sql.append("?,"); + } + if (dirtyFlag_[2]) + { + sql.append("?,"); + } + if (parametersCount > 0) + { + sql.resize(sql.length() - 1); + } + sql.append(1, ')'); + LOG_TRACE << sql; + return sql; + } +}; +} // namespace drogonTestMysql +} // namespace drogon_model diff --git a/orm_lib/tests/mysql/BlogTag.cc b/orm_lib/tests/mysql/BlogTag.cc new file mode 100644 index 00000000..ed042fbe --- /dev/null +++ b/orm_lib/tests/mysql/BlogTag.cc @@ -0,0 +1,564 @@ +/** + * + * BlogTag.cc + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#include "BlogTag.h" +#include +#include + +using namespace drogon; +using namespace drogon::orm; +using namespace drogon_model::drogonTestMysql; + +const std::string BlogTag::Cols::_blog_id = "blog_id"; +const std::string BlogTag::Cols::_tag_id = "tag_id"; +const std::vector BlogTag::primaryKeyName = {"blog_id", "tag_id"}; +const bool BlogTag::hasPrimaryKey = true; +const std::string BlogTag::tableName = "blog_tag"; + +const std::vector BlogTag::metaData_ = { + {"blog_id", "int32_t", "int(11)", 4, 0, 1, 1}, + {"tag_id", "int32_t", "int(11)", 4, 0, 1, 1}}; + +const std::string &BlogTag::getColumnName(size_t index) noexcept(false) +{ + assert(index < metaData_.size()); + return metaData_[index].colName_; +} + +BlogTag::BlogTag(const Row &r, const ssize_t indexOffset) noexcept +{ + if (indexOffset < 0) + { + if (!r["blog_id"].isNull()) + { + blogId_ = std::make_shared(r["blog_id"].as()); + } + if (!r["tag_id"].isNull()) + { + tagId_ = std::make_shared(r["tag_id"].as()); + } + } + else + { + size_t offset = (size_t)indexOffset; + if (offset + 2 > r.size()) + { + LOG_FATAL << "Invalid SQL result for this model"; + return; + } + size_t index; + index = offset + 0; + if (!r[index].isNull()) + { + blogId_ = std::make_shared(r[index].as()); + } + index = offset + 1; + if (!r[index].isNull()) + { + tagId_ = std::make_shared(r[index].as()); + } + } +} + +BlogTag::BlogTag( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + dirtyFlag_[0] = true; + if (!pJson[pMasqueradingVector[0]].isNull()) + { + blogId_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + tagId_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[1]].asInt64()); + } + } +} + +BlogTag::BlogTag(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("blog_id")) + { + dirtyFlag_[0] = true; + if (!pJson["blog_id"].isNull()) + { + blogId_ = + std::make_shared((int32_t)pJson["blog_id"].asInt64()); + } + } + if (pJson.isMember("tag_id")) + { + dirtyFlag_[1] = true; + if (!pJson["tag_id"].isNull()) + { + tagId_ = + std::make_shared((int32_t)pJson["tag_id"].asInt64()); + } + } +} + +void BlogTag::updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!pJson[pMasqueradingVector[0]].isNull()) + { + blogId_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!pJson[pMasqueradingVector[1]].isNull()) + { + tagId_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[1]].asInt64()); + } + } +} + +void BlogTag::updateByJson(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("blog_id")) + { + if (!pJson["blog_id"].isNull()) + { + blogId_ = + std::make_shared((int32_t)pJson["blog_id"].asInt64()); + } + } + if (pJson.isMember("tag_id")) + { + if (!pJson["tag_id"].isNull()) + { + tagId_ = + std::make_shared((int32_t)pJson["tag_id"].asInt64()); + } + } +} + +const int32_t &BlogTag::getValueOfBlogId() const noexcept +{ + const static int32_t defaultValue = int32_t(); + if (blogId_) + return *blogId_; + return defaultValue; +} + +const std::shared_ptr &BlogTag::getBlogId() const noexcept +{ + return blogId_; +} + +void BlogTag::setBlogId(const int32_t &pBlogId) noexcept +{ + blogId_ = std::make_shared(pBlogId); + dirtyFlag_[0] = true; +} + +const int32_t &BlogTag::getValueOfTagId() const noexcept +{ + const static int32_t defaultValue = int32_t(); + if (tagId_) + return *tagId_; + return defaultValue; +} + +const std::shared_ptr &BlogTag::getTagId() const noexcept +{ + return tagId_; +} + +void BlogTag::setTagId(const int32_t &pTagId) noexcept +{ + tagId_ = std::make_shared(pTagId); + dirtyFlag_[1] = true; +} + +void BlogTag::updateId(const uint64_t id) +{ +} + +typename BlogTag::PrimaryKeyType BlogTag::getPrimaryKey() const +{ + return std::make_tuple(*blogId_, *tagId_); +} + +const std::vector &BlogTag::insertColumns() noexcept +{ + static const std::vector inCols = {"blog_id", "tag_id"}; + return inCols; +} + +void BlogTag::outputArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[0]) + { + if (getBlogId()) + { + binder << getValueOfBlogId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[1]) + { + if (getTagId()) + { + binder << getValueOfTagId(); + } + else + { + binder << nullptr; + } + } +} + +const std::vector BlogTag::updateColumns() const +{ + std::vector ret; + if (dirtyFlag_[0]) + { + ret.push_back(getColumnName(0)); + } + if (dirtyFlag_[1]) + { + ret.push_back(getColumnName(1)); + } + return ret; +} + +void BlogTag::updateArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[0]) + { + if (getBlogId()) + { + binder << getValueOfBlogId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[1]) + { + if (getTagId()) + { + binder << getValueOfTagId(); + } + else + { + binder << nullptr; + } + } +} + +Json::Value BlogTag::toJson() const +{ + Json::Value ret; + if (getBlogId()) + { + ret["blog_id"] = getValueOfBlogId(); + } + else + { + ret["blog_id"] = Json::Value(); + } + if (getTagId()) + { + ret["tag_id"] = getValueOfTagId(); + } + else + { + ret["tag_id"] = Json::Value(); + } + return ret; +} + +Json::Value BlogTag::toMasqueradedJson( + const std::vector &pMasqueradingVector) const +{ + Json::Value ret; + if (pMasqueradingVector.size() == 2) + { + if (!pMasqueradingVector[0].empty()) + { + if (getBlogId()) + { + ret[pMasqueradingVector[0]] = getValueOfBlogId(); + } + else + { + ret[pMasqueradingVector[0]] = Json::Value(); + } + } + if (!pMasqueradingVector[1].empty()) + { + if (getTagId()) + { + ret[pMasqueradingVector[1]] = getValueOfTagId(); + } + else + { + ret[pMasqueradingVector[1]] = Json::Value(); + } + } + return ret; + } + LOG_ERROR << "Masquerade failed"; + if (getBlogId()) + { + ret["blog_id"] = getValueOfBlogId(); + } + else + { + ret["blog_id"] = Json::Value(); + } + if (getTagId()) + { + ret["tag_id"] = getValueOfTagId(); + } + else + { + ret["tag_id"] = Json::Value(); + } + return ret; +} + +bool BlogTag::validateJsonForCreation(const Json::Value &pJson, + std::string &err) +{ + if (pJson.isMember("blog_id")) + { + if (!validJsonOfField(0, "blog_id", pJson["blog_id"], err, true)) + return false; + } + else + { + err = "The blog_id column cannot be null"; + return false; + } + if (pJson.isMember("tag_id")) + { + if (!validJsonOfField(1, "tag_id", pJson["tag_id"], err, true)) + return false; + } + else + { + err = "The tag_id column cannot be null"; + return false; + } + return true; +} + +bool BlogTag::validateMasqueradedJsonForCreation( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty()) + { + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + else + { + err = + "The " + pMasqueradingVector[0] + " column cannot be null"; + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + else + { + err = + "The " + pMasqueradingVector[1] + " column cannot be null"; + return false; + } + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool BlogTag::validateJsonForUpdate(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("blog_id")) + { + if (!validJsonOfField(0, "blog_id", pJson["blog_id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (pJson.isMember("tag_id")) + { + if (!validJsonOfField(1, "tag_id", pJson["tag_id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + return true; +} + +bool BlogTag::validateMasqueradedJsonForUpdate( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool BlogTag::validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation) +{ + switch (index) + { + case 0: + if (pJson.isNull()) + { + err = "The " + fieldName + " column cannot be null"; + return false; + } + if (!pJson.isInt()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 1: + if (pJson.isNull()) + { + err = "The " + fieldName + " column cannot be null"; + return false; + } + if (!pJson.isInt()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + default: + err = "Internal error in the server"; + return false; + } + return true; +} diff --git a/orm_lib/tests/mysql/BlogTag.h b/orm_lib/tests/mysql/BlogTag.h new file mode 100644 index 00000000..2ec1e9db --- /dev/null +++ b/orm_lib/tests/mysql/BlogTag.h @@ -0,0 +1,228 @@ +/** + * + * BlogTag.h + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#ifdef __cpp_impl_coroutine +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClient; +using DbClientPtr = std::shared_ptr; +} // namespace orm +} // namespace drogon + +namespace drogon_model +{ +namespace drogonTestMysql +{ + +class BlogTag +{ + public: + struct Cols + { + static const std::string _blog_id; + static const std::string _tag_id; + }; + + const static int primaryKeyNumber; + const static std::string tableName; + const static bool hasPrimaryKey; + const static std::vector primaryKeyName; + using PrimaryKeyType = std::tuple; // blog_id,tag_id + PrimaryKeyType getPrimaryKey() const; + + /** + * @brief constructor + * @param r One row of records in the SQL query result. + * @param indexOffset Set the offset to -1 to access all columns by column + * names, otherwise access all columns by offsets. + * @note If the SQL is not a style of 'select * from table_name ...' (select + * all columns by an asterisk), please set the offset to -1. + */ + explicit BlogTag(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + */ + explicit BlogTag(const Json::Value &pJson) noexcept(false); + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + * @param pMasqueradingVector The aliases of table columns. + */ + BlogTag( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + + BlogTag() = default; + + void updateByJson(const Json::Value &pJson) noexcept(false); + void updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + static bool validateJsonForCreation(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForCreation( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validateJsonForUpdate(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForUpdate( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation); + + /** For column blog_id */ + /// Get the value of the column blog_id, returns the default value if the + /// column is null + const int32_t &getValueOfBlogId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getBlogId() const noexcept; + /// Set the value of the column blog_id + void setBlogId(const int32_t &pBlogId) noexcept; + + /** For column tag_id */ + /// Get the value of the column tag_id, returns the default value if the + /// column is null + const int32_t &getValueOfTagId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getTagId() const noexcept; + /// Set the value of the column tag_id + void setTagId(const int32_t &pTagId) noexcept; + + static size_t getColumnNumber() noexcept + { + return 2; + } + + static const std::string &getColumnName(size_t index) noexcept(false); + + Json::Value toJson() const; + Json::Value toMasqueradedJson( + const std::vector &pMasqueradingVector) const; + /// Relationship interfaces + private: + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; +#ifdef __cpp_impl_coroutine + friend drogon::orm::CoroMapper; +#endif + static const std::vector &insertColumns() noexcept; + void outputArgs(drogon::orm::internal::SqlBinder &binder) const; + const std::vector updateColumns() const; + void updateArgs(drogon::orm::internal::SqlBinder &binder) const; + /// For mysql or sqlite3 + void updateId(const uint64_t id); + std::shared_ptr blogId_; + std::shared_ptr tagId_; + + struct MetaData + { + const std::string colName_; + const std::string colType_; + const std::string colDatabaseType_; + const ssize_t colLength_; + const bool isAutoVal_; + const bool isPrimaryKey_; + const bool notNull_; + }; + + static const std::vector metaData_; + bool dirtyFlag_[2] = {false}; + + public: + static const std::string &sqlForFindingByPrimaryKey() + { + static const std::string sql = + "select * from " + tableName + " where blog_id = ? and tag_id = ?"; + return sql; + } + + static const std::string &sqlForDeletingByPrimaryKey() + { + static const std::string sql = + "delete from " + tableName + " where blog_id = ? and tag_id = ?"; + return sql; + } + + std::string sqlForInserting(bool &needSelection) const + { + std::string sql = "insert into " + tableName + " ("; + size_t parametersCount = 0; + needSelection = false; + if (dirtyFlag_[0]) + { + sql += "blog_id,"; + ++parametersCount; + } + if (dirtyFlag_[1]) + { + sql += "tag_id,"; + ++parametersCount; + } + if (parametersCount > 0) + { + sql[sql.length() - 1] = ')'; + sql += " values ("; + } + else + sql += ") values ("; + + if (dirtyFlag_[0]) + { + sql.append("?,"); + } + if (dirtyFlag_[1]) + { + sql.append("?,"); + } + if (parametersCount > 0) + { + sql.resize(sql.length() - 1); + } + sql.append(1, ')'); + LOG_TRACE << sql; + return sql; + } +}; +} // namespace drogonTestMysql +} // namespace drogon_model diff --git a/orm_lib/tests/mysql/Category.cc b/orm_lib/tests/mysql/Category.cc new file mode 100644 index 00000000..bf177eba --- /dev/null +++ b/orm_lib/tests/mysql/Category.cc @@ -0,0 +1,563 @@ +/** + * + * Category.cc + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#include "Category.h" +#include "Blog.h" +#include +#include + +using namespace drogon; +using namespace drogon::orm; +using namespace drogon_model::drogonTestMysql; + +const std::string Category::Cols::_id = "id"; +const std::string Category::Cols::_name = "name"; +const std::string Category::primaryKeyName = "id"; +const bool Category::hasPrimaryKey = true; +const std::string Category::tableName = "category"; + +const std::vector Category::metaData_ = { + {"id", "int32_t", "int(11)", 4, 1, 1, 1}, + {"name", "std::string", "varchar(30)", 30, 0, 0, 0}}; + +const std::string &Category::getColumnName(size_t index) noexcept(false) +{ + assert(index < metaData_.size()); + return metaData_[index].colName_; +} + +Category::Category(const Row &r, const ssize_t indexOffset) noexcept +{ + if (indexOffset < 0) + { + if (!r["id"].isNull()) + { + id_ = std::make_shared(r["id"].as()); + } + if (!r["name"].isNull()) + { + name_ = std::make_shared(r["name"].as()); + } + } + else + { + size_t offset = (size_t)indexOffset; + if (offset + 2 > r.size()) + { + LOG_FATAL << "Invalid SQL result for this model"; + return; + } + size_t index; + index = offset + 0; + if (!r[index].isNull()) + { + id_ = std::make_shared(r[index].as()); + } + index = offset + 1; + if (!r[index].isNull()) + { + name_ = std::make_shared(r[index].as()); + } + } +} + +Category::Category( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + dirtyFlag_[0] = true; + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + name_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } +} + +Category::Category(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + dirtyFlag_[0] = true; + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("name")) + { + dirtyFlag_[1] = true; + if (!pJson["name"].isNull()) + { + name_ = std::make_shared(pJson["name"].asString()); + } + } +} + +void Category::updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + name_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } +} + +void Category::updateByJson(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("name")) + { + dirtyFlag_[1] = true; + if (!pJson["name"].isNull()) + { + name_ = std::make_shared(pJson["name"].asString()); + } + } +} + +const int32_t &Category::getValueOfId() const noexcept +{ + const static int32_t defaultValue = int32_t(); + if (id_) + return *id_; + return defaultValue; +} + +const std::shared_ptr &Category::getId() const noexcept +{ + return id_; +} + +void Category::setId(const int32_t &pId) noexcept +{ + id_ = std::make_shared(pId); + dirtyFlag_[0] = true; +} + +const typename Category::PrimaryKeyType &Category::getPrimaryKey() const +{ + assert(id_); + return *id_; +} + +const std::string &Category::getValueOfName() const noexcept +{ + const static std::string defaultValue = std::string(); + if (name_) + return *name_; + return defaultValue; +} + +const std::shared_ptr &Category::getName() const noexcept +{ + return name_; +} + +void Category::setName(const std::string &pName) noexcept +{ + name_ = std::make_shared(pName); + dirtyFlag_[1] = true; +} + +void Category::setName(std::string &&pName) noexcept +{ + name_ = std::make_shared(std::move(pName)); + dirtyFlag_[1] = true; +} + +void Category::setNameToNull() noexcept +{ + name_.reset(); + dirtyFlag_[1] = true; +} + +void Category::updateId(const uint64_t id) +{ + id_ = std::make_shared(static_cast(id)); +} + +const std::vector &Category::insertColumns() noexcept +{ + static const std::vector inCols = {"name"}; + return inCols; +} + +void Category::outputArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getName()) + { + binder << getValueOfName(); + } + else + { + binder << nullptr; + } + } +} + +const std::vector Category::updateColumns() const +{ + std::vector ret; + if (dirtyFlag_[1]) + { + ret.push_back(getColumnName(1)); + } + return ret; +} + +void Category::updateArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getName()) + { + binder << getValueOfName(); + } + else + { + binder << nullptr; + } + } +} + +Json::Value Category::toJson() const +{ + Json::Value ret; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getName()) + { + ret["name"] = getValueOfName(); + } + else + { + ret["name"] = Json::Value(); + } + return ret; +} + +Json::Value Category::toMasqueradedJson( + const std::vector &pMasqueradingVector) const +{ + Json::Value ret; + if (pMasqueradingVector.size() == 2) + { + if (!pMasqueradingVector[0].empty()) + { + if (getId()) + { + ret[pMasqueradingVector[0]] = getValueOfId(); + } + else + { + ret[pMasqueradingVector[0]] = Json::Value(); + } + } + if (!pMasqueradingVector[1].empty()) + { + if (getName()) + { + ret[pMasqueradingVector[1]] = getValueOfName(); + } + else + { + ret[pMasqueradingVector[1]] = Json::Value(); + } + } + return ret; + } + LOG_ERROR << "Masquerade failed"; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getName()) + { + ret["name"] = getValueOfName(); + } + else + { + ret["name"] = Json::Value(); + } + return ret; +} + +bool Category::validateJsonForCreation(const Json::Value &pJson, + std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, true)) + return false; + } + if (pJson.isMember("name")) + { + if (!validJsonOfField(1, "name", pJson["name"], err, true)) + return false; + } + return true; +} + +bool Category::validateMasqueradedJsonForCreation( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty()) + { + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Category::validateJsonForUpdate(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (pJson.isMember("name")) + { + if (!validJsonOfField(1, "name", pJson["name"], err, false)) + return false; + } + return true; +} + +bool Category::validateMasqueradedJsonForUpdate( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Category::validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation) +{ + switch (index) + { + case 0: + if (pJson.isNull()) + { + err = "The " + fieldName + " column cannot be null"; + return false; + } + if (isForCreation) + { + err = "The automatic primary key cannot be set"; + return false; + } + if (!pJson.isInt()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 1: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isString()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + // asString().length() creates a string object, is there any better + // way to validate the length? + if (pJson.isString() && pJson.asString().length() > 30) + { + err = "String length exceeds limit for the " + fieldName + + " field (the maximum value is 30)"; + return false; + } + + break; + default: + err = "Internal error in the server"; + return false; + } + return true; +} + +std::vector Category::getBlogs(const DbClientPtr &clientPtr) const +{ + const static std::string sql = "select * from blog where category_id = ?"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *id_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + std::vector ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back(Blog(row)); + } + return ret; +} + +void Category::getBlogs(const DbClientPtr &clientPtr, + const std::function)> &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = "select * from blog where category_id = ?"; + *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) { + std::vector ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back(Blog(row)); + } + rcb(ret); + } >> ecb; +} diff --git a/orm_lib/tests/mysql/Category.h b/orm_lib/tests/mysql/Category.h new file mode 100644 index 00000000..72b6b8a4 --- /dev/null +++ b/orm_lib/tests/mysql/Category.h @@ -0,0 +1,231 @@ +/** + * + * Category.h + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#ifdef __cpp_impl_coroutine +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClient; +using DbClientPtr = std::shared_ptr; +} // namespace orm +} // namespace drogon + +namespace drogon_model +{ +namespace drogonTestMysql +{ +class Blog; + +class Category +{ + public: + struct Cols + { + static const std::string _id; + static const std::string _name; + }; + + const static int primaryKeyNumber; + const static std::string tableName; + const static bool hasPrimaryKey; + const static std::string primaryKeyName; + using PrimaryKeyType = int32_t; + const PrimaryKeyType &getPrimaryKey() const; + + /** + * @brief constructor + * @param r One row of records in the SQL query result. + * @param indexOffset Set the offset to -1 to access all columns by column + * names, otherwise access all columns by offsets. + * @note If the SQL is not a style of 'select * from table_name ...' (select + * all columns by an asterisk), please set the offset to -1. + */ + explicit Category(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + */ + explicit Category(const Json::Value &pJson) noexcept(false); + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + * @param pMasqueradingVector The aliases of table columns. + */ + Category( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + + Category() = default; + + void updateByJson(const Json::Value &pJson) noexcept(false); + void updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + static bool validateJsonForCreation(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForCreation( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validateJsonForUpdate(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForUpdate( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation); + + /** For column id */ + /// Get the value of the column id, returns the default value if the column + /// is null + const int32_t &getValueOfId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getId() const noexcept; + /// Set the value of the column id + void setId(const int32_t &pId) noexcept; + + /** For column name */ + /// Get the value of the column name, returns the default value if the + /// column is null + const std::string &getValueOfName() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getName() const noexcept; + /// Set the value of the column name + void setName(const std::string &pName) noexcept; + void setName(std::string &&pName) noexcept; + void setNameToNull() noexcept; + + static size_t getColumnNumber() noexcept + { + return 2; + } + + static const std::string &getColumnName(size_t index) noexcept(false); + + Json::Value toJson() const; + Json::Value toMasqueradedJson( + const std::vector &pMasqueradingVector) const; + /// Relationship interfaces + std::vector getBlogs(const drogon::orm::DbClientPtr &clientPtr) const; + void getBlogs(const drogon::orm::DbClientPtr &clientPtr, + const std::function)> &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + + private: + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; +#ifdef __cpp_impl_coroutine + friend drogon::orm::CoroMapper; +#endif + static const std::vector &insertColumns() noexcept; + void outputArgs(drogon::orm::internal::SqlBinder &binder) const; + const std::vector updateColumns() const; + void updateArgs(drogon::orm::internal::SqlBinder &binder) const; + /// For mysql or sqlite3 + void updateId(const uint64_t id); + std::shared_ptr id_; + std::shared_ptr name_; + + struct MetaData + { + const std::string colName_; + const std::string colType_; + const std::string colDatabaseType_; + const ssize_t colLength_; + const bool isAutoVal_; + const bool isPrimaryKey_; + const bool notNull_; + }; + + static const std::vector metaData_; + bool dirtyFlag_[2] = {false}; + + public: + static const std::string &sqlForFindingByPrimaryKey() + { + static const std::string sql = + "select * from " + tableName + " where id = ?"; + return sql; + } + + static const std::string &sqlForDeletingByPrimaryKey() + { + static const std::string sql = + "delete from " + tableName + " where id = ?"; + return sql; + } + + std::string sqlForInserting(bool &needSelection) const + { + std::string sql = "insert into " + tableName + " ("; + size_t parametersCount = 0; + needSelection = false; + sql += "id,"; + ++parametersCount; + if (dirtyFlag_[1]) + { + sql += "name,"; + ++parametersCount; + } + needSelection = true; + if (parametersCount > 0) + { + sql[sql.length() - 1] = ')'; + sql += " values ("; + } + else + sql += ") values ("; + + sql += "default,"; + if (dirtyFlag_[1]) + { + sql.append("?,"); + } + if (parametersCount > 0) + { + sql.resize(sql.length() - 1); + } + sql.append(1, ')'); + LOG_TRACE << sql; + return sql; + } +}; +} // namespace drogonTestMysql +} // namespace drogon_model diff --git a/orm_lib/tests/mysql/Tag.cc b/orm_lib/tests/mysql/Tag.cc new file mode 100644 index 00000000..6a0ad363 --- /dev/null +++ b/orm_lib/tests/mysql/Tag.cc @@ -0,0 +1,571 @@ +/** + * + * Tag.cc + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#include "Tag.h" +#include "Blog.h" +#include "BlogTag.h" +#include +#include + +using namespace drogon; +using namespace drogon::orm; +using namespace drogon_model::drogonTestMysql; + +const std::string Tag::Cols::_id = "id"; +const std::string Tag::Cols::_name = "name"; +const std::string Tag::primaryKeyName = "id"; +const bool Tag::hasPrimaryKey = true; +const std::string Tag::tableName = "tag"; + +const std::vector Tag::metaData_ = { + {"id", "int32_t", "int(11)", 4, 1, 1, 1}, + {"name", "std::string", "varchar(30)", 30, 0, 0, 0}}; + +const std::string &Tag::getColumnName(size_t index) noexcept(false) +{ + assert(index < metaData_.size()); + return metaData_[index].colName_; +} + +Tag::Tag(const Row &r, const ssize_t indexOffset) noexcept +{ + if (indexOffset < 0) + { + if (!r["id"].isNull()) + { + id_ = std::make_shared(r["id"].as()); + } + if (!r["name"].isNull()) + { + name_ = std::make_shared(r["name"].as()); + } + } + else + { + size_t offset = (size_t)indexOffset; + if (offset + 2 > r.size()) + { + LOG_FATAL << "Invalid SQL result for this model"; + return; + } + size_t index; + index = offset + 0; + if (!r[index].isNull()) + { + id_ = std::make_shared(r[index].as()); + } + index = offset + 1; + if (!r[index].isNull()) + { + name_ = std::make_shared(r[index].as()); + } + } +} + +Tag::Tag(const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + dirtyFlag_[0] = true; + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + name_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } +} + +Tag::Tag(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + dirtyFlag_[0] = true; + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("name")) + { + dirtyFlag_[1] = true; + if (!pJson["name"].isNull()) + { + name_ = std::make_shared(pJson["name"].asString()); + } + } +} + +void Tag::updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + name_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } +} + +void Tag::updateByJson(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("name")) + { + dirtyFlag_[1] = true; + if (!pJson["name"].isNull()) + { + name_ = std::make_shared(pJson["name"].asString()); + } + } +} + +const int32_t &Tag::getValueOfId() const noexcept +{ + const static int32_t defaultValue = int32_t(); + if (id_) + return *id_; + return defaultValue; +} + +const std::shared_ptr &Tag::getId() const noexcept +{ + return id_; +} + +void Tag::setId(const int32_t &pId) noexcept +{ + id_ = std::make_shared(pId); + dirtyFlag_[0] = true; +} + +const typename Tag::PrimaryKeyType &Tag::getPrimaryKey() const +{ + assert(id_); + return *id_; +} + +const std::string &Tag::getValueOfName() const noexcept +{ + const static std::string defaultValue = std::string(); + if (name_) + return *name_; + return defaultValue; +} + +const std::shared_ptr &Tag::getName() const noexcept +{ + return name_; +} + +void Tag::setName(const std::string &pName) noexcept +{ + name_ = std::make_shared(pName); + dirtyFlag_[1] = true; +} + +void Tag::setName(std::string &&pName) noexcept +{ + name_ = std::make_shared(std::move(pName)); + dirtyFlag_[1] = true; +} + +void Tag::setNameToNull() noexcept +{ + name_.reset(); + dirtyFlag_[1] = true; +} + +void Tag::updateId(const uint64_t id) +{ + id_ = std::make_shared(static_cast(id)); +} + +const std::vector &Tag::insertColumns() noexcept +{ + static const std::vector inCols = {"name"}; + return inCols; +} + +void Tag::outputArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getName()) + { + binder << getValueOfName(); + } + else + { + binder << nullptr; + } + } +} + +const std::vector Tag::updateColumns() const +{ + std::vector ret; + if (dirtyFlag_[1]) + { + ret.push_back(getColumnName(1)); + } + return ret; +} + +void Tag::updateArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getName()) + { + binder << getValueOfName(); + } + else + { + binder << nullptr; + } + } +} + +Json::Value Tag::toJson() const +{ + Json::Value ret; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getName()) + { + ret["name"] = getValueOfName(); + } + else + { + ret["name"] = Json::Value(); + } + return ret; +} + +Json::Value Tag::toMasqueradedJson( + const std::vector &pMasqueradingVector) const +{ + Json::Value ret; + if (pMasqueradingVector.size() == 2) + { + if (!pMasqueradingVector[0].empty()) + { + if (getId()) + { + ret[pMasqueradingVector[0]] = getValueOfId(); + } + else + { + ret[pMasqueradingVector[0]] = Json::Value(); + } + } + if (!pMasqueradingVector[1].empty()) + { + if (getName()) + { + ret[pMasqueradingVector[1]] = getValueOfName(); + } + else + { + ret[pMasqueradingVector[1]] = Json::Value(); + } + } + return ret; + } + LOG_ERROR << "Masquerade failed"; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getName()) + { + ret["name"] = getValueOfName(); + } + else + { + ret["name"] = Json::Value(); + } + return ret; +} + +bool Tag::validateJsonForCreation(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, true)) + return false; + } + if (pJson.isMember("name")) + { + if (!validJsonOfField(1, "name", pJson["name"], err, true)) + return false; + } + return true; +} + +bool Tag::validateMasqueradedJsonForCreation( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty()) + { + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Tag::validateJsonForUpdate(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (pJson.isMember("name")) + { + if (!validJsonOfField(1, "name", pJson["name"], err, false)) + return false; + } + return true; +} + +bool Tag::validateMasqueradedJsonForUpdate( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Tag::validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation) +{ + switch (index) + { + case 0: + if (pJson.isNull()) + { + err = "The " + fieldName + " column cannot be null"; + return false; + } + if (isForCreation) + { + err = "The automatic primary key cannot be set"; + return false; + } + if (!pJson.isInt()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 1: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isString()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + // asString().length() creates a string object, is there any better + // way to validate the length? + if (pJson.isString() && pJson.asString().length() > 30) + { + err = "String length exceeds limit for the " + fieldName + + " field (the maximum value is 30)"; + return false; + } + + break; + default: + err = "Internal error in the server"; + return false; + } + return true; +} + +std::vector> Tag::getBlogs( + const DbClientPtr &clientPtr) const +{ + const static std::string sql = + "select * from blog,blog_tag where blog_tag.tag_id = ? and " + "blog_tag.blog_id = blog.id"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *id_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + std::vector> ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back( + std::pair(Blog(row), + BlogTag(row, Blog::getColumnNumber()))); + } + return ret; +} + +void Tag::getBlogs( + const DbClientPtr &clientPtr, + const std::function>)> &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = + "select * from blog,blog_tag where blog_tag.tag_id = ? and " + "blog_tag.blog_id = blog.id"; + *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) { + std::vector> ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back(std::pair( + Blog(row), BlogTag(row, Blog::getColumnNumber()))); + } + rcb(ret); + } >> ecb; +} diff --git a/orm_lib/tests/mysql/Tag.h b/orm_lib/tests/mysql/Tag.h new file mode 100644 index 00000000..f7005b52 --- /dev/null +++ b/orm_lib/tests/mysql/Tag.h @@ -0,0 +1,233 @@ +/** + * + * Tag.h + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#ifdef __cpp_impl_coroutine +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClient; +using DbClientPtr = std::shared_ptr; +} // namespace orm +} // namespace drogon + +namespace drogon_model +{ +namespace drogonTestMysql +{ +class Blog; +class BlogTag; + +class Tag +{ + public: + struct Cols + { + static const std::string _id; + static const std::string _name; + }; + + const static int primaryKeyNumber; + const static std::string tableName; + const static bool hasPrimaryKey; + const static std::string primaryKeyName; + using PrimaryKeyType = int32_t; + const PrimaryKeyType &getPrimaryKey() const; + + /** + * @brief constructor + * @param r One row of records in the SQL query result. + * @param indexOffset Set the offset to -1 to access all columns by column + * names, otherwise access all columns by offsets. + * @note If the SQL is not a style of 'select * from table_name ...' (select + * all columns by an asterisk), please set the offset to -1. + */ + explicit Tag(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + */ + explicit Tag(const Json::Value &pJson) noexcept(false); + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + * @param pMasqueradingVector The aliases of table columns. + */ + Tag(const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + + Tag() = default; + + void updateByJson(const Json::Value &pJson) noexcept(false); + void updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + static bool validateJsonForCreation(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForCreation( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validateJsonForUpdate(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForUpdate( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation); + + /** For column id */ + /// Get the value of the column id, returns the default value if the column + /// is null + const int32_t &getValueOfId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getId() const noexcept; + /// Set the value of the column id + void setId(const int32_t &pId) noexcept; + + /** For column name */ + /// Get the value of the column name, returns the default value if the + /// column is null + const std::string &getValueOfName() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getName() const noexcept; + /// Set the value of the column name + void setName(const std::string &pName) noexcept; + void setName(std::string &&pName) noexcept; + void setNameToNull() noexcept; + + static size_t getColumnNumber() noexcept + { + return 2; + } + + static const std::string &getColumnName(size_t index) noexcept(false); + + Json::Value toJson() const; + Json::Value toMasqueradedJson( + const std::vector &pMasqueradingVector) const; + /// Relationship interfaces + std::vector> getBlogs( + const drogon::orm::DbClientPtr &clientPtr) const; + void getBlogs( + const drogon::orm::DbClientPtr &clientPtr, + const std::function>)> &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + + private: + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; +#ifdef __cpp_impl_coroutine + friend drogon::orm::CoroMapper; +#endif + static const std::vector &insertColumns() noexcept; + void outputArgs(drogon::orm::internal::SqlBinder &binder) const; + const std::vector updateColumns() const; + void updateArgs(drogon::orm::internal::SqlBinder &binder) const; + /// For mysql or sqlite3 + void updateId(const uint64_t id); + std::shared_ptr id_; + std::shared_ptr name_; + + struct MetaData + { + const std::string colName_; + const std::string colType_; + const std::string colDatabaseType_; + const ssize_t colLength_; + const bool isAutoVal_; + const bool isPrimaryKey_; + const bool notNull_; + }; + + static const std::vector metaData_; + bool dirtyFlag_[2] = {false}; + + public: + static const std::string &sqlForFindingByPrimaryKey() + { + static const std::string sql = + "select * from " + tableName + " where id = ?"; + return sql; + } + + static const std::string &sqlForDeletingByPrimaryKey() + { + static const std::string sql = + "delete from " + tableName + " where id = ?"; + return sql; + } + + std::string sqlForInserting(bool &needSelection) const + { + std::string sql = "insert into " + tableName + " ("; + size_t parametersCount = 0; + needSelection = false; + sql += "id,"; + ++parametersCount; + if (dirtyFlag_[1]) + { + sql += "name,"; + ++parametersCount; + } + needSelection = true; + if (parametersCount > 0) + { + sql[sql.length() - 1] = ')'; + sql += " values ("; + } + else + sql += ") values ("; + + sql += "default,"; + if (dirtyFlag_[1]) + { + sql.append("?,"); + } + if (parametersCount > 0) + { + sql.resize(sql.length() - 1); + } + sql.append(1, ')'); + LOG_TRACE << sql; + return sql; + } +}; +} // namespace drogonTestMysql +} // namespace drogon_model diff --git a/orm_lib/tests/mysql/Users.cc b/orm_lib/tests/mysql/Users.cc index a7283d5f..53f187b5 100644 --- a/orm_lib/tests/mysql/Users.cc +++ b/orm_lib/tests/mysql/Users.cc @@ -6,10 +6,12 @@ */ #include "Users.h" +#include "Wallets.h" #include #include using namespace drogon; +using namespace drogon::orm; using namespace drogon_model::drogonTestMysql; const std::string Users::Cols::_id = "id"; @@ -1343,113 +1345,121 @@ bool Users::validateMasqueradedJsonForCreation( err = "Bad masquerading vector"; return false; } - if (!pMasqueradingVector[0].empty()) + try { - if (pJson.isMember(pMasqueradingVector[0])) + if (!pMasqueradingVector[0].empty()) { - if (!validJsonOfField(0, - pMasqueradingVector[0], - pJson[pMasqueradingVector[0]], - err, - true)) - return false; + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[2].empty()) + { + if (pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[3].empty()) + { + if (pJson.isMember(pMasqueradingVector[3])) + { + if (!validJsonOfField(3, + pMasqueradingVector[3], + pJson[pMasqueradingVector[3]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[4].empty()) + { + if (pJson.isMember(pMasqueradingVector[4])) + { + if (!validJsonOfField(4, + pMasqueradingVector[4], + pJson[pMasqueradingVector[4]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[5].empty()) + { + if (pJson.isMember(pMasqueradingVector[5])) + { + if (!validJsonOfField(5, + pMasqueradingVector[5], + pJson[pMasqueradingVector[5]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[6].empty()) + { + if (pJson.isMember(pMasqueradingVector[6])) + { + if (!validJsonOfField(6, + pMasqueradingVector[6], + pJson[pMasqueradingVector[6]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[7].empty()) + { + if (pJson.isMember(pMasqueradingVector[7])) + { + if (!validJsonOfField(7, + pMasqueradingVector[7], + pJson[pMasqueradingVector[7]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[8].empty()) + { + if (pJson.isMember(pMasqueradingVector[8])) + { + if (!validJsonOfField(8, + pMasqueradingVector[8], + pJson[pMasqueradingVector[8]], + err, + true)) + return false; + } } } - if (!pMasqueradingVector[1].empty()) + catch (const Json::LogicError &e) { - if (pJson.isMember(pMasqueradingVector[1])) - { - if (!validJsonOfField(1, - pMasqueradingVector[1], - pJson[pMasqueradingVector[1]], - err, - true)) - return false; - } - } - if (!pMasqueradingVector[2].empty()) - { - if (pJson.isMember(pMasqueradingVector[2])) - { - if (!validJsonOfField(2, - pMasqueradingVector[2], - pJson[pMasqueradingVector[2]], - err, - true)) - return false; - } - } - if (!pMasqueradingVector[3].empty()) - { - if (pJson.isMember(pMasqueradingVector[3])) - { - if (!validJsonOfField(3, - pMasqueradingVector[3], - pJson[pMasqueradingVector[3]], - err, - true)) - return false; - } - } - if (!pMasqueradingVector[4].empty()) - { - if (pJson.isMember(pMasqueradingVector[4])) - { - if (!validJsonOfField(4, - pMasqueradingVector[4], - pJson[pMasqueradingVector[4]], - err, - true)) - return false; - } - } - if (!pMasqueradingVector[5].empty()) - { - if (pJson.isMember(pMasqueradingVector[5])) - { - if (!validJsonOfField(5, - pMasqueradingVector[5], - pJson[pMasqueradingVector[5]], - err, - true)) - return false; - } - } - if (!pMasqueradingVector[6].empty()) - { - if (pJson.isMember(pMasqueradingVector[6])) - { - if (!validJsonOfField(6, - pMasqueradingVector[6], - pJson[pMasqueradingVector[6]], - err, - true)) - return false; - } - } - if (!pMasqueradingVector[7].empty()) - { - if (pJson.isMember(pMasqueradingVector[7])) - { - if (!validJsonOfField(7, - pMasqueradingVector[7], - pJson[pMasqueradingVector[7]], - err, - true)) - return false; - } - } - if (!pMasqueradingVector[8].empty()) - { - if (pJson.isMember(pMasqueradingVector[8])) - { - if (!validJsonOfField(8, - pMasqueradingVector[8], - pJson[pMasqueradingVector[8]], - err, - true)) - return false; - } + err = e.what(); + return false; } return true; } @@ -1521,103 +1531,111 @@ bool Users::validateMasqueradedJsonForUpdate( err = "Bad masquerading vector"; return false; } - if (!pMasqueradingVector[0].empty() && - pJson.isMember(pMasqueradingVector[0])) + try { - if (!validJsonOfField(0, - pMasqueradingVector[0], - pJson[pMasqueradingVector[0]], - err, - false)) + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + false)) + return false; + } + if (!pMasqueradingVector[3].empty() && + pJson.isMember(pMasqueradingVector[3])) + { + if (!validJsonOfField(3, + pMasqueradingVector[3], + pJson[pMasqueradingVector[3]], + err, + false)) + return false; + } + if (!pMasqueradingVector[4].empty() && + pJson.isMember(pMasqueradingVector[4])) + { + if (!validJsonOfField(4, + pMasqueradingVector[4], + pJson[pMasqueradingVector[4]], + err, + false)) + return false; + } + if (!pMasqueradingVector[5].empty() && + pJson.isMember(pMasqueradingVector[5])) + { + if (!validJsonOfField(5, + pMasqueradingVector[5], + pJson[pMasqueradingVector[5]], + err, + false)) + return false; + } + if (!pMasqueradingVector[6].empty() && + pJson.isMember(pMasqueradingVector[6])) + { + if (!validJsonOfField(6, + pMasqueradingVector[6], + pJson[pMasqueradingVector[6]], + err, + false)) + return false; + } + if (!pMasqueradingVector[7].empty() && + pJson.isMember(pMasqueradingVector[7])) + { + if (!validJsonOfField(7, + pMasqueradingVector[7], + pJson[pMasqueradingVector[7]], + err, + false)) + return false; + } + if (!pMasqueradingVector[8].empty() && + pJson.isMember(pMasqueradingVector[8])) + { + if (!validJsonOfField(8, + pMasqueradingVector[8], + pJson[pMasqueradingVector[8]], + err, + false)) + return false; + } } - else + catch (const Json::LogicError &e) { - err = - "The value of primary key must be set in the json object for " - "update"; + err = e.what(); return false; } - if (!pMasqueradingVector[1].empty() && - pJson.isMember(pMasqueradingVector[1])) - { - if (!validJsonOfField(1, - pMasqueradingVector[1], - pJson[pMasqueradingVector[1]], - err, - false)) - return false; - } - if (!pMasqueradingVector[2].empty() && - pJson.isMember(pMasqueradingVector[2])) - { - if (!validJsonOfField(2, - pMasqueradingVector[2], - pJson[pMasqueradingVector[2]], - err, - false)) - return false; - } - if (!pMasqueradingVector[3].empty() && - pJson.isMember(pMasqueradingVector[3])) - { - if (!validJsonOfField(3, - pMasqueradingVector[3], - pJson[pMasqueradingVector[3]], - err, - false)) - return false; - } - if (!pMasqueradingVector[4].empty() && - pJson.isMember(pMasqueradingVector[4])) - { - if (!validJsonOfField(4, - pMasqueradingVector[4], - pJson[pMasqueradingVector[4]], - err, - false)) - return false; - } - if (!pMasqueradingVector[5].empty() && - pJson.isMember(pMasqueradingVector[5])) - { - if (!validJsonOfField(5, - pMasqueradingVector[5], - pJson[pMasqueradingVector[5]], - err, - false)) - return false; - } - if (!pMasqueradingVector[6].empty() && - pJson.isMember(pMasqueradingVector[6])) - { - if (!validJsonOfField(6, - pMasqueradingVector[6], - pJson[pMasqueradingVector[6]], - err, - false)) - return false; - } - if (!pMasqueradingVector[7].empty() && - pJson.isMember(pMasqueradingVector[7])) - { - if (!validJsonOfField(7, - pMasqueradingVector[7], - pJson[pMasqueradingVector[7]], - err, - false)) - return false; - } - if (!pMasqueradingVector[8].empty() && - pJson.isMember(pMasqueradingVector[8])) - { - if (!validJsonOfField(8, - pMasqueradingVector[8], - pJson[pMasqueradingVector[8]], - err, - false)) - return false; - } return true; } @@ -1797,11 +1815,52 @@ bool Users::validJsonOfField(size_t index, return false; } break; - default: err = "Internal error in the server"; return false; - break; } return true; } + +Wallets Users::getWallet(const DbClientPtr &clientPtr) const +{ + const static std::string sql = "select * from wallets where user_id = ?"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *userId_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + if (r.size() == 0) + { + throw UnexpectedRows("0 rows found"); + } + else if (r.size() > 1) + { + throw UnexpectedRows("Found more than one row"); + } + return Wallets(r[0]); +} + +void Users::getWallet(const DbClientPtr &clientPtr, + const std::function &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = "select * from wallets where user_id = ?"; + *clientPtr << sql << *userId_ >> [rcb = std::move(rcb), + ecb](const Result &r) { + if (r.size() == 0) + { + ecb(UnexpectedRows("0 rows found")); + } + else if (r.size() > 1) + { + ecb(UnexpectedRows("Found more than one row")); + } + else + { + rcb(Wallets(r[0])); + } + } >> ecb; +} diff --git a/orm_lib/tests/mysql/Users.h b/orm_lib/tests/mysql/Users.h index 9c56fa8d..49abd02d 100644 --- a/orm_lib/tests/mysql/Users.h +++ b/orm_lib/tests/mysql/Users.h @@ -19,14 +19,13 @@ #include #include #include +#include #include #include #include #include #include -using namespace drogon::orm; - namespace drogon { namespace orm @@ -40,6 +39,8 @@ namespace drogon_model { namespace drogonTestMysql { +class Wallets; + class Users { public: @@ -71,7 +72,8 @@ class Users * @note If the SQL is not a style of 'select * from table_name ...' (select * all columns by an asterisk), please set the offset to -1. */ - explicit Users(const Row &r, const ssize_t indexOffset = 0) noexcept; + explicit Users(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; /** * @brief constructor @@ -118,7 +120,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getId() const noexcept; - /// Set the value of the column id void setId(const int32_t &pId) noexcept; @@ -129,7 +130,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getUserId() const noexcept; - /// Set the value of the column user_id void setUserId(const std::string &pUserId) noexcept; void setUserId(std::string &&pUserId) noexcept; @@ -142,7 +142,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getUserName() const noexcept; - /// Set the value of the column user_name void setUserName(const std::string &pUserName) noexcept; void setUserName(std::string &&pUserName) noexcept; @@ -155,7 +154,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getPassword() const noexcept; - /// Set the value of the column password void setPassword(const std::string &pPassword) noexcept; void setPassword(std::string &&pPassword) noexcept; @@ -168,7 +166,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getOrgName() const noexcept; - /// Set the value of the column org_name void setOrgName(const std::string &pOrgName) noexcept; void setOrgName(std::string &&pOrgName) noexcept; @@ -181,7 +178,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getSignature() const noexcept; - /// Set the value of the column signature void setSignature(const std::string &pSignature) noexcept; void setSignature(std::string &&pSignature) noexcept; @@ -194,7 +190,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getAvatarId() const noexcept; - /// Set the value of the column avatar_id void setAvatarId(const std::string &pAvatarId) noexcept; void setAvatarId(std::string &&pAvatarId) noexcept; @@ -207,7 +202,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getSalt() const noexcept; - /// Set the value of the column salt void setSalt(const std::string &pSalt) noexcept; void setSalt(std::string &&pSalt) noexcept; @@ -220,7 +214,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getAdmin() const noexcept; - /// Set the value of the column admin void setAdmin(const int8_t &pAdmin) noexcept; void setAdminToNull() noexcept; @@ -236,14 +229,19 @@ class Users Json::Value toMasqueradedJson( const std::vector &pMasqueradingVector) const; /// Relationship interfaces + Wallets getWallet(const drogon::orm::DbClientPtr &clientPtr) const; + void getWallet(const drogon::orm::DbClientPtr &clientPtr, + const std::function &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + private: - friend Mapper; - friend BaseBuilder; - friend BaseBuilder; - friend BaseBuilder; - friend BaseBuilder; + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; #ifdef __cpp_impl_coroutine - friend CoroMapper; + friend drogon::orm::CoroMapper; #endif static const std::vector &insertColumns() noexcept; void outputArgs(drogon::orm::internal::SqlBinder &binder) const; diff --git a/orm_lib/tests/mysql/Wallets.cc b/orm_lib/tests/mysql/Wallets.cc new file mode 100644 index 00000000..cef92b7e --- /dev/null +++ b/orm_lib/tests/mysql/Wallets.cc @@ -0,0 +1,749 @@ +/** + * + * Wallets.cc + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#include "Wallets.h" +#include "Users.h" +#include +#include + +using namespace drogon; +using namespace drogon::orm; +using namespace drogon_model::drogonTestMysql; + +const std::string Wallets::Cols::_id = "id"; +const std::string Wallets::Cols::_user_id = "user_id"; +const std::string Wallets::Cols::_amount = "amount"; +const std::string Wallets::primaryKeyName = "id"; +const bool Wallets::hasPrimaryKey = true; +const std::string Wallets::tableName = "wallets"; + +const std::vector Wallets::metaData_ = { + {"id", "int32_t", "int(11)", 4, 1, 1, 1}, + {"user_id", "std::string", "varchar(32)", 32, 0, 0, 0}, + {"amount", "std::string", "decimal(16,2)", 0, 0, 0, 0}}; + +const std::string &Wallets::getColumnName(size_t index) noexcept(false) +{ + assert(index < metaData_.size()); + return metaData_[index].colName_; +} + +Wallets::Wallets(const Row &r, const ssize_t indexOffset) noexcept +{ + if (indexOffset < 0) + { + if (!r["id"].isNull()) + { + id_ = std::make_shared(r["id"].as()); + } + if (!r["user_id"].isNull()) + { + userId_ = + std::make_shared(r["user_id"].as()); + } + if (!r["amount"].isNull()) + { + amount_ = + std::make_shared(r["amount"].as()); + } + } + else + { + size_t offset = (size_t)indexOffset; + if (offset + 3 > r.size()) + { + LOG_FATAL << "Invalid SQL result for this model"; + return; + } + size_t index; + index = offset + 0; + if (!r[index].isNull()) + { + id_ = std::make_shared(r[index].as()); + } + index = offset + 1; + if (!r[index].isNull()) + { + userId_ = std::make_shared(r[index].as()); + } + index = offset + 2; + if (!r[index].isNull()) + { + amount_ = std::make_shared(r[index].as()); + } + } +} + +Wallets::Wallets( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 3) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + dirtyFlag_[0] = true; + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + userId_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + dirtyFlag_[2] = true; + if (!pJson[pMasqueradingVector[2]].isNull()) + { + amount_ = std::make_shared( + pJson[pMasqueradingVector[2]].asString()); + } + } +} + +Wallets::Wallets(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + dirtyFlag_[0] = true; + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("user_id")) + { + dirtyFlag_[1] = true; + if (!pJson["user_id"].isNull()) + { + userId_ = + std::make_shared(pJson["user_id"].asString()); + } + } + if (pJson.isMember("amount")) + { + dirtyFlag_[2] = true; + if (!pJson["amount"].isNull()) + { + amount_ = std::make_shared(pJson["amount"].asString()); + } + } +} + +void Wallets::updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 3) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + userId_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + dirtyFlag_[2] = true; + if (!pJson[pMasqueradingVector[2]].isNull()) + { + amount_ = std::make_shared( + pJson[pMasqueradingVector[2]].asString()); + } + } +} + +void Wallets::updateByJson(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("user_id")) + { + dirtyFlag_[1] = true; + if (!pJson["user_id"].isNull()) + { + userId_ = + std::make_shared(pJson["user_id"].asString()); + } + } + if (pJson.isMember("amount")) + { + dirtyFlag_[2] = true; + if (!pJson["amount"].isNull()) + { + amount_ = std::make_shared(pJson["amount"].asString()); + } + } +} + +const int32_t &Wallets::getValueOfId() const noexcept +{ + const static int32_t defaultValue = int32_t(); + if (id_) + return *id_; + return defaultValue; +} + +const std::shared_ptr &Wallets::getId() const noexcept +{ + return id_; +} + +void Wallets::setId(const int32_t &pId) noexcept +{ + id_ = std::make_shared(pId); + dirtyFlag_[0] = true; +} + +const typename Wallets::PrimaryKeyType &Wallets::getPrimaryKey() const +{ + assert(id_); + return *id_; +} + +const std::string &Wallets::getValueOfUserId() const noexcept +{ + const static std::string defaultValue = std::string(); + if (userId_) + return *userId_; + return defaultValue; +} + +const std::shared_ptr &Wallets::getUserId() const noexcept +{ + return userId_; +} + +void Wallets::setUserId(const std::string &pUserId) noexcept +{ + userId_ = std::make_shared(pUserId); + dirtyFlag_[1] = true; +} + +void Wallets::setUserId(std::string &&pUserId) noexcept +{ + userId_ = std::make_shared(std::move(pUserId)); + dirtyFlag_[1] = true; +} + +void Wallets::setUserIdToNull() noexcept +{ + userId_.reset(); + dirtyFlag_[1] = true; +} + +const std::string &Wallets::getValueOfAmount() const noexcept +{ + const static std::string defaultValue = std::string(); + if (amount_) + return *amount_; + return defaultValue; +} + +const std::shared_ptr &Wallets::getAmount() const noexcept +{ + return amount_; +} + +void Wallets::setAmount(const std::string &pAmount) noexcept +{ + amount_ = std::make_shared(pAmount); + dirtyFlag_[2] = true; +} + +void Wallets::setAmount(std::string &&pAmount) noexcept +{ + amount_ = std::make_shared(std::move(pAmount)); + dirtyFlag_[2] = true; +} + +void Wallets::setAmountToNull() noexcept +{ + amount_.reset(); + dirtyFlag_[2] = true; +} + +void Wallets::updateId(const uint64_t id) +{ + id_ = std::make_shared(static_cast(id)); +} + +const std::vector &Wallets::insertColumns() noexcept +{ + static const std::vector inCols = {"user_id", "amount"}; + return inCols; +} + +void Wallets::outputArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getUserId()) + { + binder << getValueOfUserId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[2]) + { + if (getAmount()) + { + binder << getValueOfAmount(); + } + else + { + binder << nullptr; + } + } +} + +const std::vector Wallets::updateColumns() const +{ + std::vector ret; + if (dirtyFlag_[1]) + { + ret.push_back(getColumnName(1)); + } + if (dirtyFlag_[2]) + { + ret.push_back(getColumnName(2)); + } + return ret; +} + +void Wallets::updateArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getUserId()) + { + binder << getValueOfUserId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[2]) + { + if (getAmount()) + { + binder << getValueOfAmount(); + } + else + { + binder << nullptr; + } + } +} + +Json::Value Wallets::toJson() const +{ + Json::Value ret; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getUserId()) + { + ret["user_id"] = getValueOfUserId(); + } + else + { + ret["user_id"] = Json::Value(); + } + if (getAmount()) + { + ret["amount"] = getValueOfAmount(); + } + else + { + ret["amount"] = Json::Value(); + } + return ret; +} + +Json::Value Wallets::toMasqueradedJson( + const std::vector &pMasqueradingVector) const +{ + Json::Value ret; + if (pMasqueradingVector.size() == 3) + { + if (!pMasqueradingVector[0].empty()) + { + if (getId()) + { + ret[pMasqueradingVector[0]] = getValueOfId(); + } + else + { + ret[pMasqueradingVector[0]] = Json::Value(); + } + } + if (!pMasqueradingVector[1].empty()) + { + if (getUserId()) + { + ret[pMasqueradingVector[1]] = getValueOfUserId(); + } + else + { + ret[pMasqueradingVector[1]] = Json::Value(); + } + } + if (!pMasqueradingVector[2].empty()) + { + if (getAmount()) + { + ret[pMasqueradingVector[2]] = getValueOfAmount(); + } + else + { + ret[pMasqueradingVector[2]] = Json::Value(); + } + } + return ret; + } + LOG_ERROR << "Masquerade failed"; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getUserId()) + { + ret["user_id"] = getValueOfUserId(); + } + else + { + ret["user_id"] = Json::Value(); + } + if (getAmount()) + { + ret["amount"] = getValueOfAmount(); + } + else + { + ret["amount"] = Json::Value(); + } + return ret; +} + +bool Wallets::validateJsonForCreation(const Json::Value &pJson, + std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, true)) + return false; + } + if (pJson.isMember("user_id")) + { + if (!validJsonOfField(1, "user_id", pJson["user_id"], err, true)) + return false; + } + if (pJson.isMember("amount")) + { + if (!validJsonOfField(2, "amount", pJson["amount"], err, true)) + return false; + } + return true; +} + +bool Wallets::validateMasqueradedJsonForCreation( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 3) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty()) + { + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[2].empty()) + { + if (pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + true)) + return false; + } + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Wallets::validateJsonForUpdate(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (pJson.isMember("user_id")) + { + if (!validJsonOfField(1, "user_id", pJson["user_id"], err, false)) + return false; + } + if (pJson.isMember("amount")) + { + if (!validJsonOfField(2, "amount", pJson["amount"], err, false)) + return false; + } + return true; +} + +bool Wallets::validateMasqueradedJsonForUpdate( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 3) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + false)) + return false; + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Wallets::validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation) +{ + switch (index) + { + case 0: + if (pJson.isNull()) + { + err = "The " + fieldName + " column cannot be null"; + return false; + } + if (isForCreation) + { + err = "The automatic primary key cannot be set"; + return false; + } + if (!pJson.isInt()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 1: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isString()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + // asString().length() creates a string object, is there any better + // way to validate the length? + if (pJson.isString() && pJson.asString().length() > 32) + { + err = "String length exceeds limit for the " + fieldName + + " field (the maximum value is 32)"; + return false; + } + + break; + case 2: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isString()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + default: + err = "Internal error in the server"; + return false; + } + return true; +} + +Users Wallets::getUser(const DbClientPtr &clientPtr) const +{ + const static std::string sql = "select * from users where user_id = ?"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *userId_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + if (r.size() == 0) + { + throw UnexpectedRows("0 rows found"); + } + else if (r.size() > 1) + { + throw UnexpectedRows("Found more than one row"); + } + return Users(r[0]); +} + +void Wallets::getUser(const DbClientPtr &clientPtr, + const std::function &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = "select * from users where user_id = ?"; + *clientPtr << sql << *userId_ >> [rcb = std::move(rcb), + ecb](const Result &r) { + if (r.size() == 0) + { + ecb(UnexpectedRows("0 rows found")); + } + else if (r.size() > 1) + { + ecb(UnexpectedRows("Found more than one row")); + } + else + { + rcb(Users(r[0])); + } + } >> ecb; +} diff --git a/orm_lib/tests/mysql/Wallets.h b/orm_lib/tests/mysql/Wallets.h new file mode 100644 index 00000000..7f5bd50f --- /dev/null +++ b/orm_lib/tests/mysql/Wallets.h @@ -0,0 +1,254 @@ +/** + * + * Wallets.h + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#ifdef __cpp_impl_coroutine +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClient; +using DbClientPtr = std::shared_ptr; +} // namespace orm +} // namespace drogon + +namespace drogon_model +{ +namespace drogonTestMysql +{ +class Users; + +class Wallets +{ + public: + struct Cols + { + static const std::string _id; + static const std::string _user_id; + static const std::string _amount; + }; + + const static int primaryKeyNumber; + const static std::string tableName; + const static bool hasPrimaryKey; + const static std::string primaryKeyName; + using PrimaryKeyType = int32_t; + const PrimaryKeyType &getPrimaryKey() const; + + /** + * @brief constructor + * @param r One row of records in the SQL query result. + * @param indexOffset Set the offset to -1 to access all columns by column + * names, otherwise access all columns by offsets. + * @note If the SQL is not a style of 'select * from table_name ...' (select + * all columns by an asterisk), please set the offset to -1. + */ + explicit Wallets(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + */ + explicit Wallets(const Json::Value &pJson) noexcept(false); + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + * @param pMasqueradingVector The aliases of table columns. + */ + Wallets( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + + Wallets() = default; + + void updateByJson(const Json::Value &pJson) noexcept(false); + void updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + static bool validateJsonForCreation(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForCreation( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validateJsonForUpdate(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForUpdate( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation); + + /** For column id */ + /// Get the value of the column id, returns the default value if the column + /// is null + const int32_t &getValueOfId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getId() const noexcept; + /// Set the value of the column id + void setId(const int32_t &pId) noexcept; + + /** For column user_id */ + /// Get the value of the column user_id, returns the default value if the + /// column is null + const std::string &getValueOfUserId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getUserId() const noexcept; + /// Set the value of the column user_id + void setUserId(const std::string &pUserId) noexcept; + void setUserId(std::string &&pUserId) noexcept; + void setUserIdToNull() noexcept; + + /** For column amount */ + /// Get the value of the column amount, returns the default value if the + /// column is null + const std::string &getValueOfAmount() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getAmount() const noexcept; + /// Set the value of the column amount + void setAmount(const std::string &pAmount) noexcept; + void setAmount(std::string &&pAmount) noexcept; + void setAmountToNull() noexcept; + + static size_t getColumnNumber() noexcept + { + return 3; + } + + static const std::string &getColumnName(size_t index) noexcept(false); + + Json::Value toJson() const; + Json::Value toMasqueradedJson( + const std::vector &pMasqueradingVector) const; + /// Relationship interfaces + Users getUser(const drogon::orm::DbClientPtr &clientPtr) const; + void getUser(const drogon::orm::DbClientPtr &clientPtr, + const std::function &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + + private: + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; +#ifdef __cpp_impl_coroutine + friend drogon::orm::CoroMapper; +#endif + static const std::vector &insertColumns() noexcept; + void outputArgs(drogon::orm::internal::SqlBinder &binder) const; + const std::vector updateColumns() const; + void updateArgs(drogon::orm::internal::SqlBinder &binder) const; + /// For mysql or sqlite3 + void updateId(const uint64_t id); + std::shared_ptr id_; + std::shared_ptr userId_; + std::shared_ptr amount_; + + struct MetaData + { + const std::string colName_; + const std::string colType_; + const std::string colDatabaseType_; + const ssize_t colLength_; + const bool isAutoVal_; + const bool isPrimaryKey_; + const bool notNull_; + }; + + static const std::vector metaData_; + bool dirtyFlag_[3] = {false}; + + public: + static const std::string &sqlForFindingByPrimaryKey() + { + static const std::string sql = + "select * from " + tableName + " where id = ?"; + return sql; + } + + static const std::string &sqlForDeletingByPrimaryKey() + { + static const std::string sql = + "delete from " + tableName + " where id = ?"; + return sql; + } + + std::string sqlForInserting(bool &needSelection) const + { + std::string sql = "insert into " + tableName + " ("; + size_t parametersCount = 0; + needSelection = false; + sql += "id,"; + ++parametersCount; + if (dirtyFlag_[1]) + { + sql += "user_id,"; + ++parametersCount; + } + if (dirtyFlag_[2]) + { + sql += "amount,"; + ++parametersCount; + } + needSelection = true; + if (parametersCount > 0) + { + sql[sql.length() - 1] = ')'; + sql += " values ("; + } + else + sql += ") values ("; + + sql += "default,"; + if (dirtyFlag_[1]) + { + sql.append("?,"); + } + if (dirtyFlag_[2]) + { + sql.append("?,"); + } + if (parametersCount > 0) + { + sql.resize(sql.length() - 1); + } + sql.append(1, ')'); + LOG_TRACE << sql; + return sql; + } +}; +} // namespace drogonTestMysql +} // namespace drogon_model diff --git a/orm_lib/tests/mysql/model.json b/orm_lib/tests/mysql/model.json index 4bba50e1..9ee285cd 100644 --- a/orm_lib/tests/mysql/model.json +++ b/orm_lib/tests/mysql/model.json @@ -1,8 +1,55 @@ { - "rdbms":"mysql", - "host":"localhost", - "port":3306, - "dbname":"drogonTestMysql", - "user":"root", - "tables":["users"] + "rdbms": "mysql", + "host": "127.0.0.1", + "port": 3306, + "dbname": "drogonTestMysql", + "user": "root", + "tables": [ + "users", + "wallets", + "blog", + "category", + "blog_tag", + "tag" + ], + "relationships": { + "enabled": true, + "items": [ + { + "type": "has one", + "original_table_name": "users", + "original_table_alias": "user", + "original_key": "user_id", + "target_table_name": "wallets", + "target_table_alias": "wallet", + "target_key": "user_id", + "enable_reverse": true + }, + { + "type": "has many", + "original_table_name": "category", + "original_table_alias": "category", + "original_key": "id", + "target_table_name": "blog", + "target_table_alias": "blogs", + "target_key": "category_id", + "enable_reverse": true + }, + { + "type": "many to many", + "original_table_name": "blog", + "original_table_alias": "blogs", + "original_key": "id", + "pivot_table": { + "table_name": "blog_tag", + "original_key": "blog_id", + "target_key": "tag_id" + }, + "target_table_name": "tag", + "target_table_alias": "tags", + "target_key": "id", + "enable_reverse": true + } + ] + } } diff --git a/orm_lib/tests/postgresql/Blog.cc b/orm_lib/tests/postgresql/Blog.cc new file mode 100644 index 00000000..83bef766 --- /dev/null +++ b/orm_lib/tests/postgresql/Blog.cc @@ -0,0 +1,789 @@ +/** + * + * Blog.cc + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#include "Blog.h" +#include "BlogTag.h" +#include "Category.h" +#include "Tag.h" +#include +#include + +using namespace drogon; +using namespace drogon::orm; +using namespace drogon_model::postgres; + +const std::string Blog::Cols::_id = "id"; +const std::string Blog::Cols::_title = "title"; +const std::string Blog::Cols::_category_id = "category_id"; +const std::string Blog::primaryKeyName = "id"; +const bool Blog::hasPrimaryKey = true; +const std::string Blog::tableName = "blog"; + +const std::vector Blog::metaData_ = { + {"id", "int32_t", "integer", 4, 1, 1, 1}, + {"title", "std::string", "character varying", 30, 0, 0, 0}, + {"category_id", "int32_t", "integer", 4, 0, 0, 0}}; + +const std::string &Blog::getColumnName(size_t index) noexcept(false) +{ + assert(index < metaData_.size()); + return metaData_[index].colName_; +} + +Blog::Blog(const Row &r, const ssize_t indexOffset) noexcept +{ + if (indexOffset < 0) + { + if (!r["id"].isNull()) + { + id_ = std::make_shared(r["id"].as()); + } + if (!r["title"].isNull()) + { + title_ = + std::make_shared(r["title"].as()); + } + if (!r["category_id"].isNull()) + { + categoryId_ = + std::make_shared(r["category_id"].as()); + } + } + else + { + size_t offset = (size_t)indexOffset; + if (offset + 3 > r.size()) + { + LOG_FATAL << "Invalid SQL result for this model"; + return; + } + size_t index; + index = offset + 0; + if (!r[index].isNull()) + { + id_ = std::make_shared(r[index].as()); + } + index = offset + 1; + if (!r[index].isNull()) + { + title_ = std::make_shared(r[index].as()); + } + index = offset + 2; + if (!r[index].isNull()) + { + categoryId_ = std::make_shared(r[index].as()); + } + } +} + +Blog::Blog(const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 3) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + dirtyFlag_[0] = true; + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + title_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + dirtyFlag_[2] = true; + if (!pJson[pMasqueradingVector[2]].isNull()) + { + categoryId_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[2]].asInt64()); + } + } +} + +Blog::Blog(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + dirtyFlag_[0] = true; + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("title")) + { + dirtyFlag_[1] = true; + if (!pJson["title"].isNull()) + { + title_ = std::make_shared(pJson["title"].asString()); + } + } + if (pJson.isMember("category_id")) + { + dirtyFlag_[2] = true; + if (!pJson["category_id"].isNull()) + { + categoryId_ = std::make_shared( + (int32_t)pJson["category_id"].asInt64()); + } + } +} + +void Blog::updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 3) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + title_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + dirtyFlag_[2] = true; + if (!pJson[pMasqueradingVector[2]].isNull()) + { + categoryId_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[2]].asInt64()); + } + } +} + +void Blog::updateByJson(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("title")) + { + dirtyFlag_[1] = true; + if (!pJson["title"].isNull()) + { + title_ = std::make_shared(pJson["title"].asString()); + } + } + if (pJson.isMember("category_id")) + { + dirtyFlag_[2] = true; + if (!pJson["category_id"].isNull()) + { + categoryId_ = std::make_shared( + (int32_t)pJson["category_id"].asInt64()); + } + } +} + +const int32_t &Blog::getValueOfId() const noexcept +{ + const static int32_t defaultValue = int32_t(); + if (id_) + return *id_; + return defaultValue; +} + +const std::shared_ptr &Blog::getId() const noexcept +{ + return id_; +} + +void Blog::setId(const int32_t &pId) noexcept +{ + id_ = std::make_shared(pId); + dirtyFlag_[0] = true; +} + +const typename Blog::PrimaryKeyType &Blog::getPrimaryKey() const +{ + assert(id_); + return *id_; +} + +const std::string &Blog::getValueOfTitle() const noexcept +{ + const static std::string defaultValue = std::string(); + if (title_) + return *title_; + return defaultValue; +} + +const std::shared_ptr &Blog::getTitle() const noexcept +{ + return title_; +} + +void Blog::setTitle(const std::string &pTitle) noexcept +{ + title_ = std::make_shared(pTitle); + dirtyFlag_[1] = true; +} + +void Blog::setTitle(std::string &&pTitle) noexcept +{ + title_ = std::make_shared(std::move(pTitle)); + dirtyFlag_[1] = true; +} + +void Blog::setTitleToNull() noexcept +{ + title_.reset(); + dirtyFlag_[1] = true; +} + +const int32_t &Blog::getValueOfCategoryId() const noexcept +{ + const static int32_t defaultValue = int32_t(); + if (categoryId_) + return *categoryId_; + return defaultValue; +} + +const std::shared_ptr &Blog::getCategoryId() const noexcept +{ + return categoryId_; +} + +void Blog::setCategoryId(const int32_t &pCategoryId) noexcept +{ + categoryId_ = std::make_shared(pCategoryId); + dirtyFlag_[2] = true; +} + +void Blog::setCategoryIdToNull() noexcept +{ + categoryId_.reset(); + dirtyFlag_[2] = true; +} + +void Blog::updateId(const uint64_t id) +{ +} + +const std::vector &Blog::insertColumns() noexcept +{ + static const std::vector inCols = {"title", "category_id"}; + return inCols; +} + +void Blog::outputArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getTitle()) + { + binder << getValueOfTitle(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[2]) + { + if (getCategoryId()) + { + binder << getValueOfCategoryId(); + } + else + { + binder << nullptr; + } + } +} + +const std::vector Blog::updateColumns() const +{ + std::vector ret; + if (dirtyFlag_[1]) + { + ret.push_back(getColumnName(1)); + } + if (dirtyFlag_[2]) + { + ret.push_back(getColumnName(2)); + } + return ret; +} + +void Blog::updateArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getTitle()) + { + binder << getValueOfTitle(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[2]) + { + if (getCategoryId()) + { + binder << getValueOfCategoryId(); + } + else + { + binder << nullptr; + } + } +} + +Json::Value Blog::toJson() const +{ + Json::Value ret; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getTitle()) + { + ret["title"] = getValueOfTitle(); + } + else + { + ret["title"] = Json::Value(); + } + if (getCategoryId()) + { + ret["category_id"] = getValueOfCategoryId(); + } + else + { + ret["category_id"] = Json::Value(); + } + return ret; +} + +Json::Value Blog::toMasqueradedJson( + const std::vector &pMasqueradingVector) const +{ + Json::Value ret; + if (pMasqueradingVector.size() == 3) + { + if (!pMasqueradingVector[0].empty()) + { + if (getId()) + { + ret[pMasqueradingVector[0]] = getValueOfId(); + } + else + { + ret[pMasqueradingVector[0]] = Json::Value(); + } + } + if (!pMasqueradingVector[1].empty()) + { + if (getTitle()) + { + ret[pMasqueradingVector[1]] = getValueOfTitle(); + } + else + { + ret[pMasqueradingVector[1]] = Json::Value(); + } + } + if (!pMasqueradingVector[2].empty()) + { + if (getCategoryId()) + { + ret[pMasqueradingVector[2]] = getValueOfCategoryId(); + } + else + { + ret[pMasqueradingVector[2]] = Json::Value(); + } + } + return ret; + } + LOG_ERROR << "Masquerade failed"; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getTitle()) + { + ret["title"] = getValueOfTitle(); + } + else + { + ret["title"] = Json::Value(); + } + if (getCategoryId()) + { + ret["category_id"] = getValueOfCategoryId(); + } + else + { + ret["category_id"] = Json::Value(); + } + return ret; +} + +bool Blog::validateJsonForCreation(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, true)) + return false; + } + if (pJson.isMember("title")) + { + if (!validJsonOfField(1, "title", pJson["title"], err, true)) + return false; + } + if (pJson.isMember("category_id")) + { + if (!validJsonOfField( + 2, "category_id", pJson["category_id"], err, true)) + return false; + } + return true; +} + +bool Blog::validateMasqueradedJsonForCreation( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 3) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty()) + { + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[2].empty()) + { + if (pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + true)) + return false; + } + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Blog::validateJsonForUpdate(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (pJson.isMember("title")) + { + if (!validJsonOfField(1, "title", pJson["title"], err, false)) + return false; + } + if (pJson.isMember("category_id")) + { + if (!validJsonOfField( + 2, "category_id", pJson["category_id"], err, false)) + return false; + } + return true; +} + +bool Blog::validateMasqueradedJsonForUpdate( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 3) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + false)) + return false; + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Blog::validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation) +{ + switch (index) + { + case 0: + if (pJson.isNull()) + { + err = "The " + fieldName + " column cannot be null"; + return false; + } + if (isForCreation) + { + err = "The automatic primary key cannot be set"; + return false; + } + if (!pJson.isInt()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 1: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isString()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + // asString().length() creates a string object, is there any better + // way to validate the length? + if (pJson.isString() && pJson.asString().length() > 30) + { + err = "String length exceeds limit for the " + fieldName + + " field (the maximum value is 30)"; + return false; + } + + break; + case 2: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isInt()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + default: + err = "Internal error in the server"; + return false; + } + return true; +} + +Category Blog::getCategory(const DbClientPtr &clientPtr) const +{ + const static std::string sql = "select * from category where id = $1"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *categoryId_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + if (r.size() == 0) + { + throw UnexpectedRows("0 rows found"); + } + else if (r.size() > 1) + { + throw UnexpectedRows("Found more than one row"); + } + return Category(r[0]); +} + +void Blog::getCategory(const DbClientPtr &clientPtr, + const std::function &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = "select * from category where id = $1"; + *clientPtr << sql << *categoryId_ >> [rcb = std::move(rcb), + ecb](const Result &r) { + if (r.size() == 0) + { + ecb(UnexpectedRows("0 rows found")); + } + else if (r.size() > 1) + { + ecb(UnexpectedRows("Found more than one row")); + } + else + { + rcb(Category(r[0])); + } + } >> ecb; +} + +std::vector> Blog::getTags( + const DbClientPtr &clientPtr) const +{ + const static std::string sql = + "select * from tag,blog_tag where blog_tag.blog_id = $1 and " + "blog_tag.tag_id = tag.id"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *id_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + std::vector> ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back( + std::pair(Tag(row), + BlogTag(row, Tag::getColumnNumber()))); + } + return ret; +} + +void Blog::getTags( + const DbClientPtr &clientPtr, + const std::function>)> &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = + "select * from tag,blog_tag where blog_tag.blog_id = $1 and " + "blog_tag.tag_id = tag.id"; + *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) { + std::vector> ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back( + std::pair(Tag(row), + BlogTag(row, Tag::getColumnNumber()))); + } + rcb(ret); + } >> ecb; +} diff --git a/orm_lib/tests/postgresql/Blog.h b/orm_lib/tests/postgresql/Blog.h new file mode 100644 index 00000000..85042c75 --- /dev/null +++ b/orm_lib/tests/postgresql/Blog.h @@ -0,0 +1,283 @@ +/** + * + * Blog.h + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#ifdef __cpp_impl_coroutine +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClient; +using DbClientPtr = std::shared_ptr; +} // namespace orm +} // namespace drogon + +namespace drogon_model +{ +namespace postgres +{ +class BlogTag; +class Category; +class Tag; + +class Blog +{ + public: + struct Cols + { + static const std::string _id; + static const std::string _title; + static const std::string _category_id; + }; + + const static int primaryKeyNumber; + const static std::string tableName; + const static bool hasPrimaryKey; + const static std::string primaryKeyName; + using PrimaryKeyType = int32_t; + const PrimaryKeyType &getPrimaryKey() const; + + /** + * @brief constructor + * @param r One row of records in the SQL query result. + * @param indexOffset Set the offset to -1 to access all columns by column + * names, otherwise access all columns by offsets. + * @note If the SQL is not a style of 'select * from table_name ...' (select + * all columns by an asterisk), please set the offset to -1. + */ + explicit Blog(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + */ + explicit Blog(const Json::Value &pJson) noexcept(false); + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + * @param pMasqueradingVector The aliases of table columns. + */ + Blog(const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + + Blog() = default; + + void updateByJson(const Json::Value &pJson) noexcept(false); + void updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + static bool validateJsonForCreation(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForCreation( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validateJsonForUpdate(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForUpdate( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation); + + /** For column id */ + /// Get the value of the column id, returns the default value if the column + /// is null + const int32_t &getValueOfId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getId() const noexcept; + /// Set the value of the column id + void setId(const int32_t &pId) noexcept; + + /** For column title */ + /// Get the value of the column title, returns the default value if the + /// column is null + const std::string &getValueOfTitle() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getTitle() const noexcept; + /// Set the value of the column title + void setTitle(const std::string &pTitle) noexcept; + void setTitle(std::string &&pTitle) noexcept; + void setTitleToNull() noexcept; + + /** For column category_id */ + /// Get the value of the column category_id, returns the default value if + /// the column is null + const int32_t &getValueOfCategoryId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getCategoryId() const noexcept; + /// Set the value of the column category_id + void setCategoryId(const int32_t &pCategoryId) noexcept; + void setCategoryIdToNull() noexcept; + + static size_t getColumnNumber() noexcept + { + return 3; + } + + static const std::string &getColumnName(size_t index) noexcept(false); + + Json::Value toJson() const; + Json::Value toMasqueradedJson( + const std::vector &pMasqueradingVector) const; + /// Relationship interfaces + Category getCategory(const drogon::orm::DbClientPtr &clientPtr) const; + void getCategory(const drogon::orm::DbClientPtr &clientPtr, + const std::function &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + std::vector> getTags( + const drogon::orm::DbClientPtr &clientPtr) const; + void getTags( + const drogon::orm::DbClientPtr &clientPtr, + const std::function>)> &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + + private: + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; +#ifdef __cpp_impl_coroutine + friend drogon::orm::CoroMapper; +#endif + static const std::vector &insertColumns() noexcept; + void outputArgs(drogon::orm::internal::SqlBinder &binder) const; + const std::vector updateColumns() const; + void updateArgs(drogon::orm::internal::SqlBinder &binder) const; + /// For mysql or sqlite3 + void updateId(const uint64_t id); + std::shared_ptr id_; + std::shared_ptr title_; + std::shared_ptr categoryId_; + + struct MetaData + { + const std::string colName_; + const std::string colType_; + const std::string colDatabaseType_; + const ssize_t colLength_; + const bool isAutoVal_; + const bool isPrimaryKey_; + const bool notNull_; + }; + + static const std::vector metaData_; + bool dirtyFlag_[3] = {false}; + + public: + static const std::string &sqlForFindingByPrimaryKey() + { + static const std::string sql = + "select * from " + tableName + " where id = $1"; + return sql; + } + + static const std::string &sqlForDeletingByPrimaryKey() + { + static const std::string sql = + "delete from " + tableName + " where id = $1"; + return sql; + } + + std::string sqlForInserting(bool &needSelection) const + { + std::string sql = "insert into " + tableName + " ("; + size_t parametersCount = 0; + needSelection = false; + sql += "id,"; + ++parametersCount; + sql += "title,"; + ++parametersCount; + if (!dirtyFlag_[1]) + { + needSelection = true; + } + if (dirtyFlag_[2]) + { + sql += "category_id,"; + ++parametersCount; + } + needSelection = true; + if (parametersCount > 0) + { + sql[sql.length() - 1] = ')'; + sql += " values ("; + } + else + sql += ") values ("; + + int placeholder = 1; + char placeholderStr[64]; + size_t n = 0; + sql += "default,"; + if (dirtyFlag_[1]) + { + n = snprintf(placeholderStr, + sizeof(placeholderStr), + "$%d,", + placeholder++); + sql.append(placeholderStr, n); + } + else + { + sql += "default,"; + } + if (dirtyFlag_[2]) + { + n = snprintf(placeholderStr, + sizeof(placeholderStr), + "$%d,", + placeholder++); + sql.append(placeholderStr, n); + } + if (parametersCount > 0) + { + sql.resize(sql.length() - 1); + } + if (needSelection) + { + sql.append(") returning *"); + } + else + { + sql.append(1, ')'); + } + LOG_TRACE << sql; + return sql; + } +}; +} // namespace postgres +} // namespace drogon_model diff --git a/orm_lib/tests/postgresql/BlogTag.cc b/orm_lib/tests/postgresql/BlogTag.cc new file mode 100644 index 00000000..9be89dd1 --- /dev/null +++ b/orm_lib/tests/postgresql/BlogTag.cc @@ -0,0 +1,564 @@ +/** + * + * BlogTag.cc + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#include "BlogTag.h" +#include +#include + +using namespace drogon; +using namespace drogon::orm; +using namespace drogon_model::postgres; + +const std::string BlogTag::Cols::_blog_id = "blog_id"; +const std::string BlogTag::Cols::_tag_id = "tag_id"; +const std::vector BlogTag::primaryKeyName = {"blog_id", "tag_id"}; +const bool BlogTag::hasPrimaryKey = true; +const std::string BlogTag::tableName = "blog_tag"; + +const std::vector BlogTag::metaData_ = { + {"blog_id", "int32_t", "integer", 4, 0, 1, 1}, + {"tag_id", "int32_t", "integer", 4, 0, 1, 1}}; + +const std::string &BlogTag::getColumnName(size_t index) noexcept(false) +{ + assert(index < metaData_.size()); + return metaData_[index].colName_; +} + +BlogTag::BlogTag(const Row &r, const ssize_t indexOffset) noexcept +{ + if (indexOffset < 0) + { + if (!r["blog_id"].isNull()) + { + blogId_ = std::make_shared(r["blog_id"].as()); + } + if (!r["tag_id"].isNull()) + { + tagId_ = std::make_shared(r["tag_id"].as()); + } + } + else + { + size_t offset = (size_t)indexOffset; + if (offset + 2 > r.size()) + { + LOG_FATAL << "Invalid SQL result for this model"; + return; + } + size_t index; + index = offset + 0; + if (!r[index].isNull()) + { + blogId_ = std::make_shared(r[index].as()); + } + index = offset + 1; + if (!r[index].isNull()) + { + tagId_ = std::make_shared(r[index].as()); + } + } +} + +BlogTag::BlogTag( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + dirtyFlag_[0] = true; + if (!pJson[pMasqueradingVector[0]].isNull()) + { + blogId_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + tagId_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[1]].asInt64()); + } + } +} + +BlogTag::BlogTag(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("blog_id")) + { + dirtyFlag_[0] = true; + if (!pJson["blog_id"].isNull()) + { + blogId_ = + std::make_shared((int32_t)pJson["blog_id"].asInt64()); + } + } + if (pJson.isMember("tag_id")) + { + dirtyFlag_[1] = true; + if (!pJson["tag_id"].isNull()) + { + tagId_ = + std::make_shared((int32_t)pJson["tag_id"].asInt64()); + } + } +} + +void BlogTag::updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!pJson[pMasqueradingVector[0]].isNull()) + { + blogId_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!pJson[pMasqueradingVector[1]].isNull()) + { + tagId_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[1]].asInt64()); + } + } +} + +void BlogTag::updateByJson(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("blog_id")) + { + if (!pJson["blog_id"].isNull()) + { + blogId_ = + std::make_shared((int32_t)pJson["blog_id"].asInt64()); + } + } + if (pJson.isMember("tag_id")) + { + if (!pJson["tag_id"].isNull()) + { + tagId_ = + std::make_shared((int32_t)pJson["tag_id"].asInt64()); + } + } +} + +const int32_t &BlogTag::getValueOfBlogId() const noexcept +{ + const static int32_t defaultValue = int32_t(); + if (blogId_) + return *blogId_; + return defaultValue; +} + +const std::shared_ptr &BlogTag::getBlogId() const noexcept +{ + return blogId_; +} + +void BlogTag::setBlogId(const int32_t &pBlogId) noexcept +{ + blogId_ = std::make_shared(pBlogId); + dirtyFlag_[0] = true; +} + +const int32_t &BlogTag::getValueOfTagId() const noexcept +{ + const static int32_t defaultValue = int32_t(); + if (tagId_) + return *tagId_; + return defaultValue; +} + +const std::shared_ptr &BlogTag::getTagId() const noexcept +{ + return tagId_; +} + +void BlogTag::setTagId(const int32_t &pTagId) noexcept +{ + tagId_ = std::make_shared(pTagId); + dirtyFlag_[1] = true; +} + +void BlogTag::updateId(const uint64_t id) +{ +} + +typename BlogTag::PrimaryKeyType BlogTag::getPrimaryKey() const +{ + return std::make_tuple(*blogId_, *tagId_); +} + +const std::vector &BlogTag::insertColumns() noexcept +{ + static const std::vector inCols = {"blog_id", "tag_id"}; + return inCols; +} + +void BlogTag::outputArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[0]) + { + if (getBlogId()) + { + binder << getValueOfBlogId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[1]) + { + if (getTagId()) + { + binder << getValueOfTagId(); + } + else + { + binder << nullptr; + } + } +} + +const std::vector BlogTag::updateColumns() const +{ + std::vector ret; + if (dirtyFlag_[0]) + { + ret.push_back(getColumnName(0)); + } + if (dirtyFlag_[1]) + { + ret.push_back(getColumnName(1)); + } + return ret; +} + +void BlogTag::updateArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[0]) + { + if (getBlogId()) + { + binder << getValueOfBlogId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[1]) + { + if (getTagId()) + { + binder << getValueOfTagId(); + } + else + { + binder << nullptr; + } + } +} + +Json::Value BlogTag::toJson() const +{ + Json::Value ret; + if (getBlogId()) + { + ret["blog_id"] = getValueOfBlogId(); + } + else + { + ret["blog_id"] = Json::Value(); + } + if (getTagId()) + { + ret["tag_id"] = getValueOfTagId(); + } + else + { + ret["tag_id"] = Json::Value(); + } + return ret; +} + +Json::Value BlogTag::toMasqueradedJson( + const std::vector &pMasqueradingVector) const +{ + Json::Value ret; + if (pMasqueradingVector.size() == 2) + { + if (!pMasqueradingVector[0].empty()) + { + if (getBlogId()) + { + ret[pMasqueradingVector[0]] = getValueOfBlogId(); + } + else + { + ret[pMasqueradingVector[0]] = Json::Value(); + } + } + if (!pMasqueradingVector[1].empty()) + { + if (getTagId()) + { + ret[pMasqueradingVector[1]] = getValueOfTagId(); + } + else + { + ret[pMasqueradingVector[1]] = Json::Value(); + } + } + return ret; + } + LOG_ERROR << "Masquerade failed"; + if (getBlogId()) + { + ret["blog_id"] = getValueOfBlogId(); + } + else + { + ret["blog_id"] = Json::Value(); + } + if (getTagId()) + { + ret["tag_id"] = getValueOfTagId(); + } + else + { + ret["tag_id"] = Json::Value(); + } + return ret; +} + +bool BlogTag::validateJsonForCreation(const Json::Value &pJson, + std::string &err) +{ + if (pJson.isMember("blog_id")) + { + if (!validJsonOfField(0, "blog_id", pJson["blog_id"], err, true)) + return false; + } + else + { + err = "The blog_id column cannot be null"; + return false; + } + if (pJson.isMember("tag_id")) + { + if (!validJsonOfField(1, "tag_id", pJson["tag_id"], err, true)) + return false; + } + else + { + err = "The tag_id column cannot be null"; + return false; + } + return true; +} + +bool BlogTag::validateMasqueradedJsonForCreation( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty()) + { + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + else + { + err = + "The " + pMasqueradingVector[0] + " column cannot be null"; + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + else + { + err = + "The " + pMasqueradingVector[1] + " column cannot be null"; + return false; + } + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool BlogTag::validateJsonForUpdate(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("blog_id")) + { + if (!validJsonOfField(0, "blog_id", pJson["blog_id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (pJson.isMember("tag_id")) + { + if (!validJsonOfField(1, "tag_id", pJson["tag_id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + return true; +} + +bool BlogTag::validateMasqueradedJsonForUpdate( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool BlogTag::validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation) +{ + switch (index) + { + case 0: + if (pJson.isNull()) + { + err = "The " + fieldName + " column cannot be null"; + return false; + } + if (!pJson.isInt()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 1: + if (pJson.isNull()) + { + err = "The " + fieldName + " column cannot be null"; + return false; + } + if (!pJson.isInt()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + default: + err = "Internal error in the server"; + return false; + } + return true; +} diff --git a/orm_lib/tests/postgresql/BlogTag.h b/orm_lib/tests/postgresql/BlogTag.h new file mode 100644 index 00000000..44b2d95b --- /dev/null +++ b/orm_lib/tests/postgresql/BlogTag.h @@ -0,0 +1,246 @@ +/** + * + * BlogTag.h + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#ifdef __cpp_impl_coroutine +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClient; +using DbClientPtr = std::shared_ptr; +} // namespace orm +} // namespace drogon + +namespace drogon_model +{ +namespace postgres +{ + +class BlogTag +{ + public: + struct Cols + { + static const std::string _blog_id; + static const std::string _tag_id; + }; + + const static int primaryKeyNumber; + const static std::string tableName; + const static bool hasPrimaryKey; + const static std::vector primaryKeyName; + using PrimaryKeyType = std::tuple; // blog_id,tag_id + PrimaryKeyType getPrimaryKey() const; + + /** + * @brief constructor + * @param r One row of records in the SQL query result. + * @param indexOffset Set the offset to -1 to access all columns by column + * names, otherwise access all columns by offsets. + * @note If the SQL is not a style of 'select * from table_name ...' (select + * all columns by an asterisk), please set the offset to -1. + */ + explicit BlogTag(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + */ + explicit BlogTag(const Json::Value &pJson) noexcept(false); + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + * @param pMasqueradingVector The aliases of table columns. + */ + BlogTag( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + + BlogTag() = default; + + void updateByJson(const Json::Value &pJson) noexcept(false); + void updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + static bool validateJsonForCreation(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForCreation( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validateJsonForUpdate(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForUpdate( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation); + + /** For column blog_id */ + /// Get the value of the column blog_id, returns the default value if the + /// column is null + const int32_t &getValueOfBlogId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getBlogId() const noexcept; + /// Set the value of the column blog_id + void setBlogId(const int32_t &pBlogId) noexcept; + + /** For column tag_id */ + /// Get the value of the column tag_id, returns the default value if the + /// column is null + const int32_t &getValueOfTagId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getTagId() const noexcept; + /// Set the value of the column tag_id + void setTagId(const int32_t &pTagId) noexcept; + + static size_t getColumnNumber() noexcept + { + return 2; + } + + static const std::string &getColumnName(size_t index) noexcept(false); + + Json::Value toJson() const; + Json::Value toMasqueradedJson( + const std::vector &pMasqueradingVector) const; + /// Relationship interfaces + private: + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; +#ifdef __cpp_impl_coroutine + friend drogon::orm::CoroMapper; +#endif + static const std::vector &insertColumns() noexcept; + void outputArgs(drogon::orm::internal::SqlBinder &binder) const; + const std::vector updateColumns() const; + void updateArgs(drogon::orm::internal::SqlBinder &binder) const; + /// For mysql or sqlite3 + void updateId(const uint64_t id); + std::shared_ptr blogId_; + std::shared_ptr tagId_; + + struct MetaData + { + const std::string colName_; + const std::string colType_; + const std::string colDatabaseType_; + const ssize_t colLength_; + const bool isAutoVal_; + const bool isPrimaryKey_; + const bool notNull_; + }; + + static const std::vector metaData_; + bool dirtyFlag_[2] = {false}; + + public: + static const std::string &sqlForFindingByPrimaryKey() + { + static const std::string sql = "select * from " + tableName + + " where blog_id = $1 and tag_id = $2"; + return sql; + } + + static const std::string &sqlForDeletingByPrimaryKey() + { + static const std::string sql = + "delete from " + tableName + " where blog_id = $1 and tag_id = $2"; + return sql; + } + + std::string sqlForInserting(bool &needSelection) const + { + std::string sql = "insert into " + tableName + " ("; + size_t parametersCount = 0; + needSelection = false; + if (dirtyFlag_[0]) + { + sql += "blog_id,"; + ++parametersCount; + } + if (dirtyFlag_[1]) + { + sql += "tag_id,"; + ++parametersCount; + } + if (parametersCount > 0) + { + sql[sql.length() - 1] = ')'; + sql += " values ("; + } + else + sql += ") values ("; + + int placeholder = 1; + char placeholderStr[64]; + size_t n = 0; + if (dirtyFlag_[0]) + { + n = snprintf(placeholderStr, + sizeof(placeholderStr), + "$%d,", + placeholder++); + sql.append(placeholderStr, n); + } + if (dirtyFlag_[1]) + { + n = snprintf(placeholderStr, + sizeof(placeholderStr), + "$%d,", + placeholder++); + sql.append(placeholderStr, n); + } + if (parametersCount > 0) + { + sql.resize(sql.length() - 1); + } + if (needSelection) + { + sql.append(") returning *"); + } + else + { + sql.append(1, ')'); + } + LOG_TRACE << sql; + return sql; + } +}; +} // namespace postgres +} // namespace drogon_model diff --git a/orm_lib/tests/postgresql/Category.cc b/orm_lib/tests/postgresql/Category.cc new file mode 100644 index 00000000..1331fab1 --- /dev/null +++ b/orm_lib/tests/postgresql/Category.cc @@ -0,0 +1,562 @@ +/** + * + * Category.cc + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#include "Category.h" +#include "Blog.h" +#include +#include + +using namespace drogon; +using namespace drogon::orm; +using namespace drogon_model::postgres; + +const std::string Category::Cols::_id = "id"; +const std::string Category::Cols::_name = "name"; +const std::string Category::primaryKeyName = "id"; +const bool Category::hasPrimaryKey = true; +const std::string Category::tableName = "category"; + +const std::vector Category::metaData_ = { + {"id", "int32_t", "integer", 4, 1, 1, 1}, + {"name", "std::string", "character varying", 30, 0, 0, 0}}; + +const std::string &Category::getColumnName(size_t index) noexcept(false) +{ + assert(index < metaData_.size()); + return metaData_[index].colName_; +} + +Category::Category(const Row &r, const ssize_t indexOffset) noexcept +{ + if (indexOffset < 0) + { + if (!r["id"].isNull()) + { + id_ = std::make_shared(r["id"].as()); + } + if (!r["name"].isNull()) + { + name_ = std::make_shared(r["name"].as()); + } + } + else + { + size_t offset = (size_t)indexOffset; + if (offset + 2 > r.size()) + { + LOG_FATAL << "Invalid SQL result for this model"; + return; + } + size_t index; + index = offset + 0; + if (!r[index].isNull()) + { + id_ = std::make_shared(r[index].as()); + } + index = offset + 1; + if (!r[index].isNull()) + { + name_ = std::make_shared(r[index].as()); + } + } +} + +Category::Category( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + dirtyFlag_[0] = true; + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + name_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } +} + +Category::Category(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + dirtyFlag_[0] = true; + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("name")) + { + dirtyFlag_[1] = true; + if (!pJson["name"].isNull()) + { + name_ = std::make_shared(pJson["name"].asString()); + } + } +} + +void Category::updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + name_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } +} + +void Category::updateByJson(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("name")) + { + dirtyFlag_[1] = true; + if (!pJson["name"].isNull()) + { + name_ = std::make_shared(pJson["name"].asString()); + } + } +} + +const int32_t &Category::getValueOfId() const noexcept +{ + const static int32_t defaultValue = int32_t(); + if (id_) + return *id_; + return defaultValue; +} + +const std::shared_ptr &Category::getId() const noexcept +{ + return id_; +} + +void Category::setId(const int32_t &pId) noexcept +{ + id_ = std::make_shared(pId); + dirtyFlag_[0] = true; +} + +const typename Category::PrimaryKeyType &Category::getPrimaryKey() const +{ + assert(id_); + return *id_; +} + +const std::string &Category::getValueOfName() const noexcept +{ + const static std::string defaultValue = std::string(); + if (name_) + return *name_; + return defaultValue; +} + +const std::shared_ptr &Category::getName() const noexcept +{ + return name_; +} + +void Category::setName(const std::string &pName) noexcept +{ + name_ = std::make_shared(pName); + dirtyFlag_[1] = true; +} + +void Category::setName(std::string &&pName) noexcept +{ + name_ = std::make_shared(std::move(pName)); + dirtyFlag_[1] = true; +} + +void Category::setNameToNull() noexcept +{ + name_.reset(); + dirtyFlag_[1] = true; +} + +void Category::updateId(const uint64_t id) +{ +} + +const std::vector &Category::insertColumns() noexcept +{ + static const std::vector inCols = {"name"}; + return inCols; +} + +void Category::outputArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getName()) + { + binder << getValueOfName(); + } + else + { + binder << nullptr; + } + } +} + +const std::vector Category::updateColumns() const +{ + std::vector ret; + if (dirtyFlag_[1]) + { + ret.push_back(getColumnName(1)); + } + return ret; +} + +void Category::updateArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getName()) + { + binder << getValueOfName(); + } + else + { + binder << nullptr; + } + } +} + +Json::Value Category::toJson() const +{ + Json::Value ret; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getName()) + { + ret["name"] = getValueOfName(); + } + else + { + ret["name"] = Json::Value(); + } + return ret; +} + +Json::Value Category::toMasqueradedJson( + const std::vector &pMasqueradingVector) const +{ + Json::Value ret; + if (pMasqueradingVector.size() == 2) + { + if (!pMasqueradingVector[0].empty()) + { + if (getId()) + { + ret[pMasqueradingVector[0]] = getValueOfId(); + } + else + { + ret[pMasqueradingVector[0]] = Json::Value(); + } + } + if (!pMasqueradingVector[1].empty()) + { + if (getName()) + { + ret[pMasqueradingVector[1]] = getValueOfName(); + } + else + { + ret[pMasqueradingVector[1]] = Json::Value(); + } + } + return ret; + } + LOG_ERROR << "Masquerade failed"; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getName()) + { + ret["name"] = getValueOfName(); + } + else + { + ret["name"] = Json::Value(); + } + return ret; +} + +bool Category::validateJsonForCreation(const Json::Value &pJson, + std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, true)) + return false; + } + if (pJson.isMember("name")) + { + if (!validJsonOfField(1, "name", pJson["name"], err, true)) + return false; + } + return true; +} + +bool Category::validateMasqueradedJsonForCreation( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty()) + { + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Category::validateJsonForUpdate(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (pJson.isMember("name")) + { + if (!validJsonOfField(1, "name", pJson["name"], err, false)) + return false; + } + return true; +} + +bool Category::validateMasqueradedJsonForUpdate( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Category::validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation) +{ + switch (index) + { + case 0: + if (pJson.isNull()) + { + err = "The " + fieldName + " column cannot be null"; + return false; + } + if (isForCreation) + { + err = "The automatic primary key cannot be set"; + return false; + } + if (!pJson.isInt()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 1: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isString()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + // asString().length() creates a string object, is there any better + // way to validate the length? + if (pJson.isString() && pJson.asString().length() > 30) + { + err = "String length exceeds limit for the " + fieldName + + " field (the maximum value is 30)"; + return false; + } + + break; + default: + err = "Internal error in the server"; + return false; + } + return true; +} + +std::vector Category::getBlogs(const DbClientPtr &clientPtr) const +{ + const static std::string sql = "select * from blog where category_id = $1"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *id_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + std::vector ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back(Blog(row)); + } + return ret; +} + +void Category::getBlogs(const DbClientPtr &clientPtr, + const std::function)> &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = "select * from blog where category_id = $1"; + *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) { + std::vector ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back(Blog(row)); + } + rcb(ret); + } >> ecb; +} diff --git a/orm_lib/tests/postgresql/Category.h b/orm_lib/tests/postgresql/Category.h new file mode 100644 index 00000000..7bb584b4 --- /dev/null +++ b/orm_lib/tests/postgresql/Category.h @@ -0,0 +1,250 @@ +/** + * + * Category.h + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#ifdef __cpp_impl_coroutine +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClient; +using DbClientPtr = std::shared_ptr; +} // namespace orm +} // namespace drogon + +namespace drogon_model +{ +namespace postgres +{ +class Blog; + +class Category +{ + public: + struct Cols + { + static const std::string _id; + static const std::string _name; + }; + + const static int primaryKeyNumber; + const static std::string tableName; + const static bool hasPrimaryKey; + const static std::string primaryKeyName; + using PrimaryKeyType = int32_t; + const PrimaryKeyType &getPrimaryKey() const; + + /** + * @brief constructor + * @param r One row of records in the SQL query result. + * @param indexOffset Set the offset to -1 to access all columns by column + * names, otherwise access all columns by offsets. + * @note If the SQL is not a style of 'select * from table_name ...' (select + * all columns by an asterisk), please set the offset to -1. + */ + explicit Category(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + */ + explicit Category(const Json::Value &pJson) noexcept(false); + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + * @param pMasqueradingVector The aliases of table columns. + */ + Category( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + + Category() = default; + + void updateByJson(const Json::Value &pJson) noexcept(false); + void updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + static bool validateJsonForCreation(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForCreation( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validateJsonForUpdate(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForUpdate( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation); + + /** For column id */ + /// Get the value of the column id, returns the default value if the column + /// is null + const int32_t &getValueOfId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getId() const noexcept; + /// Set the value of the column id + void setId(const int32_t &pId) noexcept; + + /** For column name */ + /// Get the value of the column name, returns the default value if the + /// column is null + const std::string &getValueOfName() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getName() const noexcept; + /// Set the value of the column name + void setName(const std::string &pName) noexcept; + void setName(std::string &&pName) noexcept; + void setNameToNull() noexcept; + + static size_t getColumnNumber() noexcept + { + return 2; + } + + static const std::string &getColumnName(size_t index) noexcept(false); + + Json::Value toJson() const; + Json::Value toMasqueradedJson( + const std::vector &pMasqueradingVector) const; + /// Relationship interfaces + std::vector getBlogs(const drogon::orm::DbClientPtr &clientPtr) const; + void getBlogs(const drogon::orm::DbClientPtr &clientPtr, + const std::function)> &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + + private: + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; +#ifdef __cpp_impl_coroutine + friend drogon::orm::CoroMapper; +#endif + static const std::vector &insertColumns() noexcept; + void outputArgs(drogon::orm::internal::SqlBinder &binder) const; + const std::vector updateColumns() const; + void updateArgs(drogon::orm::internal::SqlBinder &binder) const; + /// For mysql or sqlite3 + void updateId(const uint64_t id); + std::shared_ptr id_; + std::shared_ptr name_; + + struct MetaData + { + const std::string colName_; + const std::string colType_; + const std::string colDatabaseType_; + const ssize_t colLength_; + const bool isAutoVal_; + const bool isPrimaryKey_; + const bool notNull_; + }; + + static const std::vector metaData_; + bool dirtyFlag_[2] = {false}; + + public: + static const std::string &sqlForFindingByPrimaryKey() + { + static const std::string sql = + "select * from " + tableName + " where id = $1"; + return sql; + } + + static const std::string &sqlForDeletingByPrimaryKey() + { + static const std::string sql = + "delete from " + tableName + " where id = $1"; + return sql; + } + + std::string sqlForInserting(bool &needSelection) const + { + std::string sql = "insert into " + tableName + " ("; + size_t parametersCount = 0; + needSelection = false; + sql += "id,"; + ++parametersCount; + sql += "name,"; + ++parametersCount; + if (!dirtyFlag_[1]) + { + needSelection = true; + } + needSelection = true; + if (parametersCount > 0) + { + sql[sql.length() - 1] = ')'; + sql += " values ("; + } + else + sql += ") values ("; + + int placeholder = 1; + char placeholderStr[64]; + size_t n = 0; + sql += "default,"; + if (dirtyFlag_[1]) + { + n = snprintf(placeholderStr, + sizeof(placeholderStr), + "$%d,", + placeholder++); + sql.append(placeholderStr, n); + } + else + { + sql += "default,"; + } + if (parametersCount > 0) + { + sql.resize(sql.length() - 1); + } + if (needSelection) + { + sql.append(") returning *"); + } + else + { + sql.append(1, ')'); + } + LOG_TRACE << sql; + return sql; + } +}; +} // namespace postgres +} // namespace drogon_model diff --git a/orm_lib/tests/postgresql/Tag.cc b/orm_lib/tests/postgresql/Tag.cc new file mode 100644 index 00000000..fe48fe18 --- /dev/null +++ b/orm_lib/tests/postgresql/Tag.cc @@ -0,0 +1,570 @@ +/** + * + * Tag.cc + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#include "Tag.h" +#include "Blog.h" +#include "BlogTag.h" +#include +#include + +using namespace drogon; +using namespace drogon::orm; +using namespace drogon_model::postgres; + +const std::string Tag::Cols::_id = "id"; +const std::string Tag::Cols::_name = "name"; +const std::string Tag::primaryKeyName = "id"; +const bool Tag::hasPrimaryKey = true; +const std::string Tag::tableName = "tag"; + +const std::vector Tag::metaData_ = { + {"id", "int32_t", "integer", 4, 1, 1, 1}, + {"name", "std::string", "character varying", 30, 0, 0, 0}}; + +const std::string &Tag::getColumnName(size_t index) noexcept(false) +{ + assert(index < metaData_.size()); + return metaData_[index].colName_; +} + +Tag::Tag(const Row &r, const ssize_t indexOffset) noexcept +{ + if (indexOffset < 0) + { + if (!r["id"].isNull()) + { + id_ = std::make_shared(r["id"].as()); + } + if (!r["name"].isNull()) + { + name_ = std::make_shared(r["name"].as()); + } + } + else + { + size_t offset = (size_t)indexOffset; + if (offset + 2 > r.size()) + { + LOG_FATAL << "Invalid SQL result for this model"; + return; + } + size_t index; + index = offset + 0; + if (!r[index].isNull()) + { + id_ = std::make_shared(r[index].as()); + } + index = offset + 1; + if (!r[index].isNull()) + { + name_ = std::make_shared(r[index].as()); + } + } +} + +Tag::Tag(const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + dirtyFlag_[0] = true; + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + name_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } +} + +Tag::Tag(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + dirtyFlag_[0] = true; + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("name")) + { + dirtyFlag_[1] = true; + if (!pJson["name"].isNull()) + { + name_ = std::make_shared(pJson["name"].asString()); + } + } +} + +void Tag::updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + name_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } +} + +void Tag::updateByJson(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("name")) + { + dirtyFlag_[1] = true; + if (!pJson["name"].isNull()) + { + name_ = std::make_shared(pJson["name"].asString()); + } + } +} + +const int32_t &Tag::getValueOfId() const noexcept +{ + const static int32_t defaultValue = int32_t(); + if (id_) + return *id_; + return defaultValue; +} + +const std::shared_ptr &Tag::getId() const noexcept +{ + return id_; +} + +void Tag::setId(const int32_t &pId) noexcept +{ + id_ = std::make_shared(pId); + dirtyFlag_[0] = true; +} + +const typename Tag::PrimaryKeyType &Tag::getPrimaryKey() const +{ + assert(id_); + return *id_; +} + +const std::string &Tag::getValueOfName() const noexcept +{ + const static std::string defaultValue = std::string(); + if (name_) + return *name_; + return defaultValue; +} + +const std::shared_ptr &Tag::getName() const noexcept +{ + return name_; +} + +void Tag::setName(const std::string &pName) noexcept +{ + name_ = std::make_shared(pName); + dirtyFlag_[1] = true; +} + +void Tag::setName(std::string &&pName) noexcept +{ + name_ = std::make_shared(std::move(pName)); + dirtyFlag_[1] = true; +} + +void Tag::setNameToNull() noexcept +{ + name_.reset(); + dirtyFlag_[1] = true; +} + +void Tag::updateId(const uint64_t id) +{ +} + +const std::vector &Tag::insertColumns() noexcept +{ + static const std::vector inCols = {"name"}; + return inCols; +} + +void Tag::outputArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getName()) + { + binder << getValueOfName(); + } + else + { + binder << nullptr; + } + } +} + +const std::vector Tag::updateColumns() const +{ + std::vector ret; + if (dirtyFlag_[1]) + { + ret.push_back(getColumnName(1)); + } + return ret; +} + +void Tag::updateArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getName()) + { + binder << getValueOfName(); + } + else + { + binder << nullptr; + } + } +} + +Json::Value Tag::toJson() const +{ + Json::Value ret; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getName()) + { + ret["name"] = getValueOfName(); + } + else + { + ret["name"] = Json::Value(); + } + return ret; +} + +Json::Value Tag::toMasqueradedJson( + const std::vector &pMasqueradingVector) const +{ + Json::Value ret; + if (pMasqueradingVector.size() == 2) + { + if (!pMasqueradingVector[0].empty()) + { + if (getId()) + { + ret[pMasqueradingVector[0]] = getValueOfId(); + } + else + { + ret[pMasqueradingVector[0]] = Json::Value(); + } + } + if (!pMasqueradingVector[1].empty()) + { + if (getName()) + { + ret[pMasqueradingVector[1]] = getValueOfName(); + } + else + { + ret[pMasqueradingVector[1]] = Json::Value(); + } + } + return ret; + } + LOG_ERROR << "Masquerade failed"; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getName()) + { + ret["name"] = getValueOfName(); + } + else + { + ret["name"] = Json::Value(); + } + return ret; +} + +bool Tag::validateJsonForCreation(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, true)) + return false; + } + if (pJson.isMember("name")) + { + if (!validJsonOfField(1, "name", pJson["name"], err, true)) + return false; + } + return true; +} + +bool Tag::validateMasqueradedJsonForCreation( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty()) + { + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Tag::validateJsonForUpdate(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (pJson.isMember("name")) + { + if (!validJsonOfField(1, "name", pJson["name"], err, false)) + return false; + } + return true; +} + +bool Tag::validateMasqueradedJsonForUpdate( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Tag::validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation) +{ + switch (index) + { + case 0: + if (pJson.isNull()) + { + err = "The " + fieldName + " column cannot be null"; + return false; + } + if (isForCreation) + { + err = "The automatic primary key cannot be set"; + return false; + } + if (!pJson.isInt()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 1: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isString()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + // asString().length() creates a string object, is there any better + // way to validate the length? + if (pJson.isString() && pJson.asString().length() > 30) + { + err = "String length exceeds limit for the " + fieldName + + " field (the maximum value is 30)"; + return false; + } + + break; + default: + err = "Internal error in the server"; + return false; + } + return true; +} + +std::vector> Tag::getBlogs( + const DbClientPtr &clientPtr) const +{ + const static std::string sql = + "select * from blog,blog_tag where blog_tag.tag_id = $1 and " + "blog_tag.blog_id = blog.id"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *id_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + std::vector> ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back( + std::pair(Blog(row), + BlogTag(row, Blog::getColumnNumber()))); + } + return ret; +} + +void Tag::getBlogs( + const DbClientPtr &clientPtr, + const std::function>)> &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = + "select * from blog,blog_tag where blog_tag.tag_id = $1 and " + "blog_tag.blog_id = blog.id"; + *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) { + std::vector> ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back(std::pair( + Blog(row), BlogTag(row, Blog::getColumnNumber()))); + } + rcb(ret); + } >> ecb; +} diff --git a/orm_lib/tests/postgresql/Tag.h b/orm_lib/tests/postgresql/Tag.h new file mode 100644 index 00000000..6f9f280c --- /dev/null +++ b/orm_lib/tests/postgresql/Tag.h @@ -0,0 +1,252 @@ +/** + * + * Tag.h + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#ifdef __cpp_impl_coroutine +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClient; +using DbClientPtr = std::shared_ptr; +} // namespace orm +} // namespace drogon + +namespace drogon_model +{ +namespace postgres +{ +class Blog; +class BlogTag; + +class Tag +{ + public: + struct Cols + { + static const std::string _id; + static const std::string _name; + }; + + const static int primaryKeyNumber; + const static std::string tableName; + const static bool hasPrimaryKey; + const static std::string primaryKeyName; + using PrimaryKeyType = int32_t; + const PrimaryKeyType &getPrimaryKey() const; + + /** + * @brief constructor + * @param r One row of records in the SQL query result. + * @param indexOffset Set the offset to -1 to access all columns by column + * names, otherwise access all columns by offsets. + * @note If the SQL is not a style of 'select * from table_name ...' (select + * all columns by an asterisk), please set the offset to -1. + */ + explicit Tag(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + */ + explicit Tag(const Json::Value &pJson) noexcept(false); + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + * @param pMasqueradingVector The aliases of table columns. + */ + Tag(const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + + Tag() = default; + + void updateByJson(const Json::Value &pJson) noexcept(false); + void updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + static bool validateJsonForCreation(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForCreation( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validateJsonForUpdate(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForUpdate( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation); + + /** For column id */ + /// Get the value of the column id, returns the default value if the column + /// is null + const int32_t &getValueOfId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getId() const noexcept; + /// Set the value of the column id + void setId(const int32_t &pId) noexcept; + + /** For column name */ + /// Get the value of the column name, returns the default value if the + /// column is null + const std::string &getValueOfName() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getName() const noexcept; + /// Set the value of the column name + void setName(const std::string &pName) noexcept; + void setName(std::string &&pName) noexcept; + void setNameToNull() noexcept; + + static size_t getColumnNumber() noexcept + { + return 2; + } + + static const std::string &getColumnName(size_t index) noexcept(false); + + Json::Value toJson() const; + Json::Value toMasqueradedJson( + const std::vector &pMasqueradingVector) const; + /// Relationship interfaces + std::vector> getBlogs( + const drogon::orm::DbClientPtr &clientPtr) const; + void getBlogs( + const drogon::orm::DbClientPtr &clientPtr, + const std::function>)> &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + + private: + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; +#ifdef __cpp_impl_coroutine + friend drogon::orm::CoroMapper; +#endif + static const std::vector &insertColumns() noexcept; + void outputArgs(drogon::orm::internal::SqlBinder &binder) const; + const std::vector updateColumns() const; + void updateArgs(drogon::orm::internal::SqlBinder &binder) const; + /// For mysql or sqlite3 + void updateId(const uint64_t id); + std::shared_ptr id_; + std::shared_ptr name_; + + struct MetaData + { + const std::string colName_; + const std::string colType_; + const std::string colDatabaseType_; + const ssize_t colLength_; + const bool isAutoVal_; + const bool isPrimaryKey_; + const bool notNull_; + }; + + static const std::vector metaData_; + bool dirtyFlag_[2] = {false}; + + public: + static const std::string &sqlForFindingByPrimaryKey() + { + static const std::string sql = + "select * from " + tableName + " where id = $1"; + return sql; + } + + static const std::string &sqlForDeletingByPrimaryKey() + { + static const std::string sql = + "delete from " + tableName + " where id = $1"; + return sql; + } + + std::string sqlForInserting(bool &needSelection) const + { + std::string sql = "insert into " + tableName + " ("; + size_t parametersCount = 0; + needSelection = false; + sql += "id,"; + ++parametersCount; + sql += "name,"; + ++parametersCount; + if (!dirtyFlag_[1]) + { + needSelection = true; + } + needSelection = true; + if (parametersCount > 0) + { + sql[sql.length() - 1] = ')'; + sql += " values ("; + } + else + sql += ") values ("; + + int placeholder = 1; + char placeholderStr[64]; + size_t n = 0; + sql += "default,"; + if (dirtyFlag_[1]) + { + n = snprintf(placeholderStr, + sizeof(placeholderStr), + "$%d,", + placeholder++); + sql.append(placeholderStr, n); + } + else + { + sql += "default,"; + } + if (parametersCount > 0) + { + sql.resize(sql.length() - 1); + } + if (needSelection) + { + sql.append(") returning *"); + } + else + { + sql.append(1, ')'); + } + LOG_TRACE << sql; + return sql; + } +}; +} // namespace postgres +} // namespace drogon_model diff --git a/orm_lib/tests/postgresql/Users.cc b/orm_lib/tests/postgresql/Users.cc index 77b36843..5a373239 100644 --- a/orm_lib/tests/postgresql/Users.cc +++ b/orm_lib/tests/postgresql/Users.cc @@ -6,10 +6,12 @@ */ #include "Users.h" +#include "Wallets.h" #include #include using namespace drogon; +using namespace drogon::orm; using namespace drogon_model::postgres; const std::string Users::Cols::_user_id = "user_id"; @@ -1342,113 +1344,121 @@ bool Users::validateMasqueradedJsonForCreation( err = "Bad masquerading vector"; return false; } - if (!pMasqueradingVector[0].empty()) + try { - if (pJson.isMember(pMasqueradingVector[0])) + if (!pMasqueradingVector[0].empty()) { - if (!validJsonOfField(0, - pMasqueradingVector[0], - pJson[pMasqueradingVector[0]], - err, - true)) - return false; + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[2].empty()) + { + if (pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[3].empty()) + { + if (pJson.isMember(pMasqueradingVector[3])) + { + if (!validJsonOfField(3, + pMasqueradingVector[3], + pJson[pMasqueradingVector[3]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[4].empty()) + { + if (pJson.isMember(pMasqueradingVector[4])) + { + if (!validJsonOfField(4, + pMasqueradingVector[4], + pJson[pMasqueradingVector[4]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[5].empty()) + { + if (pJson.isMember(pMasqueradingVector[5])) + { + if (!validJsonOfField(5, + pMasqueradingVector[5], + pJson[pMasqueradingVector[5]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[6].empty()) + { + if (pJson.isMember(pMasqueradingVector[6])) + { + if (!validJsonOfField(6, + pMasqueradingVector[6], + pJson[pMasqueradingVector[6]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[7].empty()) + { + if (pJson.isMember(pMasqueradingVector[7])) + { + if (!validJsonOfField(7, + pMasqueradingVector[7], + pJson[pMasqueradingVector[7]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[8].empty()) + { + if (pJson.isMember(pMasqueradingVector[8])) + { + if (!validJsonOfField(8, + pMasqueradingVector[8], + pJson[pMasqueradingVector[8]], + err, + true)) + return false; + } } } - if (!pMasqueradingVector[1].empty()) + catch (const Json::LogicError &e) { - if (pJson.isMember(pMasqueradingVector[1])) - { - if (!validJsonOfField(1, - pMasqueradingVector[1], - pJson[pMasqueradingVector[1]], - err, - true)) - return false; - } - } - if (!pMasqueradingVector[2].empty()) - { - if (pJson.isMember(pMasqueradingVector[2])) - { - if (!validJsonOfField(2, - pMasqueradingVector[2], - pJson[pMasqueradingVector[2]], - err, - true)) - return false; - } - } - if (!pMasqueradingVector[3].empty()) - { - if (pJson.isMember(pMasqueradingVector[3])) - { - if (!validJsonOfField(3, - pMasqueradingVector[3], - pJson[pMasqueradingVector[3]], - err, - true)) - return false; - } - } - if (!pMasqueradingVector[4].empty()) - { - if (pJson.isMember(pMasqueradingVector[4])) - { - if (!validJsonOfField(4, - pMasqueradingVector[4], - pJson[pMasqueradingVector[4]], - err, - true)) - return false; - } - } - if (!pMasqueradingVector[5].empty()) - { - if (pJson.isMember(pMasqueradingVector[5])) - { - if (!validJsonOfField(5, - pMasqueradingVector[5], - pJson[pMasqueradingVector[5]], - err, - true)) - return false; - } - } - if (!pMasqueradingVector[6].empty()) - { - if (pJson.isMember(pMasqueradingVector[6])) - { - if (!validJsonOfField(6, - pMasqueradingVector[6], - pJson[pMasqueradingVector[6]], - err, - true)) - return false; - } - } - if (!pMasqueradingVector[7].empty()) - { - if (pJson.isMember(pMasqueradingVector[7])) - { - if (!validJsonOfField(7, - pMasqueradingVector[7], - pJson[pMasqueradingVector[7]], - err, - true)) - return false; - } - } - if (!pMasqueradingVector[8].empty()) - { - if (pJson.isMember(pMasqueradingVector[8])) - { - if (!validJsonOfField(8, - pMasqueradingVector[8], - pJson[pMasqueradingVector[8]], - err, - true)) - return false; - } + err = e.what(); + return false; } return true; } @@ -1520,103 +1530,111 @@ bool Users::validateMasqueradedJsonForUpdate( err = "Bad masquerading vector"; return false; } - if (!pMasqueradingVector[0].empty() && - pJson.isMember(pMasqueradingVector[0])) + try { - if (!validJsonOfField(0, - pMasqueradingVector[0], - pJson[pMasqueradingVector[0]], - err, - false)) + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + false)) + return false; + } + if (!pMasqueradingVector[3].empty() && + pJson.isMember(pMasqueradingVector[3])) + { + if (!validJsonOfField(3, + pMasqueradingVector[3], + pJson[pMasqueradingVector[3]], + err, + false)) + return false; + } + if (!pMasqueradingVector[4].empty() && + pJson.isMember(pMasqueradingVector[4])) + { + if (!validJsonOfField(4, + pMasqueradingVector[4], + pJson[pMasqueradingVector[4]], + err, + false)) + return false; + } + if (!pMasqueradingVector[5].empty() && + pJson.isMember(pMasqueradingVector[5])) + { + if (!validJsonOfField(5, + pMasqueradingVector[5], + pJson[pMasqueradingVector[5]], + err, + false)) + return false; + } + if (!pMasqueradingVector[6].empty() && + pJson.isMember(pMasqueradingVector[6])) + { + if (!validJsonOfField(6, + pMasqueradingVector[6], + pJson[pMasqueradingVector[6]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; return false; + } + if (!pMasqueradingVector[7].empty() && + pJson.isMember(pMasqueradingVector[7])) + { + if (!validJsonOfField(7, + pMasqueradingVector[7], + pJson[pMasqueradingVector[7]], + err, + false)) + return false; + } + if (!pMasqueradingVector[8].empty() && + pJson.isMember(pMasqueradingVector[8])) + { + if (!validJsonOfField(8, + pMasqueradingVector[8], + pJson[pMasqueradingVector[8]], + err, + false)) + return false; + } } - if (!pMasqueradingVector[1].empty() && - pJson.isMember(pMasqueradingVector[1])) + catch (const Json::LogicError &e) { - if (!validJsonOfField(1, - pMasqueradingVector[1], - pJson[pMasqueradingVector[1]], - err, - false)) - return false; - } - if (!pMasqueradingVector[2].empty() && - pJson.isMember(pMasqueradingVector[2])) - { - if (!validJsonOfField(2, - pMasqueradingVector[2], - pJson[pMasqueradingVector[2]], - err, - false)) - return false; - } - if (!pMasqueradingVector[3].empty() && - pJson.isMember(pMasqueradingVector[3])) - { - if (!validJsonOfField(3, - pMasqueradingVector[3], - pJson[pMasqueradingVector[3]], - err, - false)) - return false; - } - if (!pMasqueradingVector[4].empty() && - pJson.isMember(pMasqueradingVector[4])) - { - if (!validJsonOfField(4, - pMasqueradingVector[4], - pJson[pMasqueradingVector[4]], - err, - false)) - return false; - } - if (!pMasqueradingVector[5].empty() && - pJson.isMember(pMasqueradingVector[5])) - { - if (!validJsonOfField(5, - pMasqueradingVector[5], - pJson[pMasqueradingVector[5]], - err, - false)) - return false; - } - if (!pMasqueradingVector[6].empty() && - pJson.isMember(pMasqueradingVector[6])) - { - if (!validJsonOfField(6, - pMasqueradingVector[6], - pJson[pMasqueradingVector[6]], - err, - false)) - return false; - } - else - { - err = - "The value of primary key must be set in the json object for " - "update"; + err = e.what(); return false; } - if (!pMasqueradingVector[7].empty() && - pJson.isMember(pMasqueradingVector[7])) - { - if (!validJsonOfField(7, - pMasqueradingVector[7], - pJson[pMasqueradingVector[7]], - err, - false)) - return false; - } - if (!pMasqueradingVector[8].empty() && - pJson.isMember(pMasqueradingVector[8])) - { - if (!validJsonOfField(8, - pMasqueradingVector[8], - pJson[pMasqueradingVector[8]], - err, - false)) - return false; - } return true; } @@ -1796,11 +1814,52 @@ bool Users::validJsonOfField(size_t index, return false; } break; - default: err = "Internal error in the server"; return false; - break; } return true; } + +Wallets Users::getWallet(const DbClientPtr &clientPtr) const +{ + const static std::string sql = "select * from wallets where user_id = $1"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *userId_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + if (r.size() == 0) + { + throw UnexpectedRows("0 rows found"); + } + else if (r.size() > 1) + { + throw UnexpectedRows("Found more than one row"); + } + return Wallets(r[0]); +} + +void Users::getWallet(const DbClientPtr &clientPtr, + const std::function &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = "select * from wallets where user_id = $1"; + *clientPtr << sql << *userId_ >> [rcb = std::move(rcb), + ecb](const Result &r) { + if (r.size() == 0) + { + ecb(UnexpectedRows("0 rows found")); + } + else if (r.size() > 1) + { + ecb(UnexpectedRows("Found more than one row")); + } + else + { + rcb(Wallets(r[0])); + } + } >> ecb; +} diff --git a/orm_lib/tests/postgresql/Users.h b/orm_lib/tests/postgresql/Users.h index bf208694..c5e0d4f3 100644 --- a/orm_lib/tests/postgresql/Users.h +++ b/orm_lib/tests/postgresql/Users.h @@ -19,14 +19,13 @@ #include #include #include +#include #include #include #include #include #include -using namespace drogon::orm; - namespace drogon { namespace orm @@ -40,6 +39,8 @@ namespace drogon_model { namespace postgres { +class Wallets; + class Users { public: @@ -71,7 +72,8 @@ class Users * @note If the SQL is not a style of 'select * from table_name ...' (select * all columns by an asterisk), please set the offset to -1. */ - explicit Users(const Row &r, const ssize_t indexOffset = 0) noexcept; + explicit Users(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; /** * @brief constructor @@ -118,7 +120,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getUserId() const noexcept; - /// Set the value of the column user_id void setUserId(const std::string &pUserId) noexcept; void setUserId(std::string &&pUserId) noexcept; @@ -131,7 +132,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getUserName() const noexcept; - /// Set the value of the column user_name void setUserName(const std::string &pUserName) noexcept; void setUserName(std::string &&pUserName) noexcept; @@ -144,7 +144,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getPassword() const noexcept; - /// Set the value of the column password void setPassword(const std::string &pPassword) noexcept; void setPassword(std::string &&pPassword) noexcept; @@ -157,7 +156,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getOrgName() const noexcept; - /// Set the value of the column org_name void setOrgName(const std::string &pOrgName) noexcept; void setOrgName(std::string &&pOrgName) noexcept; @@ -170,7 +168,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getSignature() const noexcept; - /// Set the value of the column signature void setSignature(const std::string &pSignature) noexcept; void setSignature(std::string &&pSignature) noexcept; @@ -183,7 +180,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getAvatarId() const noexcept; - /// Set the value of the column avatar_id void setAvatarId(const std::string &pAvatarId) noexcept; void setAvatarId(std::string &&pAvatarId) noexcept; @@ -196,7 +192,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getId() const noexcept; - /// Set the value of the column id void setId(const int32_t &pId) noexcept; @@ -207,7 +202,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getSalt() const noexcept; - /// Set the value of the column salt void setSalt(const std::string &pSalt) noexcept; void setSalt(std::string &&pSalt) noexcept; @@ -220,7 +214,6 @@ class Users /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null const std::shared_ptr &getAdmin() const noexcept; - /// Set the value of the column admin void setAdmin(const bool &pAdmin) noexcept; void setAdminToNull() noexcept; @@ -236,14 +229,19 @@ class Users Json::Value toMasqueradedJson( const std::vector &pMasqueradingVector) const; /// Relationship interfaces + Wallets getWallet(const drogon::orm::DbClientPtr &clientPtr) const; + void getWallet(const drogon::orm::DbClientPtr &clientPtr, + const std::function &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + private: - friend Mapper; - friend BaseBuilder; - friend BaseBuilder; - friend BaseBuilder; - friend BaseBuilder; + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; #ifdef __cpp_impl_coroutine - friend CoroMapper; + friend drogon::orm::CoroMapper; #endif static const std::vector &insertColumns() noexcept; void outputArgs(drogon::orm::internal::SqlBinder &binder) const; diff --git a/orm_lib/tests/postgresql/Wallets.cc b/orm_lib/tests/postgresql/Wallets.cc new file mode 100644 index 00000000..5dd4d776 --- /dev/null +++ b/orm_lib/tests/postgresql/Wallets.cc @@ -0,0 +1,748 @@ +/** + * + * Wallets.cc + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#include "Wallets.h" +#include "Users.h" +#include +#include + +using namespace drogon; +using namespace drogon::orm; +using namespace drogon_model::postgres; + +const std::string Wallets::Cols::_id = "id"; +const std::string Wallets::Cols::_user_id = "user_id"; +const std::string Wallets::Cols::_amount = "amount"; +const std::string Wallets::primaryKeyName = "id"; +const bool Wallets::hasPrimaryKey = true; +const std::string Wallets::tableName = "wallets"; + +const std::vector Wallets::metaData_ = { + {"id", "int32_t", "integer", 4, 1, 1, 1}, + {"user_id", "std::string", "character varying", 32, 0, 0, 0}, + {"amount", "std::string", "numeric", 0, 0, 0, 0}}; + +const std::string &Wallets::getColumnName(size_t index) noexcept(false) +{ + assert(index < metaData_.size()); + return metaData_[index].colName_; +} + +Wallets::Wallets(const Row &r, const ssize_t indexOffset) noexcept +{ + if (indexOffset < 0) + { + if (!r["id"].isNull()) + { + id_ = std::make_shared(r["id"].as()); + } + if (!r["user_id"].isNull()) + { + userId_ = + std::make_shared(r["user_id"].as()); + } + if (!r["amount"].isNull()) + { + amount_ = + std::make_shared(r["amount"].as()); + } + } + else + { + size_t offset = (size_t)indexOffset; + if (offset + 3 > r.size()) + { + LOG_FATAL << "Invalid SQL result for this model"; + return; + } + size_t index; + index = offset + 0; + if (!r[index].isNull()) + { + id_ = std::make_shared(r[index].as()); + } + index = offset + 1; + if (!r[index].isNull()) + { + userId_ = std::make_shared(r[index].as()); + } + index = offset + 2; + if (!r[index].isNull()) + { + amount_ = std::make_shared(r[index].as()); + } + } +} + +Wallets::Wallets( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 3) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + dirtyFlag_[0] = true; + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + userId_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + dirtyFlag_[2] = true; + if (!pJson[pMasqueradingVector[2]].isNull()) + { + amount_ = std::make_shared( + pJson[pMasqueradingVector[2]].asString()); + } + } +} + +Wallets::Wallets(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + dirtyFlag_[0] = true; + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("user_id")) + { + dirtyFlag_[1] = true; + if (!pJson["user_id"].isNull()) + { + userId_ = + std::make_shared(pJson["user_id"].asString()); + } + } + if (pJson.isMember("amount")) + { + dirtyFlag_[2] = true; + if (!pJson["amount"].isNull()) + { + amount_ = std::make_shared(pJson["amount"].asString()); + } + } +} + +void Wallets::updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 3) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int32_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + userId_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + dirtyFlag_[2] = true; + if (!pJson[pMasqueradingVector[2]].isNull()) + { + amount_ = std::make_shared( + pJson[pMasqueradingVector[2]].asString()); + } + } +} + +void Wallets::updateByJson(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int32_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("user_id")) + { + dirtyFlag_[1] = true; + if (!pJson["user_id"].isNull()) + { + userId_ = + std::make_shared(pJson["user_id"].asString()); + } + } + if (pJson.isMember("amount")) + { + dirtyFlag_[2] = true; + if (!pJson["amount"].isNull()) + { + amount_ = std::make_shared(pJson["amount"].asString()); + } + } +} + +const int32_t &Wallets::getValueOfId() const noexcept +{ + const static int32_t defaultValue = int32_t(); + if (id_) + return *id_; + return defaultValue; +} + +const std::shared_ptr &Wallets::getId() const noexcept +{ + return id_; +} + +void Wallets::setId(const int32_t &pId) noexcept +{ + id_ = std::make_shared(pId); + dirtyFlag_[0] = true; +} + +const typename Wallets::PrimaryKeyType &Wallets::getPrimaryKey() const +{ + assert(id_); + return *id_; +} + +const std::string &Wallets::getValueOfUserId() const noexcept +{ + const static std::string defaultValue = std::string(); + if (userId_) + return *userId_; + return defaultValue; +} + +const std::shared_ptr &Wallets::getUserId() const noexcept +{ + return userId_; +} + +void Wallets::setUserId(const std::string &pUserId) noexcept +{ + userId_ = std::make_shared(pUserId); + dirtyFlag_[1] = true; +} + +void Wallets::setUserId(std::string &&pUserId) noexcept +{ + userId_ = std::make_shared(std::move(pUserId)); + dirtyFlag_[1] = true; +} + +void Wallets::setUserIdToNull() noexcept +{ + userId_.reset(); + dirtyFlag_[1] = true; +} + +const std::string &Wallets::getValueOfAmount() const noexcept +{ + const static std::string defaultValue = std::string(); + if (amount_) + return *amount_; + return defaultValue; +} + +const std::shared_ptr &Wallets::getAmount() const noexcept +{ + return amount_; +} + +void Wallets::setAmount(const std::string &pAmount) noexcept +{ + amount_ = std::make_shared(pAmount); + dirtyFlag_[2] = true; +} + +void Wallets::setAmount(std::string &&pAmount) noexcept +{ + amount_ = std::make_shared(std::move(pAmount)); + dirtyFlag_[2] = true; +} + +void Wallets::setAmountToNull() noexcept +{ + amount_.reset(); + dirtyFlag_[2] = true; +} + +void Wallets::updateId(const uint64_t id) +{ +} + +const std::vector &Wallets::insertColumns() noexcept +{ + static const std::vector inCols = {"user_id", "amount"}; + return inCols; +} + +void Wallets::outputArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getUserId()) + { + binder << getValueOfUserId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[2]) + { + if (getAmount()) + { + binder << getValueOfAmount(); + } + else + { + binder << nullptr; + } + } +} + +const std::vector Wallets::updateColumns() const +{ + std::vector ret; + if (dirtyFlag_[1]) + { + ret.push_back(getColumnName(1)); + } + if (dirtyFlag_[2]) + { + ret.push_back(getColumnName(2)); + } + return ret; +} + +void Wallets::updateArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getUserId()) + { + binder << getValueOfUserId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[2]) + { + if (getAmount()) + { + binder << getValueOfAmount(); + } + else + { + binder << nullptr; + } + } +} + +Json::Value Wallets::toJson() const +{ + Json::Value ret; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getUserId()) + { + ret["user_id"] = getValueOfUserId(); + } + else + { + ret["user_id"] = Json::Value(); + } + if (getAmount()) + { + ret["amount"] = getValueOfAmount(); + } + else + { + ret["amount"] = Json::Value(); + } + return ret; +} + +Json::Value Wallets::toMasqueradedJson( + const std::vector &pMasqueradingVector) const +{ + Json::Value ret; + if (pMasqueradingVector.size() == 3) + { + if (!pMasqueradingVector[0].empty()) + { + if (getId()) + { + ret[pMasqueradingVector[0]] = getValueOfId(); + } + else + { + ret[pMasqueradingVector[0]] = Json::Value(); + } + } + if (!pMasqueradingVector[1].empty()) + { + if (getUserId()) + { + ret[pMasqueradingVector[1]] = getValueOfUserId(); + } + else + { + ret[pMasqueradingVector[1]] = Json::Value(); + } + } + if (!pMasqueradingVector[2].empty()) + { + if (getAmount()) + { + ret[pMasqueradingVector[2]] = getValueOfAmount(); + } + else + { + ret[pMasqueradingVector[2]] = Json::Value(); + } + } + return ret; + } + LOG_ERROR << "Masquerade failed"; + if (getId()) + { + ret["id"] = getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getUserId()) + { + ret["user_id"] = getValueOfUserId(); + } + else + { + ret["user_id"] = Json::Value(); + } + if (getAmount()) + { + ret["amount"] = getValueOfAmount(); + } + else + { + ret["amount"] = Json::Value(); + } + return ret; +} + +bool Wallets::validateJsonForCreation(const Json::Value &pJson, + std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, true)) + return false; + } + if (pJson.isMember("user_id")) + { + if (!validJsonOfField(1, "user_id", pJson["user_id"], err, true)) + return false; + } + if (pJson.isMember("amount")) + { + if (!validJsonOfField(2, "amount", pJson["amount"], err, true)) + return false; + } + return true; +} + +bool Wallets::validateMasqueradedJsonForCreation( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 3) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty()) + { + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[2].empty()) + { + if (pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + true)) + return false; + } + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Wallets::validateJsonForUpdate(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (pJson.isMember("user_id")) + { + if (!validJsonOfField(1, "user_id", pJson["user_id"], err, false)) + return false; + } + if (pJson.isMember("amount")) + { + if (!validJsonOfField(2, "amount", pJson["amount"], err, false)) + return false; + } + return true; +} + +bool Wallets::validateMasqueradedJsonForUpdate( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 3) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + false)) + return false; + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Wallets::validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation) +{ + switch (index) + { + case 0: + if (pJson.isNull()) + { + err = "The " + fieldName + " column cannot be null"; + return false; + } + if (isForCreation) + { + err = "The automatic primary key cannot be set"; + return false; + } + if (!pJson.isInt()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 1: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isString()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + // asString().length() creates a string object, is there any better + // way to validate the length? + if (pJson.isString() && pJson.asString().length() > 32) + { + err = "String length exceeds limit for the " + fieldName + + " field (the maximum value is 32)"; + return false; + } + + break; + case 2: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isString()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + default: + err = "Internal error in the server"; + return false; + } + return true; +} + +Users Wallets::getUser(const DbClientPtr &clientPtr) const +{ + const static std::string sql = "select * from users where user_id = $1"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *userId_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + if (r.size() == 0) + { + throw UnexpectedRows("0 rows found"); + } + else if (r.size() > 1) + { + throw UnexpectedRows("Found more than one row"); + } + return Users(r[0]); +} + +void Wallets::getUser(const DbClientPtr &clientPtr, + const std::function &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = "select * from users where user_id = $1"; + *clientPtr << sql << *userId_ >> [rcb = std::move(rcb), + ecb](const Result &r) { + if (r.size() == 0) + { + ecb(UnexpectedRows("0 rows found")); + } + else if (r.size() > 1) + { + ecb(UnexpectedRows("Found more than one row")); + } + else + { + rcb(Users(r[0])); + } + } >> ecb; +} diff --git a/orm_lib/tests/postgresql/Wallets.h b/orm_lib/tests/postgresql/Wallets.h new file mode 100644 index 00000000..0637f9bb --- /dev/null +++ b/orm_lib/tests/postgresql/Wallets.h @@ -0,0 +1,282 @@ +/** + * + * Wallets.h + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#ifdef __cpp_impl_coroutine +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClient; +using DbClientPtr = std::shared_ptr; +} // namespace orm +} // namespace drogon + +namespace drogon_model +{ +namespace postgres +{ +class Users; + +class Wallets +{ + public: + struct Cols + { + static const std::string _id; + static const std::string _user_id; + static const std::string _amount; + }; + + const static int primaryKeyNumber; + const static std::string tableName; + const static bool hasPrimaryKey; + const static std::string primaryKeyName; + using PrimaryKeyType = int32_t; + const PrimaryKeyType &getPrimaryKey() const; + + /** + * @brief constructor + * @param r One row of records in the SQL query result. + * @param indexOffset Set the offset to -1 to access all columns by column + * names, otherwise access all columns by offsets. + * @note If the SQL is not a style of 'select * from table_name ...' (select + * all columns by an asterisk), please set the offset to -1. + */ + explicit Wallets(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + */ + explicit Wallets(const Json::Value &pJson) noexcept(false); + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + * @param pMasqueradingVector The aliases of table columns. + */ + Wallets( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + + Wallets() = default; + + void updateByJson(const Json::Value &pJson) noexcept(false); + void updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + static bool validateJsonForCreation(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForCreation( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validateJsonForUpdate(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForUpdate( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation); + + /** For column id */ + /// Get the value of the column id, returns the default value if the column + /// is null + const int32_t &getValueOfId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getId() const noexcept; + /// Set the value of the column id + void setId(const int32_t &pId) noexcept; + + /** For column user_id */ + /// Get the value of the column user_id, returns the default value if the + /// column is null + const std::string &getValueOfUserId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getUserId() const noexcept; + /// Set the value of the column user_id + void setUserId(const std::string &pUserId) noexcept; + void setUserId(std::string &&pUserId) noexcept; + void setUserIdToNull() noexcept; + + /** For column amount */ + /// Get the value of the column amount, returns the default value if the + /// column is null + const std::string &getValueOfAmount() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getAmount() const noexcept; + /// Set the value of the column amount + void setAmount(const std::string &pAmount) noexcept; + void setAmount(std::string &&pAmount) noexcept; + void setAmountToNull() noexcept; + + static size_t getColumnNumber() noexcept + { + return 3; + } + + static const std::string &getColumnName(size_t index) noexcept(false); + + Json::Value toJson() const; + Json::Value toMasqueradedJson( + const std::vector &pMasqueradingVector) const; + /// Relationship interfaces + Users getUser(const drogon::orm::DbClientPtr &clientPtr) const; + void getUser(const drogon::orm::DbClientPtr &clientPtr, + const std::function &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + + private: + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; +#ifdef __cpp_impl_coroutine + friend drogon::orm::CoroMapper; +#endif + static const std::vector &insertColumns() noexcept; + void outputArgs(drogon::orm::internal::SqlBinder &binder) const; + const std::vector updateColumns() const; + void updateArgs(drogon::orm::internal::SqlBinder &binder) const; + /// For mysql or sqlite3 + void updateId(const uint64_t id); + std::shared_ptr id_; + std::shared_ptr userId_; + std::shared_ptr amount_; + + struct MetaData + { + const std::string colName_; + const std::string colType_; + const std::string colDatabaseType_; + const ssize_t colLength_; + const bool isAutoVal_; + const bool isPrimaryKey_; + const bool notNull_; + }; + + static const std::vector metaData_; + bool dirtyFlag_[3] = {false}; + + public: + static const std::string &sqlForFindingByPrimaryKey() + { + static const std::string sql = + "select * from " + tableName + " where id = $1"; + return sql; + } + + static const std::string &sqlForDeletingByPrimaryKey() + { + static const std::string sql = + "delete from " + tableName + " where id = $1"; + return sql; + } + + std::string sqlForInserting(bool &needSelection) const + { + std::string sql = "insert into " + tableName + " ("; + size_t parametersCount = 0; + needSelection = false; + sql += "id,"; + ++parametersCount; + sql += "user_id,"; + ++parametersCount; + if (!dirtyFlag_[1]) + { + needSelection = true; + } + sql += "amount,"; + ++parametersCount; + if (!dirtyFlag_[2]) + { + needSelection = true; + } + needSelection = true; + if (parametersCount > 0) + { + sql[sql.length() - 1] = ')'; + sql += " values ("; + } + else + sql += ") values ("; + + int placeholder = 1; + char placeholderStr[64]; + size_t n = 0; + sql += "default,"; + if (dirtyFlag_[1]) + { + n = snprintf(placeholderStr, + sizeof(placeholderStr), + "$%d,", + placeholder++); + sql.append(placeholderStr, n); + } + else + { + sql += "default,"; + } + if (dirtyFlag_[2]) + { + n = snprintf(placeholderStr, + sizeof(placeholderStr), + "$%d,", + placeholder++); + sql.append(placeholderStr, n); + } + else + { + sql += "default,"; + } + if (parametersCount > 0) + { + sql.resize(sql.length() - 1); + } + if (needSelection) + { + sql.append(") returning *"); + } + else + { + sql.append(1, ')'); + } + LOG_TRACE << sql; + return sql; + } +}; +} // namespace postgres +} // namespace drogon_model diff --git a/orm_lib/tests/postgresql/model.json b/orm_lib/tests/postgresql/model.json index 9f9c3e76..af5b9336 100644 --- a/orm_lib/tests/postgresql/model.json +++ b/orm_lib/tests/postgresql/model.json @@ -10,5 +10,52 @@ "user":"postgres", "passwd":"", //"tables":["group_users"] - "tables":["users"] + "tables": [ + "users", + "wallets", + "blog", + "category", + "blog_tag", + "tag" + ], + "relationships": { + "enabled": true, + "items": [ + { + "type": "has one", + "original_table_name": "users", + "original_table_alias": "user", + "original_key": "user_id", + "target_table_name": "wallets", + "target_table_alias": "wallet", + "target_key": "user_id", + "enable_reverse": true + }, + { + "type": "has many", + "original_table_name": "category", + "original_table_alias": "category", + "original_key": "id", + "target_table_name": "blog", + "target_table_alias": "blogs", + "target_key": "category_id", + "enable_reverse": true + }, + { + "type": "many to many", + "original_table_name": "blog", + "original_table_alias": "blogs", + "original_key": "id", + "pivot_table": { + "table_name": "blog_tag", + "original_key": "blog_id", + "target_key": "tag_id" + }, + "target_table_name": "tag", + "target_table_alias": "tags", + "target_key": "id", + "enable_reverse": true + } + ] + } } diff --git a/orm_lib/tests/sqlite3/Blog.cc b/orm_lib/tests/sqlite3/Blog.cc new file mode 100644 index 00000000..2675a95b --- /dev/null +++ b/orm_lib/tests/sqlite3/Blog.cc @@ -0,0 +1,809 @@ +/** + * + * Blog.cc + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#include "Blog.h" +#include "BlogTag.h" +#include "Category.h" +#include "Tag.h" +#include +#include + +using namespace drogon; +using namespace drogon::orm; +using namespace drogon_model::sqlite3; + +const std::string Blog::Cols::_id = "id"; +const std::string Blog::Cols::_title = "title"; +const std::string Blog::Cols::_category_id = "category_id"; +const std::string Blog::primaryKeyName = "id"; +const bool Blog::hasPrimaryKey = true; +const std::string Blog::tableName = "blog"; + +const std::vector Blog::metaData_ = { + {"id", "int64_t", "integer auto_increment", 8, 0, 1, 0}, + {"title", "std::string", "varchar(30)", 0, 0, 0, 0}, + {"category_id", "int64_t", "integer", 8, 0, 0, 0}}; + +const std::string &Blog::getColumnName(size_t index) noexcept(false) +{ + assert(index < metaData_.size()); + return metaData_[index].colName_; +} + +Blog::Blog(const Row &r, const ssize_t indexOffset) noexcept +{ + if (indexOffset < 0) + { + if (!r["id"].isNull()) + { + id_ = std::make_shared(r["id"].as()); + } + if (!r["title"].isNull()) + { + title_ = + std::make_shared(r["title"].as()); + } + if (!r["category_id"].isNull()) + { + categoryId_ = + std::make_shared(r["category_id"].as()); + } + } + else + { + size_t offset = (size_t)indexOffset; + if (offset + 3 > r.size()) + { + LOG_FATAL << "Invalid SQL result for this model"; + return; + } + size_t index; + index = offset + 0; + if (!r[index].isNull()) + { + id_ = std::make_shared(r[index].as()); + } + index = offset + 1; + if (!r[index].isNull()) + { + title_ = std::make_shared(r[index].as()); + } + index = offset + 2; + if (!r[index].isNull()) + { + categoryId_ = std::make_shared(r[index].as()); + } + } +} + +Blog::Blog(const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 3) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + dirtyFlag_[0] = true; + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + title_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + dirtyFlag_[2] = true; + if (!pJson[pMasqueradingVector[2]].isNull()) + { + categoryId_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[2]].asInt64()); + } + } +} + +Blog::Blog(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + dirtyFlag_[0] = true; + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int64_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("title")) + { + dirtyFlag_[1] = true; + if (!pJson["title"].isNull()) + { + title_ = std::make_shared(pJson["title"].asString()); + } + } + if (pJson.isMember("category_id")) + { + dirtyFlag_[2] = true; + if (!pJson["category_id"].isNull()) + { + categoryId_ = std::make_shared( + (int64_t)pJson["category_id"].asInt64()); + } + } +} + +void Blog::updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 3) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + title_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + dirtyFlag_[2] = true; + if (!pJson[pMasqueradingVector[2]].isNull()) + { + categoryId_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[2]].asInt64()); + } + } +} + +void Blog::updateByJson(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int64_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("title")) + { + dirtyFlag_[1] = true; + if (!pJson["title"].isNull()) + { + title_ = std::make_shared(pJson["title"].asString()); + } + } + if (pJson.isMember("category_id")) + { + dirtyFlag_[2] = true; + if (!pJson["category_id"].isNull()) + { + categoryId_ = std::make_shared( + (int64_t)pJson["category_id"].asInt64()); + } + } +} + +const int64_t &Blog::getValueOfId() const noexcept +{ + const static int64_t defaultValue = int64_t(); + if (id_) + return *id_; + return defaultValue; +} + +const std::shared_ptr &Blog::getId() const noexcept +{ + return id_; +} + +void Blog::setId(const int64_t &pId) noexcept +{ + id_ = std::make_shared(pId); + dirtyFlag_[0] = true; +} + +void Blog::setIdToNull() noexcept +{ + id_.reset(); + dirtyFlag_[0] = true; +} + +const typename Blog::PrimaryKeyType &Blog::getPrimaryKey() const +{ + assert(id_); + return *id_; +} + +const std::string &Blog::getValueOfTitle() const noexcept +{ + const static std::string defaultValue = std::string(); + if (title_) + return *title_; + return defaultValue; +} + +const std::shared_ptr &Blog::getTitle() const noexcept +{ + return title_; +} + +void Blog::setTitle(const std::string &pTitle) noexcept +{ + title_ = std::make_shared(pTitle); + dirtyFlag_[1] = true; +} + +void Blog::setTitle(std::string &&pTitle) noexcept +{ + title_ = std::make_shared(std::move(pTitle)); + dirtyFlag_[1] = true; +} + +void Blog::setTitleToNull() noexcept +{ + title_.reset(); + dirtyFlag_[1] = true; +} + +const int64_t &Blog::getValueOfCategoryId() const noexcept +{ + const static int64_t defaultValue = int64_t(); + if (categoryId_) + return *categoryId_; + return defaultValue; +} + +const std::shared_ptr &Blog::getCategoryId() const noexcept +{ + return categoryId_; +} + +void Blog::setCategoryId(const int64_t &pCategoryId) noexcept +{ + categoryId_ = std::make_shared(pCategoryId); + dirtyFlag_[2] = true; +} + +void Blog::setCategoryIdToNull() noexcept +{ + categoryId_.reset(); + dirtyFlag_[2] = true; +} + +void Blog::updateId(const uint64_t id) +{ +} + +const std::vector &Blog::insertColumns() noexcept +{ + static const std::vector inCols = {"id", + "title", + "category_id"}; + return inCols; +} + +void Blog::outputArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[0]) + { + if (getId()) + { + binder << getValueOfId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[1]) + { + if (getTitle()) + { + binder << getValueOfTitle(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[2]) + { + if (getCategoryId()) + { + binder << getValueOfCategoryId(); + } + else + { + binder << nullptr; + } + } +} + +const std::vector Blog::updateColumns() const +{ + std::vector ret; + if (dirtyFlag_[0]) + { + ret.push_back(getColumnName(0)); + } + if (dirtyFlag_[1]) + { + ret.push_back(getColumnName(1)); + } + if (dirtyFlag_[2]) + { + ret.push_back(getColumnName(2)); + } + return ret; +} + +void Blog::updateArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[0]) + { + if (getId()) + { + binder << getValueOfId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[1]) + { + if (getTitle()) + { + binder << getValueOfTitle(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[2]) + { + if (getCategoryId()) + { + binder << getValueOfCategoryId(); + } + else + { + binder << nullptr; + } + } +} + +Json::Value Blog::toJson() const +{ + Json::Value ret; + if (getId()) + { + ret["id"] = (Json::Int64)getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getTitle()) + { + ret["title"] = getValueOfTitle(); + } + else + { + ret["title"] = Json::Value(); + } + if (getCategoryId()) + { + ret["category_id"] = (Json::Int64)getValueOfCategoryId(); + } + else + { + ret["category_id"] = Json::Value(); + } + return ret; +} + +Json::Value Blog::toMasqueradedJson( + const std::vector &pMasqueradingVector) const +{ + Json::Value ret; + if (pMasqueradingVector.size() == 3) + { + if (!pMasqueradingVector[0].empty()) + { + if (getId()) + { + ret[pMasqueradingVector[0]] = (Json::Int64)getValueOfId(); + } + else + { + ret[pMasqueradingVector[0]] = Json::Value(); + } + } + if (!pMasqueradingVector[1].empty()) + { + if (getTitle()) + { + ret[pMasqueradingVector[1]] = getValueOfTitle(); + } + else + { + ret[pMasqueradingVector[1]] = Json::Value(); + } + } + if (!pMasqueradingVector[2].empty()) + { + if (getCategoryId()) + { + ret[pMasqueradingVector[2]] = + (Json::Int64)getValueOfCategoryId(); + } + else + { + ret[pMasqueradingVector[2]] = Json::Value(); + } + } + return ret; + } + LOG_ERROR << "Masquerade failed"; + if (getId()) + { + ret["id"] = (Json::Int64)getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getTitle()) + { + ret["title"] = getValueOfTitle(); + } + else + { + ret["title"] = Json::Value(); + } + if (getCategoryId()) + { + ret["category_id"] = (Json::Int64)getValueOfCategoryId(); + } + else + { + ret["category_id"] = Json::Value(); + } + return ret; +} + +bool Blog::validateJsonForCreation(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, true)) + return false; + } + if (pJson.isMember("title")) + { + if (!validJsonOfField(1, "title", pJson["title"], err, true)) + return false; + } + if (pJson.isMember("category_id")) + { + if (!validJsonOfField( + 2, "category_id", pJson["category_id"], err, true)) + return false; + } + return true; +} + +bool Blog::validateMasqueradedJsonForCreation( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 3) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty()) + { + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[2].empty()) + { + if (pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + true)) + return false; + } + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Blog::validateJsonForUpdate(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (pJson.isMember("title")) + { + if (!validJsonOfField(1, "title", pJson["title"], err, false)) + return false; + } + if (pJson.isMember("category_id")) + { + if (!validJsonOfField( + 2, "category_id", pJson["category_id"], err, false)) + return false; + } + return true; +} + +bool Blog::validateMasqueradedJsonForUpdate( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 3) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + false)) + return false; + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Blog::validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation) +{ + switch (index) + { + case 0: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isInt64()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 1: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isString()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 2: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isInt64()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + default: + err = "Internal error in the server"; + return false; + } + return true; +} + +Category Blog::getCategory(const DbClientPtr &clientPtr) const +{ + const static std::string sql = "select * from category where id = ?"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *categoryId_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + if (r.size() == 0) + { + throw UnexpectedRows("0 rows found"); + } + else if (r.size() > 1) + { + throw UnexpectedRows("Found more than one row"); + } + return Category(r[0]); +} + +void Blog::getCategory(const DbClientPtr &clientPtr, + const std::function &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = "select * from category where id = ?"; + *clientPtr << sql << *categoryId_ >> [rcb = std::move(rcb), + ecb](const Result &r) { + if (r.size() == 0) + { + ecb(UnexpectedRows("0 rows found")); + } + else if (r.size() > 1) + { + ecb(UnexpectedRows("Found more than one row")); + } + else + { + rcb(Category(r[0])); + } + } >> ecb; +} + +std::vector> Blog::getTags( + const DbClientPtr &clientPtr) const +{ + const static std::string sql = + "select * from tag,blog_tag where blog_tag.blog_id = ? and " + "blog_tag.tag_id = tag.id"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *id_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + std::vector> ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back( + std::pair(Tag(row), + BlogTag(row, Tag::getColumnNumber()))); + } + return ret; +} + +void Blog::getTags( + const DbClientPtr &clientPtr, + const std::function>)> &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = + "select * from tag,blog_tag where blog_tag.blog_id = ? and " + "blog_tag.tag_id = tag.id"; + *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) { + std::vector> ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back( + std::pair(Tag(row), + BlogTag(row, Tag::getColumnNumber()))); + } + rcb(ret); + } >> ecb; +} diff --git a/orm_lib/tests/sqlite3/Blog.h b/orm_lib/tests/sqlite3/Blog.h new file mode 100644 index 00000000..522ca42f --- /dev/null +++ b/orm_lib/tests/sqlite3/Blog.h @@ -0,0 +1,274 @@ +/** + * + * Blog.h + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#ifdef __cpp_impl_coroutine +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClient; +using DbClientPtr = std::shared_ptr; +} // namespace orm +} // namespace drogon + +namespace drogon_model +{ +namespace sqlite3 +{ +class BlogTag; +class Category; +class Tag; + +class Blog +{ + public: + struct Cols + { + static const std::string _id; + static const std::string _title; + static const std::string _category_id; + }; + + const static int primaryKeyNumber; + const static std::string tableName; + const static bool hasPrimaryKey; + const static std::string primaryKeyName; + using PrimaryKeyType = int64_t; + const PrimaryKeyType &getPrimaryKey() const; + + /** + * @brief constructor + * @param r One row of records in the SQL query result. + * @param indexOffset Set the offset to -1 to access all columns by column + * names, otherwise access all columns by offsets. + * @note If the SQL is not a style of 'select * from table_name ...' (select + * all columns by an asterisk), please set the offset to -1. + */ + explicit Blog(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + */ + explicit Blog(const Json::Value &pJson) noexcept(false); + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + * @param pMasqueradingVector The aliases of table columns. + */ + Blog(const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + + Blog() = default; + + void updateByJson(const Json::Value &pJson) noexcept(false); + void updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + static bool validateJsonForCreation(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForCreation( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validateJsonForUpdate(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForUpdate( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation); + + /** For column id */ + /// Get the value of the column id, returns the default value if the column + /// is null + const int64_t &getValueOfId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getId() const noexcept; + /// Set the value of the column id + void setId(const int64_t &pId) noexcept; + void setIdToNull() noexcept; + + /** For column title */ + /// Get the value of the column title, returns the default value if the + /// column is null + const std::string &getValueOfTitle() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getTitle() const noexcept; + /// Set the value of the column title + void setTitle(const std::string &pTitle) noexcept; + void setTitle(std::string &&pTitle) noexcept; + void setTitleToNull() noexcept; + + /** For column category_id */ + /// Get the value of the column category_id, returns the default value if + /// the column is null + const int64_t &getValueOfCategoryId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getCategoryId() const noexcept; + /// Set the value of the column category_id + void setCategoryId(const int64_t &pCategoryId) noexcept; + void setCategoryIdToNull() noexcept; + + static size_t getColumnNumber() noexcept + { + return 3; + } + + static const std::string &getColumnName(size_t index) noexcept(false); + + Json::Value toJson() const; + Json::Value toMasqueradedJson( + const std::vector &pMasqueradingVector) const; + /// Relationship interfaces + Category getCategory(const drogon::orm::DbClientPtr &clientPtr) const; + void getCategory(const drogon::orm::DbClientPtr &clientPtr, + const std::function &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + std::vector> getTags( + const drogon::orm::DbClientPtr &clientPtr) const; + void getTags( + const drogon::orm::DbClientPtr &clientPtr, + const std::function>)> &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + + private: + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; +#ifdef __cpp_impl_coroutine + friend drogon::orm::CoroMapper; +#endif + static const std::vector &insertColumns() noexcept; + void outputArgs(drogon::orm::internal::SqlBinder &binder) const; + const std::vector updateColumns() const; + void updateArgs(drogon::orm::internal::SqlBinder &binder) const; + /// For mysql or sqlite3 + void updateId(const uint64_t id); + std::shared_ptr id_; + std::shared_ptr title_; + std::shared_ptr categoryId_; + + struct MetaData + { + const std::string colName_; + const std::string colType_; + const std::string colDatabaseType_; + const ssize_t colLength_; + const bool isAutoVal_; + const bool isPrimaryKey_; + const bool notNull_; + }; + + static const std::vector metaData_; + bool dirtyFlag_[3] = {false}; + + public: + static const std::string &sqlForFindingByPrimaryKey() + { + static const std::string sql = + "select * from " + tableName + " where id = ?"; + return sql; + } + + static const std::string &sqlForDeletingByPrimaryKey() + { + static const std::string sql = + "delete from " + tableName + " where id = ?"; + return sql; + } + + std::string sqlForInserting(bool &needSelection) const + { + std::string sql = "insert into " + tableName + " ("; + size_t parametersCount = 0; + needSelection = false; + if (dirtyFlag_[0]) + { + sql += "id,"; + ++parametersCount; + } + if (dirtyFlag_[1]) + { + sql += "title,"; + ++parametersCount; + } + if (!dirtyFlag_[1]) + { + needSelection = true; + } + if (dirtyFlag_[2]) + { + sql += "category_id,"; + ++parametersCount; + } + if (!dirtyFlag_[2]) + { + needSelection = true; + } + if (parametersCount > 0) + { + sql[sql.length() - 1] = ')'; + sql += " values ("; + } + else + sql += ") values ("; + + if (dirtyFlag_[0]) + { + sql.append("?,"); + } + if (dirtyFlag_[1]) + { + sql.append("?,"); + } + if (dirtyFlag_[2]) + { + sql.append("?,"); + } + if (parametersCount > 0) + { + sql.resize(sql.length() - 1); + } + sql.append(1, ')'); + LOG_TRACE << sql; + return sql; + } +}; +} // namespace sqlite3 +} // namespace drogon_model diff --git a/orm_lib/tests/sqlite3/BlogTag.cc b/orm_lib/tests/sqlite3/BlogTag.cc new file mode 100644 index 00000000..680dcad7 --- /dev/null +++ b/orm_lib/tests/sqlite3/BlogTag.cc @@ -0,0 +1,564 @@ +/** + * + * BlogTag.cc + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#include "BlogTag.h" +#include +#include + +using namespace drogon; +using namespace drogon::orm; +using namespace drogon_model::sqlite3; + +const std::string BlogTag::Cols::_blog_id = "blog_id"; +const std::string BlogTag::Cols::_tag_id = "tag_id"; +const std::vector BlogTag::primaryKeyName = {"blog_id", "tag_id"}; +const bool BlogTag::hasPrimaryKey = true; +const std::string BlogTag::tableName = "blog_tag"; + +const std::vector BlogTag::metaData_ = { + {"blog_id", "int64_t", "integer", 8, 0, 1, 1}, + {"tag_id", "int64_t", "integer", 8, 0, 1, 1}}; + +const std::string &BlogTag::getColumnName(size_t index) noexcept(false) +{ + assert(index < metaData_.size()); + return metaData_[index].colName_; +} + +BlogTag::BlogTag(const Row &r, const ssize_t indexOffset) noexcept +{ + if (indexOffset < 0) + { + if (!r["blog_id"].isNull()) + { + blogId_ = std::make_shared(r["blog_id"].as()); + } + if (!r["tag_id"].isNull()) + { + tagId_ = std::make_shared(r["tag_id"].as()); + } + } + else + { + size_t offset = (size_t)indexOffset; + if (offset + 2 > r.size()) + { + LOG_FATAL << "Invalid SQL result for this model"; + return; + } + size_t index; + index = offset + 0; + if (!r[index].isNull()) + { + blogId_ = std::make_shared(r[index].as()); + } + index = offset + 1; + if (!r[index].isNull()) + { + tagId_ = std::make_shared(r[index].as()); + } + } +} + +BlogTag::BlogTag( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + dirtyFlag_[0] = true; + if (!pJson[pMasqueradingVector[0]].isNull()) + { + blogId_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + tagId_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[1]].asInt64()); + } + } +} + +BlogTag::BlogTag(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("blog_id")) + { + dirtyFlag_[0] = true; + if (!pJson["blog_id"].isNull()) + { + blogId_ = + std::make_shared((int64_t)pJson["blog_id"].asInt64()); + } + } + if (pJson.isMember("tag_id")) + { + dirtyFlag_[1] = true; + if (!pJson["tag_id"].isNull()) + { + tagId_ = + std::make_shared((int64_t)pJson["tag_id"].asInt64()); + } + } +} + +void BlogTag::updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!pJson[pMasqueradingVector[0]].isNull()) + { + blogId_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!pJson[pMasqueradingVector[1]].isNull()) + { + tagId_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[1]].asInt64()); + } + } +} + +void BlogTag::updateByJson(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("blog_id")) + { + if (!pJson["blog_id"].isNull()) + { + blogId_ = + std::make_shared((int64_t)pJson["blog_id"].asInt64()); + } + } + if (pJson.isMember("tag_id")) + { + if (!pJson["tag_id"].isNull()) + { + tagId_ = + std::make_shared((int64_t)pJson["tag_id"].asInt64()); + } + } +} + +const int64_t &BlogTag::getValueOfBlogId() const noexcept +{ + const static int64_t defaultValue = int64_t(); + if (blogId_) + return *blogId_; + return defaultValue; +} + +const std::shared_ptr &BlogTag::getBlogId() const noexcept +{ + return blogId_; +} + +void BlogTag::setBlogId(const int64_t &pBlogId) noexcept +{ + blogId_ = std::make_shared(pBlogId); + dirtyFlag_[0] = true; +} + +const int64_t &BlogTag::getValueOfTagId() const noexcept +{ + const static int64_t defaultValue = int64_t(); + if (tagId_) + return *tagId_; + return defaultValue; +} + +const std::shared_ptr &BlogTag::getTagId() const noexcept +{ + return tagId_; +} + +void BlogTag::setTagId(const int64_t &pTagId) noexcept +{ + tagId_ = std::make_shared(pTagId); + dirtyFlag_[1] = true; +} + +void BlogTag::updateId(const uint64_t id) +{ +} + +typename BlogTag::PrimaryKeyType BlogTag::getPrimaryKey() const +{ + return std::make_tuple(*blogId_, *tagId_); +} + +const std::vector &BlogTag::insertColumns() noexcept +{ + static const std::vector inCols = {"blog_id", "tag_id"}; + return inCols; +} + +void BlogTag::outputArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[0]) + { + if (getBlogId()) + { + binder << getValueOfBlogId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[1]) + { + if (getTagId()) + { + binder << getValueOfTagId(); + } + else + { + binder << nullptr; + } + } +} + +const std::vector BlogTag::updateColumns() const +{ + std::vector ret; + if (dirtyFlag_[0]) + { + ret.push_back(getColumnName(0)); + } + if (dirtyFlag_[1]) + { + ret.push_back(getColumnName(1)); + } + return ret; +} + +void BlogTag::updateArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[0]) + { + if (getBlogId()) + { + binder << getValueOfBlogId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[1]) + { + if (getTagId()) + { + binder << getValueOfTagId(); + } + else + { + binder << nullptr; + } + } +} + +Json::Value BlogTag::toJson() const +{ + Json::Value ret; + if (getBlogId()) + { + ret["blog_id"] = (Json::Int64)getValueOfBlogId(); + } + else + { + ret["blog_id"] = Json::Value(); + } + if (getTagId()) + { + ret["tag_id"] = (Json::Int64)getValueOfTagId(); + } + else + { + ret["tag_id"] = Json::Value(); + } + return ret; +} + +Json::Value BlogTag::toMasqueradedJson( + const std::vector &pMasqueradingVector) const +{ + Json::Value ret; + if (pMasqueradingVector.size() == 2) + { + if (!pMasqueradingVector[0].empty()) + { + if (getBlogId()) + { + ret[pMasqueradingVector[0]] = (Json::Int64)getValueOfBlogId(); + } + else + { + ret[pMasqueradingVector[0]] = Json::Value(); + } + } + if (!pMasqueradingVector[1].empty()) + { + if (getTagId()) + { + ret[pMasqueradingVector[1]] = (Json::Int64)getValueOfTagId(); + } + else + { + ret[pMasqueradingVector[1]] = Json::Value(); + } + } + return ret; + } + LOG_ERROR << "Masquerade failed"; + if (getBlogId()) + { + ret["blog_id"] = (Json::Int64)getValueOfBlogId(); + } + else + { + ret["blog_id"] = Json::Value(); + } + if (getTagId()) + { + ret["tag_id"] = (Json::Int64)getValueOfTagId(); + } + else + { + ret["tag_id"] = Json::Value(); + } + return ret; +} + +bool BlogTag::validateJsonForCreation(const Json::Value &pJson, + std::string &err) +{ + if (pJson.isMember("blog_id")) + { + if (!validJsonOfField(0, "blog_id", pJson["blog_id"], err, true)) + return false; + } + else + { + err = "The blog_id column cannot be null"; + return false; + } + if (pJson.isMember("tag_id")) + { + if (!validJsonOfField(1, "tag_id", pJson["tag_id"], err, true)) + return false; + } + else + { + err = "The tag_id column cannot be null"; + return false; + } + return true; +} + +bool BlogTag::validateMasqueradedJsonForCreation( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty()) + { + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + else + { + err = + "The " + pMasqueradingVector[0] + " column cannot be null"; + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + else + { + err = + "The " + pMasqueradingVector[1] + " column cannot be null"; + return false; + } + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool BlogTag::validateJsonForUpdate(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("blog_id")) + { + if (!validJsonOfField(0, "blog_id", pJson["blog_id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (pJson.isMember("tag_id")) + { + if (!validJsonOfField(1, "tag_id", pJson["tag_id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + return true; +} + +bool BlogTag::validateMasqueradedJsonForUpdate( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool BlogTag::validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation) +{ + switch (index) + { + case 0: + if (pJson.isNull()) + { + err = "The " + fieldName + " column cannot be null"; + return false; + } + if (!pJson.isInt64()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 1: + if (pJson.isNull()) + { + err = "The " + fieldName + " column cannot be null"; + return false; + } + if (!pJson.isInt64()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + default: + err = "Internal error in the server"; + return false; + } + return true; +} diff --git a/orm_lib/tests/sqlite3/BlogTag.h b/orm_lib/tests/sqlite3/BlogTag.h new file mode 100644 index 00000000..d7ddc358 --- /dev/null +++ b/orm_lib/tests/sqlite3/BlogTag.h @@ -0,0 +1,228 @@ +/** + * + * BlogTag.h + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#ifdef __cpp_impl_coroutine +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClient; +using DbClientPtr = std::shared_ptr; +} // namespace orm +} // namespace drogon + +namespace drogon_model +{ +namespace sqlite3 +{ + +class BlogTag +{ + public: + struct Cols + { + static const std::string _blog_id; + static const std::string _tag_id; + }; + + const static int primaryKeyNumber; + const static std::string tableName; + const static bool hasPrimaryKey; + const static std::vector primaryKeyName; + using PrimaryKeyType = std::tuple; // blog_id,tag_id + PrimaryKeyType getPrimaryKey() const; + + /** + * @brief constructor + * @param r One row of records in the SQL query result. + * @param indexOffset Set the offset to -1 to access all columns by column + * names, otherwise access all columns by offsets. + * @note If the SQL is not a style of 'select * from table_name ...' (select + * all columns by an asterisk), please set the offset to -1. + */ + explicit BlogTag(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + */ + explicit BlogTag(const Json::Value &pJson) noexcept(false); + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + * @param pMasqueradingVector The aliases of table columns. + */ + BlogTag( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + + BlogTag() = default; + + void updateByJson(const Json::Value &pJson) noexcept(false); + void updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + static bool validateJsonForCreation(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForCreation( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validateJsonForUpdate(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForUpdate( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation); + + /** For column blog_id */ + /// Get the value of the column blog_id, returns the default value if the + /// column is null + const int64_t &getValueOfBlogId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getBlogId() const noexcept; + /// Set the value of the column blog_id + void setBlogId(const int64_t &pBlogId) noexcept; + + /** For column tag_id */ + /// Get the value of the column tag_id, returns the default value if the + /// column is null + const int64_t &getValueOfTagId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getTagId() const noexcept; + /// Set the value of the column tag_id + void setTagId(const int64_t &pTagId) noexcept; + + static size_t getColumnNumber() noexcept + { + return 2; + } + + static const std::string &getColumnName(size_t index) noexcept(false); + + Json::Value toJson() const; + Json::Value toMasqueradedJson( + const std::vector &pMasqueradingVector) const; + /// Relationship interfaces + private: + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; +#ifdef __cpp_impl_coroutine + friend drogon::orm::CoroMapper; +#endif + static const std::vector &insertColumns() noexcept; + void outputArgs(drogon::orm::internal::SqlBinder &binder) const; + const std::vector updateColumns() const; + void updateArgs(drogon::orm::internal::SqlBinder &binder) const; + /// For mysql or sqlite3 + void updateId(const uint64_t id); + std::shared_ptr blogId_; + std::shared_ptr tagId_; + + struct MetaData + { + const std::string colName_; + const std::string colType_; + const std::string colDatabaseType_; + const ssize_t colLength_; + const bool isAutoVal_; + const bool isPrimaryKey_; + const bool notNull_; + }; + + static const std::vector metaData_; + bool dirtyFlag_[2] = {false}; + + public: + static const std::string &sqlForFindingByPrimaryKey() + { + static const std::string sql = + "select * from " + tableName + " where blog_id = ? and tag_id = ?"; + return sql; + } + + static const std::string &sqlForDeletingByPrimaryKey() + { + static const std::string sql = + "delete from " + tableName + " where blog_id = ? and tag_id = ?"; + return sql; + } + + std::string sqlForInserting(bool &needSelection) const + { + std::string sql = "insert into " + tableName + " ("; + size_t parametersCount = 0; + needSelection = false; + if (dirtyFlag_[0]) + { + sql += "blog_id,"; + ++parametersCount; + } + if (dirtyFlag_[1]) + { + sql += "tag_id,"; + ++parametersCount; + } + if (parametersCount > 0) + { + sql[sql.length() - 1] = ')'; + sql += " values ("; + } + else + sql += ") values ("; + + if (dirtyFlag_[0]) + { + sql.append("?,"); + } + if (dirtyFlag_[1]) + { + sql.append("?,"); + } + if (parametersCount > 0) + { + sql.resize(sql.length() - 1); + } + sql.append(1, ')'); + LOG_TRACE << sql; + return sql; + } +}; +} // namespace sqlite3 +} // namespace drogon_model diff --git a/orm_lib/tests/sqlite3/Category.cc b/orm_lib/tests/sqlite3/Category.cc new file mode 100644 index 00000000..ad27ba76 --- /dev/null +++ b/orm_lib/tests/sqlite3/Category.cc @@ -0,0 +1,579 @@ +/** + * + * Category.cc + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#include "Category.h" +#include "Blog.h" +#include +#include + +using namespace drogon; +using namespace drogon::orm; +using namespace drogon_model::sqlite3; + +const std::string Category::Cols::_id = "id"; +const std::string Category::Cols::_name = "name"; +const std::string Category::primaryKeyName = "id"; +const bool Category::hasPrimaryKey = true; +const std::string Category::tableName = "category"; + +const std::vector Category::metaData_ = { + {"id", "int64_t", "integer auto_increment", 8, 0, 1, 0}, + {"name", "std::string", "varchar(30)", 0, 0, 0, 0}}; + +const std::string &Category::getColumnName(size_t index) noexcept(false) +{ + assert(index < metaData_.size()); + return metaData_[index].colName_; +} + +Category::Category(const Row &r, const ssize_t indexOffset) noexcept +{ + if (indexOffset < 0) + { + if (!r["id"].isNull()) + { + id_ = std::make_shared(r["id"].as()); + } + if (!r["name"].isNull()) + { + name_ = std::make_shared(r["name"].as()); + } + } + else + { + size_t offset = (size_t)indexOffset; + if (offset + 2 > r.size()) + { + LOG_FATAL << "Invalid SQL result for this model"; + return; + } + size_t index; + index = offset + 0; + if (!r[index].isNull()) + { + id_ = std::make_shared(r[index].as()); + } + index = offset + 1; + if (!r[index].isNull()) + { + name_ = std::make_shared(r[index].as()); + } + } +} + +Category::Category( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + dirtyFlag_[0] = true; + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + name_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } +} + +Category::Category(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + dirtyFlag_[0] = true; + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int64_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("name")) + { + dirtyFlag_[1] = true; + if (!pJson["name"].isNull()) + { + name_ = std::make_shared(pJson["name"].asString()); + } + } +} + +void Category::updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + name_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } +} + +void Category::updateByJson(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int64_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("name")) + { + dirtyFlag_[1] = true; + if (!pJson["name"].isNull()) + { + name_ = std::make_shared(pJson["name"].asString()); + } + } +} + +const int64_t &Category::getValueOfId() const noexcept +{ + const static int64_t defaultValue = int64_t(); + if (id_) + return *id_; + return defaultValue; +} + +const std::shared_ptr &Category::getId() const noexcept +{ + return id_; +} + +void Category::setId(const int64_t &pId) noexcept +{ + id_ = std::make_shared(pId); + dirtyFlag_[0] = true; +} + +void Category::setIdToNull() noexcept +{ + id_.reset(); + dirtyFlag_[0] = true; +} + +const typename Category::PrimaryKeyType &Category::getPrimaryKey() const +{ + assert(id_); + return *id_; +} + +const std::string &Category::getValueOfName() const noexcept +{ + const static std::string defaultValue = std::string(); + if (name_) + return *name_; + return defaultValue; +} + +const std::shared_ptr &Category::getName() const noexcept +{ + return name_; +} + +void Category::setName(const std::string &pName) noexcept +{ + name_ = std::make_shared(pName); + dirtyFlag_[1] = true; +} + +void Category::setName(std::string &&pName) noexcept +{ + name_ = std::make_shared(std::move(pName)); + dirtyFlag_[1] = true; +} + +void Category::setNameToNull() noexcept +{ + name_.reset(); + dirtyFlag_[1] = true; +} + +void Category::updateId(const uint64_t id) +{ +} + +const std::vector &Category::insertColumns() noexcept +{ + static const std::vector inCols = {"id", "name"}; + return inCols; +} + +void Category::outputArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[0]) + { + if (getId()) + { + binder << getValueOfId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[1]) + { + if (getName()) + { + binder << getValueOfName(); + } + else + { + binder << nullptr; + } + } +} + +const std::vector Category::updateColumns() const +{ + std::vector ret; + if (dirtyFlag_[0]) + { + ret.push_back(getColumnName(0)); + } + if (dirtyFlag_[1]) + { + ret.push_back(getColumnName(1)); + } + return ret; +} + +void Category::updateArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[0]) + { + if (getId()) + { + binder << getValueOfId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[1]) + { + if (getName()) + { + binder << getValueOfName(); + } + else + { + binder << nullptr; + } + } +} + +Json::Value Category::toJson() const +{ + Json::Value ret; + if (getId()) + { + ret["id"] = (Json::Int64)getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getName()) + { + ret["name"] = getValueOfName(); + } + else + { + ret["name"] = Json::Value(); + } + return ret; +} + +Json::Value Category::toMasqueradedJson( + const std::vector &pMasqueradingVector) const +{ + Json::Value ret; + if (pMasqueradingVector.size() == 2) + { + if (!pMasqueradingVector[0].empty()) + { + if (getId()) + { + ret[pMasqueradingVector[0]] = (Json::Int64)getValueOfId(); + } + else + { + ret[pMasqueradingVector[0]] = Json::Value(); + } + } + if (!pMasqueradingVector[1].empty()) + { + if (getName()) + { + ret[pMasqueradingVector[1]] = getValueOfName(); + } + else + { + ret[pMasqueradingVector[1]] = Json::Value(); + } + } + return ret; + } + LOG_ERROR << "Masquerade failed"; + if (getId()) + { + ret["id"] = (Json::Int64)getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getName()) + { + ret["name"] = getValueOfName(); + } + else + { + ret["name"] = Json::Value(); + } + return ret; +} + +bool Category::validateJsonForCreation(const Json::Value &pJson, + std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, true)) + return false; + } + if (pJson.isMember("name")) + { + if (!validJsonOfField(1, "name", pJson["name"], err, true)) + return false; + } + return true; +} + +bool Category::validateMasqueradedJsonForCreation( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty()) + { + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Category::validateJsonForUpdate(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (pJson.isMember("name")) + { + if (!validJsonOfField(1, "name", pJson["name"], err, false)) + return false; + } + return true; +} + +bool Category::validateMasqueradedJsonForUpdate( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Category::validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation) +{ + switch (index) + { + case 0: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isInt64()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 1: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isString()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + default: + err = "Internal error in the server"; + return false; + } + return true; +} + +std::vector Category::getBlogs(const DbClientPtr &clientPtr) const +{ + const static std::string sql = "select * from blog where category_id = ?"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *id_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + std::vector ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back(Blog(row)); + } + return ret; +} + +void Category::getBlogs(const DbClientPtr &clientPtr, + const std::function)> &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = "select * from blog where category_id = ?"; + *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) { + std::vector ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back(Blog(row)); + } + rcb(ret); + } >> ecb; +} diff --git a/orm_lib/tests/sqlite3/Category.h b/orm_lib/tests/sqlite3/Category.h new file mode 100644 index 00000000..7d2b6433 --- /dev/null +++ b/orm_lib/tests/sqlite3/Category.h @@ -0,0 +1,241 @@ +/** + * + * Category.h + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#ifdef __cpp_impl_coroutine +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClient; +using DbClientPtr = std::shared_ptr; +} // namespace orm +} // namespace drogon + +namespace drogon_model +{ +namespace sqlite3 +{ +class Blog; + +class Category +{ + public: + struct Cols + { + static const std::string _id; + static const std::string _name; + }; + + const static int primaryKeyNumber; + const static std::string tableName; + const static bool hasPrimaryKey; + const static std::string primaryKeyName; + using PrimaryKeyType = int64_t; + const PrimaryKeyType &getPrimaryKey() const; + + /** + * @brief constructor + * @param r One row of records in the SQL query result. + * @param indexOffset Set the offset to -1 to access all columns by column + * names, otherwise access all columns by offsets. + * @note If the SQL is not a style of 'select * from table_name ...' (select + * all columns by an asterisk), please set the offset to -1. + */ + explicit Category(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + */ + explicit Category(const Json::Value &pJson) noexcept(false); + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + * @param pMasqueradingVector The aliases of table columns. + */ + Category( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + + Category() = default; + + void updateByJson(const Json::Value &pJson) noexcept(false); + void updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + static bool validateJsonForCreation(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForCreation( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validateJsonForUpdate(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForUpdate( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation); + + /** For column id */ + /// Get the value of the column id, returns the default value if the column + /// is null + const int64_t &getValueOfId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getId() const noexcept; + /// Set the value of the column id + void setId(const int64_t &pId) noexcept; + void setIdToNull() noexcept; + + /** For column name */ + /// Get the value of the column name, returns the default value if the + /// column is null + const std::string &getValueOfName() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getName() const noexcept; + /// Set the value of the column name + void setName(const std::string &pName) noexcept; + void setName(std::string &&pName) noexcept; + void setNameToNull() noexcept; + + static size_t getColumnNumber() noexcept + { + return 2; + } + + static const std::string &getColumnName(size_t index) noexcept(false); + + Json::Value toJson() const; + Json::Value toMasqueradedJson( + const std::vector &pMasqueradingVector) const; + /// Relationship interfaces + std::vector getBlogs(const drogon::orm::DbClientPtr &clientPtr) const; + void getBlogs(const drogon::orm::DbClientPtr &clientPtr, + const std::function)> &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + + private: + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; +#ifdef __cpp_impl_coroutine + friend drogon::orm::CoroMapper; +#endif + static const std::vector &insertColumns() noexcept; + void outputArgs(drogon::orm::internal::SqlBinder &binder) const; + const std::vector updateColumns() const; + void updateArgs(drogon::orm::internal::SqlBinder &binder) const; + /// For mysql or sqlite3 + void updateId(const uint64_t id); + std::shared_ptr id_; + std::shared_ptr name_; + + struct MetaData + { + const std::string colName_; + const std::string colType_; + const std::string colDatabaseType_; + const ssize_t colLength_; + const bool isAutoVal_; + const bool isPrimaryKey_; + const bool notNull_; + }; + + static const std::vector metaData_; + bool dirtyFlag_[2] = {false}; + + public: + static const std::string &sqlForFindingByPrimaryKey() + { + static const std::string sql = + "select * from " + tableName + " where id = ?"; + return sql; + } + + static const std::string &sqlForDeletingByPrimaryKey() + { + static const std::string sql = + "delete from " + tableName + " where id = ?"; + return sql; + } + + std::string sqlForInserting(bool &needSelection) const + { + std::string sql = "insert into " + tableName + " ("; + size_t parametersCount = 0; + needSelection = false; + if (dirtyFlag_[0]) + { + sql += "id,"; + ++parametersCount; + } + if (dirtyFlag_[1]) + { + sql += "name,"; + ++parametersCount; + } + if (!dirtyFlag_[1]) + { + needSelection = true; + } + if (parametersCount > 0) + { + sql[sql.length() - 1] = ')'; + sql += " values ("; + } + else + sql += ") values ("; + + if (dirtyFlag_[0]) + { + sql.append("?,"); + } + if (dirtyFlag_[1]) + { + sql.append("?,"); + } + if (parametersCount > 0) + { + sql.resize(sql.length() - 1); + } + sql.append(1, ')'); + LOG_TRACE << sql; + return sql; + } +}; +} // namespace sqlite3 +} // namespace drogon_model diff --git a/orm_lib/tests/sqlite3/Tag.cc b/orm_lib/tests/sqlite3/Tag.cc new file mode 100644 index 00000000..31787de1 --- /dev/null +++ b/orm_lib/tests/sqlite3/Tag.cc @@ -0,0 +1,587 @@ +/** + * + * Tag.cc + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#include "Tag.h" +#include "Blog.h" +#include "BlogTag.h" +#include +#include + +using namespace drogon; +using namespace drogon::orm; +using namespace drogon_model::sqlite3; + +const std::string Tag::Cols::_id = "id"; +const std::string Tag::Cols::_name = "name"; +const std::string Tag::primaryKeyName = "id"; +const bool Tag::hasPrimaryKey = true; +const std::string Tag::tableName = "tag"; + +const std::vector Tag::metaData_ = { + {"id", "int64_t", "integer auto_increment", 8, 0, 1, 0}, + {"name", "std::string", "varchar(30)", 0, 0, 0, 0}}; + +const std::string &Tag::getColumnName(size_t index) noexcept(false) +{ + assert(index < metaData_.size()); + return metaData_[index].colName_; +} + +Tag::Tag(const Row &r, const ssize_t indexOffset) noexcept +{ + if (indexOffset < 0) + { + if (!r["id"].isNull()) + { + id_ = std::make_shared(r["id"].as()); + } + if (!r["name"].isNull()) + { + name_ = std::make_shared(r["name"].as()); + } + } + else + { + size_t offset = (size_t)indexOffset; + if (offset + 2 > r.size()) + { + LOG_FATAL << "Invalid SQL result for this model"; + return; + } + size_t index; + index = offset + 0; + if (!r[index].isNull()) + { + id_ = std::make_shared(r[index].as()); + } + index = offset + 1; + if (!r[index].isNull()) + { + name_ = std::make_shared(r[index].as()); + } + } +} + +Tag::Tag(const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + dirtyFlag_[0] = true; + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + name_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } +} + +Tag::Tag(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + dirtyFlag_[0] = true; + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int64_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("name")) + { + dirtyFlag_[1] = true; + if (!pJson["name"].isNull()) + { + name_ = std::make_shared(pJson["name"].asString()); + } + } +} + +void Tag::updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 2) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + name_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } +} + +void Tag::updateByJson(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int64_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("name")) + { + dirtyFlag_[1] = true; + if (!pJson["name"].isNull()) + { + name_ = std::make_shared(pJson["name"].asString()); + } + } +} + +const int64_t &Tag::getValueOfId() const noexcept +{ + const static int64_t defaultValue = int64_t(); + if (id_) + return *id_; + return defaultValue; +} + +const std::shared_ptr &Tag::getId() const noexcept +{ + return id_; +} + +void Tag::setId(const int64_t &pId) noexcept +{ + id_ = std::make_shared(pId); + dirtyFlag_[0] = true; +} + +void Tag::setIdToNull() noexcept +{ + id_.reset(); + dirtyFlag_[0] = true; +} + +const typename Tag::PrimaryKeyType &Tag::getPrimaryKey() const +{ + assert(id_); + return *id_; +} + +const std::string &Tag::getValueOfName() const noexcept +{ + const static std::string defaultValue = std::string(); + if (name_) + return *name_; + return defaultValue; +} + +const std::shared_ptr &Tag::getName() const noexcept +{ + return name_; +} + +void Tag::setName(const std::string &pName) noexcept +{ + name_ = std::make_shared(pName); + dirtyFlag_[1] = true; +} + +void Tag::setName(std::string &&pName) noexcept +{ + name_ = std::make_shared(std::move(pName)); + dirtyFlag_[1] = true; +} + +void Tag::setNameToNull() noexcept +{ + name_.reset(); + dirtyFlag_[1] = true; +} + +void Tag::updateId(const uint64_t id) +{ +} + +const std::vector &Tag::insertColumns() noexcept +{ + static const std::vector inCols = {"id", "name"}; + return inCols; +} + +void Tag::outputArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[0]) + { + if (getId()) + { + binder << getValueOfId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[1]) + { + if (getName()) + { + binder << getValueOfName(); + } + else + { + binder << nullptr; + } + } +} + +const std::vector Tag::updateColumns() const +{ + std::vector ret; + if (dirtyFlag_[0]) + { + ret.push_back(getColumnName(0)); + } + if (dirtyFlag_[1]) + { + ret.push_back(getColumnName(1)); + } + return ret; +} + +void Tag::updateArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[0]) + { + if (getId()) + { + binder << getValueOfId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[1]) + { + if (getName()) + { + binder << getValueOfName(); + } + else + { + binder << nullptr; + } + } +} + +Json::Value Tag::toJson() const +{ + Json::Value ret; + if (getId()) + { + ret["id"] = (Json::Int64)getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getName()) + { + ret["name"] = getValueOfName(); + } + else + { + ret["name"] = Json::Value(); + } + return ret; +} + +Json::Value Tag::toMasqueradedJson( + const std::vector &pMasqueradingVector) const +{ + Json::Value ret; + if (pMasqueradingVector.size() == 2) + { + if (!pMasqueradingVector[0].empty()) + { + if (getId()) + { + ret[pMasqueradingVector[0]] = (Json::Int64)getValueOfId(); + } + else + { + ret[pMasqueradingVector[0]] = Json::Value(); + } + } + if (!pMasqueradingVector[1].empty()) + { + if (getName()) + { + ret[pMasqueradingVector[1]] = getValueOfName(); + } + else + { + ret[pMasqueradingVector[1]] = Json::Value(); + } + } + return ret; + } + LOG_ERROR << "Masquerade failed"; + if (getId()) + { + ret["id"] = (Json::Int64)getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getName()) + { + ret["name"] = getValueOfName(); + } + else + { + ret["name"] = Json::Value(); + } + return ret; +} + +bool Tag::validateJsonForCreation(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, true)) + return false; + } + if (pJson.isMember("name")) + { + if (!validJsonOfField(1, "name", pJson["name"], err, true)) + return false; + } + return true; +} + +bool Tag::validateMasqueradedJsonForCreation( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty()) + { + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Tag::validateJsonForUpdate(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (pJson.isMember("name")) + { + if (!validJsonOfField(1, "name", pJson["name"], err, false)) + return false; + } + return true; +} + +bool Tag::validateMasqueradedJsonForUpdate( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 2) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Tag::validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation) +{ + switch (index) + { + case 0: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isInt64()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 1: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isString()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + default: + err = "Internal error in the server"; + return false; + } + return true; +} + +std::vector> Tag::getBlogs( + const DbClientPtr &clientPtr) const +{ + const static std::string sql = + "select * from blog,blog_tag where blog_tag.tag_id = ? and " + "blog_tag.blog_id = blog.id"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *id_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + std::vector> ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back( + std::pair(Blog(row), + BlogTag(row, Blog::getColumnNumber()))); + } + return ret; +} + +void Tag::getBlogs( + const DbClientPtr &clientPtr, + const std::function>)> &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = + "select * from blog,blog_tag where blog_tag.tag_id = ? and " + "blog_tag.blog_id = blog.id"; + *clientPtr << sql << *id_ >> [rcb = std::move(rcb)](const Result &r) { + std::vector> ret; + ret.reserve(r.size()); + for (auto const &row : r) + { + ret.emplace_back(std::pair( + Blog(row), BlogTag(row, Blog::getColumnNumber()))); + } + rcb(ret); + } >> ecb; +} diff --git a/orm_lib/tests/sqlite3/Tag.h b/orm_lib/tests/sqlite3/Tag.h new file mode 100644 index 00000000..3908cd3c --- /dev/null +++ b/orm_lib/tests/sqlite3/Tag.h @@ -0,0 +1,243 @@ +/** + * + * Tag.h + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#ifdef __cpp_impl_coroutine +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClient; +using DbClientPtr = std::shared_ptr; +} // namespace orm +} // namespace drogon + +namespace drogon_model +{ +namespace sqlite3 +{ +class Blog; +class BlogTag; + +class Tag +{ + public: + struct Cols + { + static const std::string _id; + static const std::string _name; + }; + + const static int primaryKeyNumber; + const static std::string tableName; + const static bool hasPrimaryKey; + const static std::string primaryKeyName; + using PrimaryKeyType = int64_t; + const PrimaryKeyType &getPrimaryKey() const; + + /** + * @brief constructor + * @param r One row of records in the SQL query result. + * @param indexOffset Set the offset to -1 to access all columns by column + * names, otherwise access all columns by offsets. + * @note If the SQL is not a style of 'select * from table_name ...' (select + * all columns by an asterisk), please set the offset to -1. + */ + explicit Tag(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + */ + explicit Tag(const Json::Value &pJson) noexcept(false); + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + * @param pMasqueradingVector The aliases of table columns. + */ + Tag(const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + + Tag() = default; + + void updateByJson(const Json::Value &pJson) noexcept(false); + void updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + static bool validateJsonForCreation(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForCreation( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validateJsonForUpdate(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForUpdate( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation); + + /** For column id */ + /// Get the value of the column id, returns the default value if the column + /// is null + const int64_t &getValueOfId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getId() const noexcept; + /// Set the value of the column id + void setId(const int64_t &pId) noexcept; + void setIdToNull() noexcept; + + /** For column name */ + /// Get the value of the column name, returns the default value if the + /// column is null + const std::string &getValueOfName() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getName() const noexcept; + /// Set the value of the column name + void setName(const std::string &pName) noexcept; + void setName(std::string &&pName) noexcept; + void setNameToNull() noexcept; + + static size_t getColumnNumber() noexcept + { + return 2; + } + + static const std::string &getColumnName(size_t index) noexcept(false); + + Json::Value toJson() const; + Json::Value toMasqueradedJson( + const std::vector &pMasqueradingVector) const; + /// Relationship interfaces + std::vector> getBlogs( + const drogon::orm::DbClientPtr &clientPtr) const; + void getBlogs( + const drogon::orm::DbClientPtr &clientPtr, + const std::function>)> &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + + private: + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; +#ifdef __cpp_impl_coroutine + friend drogon::orm::CoroMapper; +#endif + static const std::vector &insertColumns() noexcept; + void outputArgs(drogon::orm::internal::SqlBinder &binder) const; + const std::vector updateColumns() const; + void updateArgs(drogon::orm::internal::SqlBinder &binder) const; + /// For mysql or sqlite3 + void updateId(const uint64_t id); + std::shared_ptr id_; + std::shared_ptr name_; + + struct MetaData + { + const std::string colName_; + const std::string colType_; + const std::string colDatabaseType_; + const ssize_t colLength_; + const bool isAutoVal_; + const bool isPrimaryKey_; + const bool notNull_; + }; + + static const std::vector metaData_; + bool dirtyFlag_[2] = {false}; + + public: + static const std::string &sqlForFindingByPrimaryKey() + { + static const std::string sql = + "select * from " + tableName + " where id = ?"; + return sql; + } + + static const std::string &sqlForDeletingByPrimaryKey() + { + static const std::string sql = + "delete from " + tableName + " where id = ?"; + return sql; + } + + std::string sqlForInserting(bool &needSelection) const + { + std::string sql = "insert into " + tableName + " ("; + size_t parametersCount = 0; + needSelection = false; + if (dirtyFlag_[0]) + { + sql += "id,"; + ++parametersCount; + } + if (dirtyFlag_[1]) + { + sql += "name,"; + ++parametersCount; + } + if (!dirtyFlag_[1]) + { + needSelection = true; + } + if (parametersCount > 0) + { + sql[sql.length() - 1] = ')'; + sql += " values ("; + } + else + sql += ") values ("; + + if (dirtyFlag_[0]) + { + sql.append("?,"); + } + if (dirtyFlag_[1]) + { + sql.append("?,"); + } + if (parametersCount > 0) + { + sql.resize(sql.length() - 1); + } + sql.append(1, ')'); + LOG_TRACE << sql; + return sql; + } +}; +} // namespace sqlite3 +} // namespace drogon_model diff --git a/orm_lib/tests/sqlite3/Users.cc b/orm_lib/tests/sqlite3/Users.cc index 00d078ea..41015590 100644 --- a/orm_lib/tests/sqlite3/Users.cc +++ b/orm_lib/tests/sqlite3/Users.cc @@ -6,6 +6,7 @@ */ #include "Users.h" +#include "Wallets.h" #include #include @@ -28,7 +29,7 @@ const bool Users::hasPrimaryKey = true; const std::string Users::tableName = "users"; const std::vector Users::metaData_ = { - {"id", "uint64_t", "integer", 8, 1, 1, 0}, + {"id", "int64_t", "integer", 8, 1, 1, 0}, {"user_id", "std::string", "varchar(32)", 0, 0, 0, 0}, {"user_name", "std::string", "varchar(64)", 0, 0, 0, 0}, {"password", "std::string", "varchar(64)", 0, 0, 0, 0}, @@ -51,7 +52,7 @@ Users::Users(const Row &r, const ssize_t indexOffset) noexcept { if (!r["id"].isNull()) { - id_ = std::make_shared(r["id"].as()); + id_ = std::make_shared(r["id"].as()); } if (!r["user_id"].isNull()) { @@ -128,7 +129,7 @@ Users::Users(const Row &r, const ssize_t indexOffset) noexcept index = offset + 0; if (!r[index].isNull()) { - id_ = std::make_shared(r[index].as()); + id_ = std::make_shared(r[index].as()); } index = offset + 1; if (!r[index].isNull()) @@ -217,8 +218,8 @@ Users::Users( dirtyFlag_[0] = true; if (!pJson[pMasqueradingVector[0]].isNull()) { - id_ = std::make_shared( - (uint64_t)pJson[pMasqueradingVector[0]].asUInt64()); + id_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[0]].asInt64()); } } if (!pMasqueradingVector[1].empty() && @@ -338,7 +339,7 @@ Users::Users(const Json::Value &pJson) noexcept(false) dirtyFlag_[0] = true; if (!pJson["id"].isNull()) { - id_ = std::make_shared((uint64_t)pJson["id"].asUInt64()); + id_ = std::make_shared((int64_t)pJson["id"].asInt64()); } } if (pJson.isMember("user_id")) @@ -454,8 +455,8 @@ void Users::updateByMasqueradedJson( { if (!pJson[pMasqueradingVector[0]].isNull()) { - id_ = std::make_shared( - (uint64_t)pJson[pMasqueradingVector[0]].asUInt64()); + id_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[0]].asInt64()); } } if (!pMasqueradingVector[1].empty() && @@ -574,7 +575,7 @@ void Users::updateByJson(const Json::Value &pJson) noexcept(false) { if (!pJson["id"].isNull()) { - id_ = std::make_shared((uint64_t)pJson["id"].asUInt64()); + id_ = std::make_shared((int64_t)pJson["id"].asInt64()); } } if (pJson.isMember("user_id")) @@ -676,22 +677,22 @@ void Users::updateByJson(const Json::Value &pJson) noexcept(false) } } -const uint64_t &Users::getValueOfId() const noexcept +const int64_t &Users::getValueOfId() const noexcept { - const static uint64_t defaultValue = uint64_t(); + const static int64_t defaultValue = int64_t(); if (id_) return *id_; return defaultValue; } -const std::shared_ptr &Users::getId() const noexcept +const std::shared_ptr &Users::getId() const noexcept { return id_; } -void Users::setId(const uint64_t &pId) noexcept +void Users::setId(const int64_t &pId) noexcept { - id_ = std::make_shared(pId); + id_ = std::make_shared(pId); dirtyFlag_[0] = true; } @@ -982,7 +983,7 @@ void Users::setCreateTimeToNull() noexcept void Users::updateId(const uint64_t id) { - id_ = std::make_shared(id); + id_ = std::make_shared(static_cast(id)); } const std::vector &Users::insertColumns() noexcept @@ -1252,7 +1253,7 @@ Json::Value Users::toJson() const Json::Value ret; if (getId()) { - ret["id"] = (Json::UInt64)getValueOfId(); + ret["id"] = (Json::Int64)getValueOfId(); } else { @@ -1343,7 +1344,7 @@ Json::Value Users::toMasqueradedJson( { if (getId()) { - ret[pMasqueradingVector[0]] = (Json::UInt64)getValueOfId(); + ret[pMasqueradingVector[0]] = (Json::Int64)getValueOfId(); } else { @@ -1455,7 +1456,7 @@ Json::Value Users::toMasqueradedJson( LOG_ERROR << "Masquerade failed"; if (getId()) { - ret["id"] = (Json::UInt64)getValueOfId(); + ret["id"] = (Json::Int64)getValueOfId(); } else { @@ -1942,7 +1943,7 @@ bool Users::validJsonOfField(size_t index, { return true; } - if (!pJson.isUInt64()) + if (!pJson.isInt64()) { err = "Type error in the " + fieldName + " field"; return false; @@ -2053,3 +2054,46 @@ bool Users::validJsonOfField(size_t index, } return true; } + +Wallets Users::getWallet(const DbClientPtr &clientPtr) const +{ + const static std::string sql = "select * from wallets where user_id = ?"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *userId_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + if (r.size() == 0) + { + throw UnexpectedRows("0 rows found"); + } + else if (r.size() > 1) + { + throw UnexpectedRows("Found more than one row"); + } + return Wallets(r[0]); +} + +void Users::getWallet(const DbClientPtr &clientPtr, + const std::function &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = "select * from wallets where user_id = ?"; + *clientPtr << sql << *userId_ >> [rcb = std::move(rcb), + ecb](const Result &r) { + if (r.size() == 0) + { + ecb(UnexpectedRows("0 rows found")); + } + else if (r.size() > 1) + { + ecb(UnexpectedRows("Found more than one row")); + } + else + { + rcb(Wallets(r[0])); + } + } >> ecb; +} diff --git a/orm_lib/tests/sqlite3/Users.h b/orm_lib/tests/sqlite3/Users.h index 51372eaa..dbdaddcf 100644 --- a/orm_lib/tests/sqlite3/Users.h +++ b/orm_lib/tests/sqlite3/Users.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,8 @@ namespace drogon_model { namespace sqlite3 { +class Wallets; + class Users { public: @@ -59,7 +62,7 @@ class Users const static std::string tableName; const static bool hasPrimaryKey; const static std::string primaryKeyName; - using PrimaryKeyType = uint64_t; + using PrimaryKeyType = int64_t; const PrimaryKeyType &getPrimaryKey() const; /** @@ -114,12 +117,12 @@ class Users /** For column id */ /// Get the value of the column id, returns the default value if the column /// is null - const uint64_t &getValueOfId() const noexcept; + const int64_t &getValueOfId() const noexcept; /// Return a shared_ptr object pointing to the column const value, or an /// empty shared_ptr object if the column is null - const std::shared_ptr &getId() const noexcept; + const std::shared_ptr &getId() const noexcept; /// Set the value of the column id - void setId(const uint64_t &pId) noexcept; + void setId(const int64_t &pId) noexcept; void setIdToNull() noexcept; /** For column user_id */ @@ -240,6 +243,11 @@ class Users Json::Value toMasqueradedJson( const std::vector &pMasqueradingVector) const; /// Relationship interfaces + Wallets getWallet(const drogon::orm::DbClientPtr &clientPtr) const; + void getWallet(const drogon::orm::DbClientPtr &clientPtr, + const std::function &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + private: friend drogon::orm::Mapper; friend drogon::orm::BaseBuilder; @@ -255,7 +263,7 @@ class Users void updateArgs(drogon::orm::internal::SqlBinder &binder) const; /// For mysql or sqlite3 void updateId(const uint64_t id); - std::shared_ptr id_; + std::shared_ptr id_; std::shared_ptr userId_; std::shared_ptr userName_; std::shared_ptr password_; diff --git a/orm_lib/tests/sqlite3/Wallets.cc b/orm_lib/tests/sqlite3/Wallets.cc new file mode 100644 index 00000000..d74ec57a --- /dev/null +++ b/orm_lib/tests/sqlite3/Wallets.cc @@ -0,0 +1,745 @@ +/** + * + * Wallets.cc + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#include "Wallets.h" +#include "Users.h" +#include +#include + +using namespace drogon; +using namespace drogon::orm; +using namespace drogon_model::sqlite3; + +const std::string Wallets::Cols::_id = "id"; +const std::string Wallets::Cols::_user_id = "user_id"; +const std::string Wallets::Cols::_amount = "amount"; +const std::string Wallets::primaryKeyName = "id"; +const bool Wallets::hasPrimaryKey = true; +const std::string Wallets::tableName = "wallets"; + +const std::vector Wallets::metaData_ = { + {"id", "int64_t", "integer", 8, 1, 1, 0}, + {"user_id", "std::string", "varchar(32)", 0, 0, 0, 0}, + {"amount", "std::string", "decimal(16,2)", 0, 0, 0, 0}}; + +const std::string &Wallets::getColumnName(size_t index) noexcept(false) +{ + assert(index < metaData_.size()); + return metaData_[index].colName_; +} + +Wallets::Wallets(const Row &r, const ssize_t indexOffset) noexcept +{ + if (indexOffset < 0) + { + if (!r["id"].isNull()) + { + id_ = std::make_shared(r["id"].as()); + } + if (!r["user_id"].isNull()) + { + userId_ = + std::make_shared(r["user_id"].as()); + } + if (!r["amount"].isNull()) + { + amount_ = + std::make_shared(r["amount"].as()); + } + } + else + { + size_t offset = (size_t)indexOffset; + if (offset + 3 > r.size()) + { + LOG_FATAL << "Invalid SQL result for this model"; + return; + } + size_t index; + index = offset + 0; + if (!r[index].isNull()) + { + id_ = std::make_shared(r[index].as()); + } + index = offset + 1; + if (!r[index].isNull()) + { + userId_ = std::make_shared(r[index].as()); + } + index = offset + 2; + if (!r[index].isNull()) + { + amount_ = std::make_shared(r[index].as()); + } + } +} + +Wallets::Wallets( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 3) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + dirtyFlag_[0] = true; + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + userId_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + dirtyFlag_[2] = true; + if (!pJson[pMasqueradingVector[2]].isNull()) + { + amount_ = std::make_shared( + pJson[pMasqueradingVector[2]].asString()); + } + } +} + +Wallets::Wallets(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + dirtyFlag_[0] = true; + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int64_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("user_id")) + { + dirtyFlag_[1] = true; + if (!pJson["user_id"].isNull()) + { + userId_ = + std::make_shared(pJson["user_id"].asString()); + } + } + if (pJson.isMember("amount")) + { + dirtyFlag_[2] = true; + if (!pJson["amount"].isNull()) + { + amount_ = std::make_shared(pJson["amount"].asString()); + } + } +} + +void Wallets::updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false) +{ + if (pMasqueradingVector.size() != 3) + { + LOG_ERROR << "Bad masquerading vector"; + return; + } + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!pJson[pMasqueradingVector[0]].isNull()) + { + id_ = std::make_shared( + (int64_t)pJson[pMasqueradingVector[0]].asInt64()); + } + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + dirtyFlag_[1] = true; + if (!pJson[pMasqueradingVector[1]].isNull()) + { + userId_ = std::make_shared( + pJson[pMasqueradingVector[1]].asString()); + } + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + dirtyFlag_[2] = true; + if (!pJson[pMasqueradingVector[2]].isNull()) + { + amount_ = std::make_shared( + pJson[pMasqueradingVector[2]].asString()); + } + } +} + +void Wallets::updateByJson(const Json::Value &pJson) noexcept(false) +{ + if (pJson.isMember("id")) + { + if (!pJson["id"].isNull()) + { + id_ = std::make_shared((int64_t)pJson["id"].asInt64()); + } + } + if (pJson.isMember("user_id")) + { + dirtyFlag_[1] = true; + if (!pJson["user_id"].isNull()) + { + userId_ = + std::make_shared(pJson["user_id"].asString()); + } + } + if (pJson.isMember("amount")) + { + dirtyFlag_[2] = true; + if (!pJson["amount"].isNull()) + { + amount_ = std::make_shared(pJson["amount"].asString()); + } + } +} + +const int64_t &Wallets::getValueOfId() const noexcept +{ + const static int64_t defaultValue = int64_t(); + if (id_) + return *id_; + return defaultValue; +} + +const std::shared_ptr &Wallets::getId() const noexcept +{ + return id_; +} + +void Wallets::setId(const int64_t &pId) noexcept +{ + id_ = std::make_shared(pId); + dirtyFlag_[0] = true; +} + +void Wallets::setIdToNull() noexcept +{ + id_.reset(); + dirtyFlag_[0] = true; +} + +const typename Wallets::PrimaryKeyType &Wallets::getPrimaryKey() const +{ + assert(id_); + return *id_; +} + +const std::string &Wallets::getValueOfUserId() const noexcept +{ + const static std::string defaultValue = std::string(); + if (userId_) + return *userId_; + return defaultValue; +} + +const std::shared_ptr &Wallets::getUserId() const noexcept +{ + return userId_; +} + +void Wallets::setUserId(const std::string &pUserId) noexcept +{ + userId_ = std::make_shared(pUserId); + dirtyFlag_[1] = true; +} + +void Wallets::setUserId(std::string &&pUserId) noexcept +{ + userId_ = std::make_shared(std::move(pUserId)); + dirtyFlag_[1] = true; +} + +void Wallets::setUserIdToNull() noexcept +{ + userId_.reset(); + dirtyFlag_[1] = true; +} + +const std::string &Wallets::getValueOfAmount() const noexcept +{ + const static std::string defaultValue = std::string(); + if (amount_) + return *amount_; + return defaultValue; +} + +const std::shared_ptr &Wallets::getAmount() const noexcept +{ + return amount_; +} + +void Wallets::setAmount(const std::string &pAmount) noexcept +{ + amount_ = std::make_shared(pAmount); + dirtyFlag_[2] = true; +} + +void Wallets::setAmount(std::string &&pAmount) noexcept +{ + amount_ = std::make_shared(std::move(pAmount)); + dirtyFlag_[2] = true; +} + +void Wallets::setAmountToNull() noexcept +{ + amount_.reset(); + dirtyFlag_[2] = true; +} + +void Wallets::updateId(const uint64_t id) +{ + id_ = std::make_shared(static_cast(id)); +} + +const std::vector &Wallets::insertColumns() noexcept +{ + static const std::vector inCols = {"user_id", "amount"}; + return inCols; +} + +void Wallets::outputArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getUserId()) + { + binder << getValueOfUserId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[2]) + { + if (getAmount()) + { + binder << getValueOfAmount(); + } + else + { + binder << nullptr; + } + } +} + +const std::vector Wallets::updateColumns() const +{ + std::vector ret; + if (dirtyFlag_[1]) + { + ret.push_back(getColumnName(1)); + } + if (dirtyFlag_[2]) + { + ret.push_back(getColumnName(2)); + } + return ret; +} + +void Wallets::updateArgs(drogon::orm::internal::SqlBinder &binder) const +{ + if (dirtyFlag_[1]) + { + if (getUserId()) + { + binder << getValueOfUserId(); + } + else + { + binder << nullptr; + } + } + if (dirtyFlag_[2]) + { + if (getAmount()) + { + binder << getValueOfAmount(); + } + else + { + binder << nullptr; + } + } +} + +Json::Value Wallets::toJson() const +{ + Json::Value ret; + if (getId()) + { + ret["id"] = (Json::Int64)getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getUserId()) + { + ret["user_id"] = getValueOfUserId(); + } + else + { + ret["user_id"] = Json::Value(); + } + if (getAmount()) + { + ret["amount"] = getValueOfAmount(); + } + else + { + ret["amount"] = Json::Value(); + } + return ret; +} + +Json::Value Wallets::toMasqueradedJson( + const std::vector &pMasqueradingVector) const +{ + Json::Value ret; + if (pMasqueradingVector.size() == 3) + { + if (!pMasqueradingVector[0].empty()) + { + if (getId()) + { + ret[pMasqueradingVector[0]] = (Json::Int64)getValueOfId(); + } + else + { + ret[pMasqueradingVector[0]] = Json::Value(); + } + } + if (!pMasqueradingVector[1].empty()) + { + if (getUserId()) + { + ret[pMasqueradingVector[1]] = getValueOfUserId(); + } + else + { + ret[pMasqueradingVector[1]] = Json::Value(); + } + } + if (!pMasqueradingVector[2].empty()) + { + if (getAmount()) + { + ret[pMasqueradingVector[2]] = getValueOfAmount(); + } + else + { + ret[pMasqueradingVector[2]] = Json::Value(); + } + } + return ret; + } + LOG_ERROR << "Masquerade failed"; + if (getId()) + { + ret["id"] = (Json::Int64)getValueOfId(); + } + else + { + ret["id"] = Json::Value(); + } + if (getUserId()) + { + ret["user_id"] = getValueOfUserId(); + } + else + { + ret["user_id"] = Json::Value(); + } + if (getAmount()) + { + ret["amount"] = getValueOfAmount(); + } + else + { + ret["amount"] = Json::Value(); + } + return ret; +} + +bool Wallets::validateJsonForCreation(const Json::Value &pJson, + std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, true)) + return false; + } + if (pJson.isMember("user_id")) + { + if (!validJsonOfField(1, "user_id", pJson["user_id"], err, true)) + return false; + } + if (pJson.isMember("amount")) + { + if (!validJsonOfField(2, "amount", pJson["amount"], err, true)) + return false; + } + return true; +} + +bool Wallets::validateMasqueradedJsonForCreation( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 3) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty()) + { + if (pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[1].empty()) + { + if (pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + true)) + return false; + } + } + if (!pMasqueradingVector[2].empty()) + { + if (pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + true)) + return false; + } + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Wallets::validateJsonForUpdate(const Json::Value &pJson, std::string &err) +{ + if (pJson.isMember("id")) + { + if (!validJsonOfField(0, "id", pJson["id"], err, false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (pJson.isMember("user_id")) + { + if (!validJsonOfField(1, "user_id", pJson["user_id"], err, false)) + return false; + } + if (pJson.isMember("amount")) + { + if (!validJsonOfField(2, "amount", pJson["amount"], err, false)) + return false; + } + return true; +} + +bool Wallets::validateMasqueradedJsonForUpdate( + const Json::Value &pJson, + const std::vector &pMasqueradingVector, + std::string &err) +{ + if (pMasqueradingVector.size() != 3) + { + err = "Bad masquerading vector"; + return false; + } + try + { + if (!pMasqueradingVector[0].empty() && + pJson.isMember(pMasqueradingVector[0])) + { + if (!validJsonOfField(0, + pMasqueradingVector[0], + pJson[pMasqueradingVector[0]], + err, + false)) + return false; + } + else + { + err = + "The value of primary key must be set in the json object for " + "update"; + return false; + } + if (!pMasqueradingVector[1].empty() && + pJson.isMember(pMasqueradingVector[1])) + { + if (!validJsonOfField(1, + pMasqueradingVector[1], + pJson[pMasqueradingVector[1]], + err, + false)) + return false; + } + if (!pMasqueradingVector[2].empty() && + pJson.isMember(pMasqueradingVector[2])) + { + if (!validJsonOfField(2, + pMasqueradingVector[2], + pJson[pMasqueradingVector[2]], + err, + false)) + return false; + } + } + catch (const Json::LogicError &e) + { + err = e.what(); + return false; + } + return true; +} + +bool Wallets::validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation) +{ + switch (index) + { + case 0: + if (isForCreation) + { + err = "The automatic primary key cannot be set"; + return false; + } + if (pJson.isNull()) + { + return true; + } + if (!pJson.isInt64()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 1: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isString()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + case 2: + if (pJson.isNull()) + { + return true; + } + if (!pJson.isString()) + { + err = "Type error in the " + fieldName + " field"; + return false; + } + break; + default: + err = "Internal error in the server"; + return false; + } + return true; +} + +Users Wallets::getUser(const DbClientPtr &clientPtr) const +{ + const static std::string sql = "select * from users where user_id = ?"; + Result r(nullptr); + { + auto binder = *clientPtr << sql; + binder << *userId_ << Mode::Blocking >> + [&r](const Result &result) { r = result; }; + binder.exec(); + } + if (r.size() == 0) + { + throw UnexpectedRows("0 rows found"); + } + else if (r.size() > 1) + { + throw UnexpectedRows("Found more than one row"); + } + return Users(r[0]); +} + +void Wallets::getUser(const DbClientPtr &clientPtr, + const std::function &rcb, + const ExceptionCallback &ecb) const +{ + const static std::string sql = "select * from users where user_id = ?"; + *clientPtr << sql << *userId_ >> [rcb = std::move(rcb), + ecb](const Result &r) { + if (r.size() == 0) + { + ecb(UnexpectedRows("0 rows found")); + } + else if (r.size() > 1) + { + ecb(UnexpectedRows("Found more than one row")); + } + else + { + rcb(Users(r[0])); + } + } >> ecb; +} diff --git a/orm_lib/tests/sqlite3/Wallets.h b/orm_lib/tests/sqlite3/Wallets.h new file mode 100644 index 00000000..7cabc579 --- /dev/null +++ b/orm_lib/tests/sqlite3/Wallets.h @@ -0,0 +1,259 @@ +/** + * + * Wallets.h + * DO NOT EDIT. This file is generated by drogon_ctl + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#ifdef __cpp_impl_coroutine +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClient; +using DbClientPtr = std::shared_ptr; +} // namespace orm +} // namespace drogon + +namespace drogon_model +{ +namespace sqlite3 +{ +class Users; + +class Wallets +{ + public: + struct Cols + { + static const std::string _id; + static const std::string _user_id; + static const std::string _amount; + }; + + const static int primaryKeyNumber; + const static std::string tableName; + const static bool hasPrimaryKey; + const static std::string primaryKeyName; + using PrimaryKeyType = int64_t; + const PrimaryKeyType &getPrimaryKey() const; + + /** + * @brief constructor + * @param r One row of records in the SQL query result. + * @param indexOffset Set the offset to -1 to access all columns by column + * names, otherwise access all columns by offsets. + * @note If the SQL is not a style of 'select * from table_name ...' (select + * all columns by an asterisk), please set the offset to -1. + */ + explicit Wallets(const drogon::orm::Row &r, + const ssize_t indexOffset = 0) noexcept; + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + */ + explicit Wallets(const Json::Value &pJson) noexcept(false); + + /** + * @brief constructor + * @param pJson The json object to construct a new instance. + * @param pMasqueradingVector The aliases of table columns. + */ + Wallets( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + + Wallets() = default; + + void updateByJson(const Json::Value &pJson) noexcept(false); + void updateByMasqueradedJson( + const Json::Value &pJson, + const std::vector &pMasqueradingVector) noexcept(false); + static bool validateJsonForCreation(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForCreation( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validateJsonForUpdate(const Json::Value &pJson, + std::string &err); + static bool validateMasqueradedJsonForUpdate( + const Json::Value &, + const std::vector &pMasqueradingVector, + std::string &err); + static bool validJsonOfField(size_t index, + const std::string &fieldName, + const Json::Value &pJson, + std::string &err, + bool isForCreation); + + /** For column id */ + /// Get the value of the column id, returns the default value if the column + /// is null + const int64_t &getValueOfId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getId() const noexcept; + /// Set the value of the column id + void setId(const int64_t &pId) noexcept; + void setIdToNull() noexcept; + + /** For column user_id */ + /// Get the value of the column user_id, returns the default value if the + /// column is null + const std::string &getValueOfUserId() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getUserId() const noexcept; + /// Set the value of the column user_id + void setUserId(const std::string &pUserId) noexcept; + void setUserId(std::string &&pUserId) noexcept; + void setUserIdToNull() noexcept; + + /** For column amount */ + /// Get the value of the column amount, returns the default value if the + /// column is null + const std::string &getValueOfAmount() const noexcept; + /// Return a shared_ptr object pointing to the column const value, or an + /// empty shared_ptr object if the column is null + const std::shared_ptr &getAmount() const noexcept; + /// Set the value of the column amount + void setAmount(const std::string &pAmount) noexcept; + void setAmount(std::string &&pAmount) noexcept; + void setAmountToNull() noexcept; + + static size_t getColumnNumber() noexcept + { + return 3; + } + + static const std::string &getColumnName(size_t index) noexcept(false); + + Json::Value toJson() const; + Json::Value toMasqueradedJson( + const std::vector &pMasqueradingVector) const; + /// Relationship interfaces + Users getUser(const drogon::orm::DbClientPtr &clientPtr) const; + void getUser(const drogon::orm::DbClientPtr &clientPtr, + const std::function &rcb, + const drogon::orm::ExceptionCallback &ecb) const; + + private: + friend drogon::orm::Mapper; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; + friend drogon::orm::BaseBuilder; +#ifdef __cpp_impl_coroutine + friend drogon::orm::CoroMapper; +#endif + static const std::vector &insertColumns() noexcept; + void outputArgs(drogon::orm::internal::SqlBinder &binder) const; + const std::vector updateColumns() const; + void updateArgs(drogon::orm::internal::SqlBinder &binder) const; + /// For mysql or sqlite3 + void updateId(const uint64_t id); + std::shared_ptr id_; + std::shared_ptr userId_; + std::shared_ptr amount_; + + struct MetaData + { + const std::string colName_; + const std::string colType_; + const std::string colDatabaseType_; + const ssize_t colLength_; + const bool isAutoVal_; + const bool isPrimaryKey_; + const bool notNull_; + }; + + static const std::vector metaData_; + bool dirtyFlag_[3] = {false}; + + public: + static const std::string &sqlForFindingByPrimaryKey() + { + static const std::string sql = + "select * from " + tableName + " where id = ?"; + return sql; + } + + static const std::string &sqlForDeletingByPrimaryKey() + { + static const std::string sql = + "delete from " + tableName + " where id = ?"; + return sql; + } + + std::string sqlForInserting(bool &needSelection) const + { + std::string sql = "insert into " + tableName + " ("; + size_t parametersCount = 0; + needSelection = false; + if (dirtyFlag_[1]) + { + sql += "user_id,"; + ++parametersCount; + } + if (!dirtyFlag_[1]) + { + needSelection = true; + } + if (dirtyFlag_[2]) + { + sql += "amount,"; + ++parametersCount; + } + if (!dirtyFlag_[2]) + { + needSelection = true; + } + if (parametersCount > 0) + { + sql[sql.length() - 1] = ')'; + sql += " values ("; + } + else + sql += ") values ("; + + if (dirtyFlag_[1]) + { + sql.append("?,"); + } + if (dirtyFlag_[2]) + { + sql.append("?,"); + } + if (parametersCount > 0) + { + sql.resize(sql.length() - 1); + } + sql.append(1, ')'); + LOG_TRACE << sql; + return sql; + } +}; +} // namespace sqlite3 +} // namespace drogon_model diff --git a/orm_lib/tests/sqlite3/model.json b/orm_lib/tests/sqlite3/model.json index 4326ac10..1b651449 100644 --- a/orm_lib/tests/sqlite3/model.json +++ b/orm_lib/tests/sqlite3/model.json @@ -1,6 +1,6 @@ { - "rdbms":"sqlite3", - "filename":"drogonTestSqlite", + "rdbms": "sqlite3", + "filename": "drogonTestSqlite", "name": "", "host": "127.0.0.1", "port": 5432, @@ -9,5 +9,52 @@ "passwd": "", "is_fast": false, "connection_number": 1, - "tables":["users"] + "tables": [ + "users", + "wallets", + "blog", + "category", + "blog_tag", + "tag" + ], + "relationships": { + "enabled": true, + "items": [ + { + "type": "has one", + "original_table_name": "users", + "original_table_alias": "user", + "original_key": "user_id", + "target_table_name": "wallets", + "target_table_alias": "wallet", + "target_key": "user_id", + "enable_reverse": true + }, + { + "type": "has many", + "original_table_name": "category", + "original_table_alias": "category", + "original_key": "id", + "target_table_name": "blog", + "target_table_alias": "blogs", + "target_key": "category_id", + "enable_reverse": true + }, + { + "type": "many to many", + "original_table_name": "blog", + "original_table_alias": "blogs", + "original_key": "id", + "pivot_table": { + "table_name": "blog_tag", + "original_key": "blog_id", + "target_key": "tag_id" + }, + "target_table_name": "tag", + "target_table_alias": "tags", + "target_key": "id", + "enable_reverse": true + } + ] + } }