Add database operations on query-builder
authorsangwan.kwon <sangwan.kwon@samsung.com>
Tue, 23 Jan 2018 06:24:38 +0000 (15:24 +0900)
committerJaemin Ryu <jm77.ryu@samsung.com>
Mon, 11 Feb 2019 04:22:16 +0000 (13:22 +0900)
- Support multi-table select clause.

Change-Id: Icb745e3be28b7379aae351c4896175307993d82e
Signed-off-by: sangwan.kwon <sangwan.kwon@samsung.com>
include/klay/db/query-builder.h
include/klay/db/query-builder/column.hxx
include/klay/db/query-builder/database-impl.hxx [new file with mode: 0644]
include/klay/db/query-builder/database.hxx [new file with mode: 0644]
include/klay/db/query-builder/table-impl.hxx
include/klay/db/query-builder/table.hxx
include/klay/db/query-builder/type.hxx
include/klay/db/query-builder/util.hxx [new file with mode: 0644]
test/query-builder.cpp

index a9f6f82f63417ea4b024a5b39f075b40bf0e4547..08c2057a59d6ee215fe3aea8ef04f2d1e08af422 100644 (file)
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include "query-builder/database.hxx"
 #include "query-builder/table.hxx"
 #include "query-builder/column.hxx"
 #include "query-builder/expression.hxx"
index 6fb99a901865ef9e35c921666e46ac0bb83f59ad..4082331f68f1cdb1750793f4d527973ce5a672eb 100644 (file)
@@ -25,6 +25,7 @@ template<typename Object, typename Field>
 struct Column {
        typedef Field Object::*Type;
        using FieldType = Field;
+       using TableType = Object;
 
        std::string name;
        Type type;
diff --git a/include/klay/db/query-builder/database-impl.hxx b/include/klay/db/query-builder/database-impl.hxx
new file mode 100644 (file)
index 0000000..6cea571
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+
+#pragma once
+
+#include "table.hxx"
+
+#include <vector>
+#include <set>
+#include <string>
+
+namespace qxx {
+namespace internal {
+
+template<typename... Base>
+class DatabaseImpl {
+public:
+       template<typename Column>
+       std::string getTableName(Column) const noexcept { return std::string(); }
+
+       template<typename Column>
+       std::string getColumnName(Column column) const noexcept { return std::string(); }
+};
+
+template<typename Front, typename... Rest>
+class DatabaseImpl<Front, Rest...> : public DatabaseImpl<Rest...> {
+public:
+       using Table = Front;
+
+       DatabaseImpl(Front front, Rest ...rest) : Base(rest...), table(front) {}
+
+       Table table;
+
+       template<typename Column>
+       std::string getTableName(Column column) const noexcept
+       {
+               if (this->table.find(column))
+                       return this->table.name;
+
+               return Base::template getTableName<Column>(column);
+       }
+
+       template<typename Column>
+       std::string getColumnName(Column column) const noexcept
+       {
+               if (this->table.find(column)) {
+                       auto cname = this->table.getColumnName(column.type);
+                       return this->table.name + "." + cname;
+               }
+
+               return Base::template getColumnName<Column>(column);
+       }
+
+private:
+       using Base = DatabaseImpl<Rest...>;
+};
+
+} // namespace internal
+} // namespace qxx
diff --git a/include/klay/db/query-builder/database.hxx b/include/klay/db/query-builder/database.hxx
new file mode 100644 (file)
index 0000000..fc49451
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ *  Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+
+#pragma once
+
+#include <vector>
+#include <set>
+#include <string>
+#include <sstream>
+#include <algorithm>
+
+#include "database-impl.hxx"
+#include "tuple-helper.hxx"
+#include "expression.hxx"
+#include "util.hxx"
+
+namespace qxx {
+
+template<typename... Tables>
+class Database {
+public:
+       using Self = Database<Tables...>;
+
+       template<typename... ColumnTypes>
+       Self select(ColumnTypes&&... cts);
+
+       template<typename Expr>
+       Self where(Expr expr);
+
+       operator std::string();
+
+       std::string name;
+
+private:
+       using ImplType = internal::DatabaseImpl<Tables...>;
+       using ColumnNames = std::vector<std::string>;
+       using TableNames = std::set<std::string>;
+
+       explicit Database(const std::string& name, ImplType impl);
+
+       template<typename ...Ts>
+       friend Database<Ts...> make_database(const std::string& name, Ts&& ...tables);
+
+       template<typename Cs>
+       std::set<std::string> getTableNames(Cs&& tuple);
+
+       template<typename Cs>
+       std::vector<std::string> getColumnNames(Cs&& tuple);
+
+       struct GetTableNames {
+               ImplType impl;
+               std::set<std::string> names;
+               GetTableNames(const ImplType &_impl) : impl(_impl) {}
+
+               template <typename T>
+               void operator()(T&& type)
+               {
+                       auto column = make_column("anonymous", type);
+                       auto name = this->impl.getTableName(std::move(column));
+                       if (!name.empty())
+                               names.emplace(name);
+               }
+       };
+
+       struct GetColumnNames {
+               ImplType impl;
+               std::vector<std::string> names;
+
+               GetColumnNames(const ImplType &_impl) : impl(_impl) {}
+
+               template <typename T>
+               void operator()(T&& type)
+               {
+                       auto column = make_column("anonymous", type);
+                       auto name = this->impl.getColumnName(std::move(column));
+                       if (!name.empty())
+                               names.emplace_back(name);
+               }
+       };
+
+       template<typename L, typename R>
+       std::string processWhere(condition::And<L,R>& expr);
+
+       template<typename L, typename R>
+       std::string processWhere(condition::Or<L,R>& expr);
+
+       template<typename Expr>
+       std::string processWhere(Expr expr);
+
+       ImplType impl;
+       std::vector<std::string> cache;
+};
+
+template<typename ...Tables>
+Database<Tables...> make_database(const std::string& name, Tables&& ...tables)
+{
+       auto impl = internal::DatabaseImpl<Tables...>(std::forward<Tables>(tables)...);
+       return Database<Tables...>(name, std::move(impl));
+}
+
+template<typename ...Tables>
+Database<Tables...>::Database(const std::string& name, ImplType impl)
+       : name(name), impl(impl) {}
+
+template<typename... Tables>
+template<typename... ColumnTypes>
+Database<Tables...> Database<Tables...>::select(ColumnTypes&&... cts)
+{
+       this->cache.clear();
+
+       auto columnTuple = std::make_tuple(std::forward<ColumnTypes>(cts)...);
+       auto columnNames = this->getColumnNames(std::move(columnTuple));
+       auto tableNames = this->getTableNames(std::move(columnTuple));
+
+       std::stringstream ss;
+       ss << "SELECT ";
+
+       for(const auto& c : columnNames)
+               ss << c << " ";
+
+       ss << "FROM ";
+
+       int i = 0;
+       for(const auto& t : tableNames) {
+               ss << t;
+
+               if(i++ < tableNames.size() - 1)
+                       ss << ", ";
+       }
+
+       cache.emplace_back(ss.str());
+
+       return *this;
+}
+
+template<typename... Tables>
+template<typename Expr>
+Database<Tables...> Database<Tables...>::where(Expr expr)
+{
+       std::stringstream ss;
+       ss << "WHERE " << this->processWhere(expr);
+
+       this->cache.emplace_back(ss.str());
+
+       return *this;
+}
+
+template<typename... Tables>
+Database<Tables...>::operator std::string()
+{
+       std::stringstream ss;
+       for (const auto& c : cache)
+               ss << c << " ";
+
+       this->cache.clear();
+       return util::rtrim(ss.str());
+}
+
+template<typename... Tables>
+template<typename Cs>
+std::set<std::string> Database<Tables...>::getTableNames(Cs&& tuple)
+{
+       GetTableNames closure(this->impl);
+       tuple_helper::for_each(std::forward<Cs>(tuple), closure);
+
+       return closure.names;
+}
+
+template<typename... Tables>
+template<typename Cs>
+std::vector<std::string> Database<Tables...>::getColumnNames(Cs&& tuple)
+{
+       GetColumnNames closure(this->impl);
+       tuple_helper::for_each(std::forward<Cs>(tuple), closure);
+
+       return closure.names;
+}
+
+template<typename... Tables>
+template<typename L, typename R>
+std::string Database<Tables...>::processWhere(condition::And<L,R>& expr)
+{
+       std::stringstream ss;
+       ss << this->processWhere(expr.l) << " ";
+       ss << static_cast<std::string>(expr) << " ";
+       ss << this->processWhere(expr.r);
+
+       return ss.str();
+}
+
+template<typename... Tables>
+template<typename L, typename R>
+std::string Database<Tables...>::processWhere(condition::Or<L,R>& expr)
+{
+       std::stringstream ss;
+       ss << this->processWhere(expr.l) << " ";
+       ss << static_cast<std::string>(expr) << " ";
+       ss << this->processWhere(expr.r);
+
+       return ss.str();
+}
+
+template<typename... Tables>
+template<typename Expr>
+std::string Database<Tables...>::processWhere(Expr expr)
+{
+       std::stringstream ss;
+       ss << this->impl.getColumnName(expr.l);
+       ss << " " << std::string(expr) << " ?";
+
+       return ss.str();
+}
+
+} // namespace qxx
index 2fa813afd0b07b41e67bf13791824810b368035d..02540f8eec44341e554225aece14c0d93534bcf8 100644 (file)
@@ -39,20 +39,23 @@ template<typename Front, typename... Rest>
 class TableImpl<Front, Rest...> : public TableImpl<Rest...> {
 public:
        using Column = Front;
+       using TableType = typename Column::TableType;
 
        explicit TableImpl(Front front, Rest ...rest) : Base(rest...), column(front) {}
 
        int size() const noexcept { return Base::size() + 1; }
 
-       std::vector<std::string> getColumnNames(void) const noexcept {
+       std::vector<std::string> getColumnNames(void) const noexcept
+       {
                auto names = Base::getColumnNames();
                names.emplace_back(this->column.name);
                return names;
        }
 
        template<typename ColumnType>
-       std::string getColumnName(ColumnType type) const noexcept {
-               if (type::compare(column.type, type))
+       std::string getColumnName(ColumnType type) const noexcept
+       {
+               if (type::cast_compare(column.type, type))
                        return column.name;
 
                return Base::template getColumnName<ColumnType>(type);
index d6a33fef26ce223b04f93894c8d55b0e623d83e0..0d54f1da7db77fa68bde5d6cda767d2f18805f44 100644 (file)
 #include "table-impl.hxx"
 #include "tuple-helper.hxx"
 #include "expression.hxx"
+#include "util.hxx"
 
 #include <vector>
 #include <string>
-#include <algorithm>
 #include <sstream>
-#include <cctype>
-
-namespace {
-
-std::string&& rtrim(std::string&& s)
-{
-       auto predicate = [](unsigned char c){ return !std::isspace(c); };
-       auto base = std::find_if(s.rbegin(), s.rend(), predicate).base();
-       s.erase(base, s.end());
-       return std::move(s);
-}
-
-} // anonymous namespace
 
 namespace qxx {
 
@@ -45,6 +32,8 @@ template<typename... Columns>
 class Table {
 public:
        using Self = Table<Columns...>;
+       using ImplType = internal::TableImpl<Columns...>;
+       using TableType = typename ImplType::TableType;
 
        template<typename... ColumnTypes>
        Self select(ColumnTypes&&... cts);
@@ -66,11 +55,17 @@ public:
        template<typename Expr>
        Self where(Expr expr);
 
+       template<typename Column>
+       bool find(Column column);
+
        operator std::string();
 
-private:
-       using ImplType = internal::TableImpl<Columns...>;
+       template<typename ColumnType>
+       std::string getColumnName(ColumnType&& type) const noexcept;
+
+       std::string name;
 
+private:
        explicit Table(const std::string& name, ImplType impl);
 
        template<typename ...Cs>
@@ -86,9 +81,6 @@ private:
 
        std::vector<std::string> getColumnNames(void) const noexcept;
 
-       template<typename ColumnType>
-       std::string getColumnName(ColumnType&& type) const noexcept;
-
        struct GetColumnNames {
                ImplType impl;
                std::vector<std::string> names;
@@ -112,9 +104,7 @@ private:
        template<typename Expr>
        std::string processWhere(Expr expr);
 
-       std::string name;
        ImplType impl;
-
        std::vector<std::string> cache;
 };
 
@@ -274,7 +264,14 @@ Table<Columns...>::operator std::string()
                ss << c << " ";
 
        this->cache.clear();
-       return rtrim(ss.str());
+       return util::rtrim(ss.str());
+}
+
+template<typename... Columns>
+template<typename Column>
+bool Table<Columns...>::find(Column column)
+{
+       return type::compare(TableType(), typename Column::TableType());
 }
 
 template<typename... Columns>
@@ -308,7 +305,8 @@ std::string Table<Columns...>::getColumnName(ColumnType&& type) const noexcept
 
 template<typename... Columns>
 template<typename L, typename R>
-std::string Table<Columns...>::processWhere(condition::And<L,R>& expr) {
+std::string Table<Columns...>::processWhere(condition::And<L,R>& expr)
+{
        std::stringstream ss;
        ss << this->processWhere(expr.l) << " ";
        ss << static_cast<std::string>(expr) << " ";
@@ -319,7 +317,8 @@ std::string Table<Columns...>::processWhere(condition::And<L,R>& expr) {
 
 template<typename... Columns>
 template<typename L, typename R>
-std::string Table<Columns...>::processWhere(condition::Or<L,R>& expr) {
+std::string Table<Columns...>::processWhere(condition::Or<L,R>& expr)
+{
        std::stringstream ss;
        ss << this->processWhere(expr.l) << " ";
        ss << static_cast<std::string>(expr) << " ";
@@ -330,7 +329,8 @@ std::string Table<Columns...>::processWhere(condition::Or<L,R>& expr) {
 
 template<typename... Columns>
 template<typename Expr>
-std::string Table<Columns...>::processWhere(Expr expr) {
+std::string Table<Columns...>::processWhere(Expr expr)
+{
        std::stringstream ss;
        ss << this->impl.getColumnName(expr.l.type);
        ss << " " << std::string(expr) << " ?";
index d1a3e3f324c9eeca83a4841e3281f0580e155adb..1b145c01ae1e1aff869a3f62e0208d961a484c2c 100644 (file)
 namespace qxx {
 namespace type {
 
+template<typename L, typename R>
+inline bool cast_compare(L l, R r)
+{
+       return l == reinterpret_cast<L>(r);
+}
+
 template<typename L, typename R>
 inline bool compare(L l, R r)
 {
-       return (l == reinterpret_cast<L>(r));
+       return std::is_same<L, R>::value;
 }
 
 template<typename L, typename R>
diff --git a/include/klay/db/query-builder/util.hxx b/include/klay/db/query-builder/util.hxx
new file mode 100644 (file)
index 0000000..316ef41
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+
+#pragma once
+
+#include <string>
+#include <algorithm>
+#include <cctype>
+
+namespace qxx {
+namespace util {
+
+std::string&& rtrim(std::string&& s)
+{
+       auto predicate = [](unsigned char c){ return !std::isspace(c); };
+       auto base = std::find_if(s.rbegin(), s.rend(), predicate).base();
+       s.erase(base, s.end());
+       return std::move(s);
+}
+
+} // namespace util
+} // namespace qxx
index faa13d41131295490fbe9adbd3fafcb1c44936ee..e59711eeecacd954b68893aea21f2b34e9884ea7 100644 (file)
@@ -31,12 +31,27 @@ struct Admin {
        int removable;
 };
 
+struct ManagedPolicy {
+       int id;
+       int aid;
+       int pid;
+       int value;
+};
+
 auto admin = make_table("admin", make_column("id", &Admin::id),
                                                                 make_column("pkg", &Admin::pkg),
                                                                 make_column("uid", &Admin::uid),
                                                                 make_column("key", &Admin::key),
                                                                 make_column("removable", &Admin::removable));
 
+auto managedPolicy = make_table("managed_policy",
+                                                                make_column("id", &ManagedPolicy::id),
+                                                                make_column("aid", &ManagedPolicy::aid),
+                                                                make_column("pid", &ManagedPolicy::pid),
+                                                                make_column("value", &ManagedPolicy::value));
+
+auto db = make_database("dpm", admin, managedPolicy);
+
 TESTCASE(SELECT)
 {
        std::string select1 = admin.select(&Admin::id, &Admin::pkg, &Admin::uid, &Admin::key);
@@ -124,3 +139,18 @@ TESTCASE(TYPE_SAFE)
                                                                                                        expr(&Admin::uid) == "dpm");
 */
 }
+
+TESTCASE(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)
+                                                                .where(expr(&Admin::uid) > 0 && expr(&ManagedPolicy::id) == 3);
+
+       TEST_EXPECT(true, multiSelect1 == "SELECT admin.uid admin.key managed_policy.id "
+                                                                         "managed_policy.value FROM admin, managed_policy");
+       TEST_EXPECT(true, multiSelect2 == "SELECT admin.uid admin.key managed_policy.id "
+                                                                         "managed_policy.value FROM admin, managed_policy "
+                                                                         "WHERE admin.uid > ? AND managed_policy.id = ?");
+}