From: sangwan.kwon Date: Wed, 24 Jan 2018 06:44:25 +0000 (+0900) Subject: Add join operation on query-builder X-Git-Tag: submit/tizen/20190219.012108~14 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b7273ea4d82989ef8710d520e1ab704d32e9eb33;p=platform%2Fcore%2Fsecurity%2Fklay.git Add join operation on query-builder - Support "INNER(default), OUTER(three types), CROSS" join clause. Change-Id: I874b965e36d712d48501ba90a3a7ef2c4cefd6dc Signed-off-by: sangwan.kwon --- diff --git a/include/klay/db/query-builder.h b/include/klay/db/query-builder.h index 08c2057..618612a 100644 --- a/include/klay/db/query-builder.h +++ b/include/klay/db/query-builder.h @@ -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 { diff --git a/include/klay/db/query-builder/column.hxx b/include/klay/db/query-builder/column.hxx index 4082331..571e43f 100644 --- a/include/klay/db/query-builder/column.hxx +++ b/include/klay/db/query-builder/column.hxx @@ -23,7 +23,7 @@ namespace qxx { template struct Column { - typedef Field Object::*Type; + using Type = Field Object::*; using FieldType = Field; using TableType = Object; @@ -32,7 +32,8 @@ struct Column { }; template -Column make_column(const std::string& name, F O::*field) { +Column 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 index 0000000..0f41d58 --- /dev/null +++ b/include/klay/db/query-builder/condition.hxx @@ -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 +struct And : public Base { + L l; + R r; + + And(L l, R r) : l(l), r(r) {} + operator std::string() const + { + return "AND"; + } +}; + +template +struct Or : public Base { + L l; + R r; + + Or(L l, R r) : l(l), r(r) {} + operator std::string() const + { + return "OR"; + } +}; + +template +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 +struct Binary : 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 diff --git a/include/klay/db/query-builder/database-impl.hxx b/include/klay/db/query-builder/database-impl.hxx index 6cea571..1312a5f 100644 --- a/include/klay/db/query-builder/database-impl.hxx +++ b/include/klay/db/query-builder/database-impl.hxx @@ -28,8 +28,8 @@ namespace internal { template class DatabaseImpl { public: - template - std::string getTableName(Column) const noexcept { return std::string(); } + template + std::string getTableName(Table) const noexcept { return std::string(); } template std::string getColumnName(Column column) const noexcept { return std::string(); } @@ -44,19 +44,20 @@ public: Table table; - template - std::string getTableName(Column column) const noexcept + template + 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); + return Base::template getTableName(table); } template 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; } diff --git a/include/klay/db/query-builder/database.hxx b/include/klay/db/query-builder/database.hxx index fc49451..060ef1b 100644 --- a/include/klay/db/query-builder/database.hxx +++ b/include/klay/db/query-builder/database.hxx @@ -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 Self where(Expr expr); + template + Self join(condition::Join type = condition::Join::INNER); + + template + 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 Database::where(Expr expr) return *this; } +template +template +Database Database::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 +template +Database Database::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 Database::operator std::string() { diff --git a/include/klay/db/query-builder/expression.hxx b/include/klay/db/query-builder/expression.hxx index 37e76bc..10a58e8 100644 --- a/include/klay/db/query-builder/expression.hxx +++ b/include/klay/db/query-builder/expression.hxx @@ -18,63 +18,11 @@ #include "column.hxx" #include "type.hxx" +#include "condition.hxx" #include namespace qxx { -namespace condition { - -struct Base {}; - -template -struct And : public Base { - L l; - R r; - - And(L l, R r) : l(l), r(r) {} - operator std::string() const - { - return "AND"; - } -}; - -template -struct Or : public Base { - L l; - R r; - - Or(L l, R r) : l(l), r(r) {} - operator std::string() const - { - return "OR"; - } -}; - -template -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 -struct Binary : 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 struct Expression { @@ -109,7 +57,8 @@ struct Equal : public condition::Binary { using condition::Binary::Binary; - operator std::string() const { + operator std::string() const + { return "="; } }; @@ -120,12 +69,35 @@ Equal operator==(Expression expr, R r) return {expr.value, r}; } +namespace join { + +template +struct Equal +{ + L l; + R r; + + operator std::string() const + { + return "="; + } +}; + +} // namespace join + +template +join::Equal operator==(Expression l, Expression r) +{ + return {l.value, r.value}; +} + template struct Greater : public condition::Binary { using condition::Binary::Binary; - operator std::string() const { + operator std::string() const + { return ">"; } }; diff --git a/include/klay/db/query-builder/table.hxx b/include/klay/db/query-builder/table.hxx index 0d54f1d..b35cb68 100644 --- a/include/klay/db/query-builder/table.hxx +++ b/include/klay/db/query-builder/table.hxx @@ -88,7 +88,8 @@ private: GetColumnNames(const ImplType &impl) : impl(impl) {} template - void operator()(T&& type) { + void operator()(T&& type) + { auto name = this->impl.getColumnName(std::forward(type)); if (!name.empty()) names.emplace_back(name); @@ -268,10 +269,11 @@ Table::operator std::string() } template -template -bool Table::find(Column column) +template +bool Table::find(That that) { - return type::compare(TableType(), typename Column::TableType()); + using This = TableType; + return type::compare(This(), That()); } template diff --git a/include/klay/db/query-builder/type.hxx b/include/klay/db/query-builder/type.hxx index 1b145c0..2ed0605 100644 --- a/include/klay/db/query-builder/type.hxx +++ b/include/klay/db/query-builder/type.hxx @@ -22,19 +22,19 @@ namespace qxx { namespace type { template -inline bool cast_compare(L l, R r) +bool cast_compare(L l, R r) { return l == reinterpret_cast(r); } template -inline bool compare(L l, R r) +bool compare(L l, R r) { return std::is_same::value; } template -inline void assert_compare(L l, R r) +void assert_compare(L l, R r) { static_assert(std::is_same::value, "Type is unsafe."); } diff --git a/include/klay/db/query-builder/util.hxx b/include/klay/db/query-builder/util.hxx index 316ef41..1ce6d7a 100644 --- a/include/klay/db/query-builder/util.hxx +++ b/include/klay/db/query-builder/util.hxx @@ -23,12 +23,12 @@ 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 diff --git a/test/query-builder.cpp b/test/query-builder.cpp index e59711e..79813c0 100644 --- a/test/query-builder.cpp +++ b/test/query-builder.cpp @@ -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(condition::Join::LEFT_OUTER); + std::string join2 = db.select(&Admin::uid, &Admin::key) + .join(condition::Join::CROSS); + std::string join3 = db.select(&ManagedPolicy::value) + .join() + .on(expr(&ManagedPolicy::pid) == expr(&PolicyDefinition::id)) + .join() + .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 = ?"); +}