Import type-safe query builder
authorSangwan Kwon <sangwan.kwon@samsung.com>
Wed, 26 Jun 2019 04:04:15 +0000 (13:04 +0900)
committerSangwan Kwon <sangwan.kwon@samsung.com>
Tue, 9 Jul 2019 23:56:30 +0000 (08:56 +0900)
Signed-off-by: Sangwan Kwon <sangwan.kwon@samsung.com>
14 files changed:
CMakeLists.txt
tsqb/CMakeLists.txt [new file with mode: 0644]
tsqb/include/column-pack.hxx [new file with mode: 0644]
tsqb/include/column.hxx [new file with mode: 0644]
tsqb/include/condition.hxx [new file with mode: 0644]
tsqb/include/database.hxx [new file with mode: 0644]
tsqb/include/expression.hxx [new file with mode: 0644]
tsqb/include/table-pack.hxx [new file with mode: 0644]
tsqb/include/table.hxx [new file with mode: 0644]
tsqb/include/tuple-helper.hxx [new file with mode: 0644]
tsqb/include/type.hxx [new file with mode: 0644]
tsqb/include/util.hxx [new file with mode: 0644]
tsqb/tsqb-tests.cpp [new file with mode: 0644]
tsqb/tsqb.hxx [new file with mode: 0644]

index 6ea29661b879070792582973df98b9cee362abc9..ac19d8e8ff2572e8e43a00ca60aae05dcf27df69 100644 (file)
@@ -56,3 +56,4 @@ EXECUTE_PROCESS(COMMAND mkdir -p "${CMAKE_BINARY_DIR}/generated")
 
 ADD_SUBDIRECTORY(osquery)
 ADD_SUBDIRECTORY(sqlite3)
+ADD_SUBDIRECTORY(tsqb) # Type-safe query builder
diff --git a/tsqb/CMakeLists.txt b/tsqb/CMakeLists.txt
new file mode 100644 (file)
index 0000000..00e8ef4
--- /dev/null
@@ -0,0 +1,18 @@
+#  Copyright (c) 2019 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
+
+
+INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/include")
+
+ADD_OSQUERY_TEST(FALSE tsqb_tests tsqb-tests.cpp)
diff --git a/tsqb/include/column-pack.hxx b/tsqb/include/column-pack.hxx
new file mode 100644 (file)
index 0000000..5125f42
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ *  Copyright (c) 2017-present 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
+ */
+/*
+ * @file column-pack.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Tie different types of columns
+ */
+
+#pragma once
+
+#include "type.hxx"
+
+#include <string>
+#include <vector>
+
+namespace tsqb {
+namespace internal {
+
+template<typename... Base>
+class ColumnPack {
+public:
+       template<typename ColumnType>
+       std::string getName(ColumnType&&) const noexcept { return std::string(); }
+       std::vector<std::string> getNames(void) const noexcept { return {}; }
+
+       int size() const noexcept { return 0; }
+};
+
+template<typename Front, typename... Rest>
+class ColumnPack<Front, Rest...> : public ColumnPack<Rest...> {
+public:
+       using Column = Front;
+       using TableType = typename Column::TableType;
+
+       explicit ColumnPack(Front&& front, Rest&& ...rest);
+
+       template<typename ColumnType>
+       std::string getName(ColumnType&& type) const noexcept;
+       std::vector<std::string> getNames(void) const noexcept;
+
+       int size() const noexcept { return Base::size() + 1; }
+
+private:
+       using Base = ColumnPack<Rest...>;
+
+       Column column;
+};
+
+template<typename Front, typename... Rest>
+ColumnPack<Front, Rest...>::ColumnPack(Front&& front, Rest&& ...rest) :
+       Base(std::forward<Rest>(rest)...), column(front)
+{
+}
+
+template<typename Front, typename... Rest>
+std::vector<std::string> ColumnPack<Front, Rest...>::getNames(void) const noexcept
+{
+       auto names = Base::getNames();
+       names.emplace_back(this->column.name);
+
+       return names;
+}
+
+template<typename Front, typename... Rest>
+template<typename ColumnType>
+std::string ColumnPack<Front, Rest...>::getName(ColumnType&& type) const noexcept
+{
+       if (type::cast_compare(column.type, std::forward<ColumnType>(type)))
+               return column.name;
+
+       return Base::template getName<ColumnType>(std::forward<ColumnType>(type));
+}
+
+} // namespace internal
+} // namespace tsqb
diff --git a/tsqb/include/column.hxx b/tsqb/include/column.hxx
new file mode 100644 (file)
index 0000000..ca17415
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *  Copyright (c) 2017-present 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
+ */
+/*
+ * @file culumn.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Capture member pointer of struct and use it for matching with column
+ */
+
+#pragma once
+
+#include <string>
+#include <tuple>
+
+namespace tsqb {
+
+template<typename Object, typename Field>
+struct Column {
+       using Type = Field Object::*;
+       using FieldType = Field;
+       using TableType = Object;
+
+       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};
+}
+
+template<typename Type>
+struct Distinct {
+       Type value;
+};
+
+template<typename... Args>
+auto distinct(Args&&... args) -> decltype(Distinct<std::tuple<Args...>>())
+{
+       return {std::make_tuple(std::forward<Args>(args)...)};
+}
+
+} // namespace tsqb
diff --git a/tsqb/include/condition.hxx b/tsqb/include/condition.hxx
new file mode 100644 (file)
index 0000000..7ec13da
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *  Copyright (c) 2017-present 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
+ */
+/*
+ * @file condition.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Represent the condition statement of SQL
+ */
+
+#pragma once
+
+#include "type.hxx"
+
+namespace tsqb {
+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 tsqb
diff --git a/tsqb/include/database.hxx b/tsqb/include/database.hxx
new file mode 100644 (file)
index 0000000..fb4fc19
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ *  Copyright (c) 2017-present 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
+ */
+/*
+ * @file database.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Represent the database scheme of SQL
+ */
+
+#pragma once
+
+#include <vector>
+#include <set>
+#include <string>
+#include <sstream>
+#include <algorithm>
+
+#include "table-pack.hxx"
+#include "tuple-helper.hxx"
+#include "condition.hxx"
+#include "expression.hxx"
+#include "util.hxx"
+
+namespace tsqb {
+
+template<typename... Tables>
+class Database {
+public:
+       using Self = Database<Tables...>;
+
+       template<typename... ColumnTypes>
+       Self& select(ColumnTypes&&... cts);
+
+       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;
+
+private:
+       using TablePackType = internal::TablePack<Tables...>;
+       using ColumnNames = std::vector<std::string>;
+       using TableNames = std::set<std::string>;
+
+       explicit Database(const std::string& name, TablePackType&& tablePack);
+
+       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 {
+               TablePackType tablePack;
+               std::set<std::string> names;
+               GetTableNames(const TablePackType& tablePack) : tablePack(tablePack) {}
+
+               template <typename T>
+               void operator()(T&& type)
+               {
+                       auto column = make_column("anonymous", type);
+                       using TableType = typename decltype(column)::TableType;
+                       auto name = this->tablePack.getName(TableType());
+                       if (!name.empty())
+                               names.emplace(name);
+               }
+       };
+
+       struct GetColumnNames {
+               TablePackType tablePack;
+               std::vector<std::string> names;
+
+               GetColumnNames(const TablePackType& tablePack) : tablePack(tablePack) {}
+
+               template <typename T>
+               void operator()(T&& type)
+               {
+                       auto column = make_column("anonymous", type);
+                       auto name = this->tablePack.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);
+
+       TablePackType tablePack;
+       std::vector<std::string> cache;
+};
+
+template<typename ...Tables>
+Database<Tables...> make_database(const std::string& name, Tables&& ...tables)
+{
+       auto tablePack = internal::TablePack<Tables...>(std::forward<Tables>(tables)...);
+       return Database<Tables...>(name, std::move(tablePack));
+}
+
+template<typename ...Tables>
+Database<Tables...>::Database(const std::string& name, TablePackType&& tablePack)
+       : name(name), tablePack(std::move(tablePack)) {}
+
+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 ";
+
+       int i = 0;
+       for (const auto& c : columnNames) {
+               ss << c;
+
+               if (i++ < columnNames.size() - 1)
+                       ss << ", ";
+       }
+
+       ss << " FROM ";
+
+       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>
+template<typename Table>
+Database<Tables...>& Database<Tables...>::join(condition::Join type)
+{
+       std::stringstream ss;
+       ss << condition::to_string(type) << " ";
+       ss << "JOIN ";
+       ss << this->tablePack.getName(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->tablePack.getColumnName(std::move(expr.l));
+       ss << lname << " ";
+
+       ss << std::string(expr) << " ";
+
+       auto rname = this->tablePack.getColumnName(std::move(expr.r));
+       ss << rname;
+
+       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->tablePack);
+       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->tablePack);
+       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->tablePack.getColumnName(expr.l);
+       ss << " " << std::string(expr) << " ?";
+
+       return ss.str();
+}
+
+} // namespace tsqb
diff --git a/tsqb/include/expression.hxx b/tsqb/include/expression.hxx
new file mode 100644 (file)
index 0000000..c901ab0
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ *  Copyright (c) 2017-present 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
+ */
+/*
+ * @file expression.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Represent the expression of SQL
+ */
+
+#pragma once
+
+#include "column.hxx"
+#include "type.hxx"
+#include "condition.hxx"
+
+#include <type_traits>
+
+namespace tsqb {
+
+template<typename Type>
+struct Expression {
+       Type value;
+};
+
+template<typename O, typename F>
+Expression<Column<O, F>> expr(F O::*field)
+{
+       Column<O, F> anonymous = {"anonymous", field};
+       return {anonymous};
+}
+
+template<typename L, typename R>
+struct Lesser : public condition::Binary<L, R> {
+       using condition::Binary<L, R>::Binary;
+
+       operator std::string() const
+       {
+               return "<";
+       }
+};
+
+template<typename L, typename R>
+Lesser<L, R> operator<(Expression<L> expr, R r)
+{
+       return {expr.value, r};
+}
+
+template<typename L, typename R>
+struct Equal : public condition::Binary<L, R>
+{
+       using condition::Binary<L, R>::Binary;
+
+       operator std::string() const
+       {
+               return "=";
+       }
+};
+
+template<typename L, typename R>
+Equal<L, R> operator==(Expression<L> expr, R r)
+{
+       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
+       {
+               return ">";
+       }
+};
+
+template<typename L, typename R>
+Greater<L, R> operator>(Expression<L> expr, R r)
+{
+       return {expr.value, r};
+}
+
+template<bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+template<typename T>
+using is_condition = typename std::is_base_of<condition::Base, T>;
+
+template<typename L,
+                typename R,
+                typename = typename tsqb::enable_if_t<is_condition<L>::value
+                                                        && tsqb::is_condition<R>::value>>
+condition::And<L, R> operator&&(const L& l, const R& r)
+{
+       return {l, r};
+}
+
+template<typename L,
+                typename R,
+                typename = typename tsqb::enable_if_t<is_condition<L>::value
+                                                        && tsqb::is_condition<R>::value>>
+condition::Or<L, R> operator||(const L& l, const R& r)
+{
+       return {l, r};
+}
+
+} // namespace tsqb
diff --git a/tsqb/include/table-pack.hxx b/tsqb/include/table-pack.hxx
new file mode 100644 (file)
index 0000000..5ff7b10
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *  Copyright (c) 2017-present 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
+ */
+/*
+ * @file table-pack.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Tie diffirent types of tables
+ */
+
+#pragma once
+
+#include <vector>
+#include <set>
+#include <string>
+
+namespace tsqb {
+namespace internal {
+
+template<typename... Base>
+class TablePack {
+public:
+       template<typename TableType>
+       std::string getName(TableType&&) const noexcept { return std::string(); }
+
+       template<typename ColumnType>
+       std::string getColumnName(ColumnType&&) const noexcept { return std::string(); }
+};
+
+template<typename Front, typename... Rest>
+class TablePack<Front, Rest...> : public TablePack<Rest...> {
+public:
+       using Table = Front;
+
+       TablePack(Front&& front, Rest&& ...rest);
+
+       template<typename TableType>
+       std::string getName(TableType&& table) const noexcept;
+
+       template<typename ColumnType>
+       std::string getColumnName(ColumnType&& column) const noexcept;
+
+private:
+       using Base = TablePack<Rest...>;
+
+       Table table;
+};
+
+template<typename Front, typename... Rest>
+TablePack<Front, Rest...>::TablePack(Front&& front, Rest&& ...rest) :
+       Base(std::forward<Rest>(rest)...), table(front)
+{
+}
+
+template<typename Front, typename... Rest>
+template<typename TableType>
+std::string TablePack<Front, Rest...>::getName(TableType&& table) const noexcept
+{
+       if (this->table.compare(table))
+               return this->table.name;
+
+       return Base::template getName<TableType>(std::forward<TableType>(table));
+}
+
+template<typename Front, typename... Rest>
+template<typename ColumnType>
+std::string TablePack<Front, Rest...>::getColumnName(ColumnType&& column) const noexcept
+{
+       using DecayColumnType = typename std::decay<ColumnType>::type;
+       using DecayTableType = typename DecayColumnType::TableType;
+       if (this->table.compare(DecayTableType())) {
+               auto cname = this->table.getColumnName(column.type);
+               return this->table.name + "." + cname;
+       }
+
+       return Base::template getColumnName<ColumnType>(std::forward<ColumnType>(column));
+}
+
+} // namespace internal
+} // namespace tsqb
diff --git a/tsqb/include/table.hxx b/tsqb/include/table.hxx
new file mode 100644 (file)
index 0000000..3c210dd
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ *  Copyright (c) 2017-present 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
+ */
+/*
+ * @file table.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Represent the table of SQL
+ */
+
+#pragma once
+
+#include "column.hxx"
+#include "column-pack.hxx"
+#include "tuple-helper.hxx"
+#include "expression.hxx"
+#include "util.hxx"
+
+#include <vector>
+#include <string>
+#include <sstream>
+
+namespace tsqb {
+
+template<typename... Columns>
+class Table {
+public:
+       using Self = Table<Columns...>;
+       using ColumnPackType = internal::ColumnPack<Columns...>;
+       using TableType = typename ColumnPackType::TableType;
+
+       template<typename... ColumnTypes>
+       Self& select(ColumnTypes&&... cts);
+
+       template<typename Type>
+       Self& select(Distinct<Type> distinct);
+
+       Self& selectAll(void);
+
+       template<typename... ColumnTypes>
+       Self& update(ColumnTypes&&... cts);
+
+       template<typename... ColumnTypes>
+       Self& insert(ColumnTypes&&... cts);
+
+       template<typename... ColumnTypes>
+       Self& remove(ColumnTypes&&... cts);
+
+       template<typename Expr>
+       Self& where(Expr expr);
+
+       template<typename That>
+       bool compare(const That& that) const noexcept;
+
+       operator std::string();
+
+       template<typename ColumnType>
+       std::string getColumnName(ColumnType&& type) const noexcept;
+
+       std::string name;
+
+private:
+       explicit Table(const std::string& name, ColumnPackType&& columnPack);
+
+       template<typename ...Cs>
+       friend Table<Cs...> make_table(const std::string& name, Cs&& ...columns);
+
+       template<typename ColumnTuple>
+       Self& selectInternal(ColumnTuple&& ct, bool distinct = false);
+
+       template<typename Cs>
+       std::vector<std::string> getColumnNames(Cs&& tuple);
+
+       int size() const noexcept;
+
+       std::vector<std::string> getColumnNames(void) const noexcept;
+
+       struct GetColumnNames {
+               ColumnPackType columnPack;
+               std::vector<std::string> names;
+
+               GetColumnNames(const ColumnPackType& columnPack) : columnPack(columnPack) {}
+
+               template <typename T>
+               void operator()(T&& type)
+               {
+                       auto name = this->columnPack.getName(std::forward<T>(type));
+                       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);
+
+       ColumnPackType columnPack;
+       std::vector<std::string> cache;
+};
+
+template<typename ...Columns>
+Table<Columns...> make_table(const std::string& name, Columns&& ...cs)
+{
+       auto columnPack = internal::ColumnPack<Columns...>(std::forward<Columns>(cs)...);
+       return Table<Columns...>(name, std::move(columnPack));
+}
+
+template<typename... Columns>
+Table<Columns...>::Table(const std::string& name, ColumnPackType&& columnPack)
+       : name(name), columnPack(std::move(columnPack)) {}
+
+template<typename... Columns>
+template<typename... ColumnTypes>
+Table<Columns...>& Table<Columns...>::select(ColumnTypes&&... cts)
+{
+       auto columnTuple = std::make_tuple(std::forward<ColumnTypes>(cts)...);
+
+       return this->selectInternal(std::move(columnTuple));
+}
+
+template<typename... Columns>
+template<typename Type>
+Table<Columns...>& Table<Columns...>::select(Distinct<Type> distinct)
+{
+       return this->selectInternal(std::move(distinct.value), true);
+}
+
+template<typename... Columns>
+template<typename ColumnTuple>
+Table<Columns...>& Table<Columns...>::selectInternal(ColumnTuple&& ct, bool distinct)
+{
+       this->cache.clear();
+
+       auto columnNames = this->getColumnNames(std::move(ct));
+
+       std::stringstream ss;
+       ss << "SELECT ";
+
+       if (distinct)
+               ss << "DISTINCT ";
+
+       int i = 0;
+       for (const auto& c : columnNames) {
+               ss << c;
+
+               if (i++ < columnNames.size() - 1)
+                       ss << ", ";
+       }
+
+       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... ColumnTypes>
+Table<Columns...>& Table<Columns...>::update(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 << "UPDATE " << this->name << " ";
+       ss << "SET ";
+
+       int i = 0;
+       for (const auto& c : columnNames) {
+               ss << c << " = ?";
+
+               if (i++ < columnNames.size() - 1)
+                       ss << ", ";
+       }
+
+       cache.emplace_back(ss.str());
+
+       return *this;
+}
+
+template<typename... Columns>
+template<typename... ColumnTypes>
+Table<Columns...>& Table<Columns...>::insert(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 << "INSERT INTO " << this->name << " (";
+
+       const int columnCount = columnNames.size();
+       for (int i = 0; i < columnCount; i++) {
+               ss << columnNames[i];
+               if (i < columnCount - 1)
+                       ss << ", ";
+       }
+
+       ss << ") VALUES (";
+
+       for (int i = 0; i < columnCount; i++) {
+               ss << "?";
+               if (i < columnCount - 1)
+                       ss << ", ";
+       }
+
+       ss << ")";
+
+       cache.emplace_back(ss.str());
+
+       return *this;
+}
+
+template<typename... Columns>
+template<typename... ColumnTypes>
+Table<Columns...>& Table<Columns...>::remove(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 << "DELETE FROM " << this->name;
+
+       cache.emplace_back(ss.str());
+
+       return *this;
+}
+
+template<typename... Columns>
+template<typename Expr>
+Table<Columns...>& Table<Columns...>::where(Expr expr)
+{
+       std::stringstream ss;
+       ss << "WHERE " << this->processWhere(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 util::rtrim(ss.str());
+}
+
+template<typename... Columns>
+template<typename That>
+bool Table<Columns...>::compare(const That& that) const noexcept
+{
+       using This = TableType;
+       return type::compare(This(), that);
+}
+
+template<typename... Columns>
+template<typename Cs>
+std::vector<std::string> Table<Columns...>::getColumnNames(Cs&& tuple)
+{
+       GetColumnNames closure(this->columnPack);
+       tuple_helper::for_each(std::forward<Cs>(tuple), closure);
+
+       return closure.names;
+}
+
+template<typename... Columns>
+int Table<Columns...>::size() const noexcept
+{
+       return this->columnPack.size();
+}
+
+template<typename... Columns>
+std::vector<std::string> Table<Columns...>::getColumnNames(void) const noexcept
+{
+       return this->columnPack.getNames();
+}
+
+template<typename... Columns>
+template<typename ColumnType>
+std::string Table<Columns...>::getColumnName(ColumnType&& type) const noexcept
+{
+       return this->columnPack.getName(std::forward<ColumnType>(type));
+}
+
+template<typename... Columns>
+template<typename L, typename R>
+std::string Table<Columns...>::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... Columns>
+template<typename L, typename R>
+std::string Table<Columns...>::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... Columns>
+template<typename Expr>
+std::string Table<Columns...>::processWhere(Expr expr)
+{
+       std::stringstream ss;
+       ss << this->columnPack.getName(expr.l.type);
+       ss << " " << std::string(expr) << " ?";
+
+       return ss.str();
+}
+
+} // namespace tsqb
diff --git a/tsqb/include/tuple-helper.hxx b/tsqb/include/tuple-helper.hxx
new file mode 100644 (file)
index 0000000..495946e
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (c) 2017-present 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
+ */
+/*
+ * @file tuple-helper.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Iterator method for tuple
+ */
+
+#pragma once
+
+#include <tuple>
+
+namespace tsqb {
+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 tsqb
diff --git a/tsqb/include/type.hxx b/tsqb/include/type.hxx
new file mode 100644 (file)
index 0000000..939b3da
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *  Copyright (c) 2017-present 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
+ */
+/*
+ * @file type.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Make type safety with compile time checking
+ */
+
+#pragma once
+
+#include <type_traits>
+
+namespace tsqb {
+namespace type {
+
+template<typename L, typename R>
+bool cast_compare(L l, R r)
+{
+       return l == reinterpret_cast<L>(r);
+}
+
+template<typename L, typename R>
+bool compare(L l, R r)
+{
+       return std::is_same<L, R>::value;
+}
+
+template<typename L, typename R>
+void assert_compare(L l, R r)
+{
+       static_assert(std::is_same<L, R>::value, "Type is unsafe.");
+}
+
+} // namespace type
+} // namespace tsqb
diff --git a/tsqb/include/util.hxx b/tsqb/include/util.hxx
new file mode 100644 (file)
index 0000000..e65583d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *  Copyright (c) 2017-present 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
+ */
+/*
+ * @file util.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ */
+
+#pragma once
+
+#include <string>
+#include <algorithm>
+#include <cctype>
+
+namespace tsqb {
+namespace util {
+
+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 s;
+}
+
+} // namespace util
+} // namespace tsqb
diff --git a/tsqb/tsqb-tests.cpp b/tsqb/tsqb-tests.cpp
new file mode 100644 (file)
index 0000000..aac1e7d
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ *  Copyright (c) 2017-present 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
+ */
+/*
+ * @file tsqb-tests.cpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Testcases of tsqb
+ */
+
+#include "tsqb.hxx"
+
+#include <gtest/gtest.h>
+
+using namespace tsqb;
+
+struct Admin {
+       int id;
+       std::string pkg;
+       int uid;
+       std::string key;
+       int removable;
+};
+
+struct ManagedPolicy {
+       int id;
+       int aid;
+       int pid;
+       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("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 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);
+
+class TsqbTests : public testing::Test {};
+
+TEST_F(TsqbTests, SELECT)
+{
+       std::string select1 = admin.select(&Admin::id, &Admin::pkg, &Admin::uid, &Admin::key);
+       std::string select2 = admin.select(&Admin::id, &Admin::uid, &Admin::key);
+
+       EXPECT_EQ(select1, "SELECT id, pkg, uid, key FROM admin");
+       EXPECT_EQ(select2, "SELECT id, uid, key FROM admin");
+}
+
+TEST_F(TsqbTests, SELECT_ALL)
+{
+       std::string select = admin.selectAll();
+
+       EXPECT_EQ(select, "SELECT * FROM admin");
+}
+
+TEST_F(TsqbTests, SELECT_WHERE)
+{
+       std::string select1 = admin.select(&Admin::uid, &Admin::key)
+                                                          .where(expr(&Admin::id) > 3);
+       std::string select2 = admin.selectAll().where(expr(&Admin::uid) > 3);
+       std::string select3 = admin.selectAll().where(expr(&Admin::uid) > 3 &&
+                                                                                                 expr(&Admin::pkg) == "dpm");
+       std::string select4 = admin.selectAll().where(expr(&Admin::uid) > 3 ||
+                                                                                                 expr(&Admin::pkg) == "dpm");
+
+       EXPECT_EQ(select1, "SELECT uid, key FROM admin WHERE id > ?");
+       EXPECT_EQ(select2, "SELECT * FROM admin WHERE uid > ?");
+       EXPECT_EQ(select3, "SELECT * FROM admin WHERE uid > ? AND pkg = ?");
+       EXPECT_EQ(select4, "SELECT * FROM admin WHERE uid > ? OR pkg = ?");
+}
+
+TEST_F(TsqbTests, SELECT_DISTINCT)
+{
+       std::string select = admin.select(distinct(&Admin::uid, &Admin::key))
+                                                          .where(expr(&Admin::id) > 3);
+
+       EXPECT_EQ(select, "SELECT DISTINCT uid, key FROM admin WHERE id > ?");
+}
+
+TEST_F(TsqbTests, UPDATE)
+{
+       int uid = 0, id = 1;
+       std::string update1 = admin.update(&Admin::id, &Admin::pkg, &Admin::uid, &Admin::key);
+       std::string update2 = admin.update(&Admin::key).where(expr(&Admin::uid) == uid &&
+                                                                                                                 expr(&Admin::id) == id);
+       std::string update3 = admin.update(&Admin::key, &Admin::pkg)
+                                                          .where(expr(&Admin::uid) == 0 && expr(&Admin::id) == 1);
+
+       EXPECT_EQ(update1, "UPDATE admin SET id = ?, pkg = ?, uid = ?, key = ?");
+       EXPECT_EQ(update2, "UPDATE admin SET key = ? WHERE uid = ? AND id = ?");
+       EXPECT_EQ(update3, "UPDATE admin SET key = ?, pkg = ? WHERE uid = ? AND id = ?");
+}
+
+TEST_F(TsqbTests, DELETE)
+{
+       std::string delete1 = admin.remove();
+       std::string delete2 = admin.remove().where(expr(&Admin::pkg) == "dpm" &&
+                                                                                          expr(&Admin::uid) == 3);
+
+       EXPECT_EQ(delete1, "DELETE FROM admin");
+       EXPECT_EQ(delete2, "DELETE FROM admin WHERE pkg = ? AND uid = ?");
+}
+
+TEST_F(TsqbTests, INSERT)
+{
+       std::string insert1 = admin.insert(&Admin::id, &Admin::pkg, &Admin::uid, &Admin::key);
+       std::string insert2 = admin.insert(&Admin::id, &Admin::pkg, &Admin::key);
+
+       EXPECT_EQ(insert1, "INSERT INTO admin (id, pkg, uid, key) VALUES (?, ?, ?, ?)");
+       EXPECT_EQ(insert2, "INSERT INTO admin (id, pkg, key) VALUES (?, ?, ?)");
+}
+
+TEST_F(TsqbTests, TYPE_SAFE)
+{
+/*
+ * Below cause complie error since expression types are dismatch.
+
+       std::string type_unsafe1 = admin.selectAll().where(expr(&Admin::uid) > "dpm");
+       std::string type_unsafe2 = admin.selectAll().where(expr(&Admin::uid) == "dpm");
+       std::string type_unsafe3 = admin.selectAll().where(expr(&Admin::pkg) == 3);
+       int pkg = 3;
+       std::string type_unsafe4 = admin.selectAll().where(expr(&Admin::pkg) < pkg);
+       std::string type_unsafe5 = admin.remove().where(expr(&Admin::pkg) == "dpm" &&
+                                                                                                       expr(&Admin::uid) == "dpm");
+*/
+}
+
+TEST_F(TsqbTests, 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);
+
+       EXPECT_EQ(multiSelect1, "SELECT admin.uid, admin.key, managed_policy.id, "
+                                                       "managed_policy.value FROM admin, managed_policy");
+       EXPECT_EQ(multiSelect2, "SELECT admin.uid, admin.key, managed_policy.id, "
+                                                       "managed_policy.value FROM admin, managed_policy "
+                                                       "WHERE admin.uid > ? AND managed_policy.id = ?");
+}
+
+TEST_F(TsqbTests, 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);
+
+       EXPECT_EQ(join1, "SELECT admin.uid, admin.key FROM admin "
+                                        "LEFT OUTER JOIN policy_definition");
+       EXPECT_EQ(join2, "SELECT admin.uid, admin.key FROM admin "
+                                        "CROSS JOIN managed_policy");
+       EXPECT_EQ(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 = ?");
+}
+
+int main(int argc, char* argv[]) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/tsqb/tsqb.hxx b/tsqb/tsqb.hxx
new file mode 100644 (file)
index 0000000..30ee010
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *  Copyright (c) 2017-present 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
+ */
+/*
+ * @file tsqb.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief TSQB is type-safe query builder
+ */
+
+#pragma once
+
+#include "database.hxx"
+#include "table.hxx"
+#include "column.hxx"
+#include "expression.hxx"
+#include "condition.hxx"
+#include "util.hxx"