Add join operation on query-builder
authorsangwan.kwon <sangwan.kwon@samsung.com>
Wed, 24 Jan 2018 06:44:25 +0000 (15:44 +0900)
committerJaemin Ryu <jm77.ryu@samsung.com>
Mon, 11 Feb 2019 04:22:16 +0000 (13:22 +0900)
- Support "INNER(default), OUTER(three types), CROSS" join clause.

Change-Id: I874b965e36d712d48501ba90a3a7ef2c4cefd6dc
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/condition.hxx [new file with mode: 0644]
include/klay/db/query-builder/database-impl.hxx
include/klay/db/query-builder/database.hxx
include/klay/db/query-builder/expression.hxx
include/klay/db/query-builder/table.hxx
include/klay/db/query-builder/type.hxx
include/klay/db/query-builder/util.hxx
test/query-builder.cpp

index 08c2057a59d6ee215fe3aea8ef04f2d1e08af422..618612a694f5029dd744520df2087b013e4e35d9 100644 (file)
@@ -20,6 +20,8 @@
 #include "query-builder/table.hxx"
 #include "query-builder/column.hxx"
 #include "query-builder/expression.hxx"
+#include "query-builder/condition.hxx"
+#include "query-builder/util.hxx"
 
 namespace query_builder {
 
index 4082331f68f1cdb1750793f4d527973ce5a672eb..571e43f6dfff76a3d03b7ece4ddbd262984bc997 100644 (file)
@@ -23,7 +23,7 @@ namespace qxx {
 
 template<typename Object, typename Field>
 struct Column {
-       typedef Field Object::*Type;
+       using Type = Field Object::*;
        using FieldType = Field;
        using TableType = Object;
 
@@ -32,7 +32,8 @@ struct Column {
 };
 
 template<typename O, typename F>
-Column<O, F> make_column(const std::string& name, F O::*field) {
+Column<O, F> make_column(const std::string& name, F O::*field)
+{
        return {name, field};
 }
 
diff --git a/include/klay/db/query-builder/condition.hxx b/include/klay/db/query-builder/condition.hxx
new file mode 100644 (file)
index 0000000..0f41d58
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *  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 "type.hxx"
+
+namespace qxx {
+namespace condition {
+
+struct Base {};
+
+template<typename L, typename R>
+struct And : public Base {
+       L l;
+       R r;
+
+       And(L l, R r) : l(l), r(r) {}
+       operator std::string() const
+       {
+               return "AND";
+       }
+};
+
+template<typename L, typename R>
+struct Or : public Base {
+       L l;
+       R r;
+
+       Or(L l, R r) : l(l), r(r) {}
+       operator std::string() const
+       {
+               return "OR";
+       }
+};
+
+template<typename L, typename R>
+struct Binary : public Base {
+       L l;
+       R r;
+
+       Binary(L l, R r) : l(l), r(r)
+       {
+               using FieldType = typename L::FieldType;
+               type::assert_compare(FieldType(), r);
+       }
+};
+
+template<typename L>
+struct Binary<L, const char*> : public Base {
+       L l;
+       std::string r;
+
+       Binary(L l, const char* r) : l(l), r(r)
+       {
+               using FieldType = typename L::FieldType;
+               type::assert_compare(FieldType(), std::string());
+       }
+};
+
+enum class Join : int {
+       INNER,
+       CROSS,
+       LEFT_OUTER,
+       RIGHT_OUTER,
+       FULL_OUTER
+};
+
+inline std::string to_string(Join type)
+{
+       switch (type) {
+       case Join::CROSS: return "CROSS";
+       case Join::LEFT_OUTER: return "LEFT OUTER";
+       case Join::RIGHT_OUTER: return "RIGHT OUTER";
+       case Join::FULL_OUTER: return "FULL OUTER";
+       case Join::INNER:
+       default:
+               return "INNER";
+       }
+}
+
+} // namespace condition
+} // namespace qxx
index 6cea57172000065ab85f83d5cdaae738667eb335..1312a5f5f785afc46c69bdf4e1cf9ec9d556f8a1 100644 (file)
@@ -28,8 +28,8 @@ namespace internal {
 template<typename... Base>
 class DatabaseImpl {
 public:
-       template<typename Column>
-       std::string getTableName(Column) const noexcept { return std::string(); }
+       template<typename Table>
+       std::string getTableName(Table) const noexcept { return std::string(); }
 
        template<typename Column>
        std::string getColumnName(Column column) const noexcept { return std::string(); }
@@ -44,19 +44,20 @@ public:
 
        Table table;
 
-       template<typename Column>
-       std::string getTableName(Column column) const noexcept
+       template<typename Table>
+       std::string getTableName(Table table) const noexcept
        {
-               if (this->table.find(column))
+               if (this->table.find(table))
                        return this->table.name;
 
-               return Base::template getTableName<Column>(column);
+               return Base::template getTableName<Table>(table);
        }
 
        template<typename Column>
        std::string getColumnName(Column column) const noexcept
        {
-               if (this->table.find(column)) {
+               using TableType = typename decltype(column)::TableType;
+               if (this->table.find(TableType())) {
                        auto cname = this->table.getColumnName(column.type);
                        return this->table.name + "." + cname;
                }
index fc494518830de292d16387e2efdb0d32a441f01e..060ef1b3d9cd7901273470c5d58bfe02156d4a44 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "database-impl.hxx"
 #include "tuple-helper.hxx"
+#include "condition.hxx"
 #include "expression.hxx"
 #include "util.hxx"
 
@@ -40,6 +41,12 @@ public:
        template<typename Expr>
        Self where(Expr expr);
 
+       template<typename Table>
+       Self join(condition::Join type = condition::Join::INNER);
+
+       template<typename Expr>
+       Self on(Expr expr);
+
        operator std::string();
 
        std::string name;
@@ -69,7 +76,8 @@ private:
                void operator()(T&& type)
                {
                        auto column = make_column("anonymous", type);
-                       auto name = this->impl.getTableName(std::move(column));
+                       using TableType = typename decltype(column)::TableType;
+                       auto name = this->impl.getTableName(TableType());
                        if (!name.empty())
                                names.emplace(name);
                }
@@ -158,6 +166,38 @@ Database<Tables...> Database<Tables...>::where(Expr expr)
        return *this;
 }
 
+template<typename... Tables>
+template<typename Table>
+Database<Tables...> Database<Tables...>::join(condition::Join type)
+{
+       std::stringstream ss;
+       ss << condition::to_string(type) << " ";
+       ss << "JOIN ";
+       ss << this->impl.getTableName(Table());
+
+       this->cache.emplace_back(ss.str());
+       return *this;
+}
+
+template<typename... Tables>
+template<typename Expr>
+Database<Tables...> Database<Tables...>::on(Expr expr)
+{
+       std::stringstream ss;
+       ss << "ON ";
+
+       auto lname = this->impl.getColumnName(std::move(expr.l));
+       ss << lname << " ";
+
+       ss << std::string(expr) << " ";
+
+       auto rname = this->impl.getColumnName(std::move(expr.r));
+       ss << rname;
+
+       this->cache.emplace_back(ss.str());
+       return *this;
+}
+
 template<typename... Tables>
 Database<Tables...>::operator std::string()
 {
index 37e76bc6ee2146b602fbcfa8de293286854ffab1..10a58e84a8cde41f4b2437b27211186c592dab12 100644 (file)
 
 #include "column.hxx"
 #include "type.hxx"
+#include "condition.hxx"
 
 #include <type_traits>
 
 namespace qxx {
-namespace condition {
-
-struct Base {};
-
-template<typename L, typename R>
-struct And : public Base {
-       L l;
-       R r;
-
-       And(L l, R r) : l(l), r(r) {}
-       operator std::string() const
-       {
-               return "AND";
-       }
-};
-
-template<typename L, typename R>
-struct Or : public Base {
-       L l;
-       R r;
-
-       Or(L l, R r) : l(l), r(r) {}
-       operator std::string() const
-       {
-               return "OR";
-       }
-};
-
-template<typename L, typename R>
-struct Binary : public Base {
-       L l;
-       R r;
-
-       Binary(L l, R r) : l(l), r(r)
-       {
-               using FieldType = typename L::FieldType;
-               type::assert_compare(FieldType(), r);
-       }
-};
-
-template<typename L>
-struct Binary<L, const char*> : public Base {
-       L l;
-       std::string r;
-
-       Binary(L l, const char* r) : l(l), r(r)
-       {
-               using FieldType = typename L::FieldType;
-               type::assert_compare(FieldType(), std::string());
-       }
-};
-
-} // namespace condition
 
 template<typename Type>
 struct Expression {
@@ -109,7 +57,8 @@ struct Equal : public condition::Binary<L, R>
 {
        using condition::Binary<L, R>::Binary;
 
-       operator std::string() const {
+       operator std::string() const
+       {
                return "=";
        }
 };
@@ -120,12 +69,35 @@ Equal<L, R> operator==(Expression<L> expr, R r)
        return {expr.value, r};
 }
 
+namespace join {
+
+template<typename L, typename R>
+struct Equal
+{
+       L l;
+       R r;
+
+       operator std::string() const
+       {
+               return "=";
+       }
+};
+
+} // namespace join
+
+template<typename L, typename R>
+join::Equal<L, R> operator==(Expression<L> l, Expression<R> r)
+{
+       return {l.value, r.value};
+}
+
 template<typename L, typename R>
 struct Greater : public condition::Binary<L, R>
 {
        using condition::Binary<L, R>::Binary;
 
-       operator std::string() const {
+       operator std::string() const
+       {
                return ">";
        }
 };
index 0d54f1da7db77fa68bde5d6cda767d2f18805f44..b35cb68387bc71cd3558b6f0aeba3dadd337ca8e 100644 (file)
@@ -88,7 +88,8 @@ private:
                GetColumnNames(const ImplType &impl) : impl(impl) {}
 
                template <typename T>
-               void operator()(T&& type) {
+               void operator()(T&& type)
+               {
                        auto name = this->impl.getColumnName(std::forward<T>(type));
                        if (!name.empty())
                                names.emplace_back(name);
@@ -268,10 +269,11 @@ Table<Columns...>::operator std::string()
 }
 
 template<typename... Columns>
-template<typename Column>
-bool Table<Columns...>::find(Column column)
+template<typename That>
+bool Table<Columns...>::find(That that)
 {
-       return type::compare(TableType(), typename Column::TableType());
+       using This = TableType;
+       return type::compare(This(), That());
 }
 
 template<typename... Columns>
index 1b145c01ae1e1aff869a3f62e0208d961a484c2c..2ed06056a2274f19e1c7285e032bb5efca845f72 100644 (file)
@@ -22,19 +22,19 @@ namespace qxx {
 namespace type {
 
 template<typename L, typename R>
-inline bool cast_compare(L l, R r)
+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)
+bool compare(L l, R r)
 {
        return std::is_same<L, R>::value;
 }
 
 template<typename L, typename R>
-inline void assert_compare(L l, R r)
+void assert_compare(L l, R r)
 {
        static_assert(std::is_same<L, R>::value, "Type is unsafe.");
 }
index 316ef41adc1d2dc2912258d34ad3a8eab0d08fbc..1ce6d7a35b77edf4d5919f6105bbbf2e1b01eac9 100644 (file)
 namespace qxx {
 namespace util {
 
-std::string&& rtrim(std::string&& s)
+inline 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);
+       return s;
 }
 
 } // namespace util
index e59711eeecacd954b68893aea21f2b34e9884ea7..79813c03fa3d571af3c061ec4f1e6ad6731dfeb2 100644 (file)
@@ -38,6 +38,13 @@ struct ManagedPolicy {
        int value;
 };
 
+struct PolicyDefinition {
+       int id;
+       int scope;
+       std::string name;
+       int ivalue;
+};
+
 auto admin = make_table("admin", make_column("id", &Admin::id),
                                                                 make_column("pkg", &Admin::pkg),
                                                                 make_column("uid", &Admin::uid),
@@ -50,7 +57,13 @@ auto managedPolicy = make_table("managed_policy",
                                                                 make_column("pid", &ManagedPolicy::pid),
                                                                 make_column("value", &ManagedPolicy::value));
 
-auto db = make_database("dpm", admin, managedPolicy);
+auto policyDefinition = make_table("policy_definition",
+                                                                  make_column("id", &PolicyDefinition::id),
+                                                                  make_column("scope", &PolicyDefinition::scope),
+                                                                  make_column("name", &PolicyDefinition::name),
+                                                                  make_column("ivalue", &PolicyDefinition::ivalue));
+
+auto db = make_database("dpm", admin, managedPolicy, policyDefinition);
 
 TESTCASE(SELECT)
 {
@@ -154,3 +167,27 @@ TESTCASE(MULTI_SELECT)
                                                                          "managed_policy.value FROM admin, managed_policy "
                                                                          "WHERE admin.uid > ? AND managed_policy.id = ?");
 }
+
+TESTCASE(JOIN)
+{
+       std::string join1 = db.select(&Admin::uid, &Admin::key)
+                                                 .join<PolicyDefinition>(condition::Join::LEFT_OUTER);
+       std::string join2 = db.select(&Admin::uid, &Admin::key)
+                                                 .join<ManagedPolicy>(condition::Join::CROSS);
+       std::string join3 = db.select(&ManagedPolicy::value)
+                                                 .join<PolicyDefinition>()
+                                                 .on(expr(&ManagedPolicy::pid) == expr(&PolicyDefinition::id))
+                                                 .join<Admin>()
+                                                 .on(expr(&ManagedPolicy::aid) == expr(&Admin::id))
+                                                 .where(expr(&ManagedPolicy::pid) == 99);
+
+       TEST_EXPECT(true, join1 == "SELECT admin.uid admin.key FROM admin "
+                                                          "LEFT OUTER JOIN policy_definition");
+       TEST_EXPECT(true, join2 == "SELECT admin.uid admin.key FROM admin "
+                                                          "CROSS JOIN managed_policy");
+       TEST_EXPECT(true, join3 == "SELECT managed_policy.value FROM managed_policy "
+                                                          "INNER JOIN policy_definition "
+                                                          "ON managed_policy.pid = policy_definition.id "
+                                                          "INNER JOIN admin ON managed_policy.aid = admin.id "
+                                                          "WHERE managed_policy.pid = ?");
+}