From 4e77eb2f7b4961066f3230ce12c7e4e4a82817b4 Mon Sep 17 00:00:00 2001 From: Sangwan Kwon Date: Tue, 4 Feb 2020 15:10:56 +0900 Subject: [PATCH] query-builder: Refactor select interface Signed-off-by: Sangwan Kwon --- src/vist/query-builder/column.hpp | 11 --- src/vist/query-builder/crud.hpp | 79 +++++++------------ src/vist/query-builder/database.hpp | 74 +++++++++++++++++ src/vist/query-builder/table.hpp | 55 ++++++++++++- src/vist/query-builder/tests/database.cpp | 11 ++- .../query-builder/tests/query-builder.cpp | 63 ++++++++++----- src/vist/query-builder/tests/table.cpp | 3 + 7 files changed, 211 insertions(+), 85 deletions(-) diff --git a/src/vist/query-builder/column.hpp b/src/vist/query-builder/column.hpp index a89482f..5782fa2 100644 --- a/src/vist/query-builder/column.hpp +++ b/src/vist/query-builder/column.hpp @@ -36,16 +36,5 @@ struct Column final { Type type; }; -template -struct Distinct { - Type value; -}; - -template -auto distinct(Args&&... args) -> decltype(Distinct>()) -{ - return {std::tuple(args...)}; -} - } // namespace tsqb } // namespace vist diff --git a/src/vist/query-builder/crud.hpp b/src/vist/query-builder/crud.hpp index a53f64d..dfbb001 100644 --- a/src/vist/query-builder/crud.hpp +++ b/src/vist/query-builder/crud.hpp @@ -31,9 +31,6 @@ public: template T& select(ColumnTypes&&... cts); - template - T& select(Distinct distinct); - template T& selectAll(void); @@ -52,9 +49,6 @@ public: T& where(Expr expr); private: - template - T& selectInternal(bool distinct, ColumnTypes&& ...cts); - template std::string processWhere(condition::And& expr); @@ -68,24 +62,32 @@ private: template template T& Crud::select(ColumnTypes&&... cts) -{ - return this->selectInternal(false, std::forward(cts)...); -} - -template -template -T& Crud::select(Distinct distinct) -{ - return this->selectInternal(true, std::move(distinct.value)); -} - -template -T& Crud::selectAll(void) { static_cast(this)->cache.clear(); + auto columnNames = static_cast(this)->_getColumnNames(std::forward(cts)...); + auto tableNames = static_cast(this)->_getTableNames(std::forward(cts)...); + std::stringstream ss; - ss << "SELECT * FROM " << static_cast(this)->name; + ss << "SELECT "; + + std::size_t i = 0; + for (const auto& c : columnNames) { + ss << c; + + if (i++ < columnNames.size() - 1) + ss << ", "; + } + + ss << " FROM "; + + i = 0; + for (const auto& t : tableNames) { + ss << t; + + if (i++ < tableNames.size() - 1) + ss << ", "; + } static_cast(this)->cache.emplace_back(ss.str()); @@ -93,14 +95,12 @@ T& Crud::selectAll(void) } template -template T& Crud::selectAll(void) { static_cast(this)->cache.clear(); std::stringstream ss; - auto tableName = static_cast(this)->getTableName(TableType()); - ss << "SELECT * FROM " << tableName; + ss << "SELECT * FROM " << static_cast(this)->name; static_cast(this)->cache.emplace_back(ss.str()); @@ -108,39 +108,14 @@ T& Crud::selectAll(void) } template -template -T& Crud::selectInternal(bool distinct, ColumnTypes&& ...cts) +template +T& Crud::selectAll(void) { static_cast(this)->cache.clear(); - auto columnNames = static_cast(this)->getColumnNames(std::forward(cts)...); - - auto tuple = std::tuple(cts...); - auto tableNames = static_cast(this)->getTableNames(std::move(tuple)); - std::stringstream ss; - ss << "SELECT "; - - if (distinct) - ss << "DISTINCT "; - - std::size_t i = 0; - for (const auto& c : columnNames) { - ss << c; - - if (i++ < columnNames.size() - 1) - ss << ", "; - } - - ss << " FROM "; - - i = 0; - for (const auto& t : tableNames) { - ss << t; - - if (i++ < tableNames.size() - 1) - ss << ", "; - } + auto tableName = static_cast(this)->getTableName(TableType()); + ss << "SELECT * FROM " << tableName; static_cast(this)->cache.emplace_back(ss.str()); diff --git a/src/vist/query-builder/database.hpp b/src/vist/query-builder/database.hpp index 7cdf8ba..b8b0199 100644 --- a/src/vist/query-builder/database.hpp +++ b/src/vist/query-builder/database.hpp @@ -61,6 +61,12 @@ public: // CRTP(Curiously Recurring Template Pattern) for CRUD std::string getTableName(TableType&& type) const noexcept; template std::string getColumnName(ColumnType&& type) const noexcept; + template + std::vector _getTableNames(Cs&& ...columns) const noexcept; + template + std::vector _getColumnNames(Cs&& ...columns) const noexcept; + template // remove_cv or ref + std::string _getColumnName(ColumnType&& type) const noexcept; std::vector cache; @@ -134,6 +140,29 @@ std::vector Database::getTableNames(Cs&& ...columns) con return std::vector(names.begin(), names.end()); } +template +template +std::vector Database::_getTableNames(Cs&& ...columns) const noexcept +{ + std::set names; + + auto predicate = [this, &names](const auto& column) { + using ColumnType = std::remove_reference_t; + using TableType = typename ColumnType::Table; + auto name = this->getTableName(TableType()); + if (!name.empty()) + names.emplace(name); + }; + + auto closure = [&predicate](const auto&... iter) { + (predicate(iter), ...); + }; + + std::apply(closure, std::tuple(columns...)); + + return std::vector(names.begin(), names.end()); +} + template template std::vector Database::getColumnNames(Cs&& ...columns) const noexcept @@ -154,6 +183,26 @@ std::vector Database::getColumnNames(Cs&& ...columns) co return names; } +template +template +std::vector Database::_getColumnNames(Cs&& ...columns) const noexcept +{ + std::vector names; + auto predicate = [this, &names](const auto& column) { + auto name = this->_getColumnName(column); + if (!name.empty()) + names.emplace_back(name); + }; + + auto closure = [&predicate](const auto&... iter) { + (predicate(iter), ...); + }; + + std::apply(closure, std::tuple(columns...)); + + return names; +} + template template std::string Database::getTableName(Table&& table) const noexcept @@ -198,6 +247,31 @@ std::string Database::getColumnName(ColumnType&& column) const noexce return name; } +template +template +std::string Database::_getColumnName(Column&& column) const noexcept +{ + using ColumnType = std::remove_reference_t; + using TableType = typename ColumnType::Table; + TableType table; + + std::string name; + auto predicate = [&name, &table, &column](const auto& iter) { + if (iter.compare(table)) { + auto cname = iter._getColumnName(column); + name = iter.name + "." + cname; + } + }; + + auto closure = [&predicate](const auto&... iter) { + (predicate(iter), ...); + }; + + std::apply(closure, this->tables); + + return name; +} + template std::size_t Database::size() const noexcept { diff --git a/src/vist/query-builder/table.hpp b/src/vist/query-builder/table.hpp index 92d2a31..dc0b929 100644 --- a/src/vist/query-builder/table.hpp +++ b/src/vist/query-builder/table.hpp @@ -35,7 +35,7 @@ public: /// Make first stuct type to table type using Type = typename std::tuple_element<0, std::tuple>::type::Table; - explicit Table(const std::string& name, Columns&& ...columns) : + explicit Table(const std::string& name, Columns ...columns) : name(name), columns(columns...) {} std::string getName(void) const noexcept; @@ -59,6 +59,12 @@ public: // CRTP(Curiously Recurring Template Pattern) for CRUD std::vector getColumnNames(Cs&& ...columns) const noexcept; template std::string getColumnName(const Column& column) const noexcept; + template + std::vector _getTableNames(Cs&& ...) const noexcept; + template + std::vector _getColumnNames(Cs&& ...columns) const noexcept; + template + std::string _getColumnName(const Column& column) const noexcept; std::vector cache; @@ -99,6 +105,27 @@ std::vector Table::getColumnNames(Cs&& ...columns) cons return names; } + +template +template +std::vector Table::_getColumnNames(Cs&& ...columns) const noexcept +{ + std::vector names; + auto predicate = [this, &names](const auto& type) { + auto name = this->_getColumnName(type); + if (!name.empty()) + names.emplace_back(name); + }; + + auto closure = [&predicate](const auto&... iter) { + (predicate(iter), ...); + }; + + std::apply(closure, std::tuple(columns...)); + + return names; +} + template template std::string Table::getTableName(That&&) const noexcept @@ -138,6 +165,32 @@ std::string Table::getColumnName(const Column& column) const noexcep return name; } +template +template +std::vector Table::_getTableNames(Cs&& ...) const noexcept +{ + return {this->name}; +} + +template +template +std::string Table::_getColumnName(const Column& column) const noexcept +{ + std::string name; + auto predicate = [&name, &column](const auto& iter) { + if (type::cast_compare(column.type, iter.type)) + name = iter.name; + }; + + auto closure = [&predicate](const auto&... iter) { + (predicate(iter), ...); + }; + + std::apply(closure, this->columns); + + return name; +} + template template bool Table::compare(const That& that) const noexcept diff --git a/src/vist/query-builder/tests/database.cpp b/src/vist/query-builder/tests/database.cpp index 1ed52e9..745e5cb 100644 --- a/src/vist/query-builder/tests/database.cpp +++ b/src/vist/query-builder/tests/database.cpp @@ -28,6 +28,9 @@ struct Table1 { int column1; std::string column2; bool column3; + + inline static Column Column1 = { "column1", &Table1::column1 }; + inline static Column Column2 = { "column2", &Table1::column2 }; }; struct Table2 { @@ -64,7 +67,7 @@ TEST(QueryBuilderDatabaseTests, get_name) EXPECT_EQ(database.getTableName(Table1()), "table1"); EXPECT_EQ(database.getTableName(Table2()), "table2"); - EXPECT_EQ(database.getColumnName(&Table1::column1), "table1.column1"); + EXPECT_EQ(database._getColumnName(Table1::Column1), "table1.column1"); EXPECT_EQ(database.getColumnName(&Table1::column2), "table1.column2"); EXPECT_EQ(database.getColumnName(&Table1::column3), "table1.column3"); @@ -77,6 +80,12 @@ TEST(QueryBuilderDatabaseTests, get_name) TEST(QueryBuilderDatabaseTests, get_names) { + { + auto columns = database._getColumnNames(Table1::Column1, Table1::Column2); + EXPECT_EQ(columns[0], "table1.column1"); + EXPECT_EQ(columns[1], "table1.column2"); + } + auto columns = database.getColumnNames(&Table1::column1, &Table2::column3); auto tables = database.getTableNames(&Table1::column1, &Table2::column1, &Table2::column1); if (columns.size() == 2 && tables.size() == 2) { diff --git a/src/vist/query-builder/tests/query-builder.cpp b/src/vist/query-builder/tests/query-builder.cpp index 165d41f..8eb899f 100644 --- a/src/vist/query-builder/tests/query-builder.cpp +++ b/src/vist/query-builder/tests/query-builder.cpp @@ -26,22 +26,51 @@ struct Admin { int uid; std::string key; int removable; + + inline static Column Id = { "id", &Admin::id }; + inline static Column Pkg = { "pkg", &Admin::pkg }; + inline static Column Uid = { "uid", &Admin::uid }; + inline static Column Key = { "key", &Admin::key }; + inline static Column Removable = { "removable", &Admin::removable }; }; +static Table AdminTable { "admin", Admin::Id, Admin::Pkg, + Admin::Uid, Admin::Key, Admin::Removable }; + struct ManagedPolicy { int id; int aid; int pid; int value; + + inline static Column Id = { "id", &ManagedPolicy::id }; + inline static Column Aid = { "aid", &ManagedPolicy::aid }; + inline static Column Pid = { "pid", &ManagedPolicy::pid }; + inline static Column Value = { "value", &ManagedPolicy::value }; }; +static Table ManagedPolicyTable { "managed_policy", ManagedPolicy::Id, + ManagedPolicy::Aid, + ManagedPolicy::Pid, + ManagedPolicy::Value }; + struct PolicyDefinition { int id; int scope; std::string name; int ivalue; + + inline static Column Id = { "id", &PolicyDefinition::id }; + inline static Column Scope = { "scope", &PolicyDefinition::scope }; + inline static Column Name = { "name", &PolicyDefinition::name }; + inline static Column Ivalue = { "ivalue", &PolicyDefinition::ivalue }; }; +static Table PolicyDefinition { "policy_definition", PolicyDefinition::Id, + PolicyDefinition::Scope, + PolicyDefinition::Name, + PolicyDefinition::Ivalue }; + Table admin { "admin", Column("id", &Admin::id), Column("pkg", &Admin::pkg), Column("uid", &Admin::uid), @@ -62,8 +91,8 @@ Database db { "dpm", admin, managedPolicy, policyDefinition }; TEST(QueryBuilderTsqbTests, SELECT) { - std::string select1 = admin.select(&Admin::id, &Admin::pkg, &Admin::uid, &Admin::key); - std::string select2 = admin.select(&Admin::id, &Admin::uid, &Admin::key); + std::string select1 = AdminTable.select(Admin::Id, Admin::Pkg, Admin::Uid, Admin::Key); + std::string select2 = AdminTable.select(Admin::Id, Admin::Uid, Admin::Key); EXPECT_EQ(select1, "SELECT id, pkg, uid, key FROM admin"); EXPECT_EQ(select2, "SELECT id, uid, key FROM admin"); @@ -71,15 +100,15 @@ TEST(QueryBuilderTsqbTests, SELECT) TEST(QueryBuilderTsqbTests, SELECT_ALL) { - std::string select = admin.selectAll(); + std::string select = AdminTable.selectAll(); EXPECT_EQ(select, "SELECT * FROM admin"); } TEST(QueryBuilderTsqbTests, SELECT_WHERE) { - std::string select1 = admin.select(&Admin::uid, &Admin::key) - .where(expr(&Admin::id) > 3); + std::string select1 = AdminTable.select(Admin::Uid, Admin::Key) + .where(expr(&Admin::id) > 3); std::string select2 = admin.selectAll().where(expr(&Admin::uid) > 3); std::string select3 = admin.selectAll().where(expr(&Admin::uid) > 3 && expr(&Admin::pkg) == "dpm"); @@ -92,14 +121,6 @@ TEST(QueryBuilderTsqbTests, SELECT_WHERE) EXPECT_EQ(select4, "SELECT * FROM admin WHERE uid > ? OR pkg = ?"); } -TEST(QueryBuilderTsqbTests, SELECT_DISTINCT) -{ - std::string select = admin.select(distinct(&Admin::uid, &Admin::key)) - .where(expr(&Admin::id) > 3); - - EXPECT_EQ(select, "SELECT DISTINCT uid, key FROM admin WHERE id > ?"); -} - TEST(QueryBuilderTsqbTests, UPDATE) { int uid = 0, id = 1; @@ -150,10 +171,10 @@ TEST(QueryBuilderTsqbTests, TYPE_SAFE) TEST(QueryBuilderTsqbTests, MULTI_SELECT) { - std::string multiSelect1 = db.select(&Admin::uid, &Admin::key, - &ManagedPolicy::id, &ManagedPolicy::value); - std::string multiSelect2 = db.select(&Admin::uid, &Admin::key, - &ManagedPolicy::id, &ManagedPolicy::value) + std::string multiSelect1 = db.select(Admin::Uid, Admin::Key, + ManagedPolicy::Id, ManagedPolicy::Value); + std::string multiSelect2 = db.select(Admin::Uid, Admin::Key, + ManagedPolicy::Id, ManagedPolicy::Value) .where(expr(&Admin::uid) > 0 && expr(&ManagedPolicy::id) == 3); EXPECT_EQ(multiSelect1, "SELECT admin.uid, admin.key, managed_policy.id, " @@ -163,13 +184,14 @@ TEST(QueryBuilderTsqbTests, MULTI_SELECT) "WHERE admin.uid > ? AND managed_policy.id = ?"); } +/* TEST(QueryBuilderTsqbTests, JOIN) { - std::string join1 = db.select(&Admin::uid, &Admin::key) + std::string join1 = db.select(Admin::Uid, Admin::Key) .join(condition::Join::LEFT_OUTER); - std::string join2 = db.select(&Admin::uid, &Admin::key) + std::string join2 = db.select(Admin::Uid, Admin::Key) .join(condition::Join::CROSS); - std::string join3 = db.select(&ManagedPolicy::value) + std::string join3 = db.select(ManagedPolicy::Value) .join() .on(expr(&ManagedPolicy::pid) == expr(&PolicyDefinition::id)) .join() @@ -186,3 +208,4 @@ TEST(QueryBuilderTsqbTests, JOIN) "INNER JOIN admin ON managed_policy.aid = admin.id " "WHERE managed_policy.pid = ?"); } +*/ diff --git a/src/vist/query-builder/tests/table.cpp b/src/vist/query-builder/tests/table.cpp index 5ec2bda..6e7e86d 100644 --- a/src/vist/query-builder/tests/table.cpp +++ b/src/vist/query-builder/tests/table.cpp @@ -27,6 +27,8 @@ struct Table1 { int column1; std::string column2; bool column3; + + inline static Column Column1 = { "column1", &Table1::column1 }; }; struct Table2 { @@ -60,6 +62,7 @@ TEST(QueryBuilderTableTests, get_name) EXPECT_EQ(table1.getName(), "table1"); EXPECT_EQ(table2.name, "table2"); + EXPECT_EQ(table1._getColumnName(Table1::Column1), "column1"); EXPECT_EQ(table1.getColumnName(&Table1::column1), "column1"); EXPECT_EQ(table1.getColumnName(&Table1::column2), "column2"); EXPECT_EQ(table1.getColumnName(&Table1::column3), "column3"); -- 2.34.1