query-builder: Refactor select interface
authorSangwan Kwon <sangwan.kwon@samsung.com>
Tue, 4 Feb 2020 06:10:56 +0000 (15:10 +0900)
committer권상완/Security 2Lab(SR)/Engineer/삼성전자 <sangwan.kwon@samsung.com>
Mon, 17 Feb 2020 11:16:16 +0000 (20:16 +0900)
Signed-off-by: Sangwan Kwon <sangwan.kwon@samsung.com>
src/vist/query-builder/column.hpp
src/vist/query-builder/crud.hpp
src/vist/query-builder/database.hpp
src/vist/query-builder/table.hpp
src/vist/query-builder/tests/database.cpp
src/vist/query-builder/tests/query-builder.cpp
src/vist/query-builder/tests/table.cpp

index a89482f..5782fa2 100644 (file)
@@ -36,16 +36,5 @@ struct Column final {
        Type type;
 };
 
-template<typename Type>
-struct Distinct {
-       Type value;
-};
-
-template<typename... Args>
-auto distinct(Args&&... args) -> decltype(Distinct<std::tuple<Args...>>())
-{
-       return {std::tuple(args...)};
-}
-
 } // namespace tsqb
 } // namespace vist
index a53f64d..dfbb001 100644 (file)
@@ -31,9 +31,6 @@ public:
        template<typename... ColumnTypes>
        T& select(ColumnTypes&&... cts);
 
-       template<typename Type>
-       T& select(Distinct<Type> distinct);
-
        template<typename TableType>
        T& selectAll(void);
 
@@ -52,9 +49,6 @@ public:
        T& where(Expr expr);
 
 private:
-       template<typename... ColumnTypes>
-       T& selectInternal(bool distinct, ColumnTypes&& ...cts);
-
        template<typename L, typename R>
        std::string processWhere(condition::And<L,R>& expr);
 
@@ -69,23 +63,31 @@ template<typename T>
 template<typename... ColumnTypes>
 T& Crud<T>::select(ColumnTypes&&... cts)
 {
-       return this->selectInternal(false, std::forward<ColumnTypes>(cts)...);
-}
-
-template<typename T>
-template<typename Type>
-T& Crud<T>::select(Distinct<Type> distinct)
-{
-       return this->selectInternal(true, std::move(distinct.value));
-}
-
-template<typename T>
-T& Crud<T>::selectAll(void)
-{
        static_cast<T*>(this)->cache.clear();
 
+       auto columnNames = static_cast<T*>(this)->_getColumnNames(std::forward<ColumnTypes>(cts)...);
+       auto tableNames = static_cast<T*>(this)->_getTableNames(std::forward<ColumnTypes>(cts)...);
+
        std::stringstream ss;
-       ss << "SELECT * FROM " << static_cast<T*>(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<T*>(this)->cache.emplace_back(ss.str());
 
@@ -93,14 +95,12 @@ T& Crud<T>::selectAll(void)
 }
 
 template<typename T>
-template<typename TableType>
 T& Crud<T>::selectAll(void)
 {
        static_cast<T*>(this)->cache.clear();
 
        std::stringstream ss;
-       auto tableName = static_cast<T*>(this)->getTableName(TableType());
-       ss << "SELECT * FROM " << tableName;
+       ss << "SELECT * FROM " << static_cast<T*>(this)->name;
 
        static_cast<T*>(this)->cache.emplace_back(ss.str());
 
@@ -108,39 +108,14 @@ T& Crud<T>::selectAll(void)
 }
 
 template<typename T>
-template<typename... ColumnTypes>
-T& Crud<T>::selectInternal(bool distinct, ColumnTypes&& ...cts)
+template<typename TableType>
+T& Crud<T>::selectAll(void)
 {
        static_cast<T*>(this)->cache.clear();
 
-       auto columnNames = static_cast<T*>(this)->getColumnNames(std::forward<ColumnTypes>(cts)...);
-
-       auto tuple = std::tuple(cts...);
-       auto tableNames = static_cast<T*>(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<T*>(this)->getTableName(TableType());
+       ss << "SELECT * FROM " << tableName;
 
        static_cast<T*>(this)->cache.emplace_back(ss.str());
 
index 7cdf8ba..b8b0199 100644 (file)
@@ -61,6 +61,12 @@ public: // CRTP(Curiously Recurring Template Pattern) for CRUD
        std::string getTableName(TableType&& type) const noexcept;
        template<typename ColumnType>
        std::string getColumnName(ColumnType&& type) const noexcept;
+       template<typename... Cs>
+       std::vector<std::string> _getTableNames(Cs&& ...columns) const noexcept;
+       template<typename... Cs> 
+       std::vector<std::string> _getColumnNames(Cs&& ...columns) const noexcept;
+       template<typename ColumnType> // remove_cv or ref
+       std::string _getColumnName(ColumnType&& type) const noexcept;
 
        std::vector<std::string> cache;
 
@@ -136,6 +142,29 @@ std::vector<std::string> Database<Tables...>::getTableNames(Cs&& ...columns) con
 
 template<typename... Tables>
 template<typename... Cs>
+std::vector<std::string> Database<Tables...>::_getTableNames(Cs&& ...columns) const noexcept
+{
+       std::set<std::string> names;
+
+       auto predicate = [this, &names](const auto& column) {
+               using ColumnType = std::remove_reference_t<decltype(column)>;
+               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<std::string>(names.begin(), names.end());
+}
+
+template<typename... Tables>
+template<typename... Cs>
 std::vector<std::string> Database<Tables...>::getColumnNames(Cs&& ...columns) const noexcept
 {
        std::vector<std::string> names;
@@ -155,6 +184,26 @@ std::vector<std::string> Database<Tables...>::getColumnNames(Cs&& ...columns) co
 }
 
 template<typename... Tables>
+template<typename... Cs>
+std::vector<std::string> Database<Tables...>::_getColumnNames(Cs&& ...columns) const noexcept
+{
+       std::vector<std::string> 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<typename... Tables>
 template<typename Table>
 std::string Database<Tables...>::getTableName(Table&& table) const noexcept
 {
@@ -199,6 +248,31 @@ std::string Database<Tables...>::getColumnName(ColumnType&& column) const noexce
 }
 
 template<typename... Tables>
+template<typename Column>
+std::string Database<Tables...>::_getColumnName(Column&& column) const noexcept
+{
+       using ColumnType = std::remove_reference_t<decltype(column)>;
+       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<typename... Tables>
 std::size_t Database<Tables...>::size() const noexcept
 {
        using TupleType = std::tuple<Tables...>;
index 92d2a31..dc0b929 100644 (file)
@@ -35,7 +35,7 @@ public:
        /// Make first stuct type to table type
        using Type = typename std::tuple_element<0, std::tuple<Columns...>>::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<std::string> getColumnNames(Cs&& ...columns) const noexcept;
        template<typename Column>
        std::string getColumnName(const Column& column) const noexcept;
+       template<typename... Cs>
+       std::vector<std::string> _getTableNames(Cs&& ...) const noexcept;
+       template<typename... Cs>
+       std::vector<std::string> _getColumnNames(Cs&& ...columns) const noexcept;
+       template<typename Column>
+       std::string _getColumnName(const Column& column) const noexcept;
 
        std::vector<std::string> cache;
 
@@ -99,6 +105,27 @@ std::vector<std::string> Table<Columns...>::getColumnNames(Cs&& ...columns) cons
        return names;
 }
 
+
+template<typename... Columns>
+template<typename... Cs>
+std::vector<std::string> Table<Columns...>::_getColumnNames(Cs&& ...columns) const noexcept
+{
+       std::vector<std::string> 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<typename... Columns>
 template<typename That>
 std::string Table<Columns...>::getTableName(That&&) const noexcept
@@ -139,6 +166,32 @@ std::string Table<Columns...>::getColumnName(const Column& column) const noexcep
 }
 
 template<typename... Columns>
+template<typename... Cs>
+std::vector<std::string> Table<Columns...>::_getTableNames(Cs&& ...) const noexcept
+{
+       return {this->name};
+}
+
+template<typename... Columns>
+template<typename Column>
+std::string Table<Columns...>::_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<typename... Columns>
 template<typename That>
 bool Table<Columns...>::compare(const That& that) const noexcept
 {
index 1ed52e9..745e5cb 100644 (file)
@@ -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) {
index 165d41f..8eb899f 100644 (file)
@@ -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<PolicyDefinition>(condition::Join::LEFT_OUTER);
-       std::string join2 = db.select(&Admin::uid, &Admin::key)
+       std::string join2 = db.select(Admin::Uid, Admin::Key)
                                                  .join<ManagedPolicy>(condition::Join::CROSS);
-       std::string join3 = db.select(&ManagedPolicy::value)
+       std::string join3 = db.select(ManagedPolicy::Value)
                                                  .join<PolicyDefinition>()
                                                  .on(expr(&ManagedPolicy::pid) == expr(&PolicyDefinition::id))
                                                  .join<Admin>()
@@ -186,3 +208,4 @@ TEST(QueryBuilderTsqbTests, JOIN)
                                         "INNER JOIN admin ON managed_policy.aid = admin.id "
                                         "WHERE managed_policy.pid = ?");
 }
+*/
index 5ec2bda..6e7e86d 100644 (file)
@@ -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");