- Support "INNER(default), OUTER(three types), CROSS" join clause.
Change-Id: I874b965e36d712d48501ba90a3a7ef2c4cefd6dc
Signed-off-by: sangwan.kwon <sangwan.kwon@samsung.com>
#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 {
template<typename Object, typename Field>
struct Column {
- typedef Field Object::*Type;
+ using Type = Field Object::*;
using FieldType = Field;
using TableType = Object;
};
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};
}
--- /dev/null
+/*
+ * 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
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(); }
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;
}
#include "database-impl.hxx"
#include "tuple-helper.hxx"
+#include "condition.hxx"
#include "expression.hxx"
#include "util.hxx"
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;
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);
}
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()
{
#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 {
{
using condition::Binary<L, R>::Binary;
- operator std::string() const {
+ operator std::string() const
+ {
return "=";
}
};
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 ">";
}
};
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);
}
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>
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.");
}
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
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),
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)
{
"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 = ?");
+}