diff --git a/include/matador/sql/condition.hpp b/include/matador/sql/condition.hpp index b605d33..f82fa82 100644 --- a/include/matador/sql/condition.hpp +++ b/include/matador/sql/condition.hpp @@ -4,11 +4,14 @@ #include "matador/sql/basic_condition.hpp" #include "matador/sql/dialect.hpp" -#include #include namespace matador::sql { +struct placeholder {}; + +const placeholder _; + /** * @class condition * @brief Represents a sql query condition @@ -29,6 +32,17 @@ class query; template class condition; +template<> +class condition::type> : public basic_column_condition +{ +public: + condition(const column &fld, basic_condition::operand_t op, const placeholder &val); + + placeholder value; + + std::string evaluate(dialect &d) const override; +}; + template class condition::value && @@ -46,13 +60,7 @@ public: std::string evaluate(dialect &d) const override { d.add_host_var(field_.name()); - std::stringstream str; - if (d.compile_type() == dialect::compile_type_t::DIRECT) { - str << d.prepare_identifier(field_.name()) << " " << operand << " " << value; - } else { - str << d.prepare_identifier(field_.name()) << " " << operand << " " << d.next_placeholder(); - } - return str.str(); + return d.prepare_identifier(field_.name()) + " " + operand + " " + std::to_string(value); } }; @@ -72,13 +80,7 @@ public: std::string evaluate(dialect &d) const override { d.add_host_var(field_.name()); - std::stringstream str; - if (d.compile_type() == dialect::compile_type_t::DIRECT) { - str << d.prepare_identifier(field_.name()) << " " << operand << " '" << value << "'"; - } else { - str << d.prepare_identifier(field_.name()) << " " << operand << " " << d.next_placeholder(); - } - return str.str(); + return d.prepare_identifier(field_.name()) + " " + operand + " '" + value + "'"; } }; @@ -98,9 +100,7 @@ public: std::string evaluate(dialect &d) const override { - std::stringstream str; - str << value << " " << operand << " " << d.prepare_identifier(field_.name()); - return str.str(); + return std::to_string(value) + " " + operand + " " + d.prepare_identifier(field_.name()); } }; @@ -119,9 +119,7 @@ public: std::string evaluate(dialect &d) const override { - std::stringstream str; - str << "'" << value << "' " << operand << " " << d.prepare_identifier(field_.name()); - return str.str(); + return "'" + std::to_string(value) + "' " + operand + " " + d.prepare_identifier(field_.name()); } }; @@ -138,74 +136,62 @@ public: * @endcode */ template < class V > -class condition> : public basic_in_condition -{ +class condition> : public basic_in_condition { public: -/** - * @brief Creates an IN condition - * - * Creates an IN condition for the given column and - * the given list of arguments. - * - * @param col Column for the IN condition - * @param args List of arguments - */ -condition(const column &col, const std::initializer_list &args) - : basic_in_condition(col) - , args_(args) -{} + /** + * @brief Creates an IN condition + * + * Creates an IN condition for the given column and + * the given list of arguments. + * + * @param col Column for the IN condition + * @param args List of arguments + */ + condition(const column &col, const std::initializer_list &args) + : basic_in_condition(col), args_(args) {} -/** - * @brief Evaluates the condition - * - * Evaluates the condition to a part of the - * query string based on the given compile type - * - * @param d The d used to evaluate - * @return A condition IN part of the query - */ -std::string evaluate(dialect &d) const override -{ - auto count = size(); - for (size_t i = 0; i < count; ++i) { - d.add_host_var(field_.name()); - } - std::stringstream str; - str << d.prepare_identifier(field_.name()) << " IN ("; - if (args_.size() > 1) { - auto first = args_.begin(); - auto last = args_.end() - 1; - while (first != last) { - if (d.compile_type() == dialect::compile_type_t::DIRECT) { - str << *first++ << ","; - } else { - ++first; - str << d.next_placeholder() << ","; + /** + * @brief Evaluates the condition + * + * Evaluates the condition to a part of the + * query string based on the given compile type + * + * @param d The d used to evaluate + * @return A condition IN part of the query + */ + std::string evaluate(dialect &d) const override { + auto count = size(); + for (size_t i = 0; i < count; ++i) { + d.add_host_var(field_.name()); + } + + + std::string result = d.prepare_identifier(field_.name()) + " IN ("; + if (args_.size() < 2) { + for (const auto &val : args_) { + result.append(std::to_string(val)); + } + } else { + auto it = args_.begin(); + result.append(std::to_string(*it++)); + for(; it != args_.end(); ++it) { + result.append(", " + std::to_string(*it)); } } + result += ")"; + return result; } - if (!args_.empty()) { - if (d.compile_type() == dialect::compile_type_t::DIRECT) { - str << args_.back(); - } else { - str << d.next_placeholder(); - } - } - str << ")"; - return str.str(); -} -/** - * @brief Returns the number of arguments in the list - * @return The number of arguments in the list - */ -size_t size() const override -{ -return args_.size(); -} + /** + * @brief Returns the number of arguments in the list + * @return The number of arguments in the list + */ + [[nodiscard]] size_t size() const override { + return args_.size(); + } private: -std::vector args_; + std::vector args_; }; /** @@ -244,23 +230,12 @@ public: * @param d The d used to evaluate * @return A condition IN part of the query */ - std::string evaluate(dialect &d) const override - { - std::string result(d.prepare_identifier(field_.name()) + " " + operand + " ("); -// result += d.continue_build(const_cast(query_), d.compile_type()); - result += (")"); - return result; - } + std::string evaluate(dialect &d) const override; private: query &query_; }; -condition::condition(column col, basic_condition::operand_t op, query &q) - : basic_column_condition(std::move(col), op) - , query_(q) -{} - /** * @brief Between condition. * @@ -270,43 +245,35 @@ condition::condition(column col, basic_condition::operand_t op, q * @tparam T The type of the boundary values */ template < class T > -class condition> : public basic_condition -{ +class condition> : public basic_condition { public: -/** - * @brief Create a new between condition - * - * @param col The column for the range check - * @param range The boundary values defining the range - */ -condition(column col, const std::pair &range) - : field_(std::move(col)), range_(range) { } + /** + * @brief Create a new between condition + * + * @param col The column for the range check + * @param range The boundary values defining the range + */ + condition(column col, const std::pair &range) + : field_(std::move(col)), range_(range) {} -/** - * @brief Evaluates the condition - * - * Evaluates the condition to a between part - * based on the given compile type - * - * @param d The d used to evaluate - * @return A condition BETWEEN part of the query - */ -std::string evaluate(dialect &d) const override -{ -d.add_host_var(field_.name()); -d.add_host_var(field_.name()); -std::stringstream str; -if (d.compile_type() == dialect::compile_type_t::DIRECT) { -str << d.prepare_identifier(field_.name()) << " BETWEEN " << range_.first << " AND " << range_.second; -} else { -str << d.prepare_identifier(field_.name()) << " BETWEEN " << d.next_placeholder() << " AND " << d.next_placeholder(); -} -return str.str(); -} + /** + * @brief Evaluates the condition + * + * Evaluates the condition to a between part + * based on the given compile type + * + * @param d The d used to evaluate + * @return A condition BETWEEN part of the query + */ + std::string evaluate(dialect &d) const override { + d.add_host_var(field_.name()); + d.add_host_var(field_.name()); + return d.prepare_identifier(field_.name()) + " BETWEEN " + std::to_string(range_.first) + " AND " + std::to_string(range_.second); + } private: -column field_; -std::pair range_; + column field_; + std::pair range_; }; /** @@ -331,7 +298,6 @@ public: * @param r right hand operator of the condition * @param op The operand (AND or OR) */ -// condition(const condition &l, const condition &r, basic_condition::operand_t op) condition(condition &&l, condition &&r, basic_condition::operand_t op) : left(std::move(l)), right(std::move(r)), operand(op) { } @@ -343,16 +309,14 @@ public: */ std::string evaluate(dialect &d) const override { - std::stringstream str; // ensure the numbering order for host vars auto cl = left.evaluate(d); auto cr = right.evaluate(d); if (operand == basic_condition::operand_t::AND) { - str << "(" << cl << " " << basic_condition::operands[operand] << " " << cr << ")"; + return "(" + cl + " " + basic_condition::operands[operand] + " " + cr + ")"; } else { - str << cl << " " << basic_condition::operands[operand] << " " << cr; + return cl + " " + basic_condition::operands[operand] + " " + cr; } - return str.str(); } private: @@ -388,9 +352,7 @@ public: */ std::string evaluate(dialect &d) const override { - std::stringstream str; - str << operand << " (" << cond.evaluate(d) << ")"; - return str.str(); + return operand + " (" + cond.evaluate(d) + ")"; } private: @@ -466,7 +428,6 @@ condition> between(const column &col, T low, T high) * @return The like condition object */ condition like(const column &col, const std::string &val); -//OOS_SQL_API condition like(const matador::column &col, const std::string &val); /** * @brief Condition equality operator for a column and a value diff --git a/include/matador/sql/connection.hpp b/include/matador/sql/connection.hpp new file mode 100644 index 0000000..110d733 --- /dev/null +++ b/include/matador/sql/connection.hpp @@ -0,0 +1,26 @@ +#ifndef QUERY_CONNECTION_HPP +#define QUERY_CONNECTION_HPP + +#include "matador/sql/connection_intermediates.hpp" +#include "matador/sql/dialect.hpp" +#include "matador/sql/query_builder.hpp" + +#include + +namespace matador::sql { + +class connection +{ +public: + connection(); + query_select_intermediate select(std::initializer_list column_names); + + result execute(const std::string &sql); + +private: + dialect dialect_; + query_builder query_; +}; + +} +#endif //QUERY_CONNECTION_HPP diff --git a/include/matador/sql/connection_intermediates.hpp b/include/matador/sql/connection_intermediates.hpp new file mode 100644 index 0000000..57f22e4 --- /dev/null +++ b/include/matador/sql/connection_intermediates.hpp @@ -0,0 +1,73 @@ +#ifndef QUERY_CONNECTION_INTERMEDIATES_HPP +#define QUERY_CONNECTION_INTERMEDIATES_HPP + +#include "matador/sql/result.hpp" + +#include + +namespace matador::sql { + +class basic_condition; +class connection; +class query_builder; + +struct query_intermediate +{ + query_intermediate(connection &db, query_builder &query); + + connection &db; + query_builder &query; +}; + +struct query_finish : query_intermediate +{ + using query_intermediate::query_intermediate; + + result fetch_all(); + result fetch_one(); + result fetch_value(); +}; + +struct query_order_direction_intermediate : query_finish +{ + using query_finish::query_finish; +}; + +struct query_group_by_intermediate : query_finish +{ + using query_finish::query_finish; + + +}; +struct query_order_by_intermediate : query_intermediate +{ + using query_intermediate::query_intermediate; + query_order_direction_intermediate asc(); + query_order_direction_intermediate desc(); + +}; + +struct query_where_intermediate : query_finish +{ + using query_finish::query_finish; +}; + +struct query_from_intermediate : query_finish +{ + using query_finish::query_finish; + + query_where_intermediate where(const basic_condition &cond); + query_group_by_intermediate group_by(const std::string &name); + query_order_by_intermediate order_by(const std::string &name); +}; + +struct query_select_intermediate : query_intermediate +{ + using query_intermediate::query_intermediate; + + query_from_intermediate from(const std::string &table, const std::string &as = ""); + +}; + +} +#endif //QUERY_CONNECTION_INTERMEDIATES_HPP diff --git a/include/matador/sql/dialect.hpp b/include/matador/sql/dialect.hpp index 5000702..c3de605 100644 --- a/include/matador/sql/dialect.hpp +++ b/include/matador/sql/dialect.hpp @@ -23,7 +23,6 @@ public: SELECT, TABLE, VALUES, - VALUE, COLUMNS, COLUMN, FROM, @@ -36,11 +35,10 @@ public: GROUP_BY, ASC, DESC, - TOP, + LIMIT, AS, OFFSET, DISTINCT, - CONDITION, SET, NOT_NULL, PRIMARY_KEY, @@ -53,14 +51,6 @@ public: NONE }; - /** - * @brief Enum representing the compile type - */ - enum class compile_type_t : uint8_t { - PREPARED, /**< Compile type for prepared statements */ - DIRECT /**< Compile type for direct execute statements */ - }; - /** * Holding enums concerning escaping identifiers */ @@ -127,7 +117,6 @@ public: */ virtual std::string next_placeholder() const; - compile_type_t compile_type() const; void add_host_var(const std::string &host_var); void add_column(const std::string &column); @@ -137,41 +126,41 @@ public: private: using token_to_string_map = std::unordered_map; - compile_type_t compile_type_ = compile_type_t::DIRECT; std::vector host_vars_; std::vector columns_; token_to_string_map tokens_ { - {token_t::CREATE, "CREATE"}, - {token_t::DROP, "DROP"}, - {token_t::REMOVE, "DELETE"}, - {token_t::INSERT, "INSERT"}, - {token_t::TABLE, "TABLE"}, - {token_t::INTO, "INTO"}, - {token_t::VALUES, "VALUES"}, - {token_t::UPDATE, "UPDATE"}, - {token_t::SELECT, "SELECT"}, - {token_t::COLUMNS, "COLUMNS"}, - {token_t::COLUMN, "COLUMN"}, - {token_t::FROM, "FROM"}, - {token_t::WHERE, "WHERE"}, - {token_t::AND, "AND"}, - {token_t::OR, "OR"}, - {token_t::LIKE, "LIKE"}, - {token_t::ORDER_BY, "ORDER BY"}, - {token_t::GROUP_BY, "GROUP BY"}, - {token_t::ASC, "ASC"}, - {token_t::DESC, "DESC"}, - {token_t::TOP, "LIMIT"}, - {token_t::AS, "AS"}, - {token_t::OFFSET, "OFFSET"}, - {token_t::DISTINCT, "DISTINCT"}, - {token_t::SET, "SET"}, - {token_t::NOT_NULL, "NOT NULL"}, + {token_t::CREATE, "CREATE"}, + {token_t::DROP, "DROP"}, + {token_t::REMOVE, "DELETE"}, + {token_t::INSERT, "INSERT"}, + {token_t::TABLE, "TABLE"}, + {token_t::INTO, "INTO"}, + {token_t::VALUES, "VALUES"}, + {token_t::UPDATE, "UPDATE"}, + {token_t::SELECT, "SELECT"}, + {token_t::COLUMNS, "COLUMNS"}, + {token_t::COLUMN, "COLUMN"}, + {token_t::FROM, "FROM"}, + {token_t::WHERE, "WHERE"}, + {token_t::AND, "AND"}, + {token_t::OR, "OR"}, + {token_t::LIKE, "LIKE"}, + {token_t::ORDER_BY, "ORDER BY"}, + {token_t::GROUP_BY, "GROUP BY"}, + {token_t::ASC, "ASC"}, + {token_t::DESC, "DESC"}, + {token_t::OFFSET, "OFFSET"}, + {token_t::LIMIT, "LIMIT"}, + {token_t::AS, "AS"}, + {token_t::OFFSET, "OFFSET"}, + {token_t::DISTINCT, "DISTINCT"}, + {token_t::SET, "SET"}, + {token_t::NOT_NULL, "NOT NULL"}, {token_t::PRIMARY_KEY, "PRIMARY KEY"}, - {token_t::BEGIN, "BEGIN TRANSACTION"}, - {token_t::COMMIT, "COMMIT TRANSACTION"}, - {token_t::ROLLBACK, "ROLLBACK TRANSACTION"}, + {token_t::BEGIN, "BEGIN TRANSACTION"}, + {token_t::COMMIT, "COMMIT TRANSACTION"}, + {token_t::ROLLBACK, "ROLLBACK TRANSACTION"}, {token_t::START_QUOTE, "\""}, {token_t::END_QUOTE, "\""}, {token_t::STRING_QUOTE, "'"}, diff --git a/include/matador/sql/query.puml b/include/matador/sql/query.puml new file mode 100644 index 0000000..b95ce47 --- /dev/null +++ b/include/matador/sql/query.puml @@ -0,0 +1,89 @@ +@startuml + +scale 800 width +state Query + +state Create +Create : Create database table +state Drop +Drop : Drop database table +state Select +Select: Query rows +Select: Add columns +state Insert +Insert: Insert rows +state Update +Update: Update rows +Update: Set table name +state Delete +Delete: Delete items from table + +state Table +Table: Set table name +state Into +Into: Set table name +Into: Add columns +state From +From: Set table name + +state Join +Join: Set table +Join: Set join type (inner left, right, outer) +state Where +Where: Add where clause +state Set +Set: Set column value pairs +state Values +Values: Set values + +state On: +On: Set Expression + +state GroupBy +GroupBy: Add column names +state OrderBy +OrderBy: Add expression +state Limit +Limit: Add number of max result elements + +[*] --> Query +Query --> Create +Query --> Drop +Query --> Select +Query --> Insert +Query --> Update +Query --> Delete + +Create --> Table +Drop --> Table +Select --> From +Insert --> Into +Delete --> From + +Into --> Values +Update --> Set +Set -> Where +From --> Where +From --> OrderBy +From --> GroupBy +From --> Join +Join --> On +On --> Where + +Where --> GroupBy +Where --> OrderBy +Where --> Limit + +GroupBy --> OrderBy +OrderBy --> Limit + +Table --> [*] +Values ---> [*] +Where --> [*] +Set --> [*] +From --> [*] +Limit --> [*] +OrderBy --> [*] +GroupBy --> [*] + +@enduml diff --git a/include/matador/sql/query_builder.hpp b/include/matador/sql/query_builder.hpp index a9abc97..ea40b98 100644 --- a/include/matador/sql/query_builder.hpp +++ b/include/matador/sql/query_builder.hpp @@ -17,7 +17,7 @@ class dialect; namespace detail { struct any_type_to_string_visitor { - any_type_to_string_visitor(const dialect &d) : d(d) {} + explicit any_type_to_string_visitor(const dialect &d) : d(d) {} void operator()(char &x) { to_string(x); } void operator()(short &x) { to_string(x); } @@ -49,6 +49,10 @@ struct any_type_to_string_visitor } +enum class join_type_t { + INNER, OUTER, LEFT, RIGHT +}; + class query_builder { private: @@ -70,6 +74,7 @@ private: QUERY_ORDER_BY, QUERY_ORDER_DIRECTION, QUERY_GROUP_BY, + QUERY_OFFSET, QUERY_LIMIT, QUERY_FINISH }; @@ -87,20 +92,28 @@ private: public: explicit query_builder(const dialect &d); - query_builder create(); - query_builder drop(); - query_builder select(std::initializer_list column_names); - query_builder insert(); - query_builder update(const std::string &table); - query_builder remove(); + query_builder& create(); + query_builder& drop(); + query_builder& select(std::initializer_list column_names); + query_builder& insert(); + query_builder& update(const std::string &table); + query_builder& remove(); query_builder& table(const std::string &table, std::initializer_list columns); query_builder& table(const std::string &table); query_builder& into(const std::string &table, std::initializer_list column_names); query_builder& values(std::initializer_list values); query_builder& from(const std::string &table, const std::string &as = ""); + query_builder& join(const std::string &table, join_type_t); + query_builder& on(const std::string &column, const std::string &join_column); query_builder& set(std::initializer_list key_values); query_builder& where(const basic_condition &cond); + query_builder& order_by(const std::string &column); + query_builder& group_by(const std::string &column); + query_builder& asc(); + query_builder& desc(); + query_builder& offset(size_t count); + query_builder& limit(size_t count); std::string compile(); diff --git a/include/matador/sql/result.hpp b/include/matador/sql/result.hpp new file mode 100644 index 0000000..241e78c --- /dev/null +++ b/include/matador/sql/result.hpp @@ -0,0 +1,14 @@ +#ifndef QUERY_RESULT_HPP +#define QUERY_RESULT_HPP + +#include + +namespace matador::sql { + +struct result +{ + std::string sql; +}; + +} +#endif //QUERY_RESULT_HPP diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 57e4162..5eac1bf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,7 +3,9 @@ set(SQL_SOURCES sql/query_builder.cpp sql/column.cpp sql/key_value_pair.cpp - sql/basic_condition.cpp) + sql/basic_condition.cpp + sql/connection.cpp + sql/connection_intermediates.cpp) set(SQL_HEADER ../include/matador/sql/dialect.hpp @@ -12,7 +14,10 @@ set(SQL_HEADER ../include/matador/sql/types.hpp ../include/matador/sql/key_value_pair.hpp ../include/matador/sql/basic_condition.hpp - ../include/matador/sql/condition.hpp) + ../include/matador/sql/condition.hpp + ../include/matador/sql/connection.hpp + ../include/matador/sql/connection_intermediates.hpp + ../include/matador/sql/result.hpp) set(UTILS_HEADER ../include/matador/utils/field_attributes.hpp @@ -21,7 +26,8 @@ set(UTILS_HEADER set(UTILS_SOURCES utils/field_attributes.cpp - utils/string.cpp) + utils/string.cpp + sql/condition.cpp) add_library(matador STATIC ${SQL_SOURCES} ${SQL_HEADER} ${UTILS_SOURCES} ${UTILS_HEADER}) set_target_properties(matador PROPERTIES LINKER_LANGUAGE CXX) diff --git a/src/sql/condition.cpp b/src/sql/condition.cpp new file mode 100644 index 0000000..125e508 --- /dev/null +++ b/src/sql/condition.cpp @@ -0,0 +1,26 @@ +#include "matador/sql/condition.hpp" + +namespace matador::sql { + +condition::type>::condition(const column &fld, basic_condition::operand_t op, const placeholder &val) +: basic_column_condition(fld, op) +, value(val) +{ } + +std::string condition::type>::evaluate(dialect &d) const { + d.add_host_var(field_.name()); + return d.prepare_identifier(field_.name()) + " " + operand + " " + d.next_placeholder(); +} + +condition::condition(column col, basic_condition::operand_t op, query &q) +: basic_column_condition(std::move(col), op) +, query_(q) +{} + +std::string condition::evaluate(dialect &d) const { + std::string result(d.prepare_identifier(field_.name()) + " " + operand + " ("); + result += (")"); + return result; +} + +} \ No newline at end of file diff --git a/src/sql/connection.cpp b/src/sql/connection.cpp new file mode 100644 index 0000000..4ad4990 --- /dev/null +++ b/src/sql/connection.cpp @@ -0,0 +1,13 @@ +#include "matador/sql/connection.hpp" + +namespace matador::sql { + + +connection::connection() +: query_(dialect_) {} + +result connection::execute(const std::string &sql) { + return {sql}; +} + +} diff --git a/src/sql/connection_intermediates.cpp b/src/sql/connection_intermediates.cpp new file mode 100644 index 0000000..cca5ea1 --- /dev/null +++ b/src/sql/connection_intermediates.cpp @@ -0,0 +1,60 @@ +#include "matador/sql/connection_intermediates.hpp" +#include "matador/sql/connection.hpp" +#include "matador/sql/query_builder.hpp" + +namespace matador::sql { + +result query_finish::fetch_all() +{ + return db.execute(query.compile()); +} + +result query_finish::fetch_one() +{ + return db.execute(query.compile()); +} + +result query_finish::fetch_value() +{ + return db.execute(query.compile()); +} + +query_intermediate::query_intermediate(connection &db, query_builder &query) +: db(db), query(query) {} + +query_order_direction_intermediate query_order_by_intermediate::asc() +{ + return {db, query.asc()}; +} + +query_order_direction_intermediate query_order_by_intermediate::desc() +{ + return {db, query.desc()}; +} + +query_group_by_intermediate query_from_intermediate::group_by(const std::string &name) +{ + return {db, query.group_by(name)}; +} + +query_order_by_intermediate query_from_intermediate::order_by(const std::string &name) +{ + return {db, query.order_by(name)}; +} + +query_where_intermediate query_from_intermediate::where(const basic_condition &cond) +{ + return query_where_intermediate{db, query.where(cond)}; +} + +query_from_intermediate query_select_intermediate::from(const std::string &table, const std::string &as) +{ + return {db, query.from(table, as)}; +} + +query_select_intermediate connection::select(std::initializer_list column_names) +{ + return query_select_intermediate{*this, query_.select(column_names)}; +} + +} \ No newline at end of file diff --git a/src/sql/dialect.cpp b/src/sql/dialect.cpp index 0164ae3..ae7f194 100644 --- a/src/sql/dialect.cpp +++ b/src/sql/dialect.cpp @@ -62,11 +62,6 @@ std::string dialect::next_placeholder() const return "?"; } -dialect::compile_type_t dialect::compile_type() const -{ - return compile_type_; -} - void dialect::add_host_var(const std::string &host_var) { host_vars_.emplace_back(host_var); diff --git a/src/sql/query_builder.cpp b/src/sql/query_builder.cpp index d440570..42db3e1 100644 --- a/src/sql/query_builder.cpp +++ b/src/sql/query_builder.cpp @@ -32,13 +32,14 @@ query_builder::query_state_transition_map query_builder::transitions_{ {state_t::QUERY_DELETE, {state_t::QUERY_FROM}}, {state_t::QUERY_TABLE_CREATE, {state_t::QUERY_FINISH}}, {state_t::QUERY_TABLE_DROP, {state_t::QUERY_FINISH}}, - {state_t::QUERY_FROM, {state_t::QUERY_WHERE, state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_FINISH}}, + {state_t::QUERY_FROM, {state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_WHERE, state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_FINISH}}, {state_t::QUERY_SET, {state_t::QUERY_WHERE, state_t::QUERY_FINISH}}, {state_t::QUERY_INTO, {state_t::QUERY_VALUES}}, - {state_t::QUERY_WHERE, {state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_LIMIT, state_t::QUERY_FINISH}}, + {state_t::QUERY_WHERE, {state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_FINISH}}, {state_t::QUERY_ORDER_BY, {state_t::QUERY_ORDER_DIRECTION}}, - {state_t::QUERY_ORDER_DIRECTION, {state_t::QUERY_LIMIT, state_t::QUERY_FINISH}}, + {state_t::QUERY_ORDER_DIRECTION, {state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_FINISH}}, {state_t::QUERY_GROUP_BY, {state_t::QUERY_FINISH}}, + {state_t::QUERY_OFFSET, {state_t::QUERY_LIMIT}}, {state_t::QUERY_LIMIT, {state_t::QUERY_FINISH}}, {state_t::QUERY_VALUES, {state_t::QUERY_FINISH}}, {state_t::QUERY_FINISH, {}}, @@ -60,6 +61,7 @@ query_builder::query_state_to_string_map query_builder::state_strings_ { { state_t::QUERY_WHERE, "where" }, { state_t::QUERY_ORDER_BY, "order_by" }, { state_t::QUERY_GROUP_BY, "group_by" }, + { state_t::QUERY_OFFSET, "offset" }, { state_t::QUERY_LIMIT, "limit" }, { state_t::QUERY_FINISH, "finish" }, }; @@ -78,7 +80,7 @@ query_builder::query_builder(const dialect &d) : dialect_(d) , value_to_string_(d) {} -query_builder query_builder::create() { +query_builder& query_builder::create() { initialize(command_t::CREATE, state_t::QUERY_CREATE); query_parts_.emplace_back(dialect_.token_at(dialect::token_t::CREATE)); @@ -86,7 +88,7 @@ query_builder query_builder::create() { return *this; } -query_builder query_builder::drop() { +query_builder& query_builder::drop() { initialize(command_t::DROP, state_t::QUERY_DROP); query_parts_.emplace_back(dialect_.token_at(dialect::token_t::DROP)); @@ -94,7 +96,7 @@ query_builder query_builder::drop() { return *this; } -query_builder query_builder::select(std::initializer_list column_names) { +query_builder& query_builder::select(std::initializer_list column_names) { initialize(command_t::SELECT, state_t::QUERY_SELECT); query_parts_.emplace_back(dialect_.token_at(dialect::token_t::SELECT) + " "); @@ -107,7 +109,7 @@ query_builder query_builder::select(std::initializer_list column_na } else { auto it = column_names.begin(); result.append(dialect_.prepare_identifier(*it++)); - for (it; it != column_names.end(); ++it) { + for (; it != column_names.end(); ++it) { result.append(", "); result.append(dialect_.prepare_identifier(*it)); } @@ -117,7 +119,7 @@ query_builder query_builder::select(std::initializer_list column_na return *this; } -query_builder query_builder::insert() { +query_builder& query_builder::insert() { initialize(command_t::INSERT, state_t::QUERY_INSERT); query_parts_.emplace_back(dialect_.token_at(dialect::token_t::INSERT)); @@ -125,7 +127,7 @@ query_builder query_builder::insert() { return *this; } -query_builder query_builder::update(const std::string &table) { +query_builder& query_builder::update(const std::string &table) { initialize(command_t::UPDATE, state_t::QUERY_UPDATE); query_parts_.emplace_back(dialect_.token_at(dialect::token_t::UPDATE) + " " + dialect_.prepare_identifier(table)); @@ -133,7 +135,7 @@ query_builder query_builder::update(const std::string &table) { return *this; } -query_builder query_builder::remove() { +query_builder& query_builder::remove() { initialize(command_t::REMOVE, state_t::QUERY_DELETE); query_parts_.emplace_back(dialect_.token_at(dialect::token_t::REMOVE)); @@ -141,7 +143,7 @@ query_builder query_builder::remove() { return *this; } -query_builder &query_builder::table(const std::string &table, std::initializer_list columns) { +query_builder& query_builder::table(const std::string &table, std::initializer_list columns) { transition_to(state_t::QUERY_TABLE_CREATE); query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table) + " "); @@ -155,7 +157,7 @@ query_builder &query_builder::table(const std::string &table, std::initializer_l } else { auto it = columns.begin(); result.append(build_create_column(*it++, dialect_)); - for (it; it != columns.end(); ++it) { + for (; it != columns.end(); ++it) { result.append(", "); result.append(build_create_column(*it, dialect_)); } @@ -166,7 +168,7 @@ query_builder &query_builder::table(const std::string &table, std::initializer_l return *this; } -query_builder &query_builder::table(const std::string &table) { +query_builder& query_builder::table(const std::string &table) { transition_to(state_t::QUERY_TABLE_DROP); query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table)); @@ -174,7 +176,7 @@ query_builder &query_builder::table(const std::string &table) { return *this; } -query_builder &query_builder::into(const std::string &table, std::initializer_list column_names) { +query_builder& query_builder::into(const std::string &table, std::initializer_list column_names) { transition_to(state_t::QUERY_INTO); query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::INTO) + " " + dialect_.prepare_identifier(table) + " "); @@ -187,7 +189,7 @@ query_builder &query_builder::into(const std::string &table, std::initializer_li } else { auto it = column_names.begin(); result.append(dialect_.prepare_identifier(*it++)); - for (it; it != column_names.end(); ++it) { + for (; it != column_names.end(); ++it) { result.append(", "); result.append(dialect_.prepare_identifier(*it)); } @@ -198,7 +200,7 @@ query_builder &query_builder::into(const std::string &table, std::initializer_li return *this; } -query_builder &query_builder::values(std::initializer_list values) { +query_builder& query_builder::values(std::initializer_list values) { transition_to(state_t::QUERY_VALUES); query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::VALUES) + " "); @@ -227,7 +229,7 @@ query_builder &query_builder::values(std::initializer_list values) { return *this; } -query_builder &query_builder::from(const std::string &table, const std::string &as) { +query_builder& query_builder::from(const std::string &table, const std::string &as) { transition_to(state_t::QUERY_FROM); query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::FROM) + " " + dialect_.prepare_identifier(table) + (as.empty() ? "" : " " + as)); @@ -235,7 +237,17 @@ query_builder &query_builder::from(const std::string &table, const std::string & return *this; } -query_builder &query_builder::set(std::initializer_list key_values) { +query_builder &query_builder::join(const std::string &table, join_type_t) +{ + return *this; +} + +query_builder &query_builder::on(const std::string &column, const std::string &join_column) +{ + return *this; +} + +query_builder& query_builder::set(std::initializer_list key_values) { transition_to(state_t::QUERY_SET); query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::SET) + " "); @@ -267,7 +279,7 @@ query_builder &query_builder::set(std::initializer_list key_valu return *this; } -query_builder &query_builder::where(const basic_condition &cond) { +query_builder& query_builder::where(const basic_condition &cond) { transition_to(state_t::QUERY_WHERE); query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::WHERE) + " "); @@ -276,6 +288,53 @@ query_builder &query_builder::where(const basic_condition &cond) { return *this; } +query_builder& query_builder::order_by(const std::string& column) { + transition_to(state_t::QUERY_ORDER_BY); + + query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::ORDER_BY) + " " + dialect_.prepare_identifier(column)); + + return *this; +} + +query_builder& query_builder::group_by(const std::string& column) { + transition_to(state_t::QUERY_GROUP_BY); + + query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::GROUP_BY) + " " + dialect_.prepare_identifier(column)); + + return *this; +} + +query_builder& query_builder::asc() { + transition_to(state_t::QUERY_ORDER_DIRECTION); + + query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::ASC)); + + return *this; +} + +query_builder& query_builder::desc() { + transition_to(state_t::QUERY_ORDER_DIRECTION); + + query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::DESC)); + + return *this; +} + +query_builder& query_builder::offset( size_t count ) { + transition_to(state_t::QUERY_OFFSET); + + query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::OFFSET) + " " + std::to_string(count)); + + return *this; +} + +query_builder& query_builder::limit( size_t count ) { + transition_to(state_t::QUERY_LIMIT); + + query_parts_.emplace_back( " " + dialect_.token_at(dialect::token_t::LIMIT) + " " + std::to_string( count)); + + return *this; +} std::string query_builder::compile() { std::string result; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a910c6c..236652d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,6 +8,7 @@ FetchContent_Declare( FetchContent_MakeAvailable(Catch2) -add_executable(tests builder.cpp) +add_executable(tests builder.cpp + connection.cpp) target_link_libraries(tests PRIVATE Catch2::Catch2WithMain matador) target_include_directories(tests PUBLIC $/include) \ No newline at end of file diff --git a/test/builder.cpp b/test/builder.cpp index 9efe6e7..21a9795 100644 --- a/test/builder.cpp +++ b/test/builder.cpp @@ -66,12 +66,53 @@ TEST_CASE("Delete", "[query]") { } TEST_CASE("Where", "[query]") { + dialect d; + query_builder query(d); + auto sql = query.select({"id", "name", "age"}) + .from("person") + .where("id"_col == 8 && "age"_col > 50) + .compile(); + + REQUIRE(sql == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = 8 AND "age" > 50))"); + + sql = query.select({"id", "name", "age"}) + .from("person") + .where("id"_col == _ && "age"_col > 50) + .compile(); + + REQUIRE(sql == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = ? AND "age" > 50))"); +} + +TEST_CASE("OrderBy", "[query]") { + dialect d; + query_builder query(d); + const auto sql = query.select({"id", "name", "age"}) + .from("person") + .order_by("name").asc() + .compile(); + + REQUIRE(sql == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "name" ASC)"); +} + +TEST_CASE("GroupBy", "[query]") { + dialect d; + query_builder query(d); + const auto sql = query.select({"id", "name", "age"}) + .from("person") + .group_by("age") + .compile(); + + REQUIRE(sql == R"(SELECT "id", "name", "age" FROM "person" GROUP BY "age")"); +} + +TEST_CASE("Limit", "[query]") { dialect d; query_builder query(d); const auto sql = query.select({"id", "name", "age"}) .from("person") - .where("id"_col == 8) + .offset(10) + .limit(20) .compile(); - REQUIRE(sql == R"(SELECT "id", "name", "age" FROM "person" WHERE "id" = 8)"); + REQUIRE(sql == R"(SELECT "id", "name", "age" FROM "person" OFFSET 10 LIMIT 20)"); } \ No newline at end of file diff --git a/test/connection.cpp b/test/connection.cpp new file mode 100644 index 0000000..c7674c2 --- /dev/null +++ b/test/connection.cpp @@ -0,0 +1,18 @@ +#include + +#include +#include +#include + +using namespace matador::sql; + +TEST_CASE("CSelect", "[connection]") { + connection c; + + auto res = c.select({"id", "name", "color"}) + .from("person") + .where("id"_col == 8) + .fetch_all(); + + REQUIRE(res.sql == R"(SELECT "id", "name", "color" FROM "person" WHERE "id" = 8)"); +} \ No newline at end of file