From: sangwan.kwon Date: Tue, 23 Jan 2018 06:24:38 +0000 (+0900) Subject: Add database operations on query-builder X-Git-Tag: submit/tizen/20190219.012108~15 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1b168771cea3d12a8d5909cecce952bb62ef5943;p=platform%2Fcore%2Fsecurity%2Fklay.git Add database operations on query-builder - Support multi-table select clause. Change-Id: Icb745e3be28b7379aae351c4896175307993d82e Signed-off-by: sangwan.kwon --- diff --git a/include/klay/db/query-builder.h b/include/klay/db/query-builder.h index a9f6f82..08c2057 100644 --- a/include/klay/db/query-builder.h +++ b/include/klay/db/query-builder.h @@ -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" diff --git a/include/klay/db/query-builder/column.hxx b/include/klay/db/query-builder/column.hxx index 6fb99a9..4082331 100644 --- a/include/klay/db/query-builder/column.hxx +++ b/include/klay/db/query-builder/column.hxx @@ -25,6 +25,7 @@ template 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 index 0000000..6cea571 --- /dev/null +++ b/include/klay/db/query-builder/database-impl.hxx @@ -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 +#include +#include + +namespace qxx { +namespace internal { + +template +class DatabaseImpl { +public: + template + std::string getTableName(Column) const noexcept { return std::string(); } + + template + std::string getColumnName(Column column) const noexcept { return std::string(); } +}; + +template +class DatabaseImpl : public DatabaseImpl { +public: + using Table = Front; + + DatabaseImpl(Front front, Rest ...rest) : Base(rest...), table(front) {} + + Table table; + + template + std::string getTableName(Column column) const noexcept + { + if (this->table.find(column)) + return this->table.name; + + return Base::template getTableName(column); + } + + template + 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); + } + +private: + using Base = DatabaseImpl; +}; + +} // 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 index 0000000..fc49451 --- /dev/null +++ b/include/klay/db/query-builder/database.hxx @@ -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 +#include +#include +#include +#include + +#include "database-impl.hxx" +#include "tuple-helper.hxx" +#include "expression.hxx" +#include "util.hxx" + +namespace qxx { + +template +class Database { +public: + using Self = Database; + + template + Self select(ColumnTypes&&... cts); + + template + Self where(Expr expr); + + operator std::string(); + + std::string name; + +private: + using ImplType = internal::DatabaseImpl; + using ColumnNames = std::vector; + using TableNames = std::set; + + explicit Database(const std::string& name, ImplType impl); + + template + friend Database make_database(const std::string& name, Ts&& ...tables); + + template + std::set getTableNames(Cs&& tuple); + + template + std::vector getColumnNames(Cs&& tuple); + + struct GetTableNames { + ImplType impl; + std::set names; + GetTableNames(const ImplType &_impl) : impl(_impl) {} + + template + 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 names; + + GetColumnNames(const ImplType &_impl) : impl(_impl) {} + + template + 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 + std::string processWhere(condition::And& expr); + + template + std::string processWhere(condition::Or& expr); + + template + std::string processWhere(Expr expr); + + ImplType impl; + std::vector cache; +}; + +template +Database make_database(const std::string& name, Tables&& ...tables) +{ + auto impl = internal::DatabaseImpl(std::forward(tables)...); + return Database(name, std::move(impl)); +} + +template +Database::Database(const std::string& name, ImplType impl) + : name(name), impl(impl) {} + +template +template +Database Database::select(ColumnTypes&&... cts) +{ + this->cache.clear(); + + auto columnTuple = std::make_tuple(std::forward(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 +template +Database Database::where(Expr expr) +{ + std::stringstream ss; + ss << "WHERE " << this->processWhere(expr); + + this->cache.emplace_back(ss.str()); + + return *this; +} + +template +Database::operator std::string() +{ + std::stringstream ss; + for (const auto& c : cache) + ss << c << " "; + + this->cache.clear(); + return util::rtrim(ss.str()); +} + +template +template +std::set Database::getTableNames(Cs&& tuple) +{ + GetTableNames closure(this->impl); + tuple_helper::for_each(std::forward(tuple), closure); + + return closure.names; +} + +template +template +std::vector Database::getColumnNames(Cs&& tuple) +{ + GetColumnNames closure(this->impl); + tuple_helper::for_each(std::forward(tuple), closure); + + return closure.names; +} + +template +template +std::string Database::processWhere(condition::And& expr) +{ + std::stringstream ss; + ss << this->processWhere(expr.l) << " "; + ss << static_cast(expr) << " "; + ss << this->processWhere(expr.r); + + return ss.str(); +} + +template +template +std::string Database::processWhere(condition::Or& expr) +{ + std::stringstream ss; + ss << this->processWhere(expr.l) << " "; + ss << static_cast(expr) << " "; + ss << this->processWhere(expr.r); + + return ss.str(); +} + +template +template +std::string Database::processWhere(Expr expr) +{ + std::stringstream ss; + ss << this->impl.getColumnName(expr.l); + ss << " " << std::string(expr) << " ?"; + + return ss.str(); +} + +} // namespace qxx diff --git a/include/klay/db/query-builder/table-impl.hxx b/include/klay/db/query-builder/table-impl.hxx index 2fa813a..02540f8 100644 --- a/include/klay/db/query-builder/table-impl.hxx +++ b/include/klay/db/query-builder/table-impl.hxx @@ -39,20 +39,23 @@ template class TableImpl : public TableImpl { 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 getColumnNames(void) const noexcept { + std::vector getColumnNames(void) const noexcept + { auto names = Base::getColumnNames(); names.emplace_back(this->column.name); return names; } template - 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(type); diff --git a/include/klay/db/query-builder/table.hxx b/include/klay/db/query-builder/table.hxx index d6a33fe..0d54f1d 100644 --- a/include/klay/db/query-builder/table.hxx +++ b/include/klay/db/query-builder/table.hxx @@ -20,24 +20,11 @@ #include "table-impl.hxx" #include "tuple-helper.hxx" #include "expression.hxx" +#include "util.hxx" #include #include -#include #include -#include - -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 class Table { public: using Self = Table; + using ImplType = internal::TableImpl; + using TableType = typename ImplType::TableType; template Self select(ColumnTypes&&... cts); @@ -66,11 +55,17 @@ public: template Self where(Expr expr); + template + bool find(Column column); + operator std::string(); -private: - using ImplType = internal::TableImpl; + template + std::string getColumnName(ColumnType&& type) const noexcept; + + std::string name; +private: explicit Table(const std::string& name, ImplType impl); template @@ -86,9 +81,6 @@ private: std::vector getColumnNames(void) const noexcept; - template - std::string getColumnName(ColumnType&& type) const noexcept; - struct GetColumnNames { ImplType impl; std::vector names; @@ -112,9 +104,7 @@ private: template std::string processWhere(Expr expr); - std::string name; ImplType impl; - std::vector cache; }; @@ -274,7 +264,14 @@ Table::operator std::string() ss << c << " "; this->cache.clear(); - return rtrim(ss.str()); + return util::rtrim(ss.str()); +} + +template +template +bool Table::find(Column column) +{ + return type::compare(TableType(), typename Column::TableType()); } template @@ -308,7 +305,8 @@ std::string Table::getColumnName(ColumnType&& type) const noexcept template template -std::string Table::processWhere(condition::And& expr) { +std::string Table::processWhere(condition::And& expr) +{ std::stringstream ss; ss << this->processWhere(expr.l) << " "; ss << static_cast(expr) << " "; @@ -319,7 +317,8 @@ std::string Table::processWhere(condition::And& expr) { template template -std::string Table::processWhere(condition::Or& expr) { +std::string Table::processWhere(condition::Or& expr) +{ std::stringstream ss; ss << this->processWhere(expr.l) << " "; ss << static_cast(expr) << " "; @@ -330,7 +329,8 @@ std::string Table::processWhere(condition::Or& expr) { template template -std::string Table::processWhere(Expr expr) { +std::string Table::processWhere(Expr expr) +{ std::stringstream ss; ss << this->impl.getColumnName(expr.l.type); ss << " " << std::string(expr) << " ?"; diff --git a/include/klay/db/query-builder/type.hxx b/include/klay/db/query-builder/type.hxx index d1a3e3f..1b145c0 100644 --- a/include/klay/db/query-builder/type.hxx +++ b/include/klay/db/query-builder/type.hxx @@ -21,10 +21,16 @@ namespace qxx { namespace type { +template +inline bool cast_compare(L l, R r) +{ + return l == reinterpret_cast(r); +} + template inline bool compare(L l, R r) { - return (l == reinterpret_cast(r)); + return std::is_same::value; } template diff --git a/include/klay/db/query-builder/util.hxx b/include/klay/db/query-builder/util.hxx new file mode 100644 index 0000000..316ef41 --- /dev/null +++ b/include/klay/db/query-builder/util.hxx @@ -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 +#include +#include + +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 diff --git a/test/query-builder.cpp b/test/query-builder.cpp index faa13d4..e59711e 100644 --- a/test/query-builder.cpp +++ b/test/query-builder.cpp @@ -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 = ?"); +}