Draft query-builder 81/166681/7
authorsangwan.kwon <sangwan.kwon@samsung.com>
Thu, 11 Jan 2018 10:50:48 +0000 (19:50 +0900)
committersangwan kwon <sangwan.kwon@samsung.com>
Thu, 18 Jan 2018 07:27:48 +0000 (07:27 +0000)
- Capture member pointer of struct. (column.hxx)
- Bind columns as table structure. (table.hxx)

- Generate select query by using Table::select(), Table::selectAll().
- Generate single where expression.

Change-Id: I929cf5d0d98f97fcb78e88162bafe9e3b955e1bb
Signed-off-by: sangwan.kwon <sangwan.kwon@samsung.com>
include/klay/db/query-builder/column.hxx [new file with mode: 0644]
include/klay/db/query-builder/expression.hxx [new file with mode: 0644]
include/klay/db/query-builder/table-impl.hxx [new file with mode: 0644]
include/klay/db/query-builder/table.hxx [new file with mode: 0644]
include/klay/db/query-builder/test-main.cpp [new file with mode: 0644]
include/klay/db/query-builder/tuple-helper.hxx [new file with mode: 0644]

diff --git a/include/klay/db/query-builder/column.hxx b/include/klay/db/query-builder/column.hxx
new file mode 100644 (file)
index 0000000..d24ae73
--- /dev/null
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <string>
+#include <tuple>
+
+namespace qxx {
+
+template<typename Object, typename Field>
+struct Column {
+       typedef Field Object::*Type;
+
+       std::string name;
+       Type type;
+};
+
+template<typename O, typename F>
+Column<O, F> make_column(const std::string& name, F O::*field) {
+       return {name, field};
+}
+
+} // namespace qxx
diff --git a/include/klay/db/query-builder/expression.hxx b/include/klay/db/query-builder/expression.hxx
new file mode 100644 (file)
index 0000000..adc6c27
--- /dev/null
@@ -0,0 +1,68 @@
+#pragma once
+
+namespace qxx {
+namespace condition {
+
+template<class L, class R>
+struct Binary {
+       L l;
+       R r;
+
+       Binary(L l, R r) : l(l), r(r) {}
+};
+
+} // namespace condition
+
+template<class Type>
+struct Expression {
+       Type value;
+};
+
+template<class Type>
+Expression<Type> expr(Type value) {
+       return {value};
+}
+
+template<class L, class R>
+struct Lesser : public condition::Binary<L, R> {
+       using condition::Binary<L, R>::Binary;
+
+       operator std::string() const {
+               return "<";
+       }
+};
+
+template<class L, class R>
+Lesser<L, R> operator<(Expression<L> expr, R r) {
+       return {expr.value, r};
+}
+
+template<class L, class R>
+struct Equal : public condition::Binary<L, R> {
+       using condition::Binary<L, R>::Binary;
+
+       operator std::string() const {
+               return "=";
+       }
+};
+
+template<class L, class R>
+Equal<L, R> operator==(Expression<L> expr, R r) {
+       return {expr.value, r};
+}
+
+template<class L, class R>
+struct Greater : public condition::Binary<L, R> {
+       using condition::Binary<L, R>::Binary;
+
+       operator std::string() const {
+               return ">";
+       }
+};
+
+template<class L, class R>
+Equal<L, R> operator>(Expression<L> expr, R r) {
+       return {expr.value, r};
+}
+
+} // namespace qxx
diff --git a/include/klay/db/query-builder/table-impl.hxx b/include/klay/db/query-builder/table-impl.hxx
new file mode 100644 (file)
index 0000000..db4b174
--- /dev/null
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace qxx {
+namespace internal {
+
+template<typename... Base>
+class TableImpl {
+public:
+       std::vector<std::string> getColumnNames() const noexcept { return {}; }
+
+       template<class Type>
+       std::string getColumnName(Type type) const noexcept { return std::string(); }
+
+       int size() const noexcept { return 0; }
+};
+
+template<typename Front, typename... Rest>
+class TableImpl<Front, Rest...> : public TableImpl<Rest...> {
+public:
+       using Column = Front;
+
+       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 {
+               auto names = Base::getColumnNames();
+               names.emplace_back(this->column.name);
+               return names;
+       }
+
+       template<typename ColumnType>
+       std::string getColumnName(ColumnType type) const noexcept {
+               // [TO-DO] Do Not Cast.
+               // [ALTER] std::is_same<F, typename T::field_type>{}
+               // [PROBLEM] Cannot multi-table select..
+               // [CRITICAL] (&Data::int == &Data2::int) is same
+               if (reinterpret_cast<ColumnType>(column.type) == type)
+                       return column.name;
+
+               return Base::template getColumnName<ColumnType>(type);
+       }
+
+private:
+       using Base = TableImpl<Rest...>;
+
+       Column column;
+};
+
+} // namespace internal
+} // namespace qxx
diff --git a/include/klay/db/query-builder/table.hxx b/include/klay/db/query-builder/table.hxx
new file mode 100644 (file)
index 0000000..d236eda
--- /dev/null
@@ -0,0 +1,167 @@
+#pragma once
+
+#include "column.hxx"
+#include "table-impl.hxx"
+#include "tuple-helper.hxx"
+
+#include <vector>
+#include <string>
+#include <algorithm>
+#include <sstream>
+
+namespace qxx {
+
+template<typename... Columns>
+class Table {
+public:
+       using Self = Table<Columns...>;
+
+       template<typename... ColumnTypes>
+       Self select(ColumnTypes&&... cts);
+
+       Self selectAll(void);
+
+       template<typename Expr>
+       Self where(Expr expr);
+
+       operator std::string();
+
+private:
+       using ImplType = internal::TableImpl<Columns...>;
+
+       explicit Table(const std::string& name, ImplType impl);
+
+       template<typename ...Cs>
+       friend Table<Cs...> make_table(const std::string& name, Cs&& ...columns);
+
+       template<typename Cs>
+       std::vector<std::string> getColumnNames(Cs&& tuple);
+
+       int size() const noexcept;
+
+       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;
+
+               GetColumnNames(const ImplType &impl) : impl(impl) {}
+
+               template <typename T>
+               void operator()(T&& type) {
+                       auto name = this->impl.getColumnName(std::forward<T>(type));
+                       if (!name.empty())
+                               names.emplace_back(name);
+               }
+       };
+
+       std::string name;
+       ImplType impl;
+
+       std::vector<std::string> cache;
+};
+
+template<typename ...Columns>
+Table<Columns...> make_table(const std::string& name, Columns&& ...cs)
+{
+       auto impl = internal::TableImpl<Columns...>(std::forward<Columns>(cs)...);
+       return Table<Columns...>(name, std::move(impl));
+}
+
+template<typename... Columns>
+Table<Columns...>::Table(const std::string& name, ImplType impl)
+       : name(name), impl(impl) {} 
+
+template<typename... Columns>
+template<typename... ColumnTypes>
+Table<Columns...> Table<Columns...>::select(ColumnTypes&&... cts)
+{
+       this->cache.clear();
+
+       auto columnTuple = std::make_tuple(std::forward<ColumnTypes>(cts)...);
+       auto columnNames = this->getColumnNames(std::move(columnTuple));
+
+       std::stringstream ss;
+       ss << "SELECT ";
+
+       for(const auto& c : columnNames)
+               ss << c << " ";
+
+       ss << "FROM " << this->name;
+
+       cache.emplace_back(ss.str());
+
+       return *this;
+}
+
+template<typename ...Columns>
+Table<Columns...> Table<Columns...>::selectAll(void)
+{
+       this->cache.clear();
+
+       std::stringstream ss;
+       ss << "SELECT * FROM " << this->name;
+
+       cache.emplace_back(ss.str());
+
+       return *this;
+}
+
+template<typename... Columns>
+template<typename Expr>
+Table<Columns...> Table<Columns...>::where(Expr expr)
+{
+       auto name = this->impl.getColumnName(expr.l); 
+
+       std::stringstream ss;
+       ss << "WHERE " << name << " " << std::string(expr) << " ?";
+
+       this->cache.emplace_back(ss.str());
+
+       return *this;
+}
+
+template<typename... Columns>
+Table<Columns...>::operator std::string()
+{
+       std::stringstream ss;
+       for (const auto& c : cache)
+               ss << c << " ";
+
+       this->cache.clear();
+       return ss.str();
+}
+
+template<typename... Columns>
+template<typename Cs>
+std::vector<std::string> Table<Columns...>::getColumnNames(Cs&& tuple)
+{
+       GetColumnNames closure(this->impl);
+       tuple_helper::for_each(std::forward<Cs>(tuple), closure);
+
+       return closure.names;
+}
+
+template<typename... Columns>
+int Table<Columns...>::size() const noexcept
+{
+       return this->impl.size();
+}
+
+template<typename... Columns>
+std::vector<std::string> Table<Columns...>::getColumnNames(void) const noexcept
+{
+       return this->impl.getColumnNames();
+}
+
+template<typename... Columns>
+template<typename ColumnType>
+std::string Table<Columns...>::getColumnName(ColumnType&& type) const noexcept
+{
+       return this->impl.getColumnName(std::forward<ColumnType>(type));
+}
+
+} // namespace qxx
diff --git a/include/klay/db/query-builder/test-main.cpp b/include/klay/db/query-builder/test-main.cpp
new file mode 100644 (file)
index 0000000..30dd09b
--- /dev/null
@@ -0,0 +1,35 @@
+#include "table.hxx"
+#include "column.hxx"
+#include "expression.hxx"
+
+#include <iostream>
+
+using namespace qxx;
+
+struct Admin {
+       int id;
+       std::string pkg;
+       int uid;
+       std::string key;
+       int removable;
+};
+
+int main() {
+       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));
+
+       std::string select1 = admin.select(&Admin::id, &Admin::pkg, &Admin::uid, &Admin::key);
+       std::string select2 = admin.select(&Admin::id, &Admin::uid, &Admin::key);
+       std::string select3 = admin.select(&Admin::uid, &Admin::key).where(expr(&Admin::id) > 3);
+       std::string select4 = admin.selectAll().where(expr(&Admin::uid) > 3);
+
+       std::cout << select1 << '\n'; // SELECT id pkg uid key FROM admin
+       std::cout << select2 << '\n'; // SELECT id uid key FROM admin
+       std::cout << select3 << '\n'; // SELECT uid key FROM admin WHERE id = ? 
+       std::cout << select4 << '\n'; // SELECT * FROM admin WHERE uid = ?
+
+       return 0;
+}
diff --git a/include/klay/db/query-builder/tuple-helper.hxx b/include/klay/db/query-builder/tuple-helper.hxx
new file mode 100644 (file)
index 0000000..0979973
--- /dev/null
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <tuple>
+
+namespace qxx {
+namespace tuple_helper {
+namespace internal {
+
+template<int n, typename T, typename C>
+class Iterator {
+public:
+       Iterator(const T& tuple, C&& closure) {
+               Iterator<n - 1, T, C> iter(tuple, std::forward<C>(closure));
+               closure(std::get<n-1>(tuple));
+       }
+};
+
+template<typename T, typename C>
+class Iterator<0, T, C> {
+public:
+       Iterator(const T&, C&&) {}
+};
+
+} // namespace internal
+
+template<typename T, typename C>
+void for_each(const T& tuple, C&& closure)
+{
+       using Iter = internal::Iterator<std::tuple_size<T>::value, T, C>;
+       Iter iter(tuple, std::forward<C>(closure));
+}
+
+} // namspace tuple-hepler
+} // namspace qxx