- Support multi-table select clause.
Change-Id: Icb745e3be28b7379aae351c4896175307993d82e
Signed-off-by: sangwan.kwon <sangwan.kwon@samsung.com>
#pragma once
+#include "query-builder/database.hxx"
#include "query-builder/table.hxx"
#include "query-builder/column.hxx"
#include "query-builder/expression.hxx"
struct Column {
typedef Field Object::*Type;
using FieldType = Field;
+ using TableType = Object;
std::string name;
Type type;
--- /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 "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
--- /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 <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
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);
#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 {
class Table {
public:
using Self = Table<Columns...>;
+ using ImplType = internal::TableImpl<Columns...>;
+ using TableType = typename ImplType::TableType;
template<typename... ColumnTypes>
Self select(ColumnTypes&&... cts);
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>
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;
template<typename Expr>
std::string processWhere(Expr expr);
- std::string name;
ImplType impl;
-
std::vector<std::string> cache;
};
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>
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) << " ";
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) << " ";
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) << " ?";
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>
--- /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 <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
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);
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 = ?");
+}