ADD_SUBDIRECTORY(osquery)
ADD_SUBDIRECTORY(sqlite3)
+ADD_SUBDIRECTORY(tsqb) # Type-safe query builder
--- /dev/null
+# 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)
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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"