diff --git a/include/matador/query/condition.hpp b/include/matador/query/condition.hpp index 2454525..91013fd 100644 --- a/include/matador/query/condition.hpp +++ b/include/matador/query/condition.hpp @@ -420,7 +420,7 @@ condition> in(const sql::column &col, std: * @param q The query to be executes as sub select * @return The condition object */ -condition in(const sql::column &col, sql::query_context &&q); +condition in_old(const sql::column &col, sql::query_context &&q); /** * @brief Creates a between condition. @@ -450,7 +450,7 @@ condition> between(const sql::column &col, T low, T * @param val The value to the like operator * @return The like condition object */ -condition like(const sql::column &col, const std::string &val); +condition like_old(const sql::column &col, const std::string &val); /** * @brief Condition equality operator for a column and a value diff --git a/include/matador/query/criteria/between_criteria.hpp b/include/matador/query/criteria/between_criteria.hpp index f79baaa..6313e0f 100644 --- a/include/matador/query/criteria/between_criteria.hpp +++ b/include/matador/query/criteria/between_criteria.hpp @@ -2,6 +2,7 @@ #define CRITERIA_BETWEEN_CRITERIA_NODE_HPP #include "matador/query/criteria/abstract_criteria.hpp" +#include "matador/query/criteria/criteria_utils.hpp" #include "matador/sql/column.hpp" @@ -12,14 +13,19 @@ namespace matador::query { class between_criteria final : public abstract_criteria { public: between_criteria() = delete; - between_criteria(sql::column column, utils::value min, utils::value max); + between_criteria(sql::column column, int64_t min, int64_t max); + between_criteria(sql::column column, utils::placeholder min, utils::placeholder max); void accept(criteria_visitor& visitor) const override; + [[nodiscard]] const sql::column& column() const; + [[nodiscard]] const criteria_value &min() const; + [[nodiscard]] const criteria_value &max() const; + private: sql::column column_; - utils::value min_; - utils::value max_; + criteria_value min_; + criteria_value max_; }; } #endif //CRITERIA_BETWEEN_CRITERIA_NODE_HPP \ No newline at end of file diff --git a/include/matador/query/criteria/binary_criteria.hpp b/include/matador/query/criteria/binary_criteria.hpp index d872a72..6e3a999 100644 --- a/include/matador/query/criteria/binary_criteria.hpp +++ b/include/matador/query/criteria/binary_criteria.hpp @@ -2,11 +2,10 @@ #define CRITERIA_BINARY_CRITERIA_NODE_HPP #include "matador/query/criteria/abstract_criteria.hpp" +#include "matador/query/criteria/criteria_utils.hpp" #include "matador/sql/column.hpp" -#include "matador/utils/value.hpp" - namespace matador::query { enum class binary_operator { EQUALS, @@ -20,14 +19,18 @@ enum class binary_operator { class binary_criteria final : public abstract_criteria { public: binary_criteria() = delete; - binary_criteria( sql::column column, binary_operator operator_, utils::value value ); + binary_criteria(sql::column column, binary_operator operand, criteria_value value); void accept( criteria_visitor& visitor ) const override; + [[nodiscard]] const sql::column& column() const; + [[nodiscard]] binary_operator operand() const; + [[nodiscard]] const criteria_value& value() const; + private: sql::column column_; binary_operator operator_{}; - utils::value value_; + criteria_value value_; }; } #endif //CRITERIA_BINARY_CRITERIA_NODE_HPP \ No newline at end of file diff --git a/include/matador/query/criteria/collection_criteria.hpp b/include/matador/query/criteria/collection_criteria.hpp index 53f3c0c..77dec0e 100644 --- a/include/matador/query/criteria/collection_criteria.hpp +++ b/include/matador/query/criteria/collection_criteria.hpp @@ -2,10 +2,10 @@ #define CRITERIA_COLLECTION_CRITERIA_NODE_HPP #include "matador/query/criteria/abstract_criteria.hpp" +#include "matador/query/criteria/criteria_utils.hpp" #include "matador/sql/column.hpp" - -#include "matador/utils/value.hpp" +#include "matador/sql/query_context.hpp" namespace matador::query { enum class collection_operator { @@ -16,14 +16,36 @@ enum class collection_operator { class collection_criteria final : public abstract_criteria { public: collection_criteria() = delete; - collection_criteria(sql::column col, collection_operator operator_, std::vector values); - collection_criteria(sql::column col, collection_operator operator_, std::initializer_list values); + collection_criteria(sql::column col, collection_operator operand_, std::vector values); + collection_criteria(sql::column col, collection_operator operand_, std::initializer_list values); void accept(criteria_visitor& visitor) const override; + + [[nodiscard]] const sql::column& column() const; + [[nodiscard]] collection_operator operand() const; + [[nodiscard]] const std::vector& values() const; + private: sql::column column_; - collection_operator operator_; - std::vector values_; + collection_operator operand_; + std::vector values_; +}; + +class collection_query_criteria final : public abstract_criteria { +public: + collection_query_criteria() = delete; + collection_query_criteria(sql::column col, collection_operator operand_, sql::query_context ctx); + + void accept(criteria_visitor& visitor) const override; + + [[nodiscard]] const sql::column& column() const; + [[nodiscard]] collection_operator operand() const; + [[nodiscard]] const sql::query_context& context() const; + +private: + sql::column column_; + collection_operator operand_; + sql::query_context query_context_; }; } #endif //CRITERIA_COLLECTION_CRITERIA_NODE_HPP \ No newline at end of file diff --git a/include/matador/query/criteria/criteria_operators.hpp b/include/matador/query/criteria/criteria_operators.hpp index 7ac98f8..a208fbb 100644 --- a/include/matador/query/criteria/criteria_operators.hpp +++ b/include/matador/query/criteria/criteria_operators.hpp @@ -6,40 +6,51 @@ #include "matador/sql/column.hpp" +#include "matador/utils/placeholder.hpp" #include "matador/utils/value.hpp" +namespace matador::sql { +struct query_context; +} namespace matador::query { template criteria_ptr operator==(const sql::column &col, Type val) { - return std::make_unique(col, binary_operator::EQUALS, val); + return std::make_unique(col, binary_operator::EQUALS, utils::value(val)); } template criteria_ptr operator!=(const sql::column &col, Type val) { - return std::make_unique(col, binary_operator::NOT_EQUALS, val); + return std::make_unique(col, binary_operator::NOT_EQUALS, utils::value(val)); } template criteria_ptr operator>(const sql::column &col, Type val) { - return std::make_unique(col, binary_operator::GREATER_THAN, val); + return std::make_unique(col, binary_operator::GREATER_THAN, utils::value(val)); } template criteria_ptr operator>=(const sql::column &col, Type val) { - return std::make_unique(col, binary_operator::GREATER_THAN_OR_EQUAL, val); + return std::make_unique(col, binary_operator::GREATER_THAN_OR_EQUAL, utils::value(val)); } template criteria_ptr operator<(const sql::column &col, Type val) { - return std::make_unique(col, binary_operator::LESS_THAN, val); + return std::make_unique(col, binary_operator::LESS_THAN, utils::value(val)); } template criteria_ptr operator<=(const sql::column &col, Type val) { - return std::make_unique(col, binary_operator::LESS_THAN_OR_EQUAL, val); + return std::make_unique(col, binary_operator::LESS_THAN_OR_EQUAL, utils::value(val)); } +criteria_ptr operator==(const sql::column &col, utils::placeholder p); +criteria_ptr operator!=(const sql::column &col, utils::placeholder p); +criteria_ptr operator>(const sql::column &col, utils::placeholder p); +criteria_ptr operator>=(const sql::column &col, utils::placeholder p); +criteria_ptr operator<(const sql::column &col, utils::placeholder p); +criteria_ptr operator<=(const sql::column &col, utils::placeholder p); + criteria_ptr operator&&(criteria_ptr left, criteria_ptr right); criteria_ptr operator||(criteria_ptr left, criteria_ptr right); @@ -48,23 +59,34 @@ criteria_ptr operator!(criteria_ptr clause); template < class Type > criteria_ptr in(const sql::column &col, std::initializer_list args) { - std::vector values; + std::vector values; for ( auto &&arg : args ) { - values.emplace_back(std::move(arg)); + values.emplace_back(utils::value{std::move(arg)}); } return std::make_unique(col, collection_operator::IN, std::move(values)); } -template < class V > -criteria_ptr out(const sql::column &col, std::initializer_list args) { - std::vector values; +template <> +criteria_ptr in(const sql::column &col, std::initializer_list args); + +criteria_ptr in(const sql::column &col, sql::query_context &&ctx); + +template < class Type > +criteria_ptr out(const sql::column &col, std::initializer_list args) { + std::vector values; for ( auto &&arg : args ) { - values.emplace_back(std::move(arg)); + values.emplace_back(utils::value{std::move(arg)}); } return std::make_unique(col, collection_operator::OUT, values); } -criteria_ptr between(const sql::column &col, utils::value min, utils::value max); +template <> +criteria_ptr out(const sql::column &col, std::initializer_list args); + +criteria_ptr out(const sql::column &col, sql::query_context &&ctx); + +criteria_ptr between(const sql::column &col, int64_t min, int64_t max); +criteria_ptr between(const sql::column &col, utils::placeholder min, utils::placeholder max); criteria_ptr like(const sql::column &col, const std::string &pattern); diff --git a/include/matador/query/criteria/criteria_utils.hpp b/include/matador/query/criteria/criteria_utils.hpp new file mode 100644 index 0000000..f519e2a --- /dev/null +++ b/include/matador/query/criteria/criteria_utils.hpp @@ -0,0 +1,11 @@ +#ifndef MATADOR_CRITERIA_UTILS_HPP +#define MATADOR_CRITERIA_UTILS_HPP + +#include "matador/utils/placeholder.hpp" +#include "matador/utils/value.hpp" + +namespace matador::query { +using criteria_value = std::variant; +} + +#endif //MATADOR_CRITERIA_UTILS_HPP \ No newline at end of file diff --git a/include/matador/query/criteria/criteria_visitor.hpp b/include/matador/query/criteria/criteria_visitor.hpp index 6585096..8688fbf 100644 --- a/include/matador/query/criteria/criteria_visitor.hpp +++ b/include/matador/query/criteria/criteria_visitor.hpp @@ -8,6 +8,7 @@ class like_criteria; class logical_criteria; class not_criteria; class collection_criteria; +class collection_query_criteria; class criteria_visitor { public: @@ -16,6 +17,7 @@ public: virtual void visit(const between_criteria &node) = 0; virtual void visit(const binary_criteria &node) = 0; virtual void visit(const collection_criteria &node) = 0; + virtual void visit(const collection_query_criteria &node) = 0; virtual void visit(const like_criteria &node) = 0; virtual void visit(const logical_criteria &node) = 0; virtual void visit(const not_criteria &node) = 0; diff --git a/include/matador/query/criteria/like_criteria.hpp b/include/matador/query/criteria/like_criteria.hpp index b439723..724dd9d 100644 --- a/include/matador/query/criteria/like_criteria.hpp +++ b/include/matador/query/criteria/like_criteria.hpp @@ -13,6 +13,9 @@ public: void accept(criteria_visitor &visitor) const override; + [[nodiscard]] const sql::column& column() const; + [[nodiscard]] const std::string& pattern() const; + private: sql::column column_; std::string pattern_; diff --git a/include/matador/query/criteria/logical_criteria.hpp b/include/matador/query/criteria/logical_criteria.hpp index ec63851..af4e9c0 100644 --- a/include/matador/query/criteria/logical_criteria.hpp +++ b/include/matador/query/criteria/logical_criteria.hpp @@ -16,6 +16,10 @@ public: void accept(criteria_visitor& visitor) const override; + [[nodiscard]] const criteria_ptr& left_clause() const; + [[nodiscard]] logical_operator operand() const; + [[nodiscard]] const criteria_ptr& right_clause() const; + private: std::unique_ptr left_criteria_; logical_operator operand_; diff --git a/include/matador/query/criteria/not_criteria.hpp b/include/matador/query/criteria/not_criteria.hpp index b0f583c..e1f25be 100644 --- a/include/matador/query/criteria/not_criteria.hpp +++ b/include/matador/query/criteria/not_criteria.hpp @@ -6,12 +6,14 @@ namespace matador::query { class not_criteria final : public abstract_criteria { public: - explicit not_criteria(criteria_ptr clause); + explicit not_criteria(criteria_ptr clause); - void accept(criteria_visitor& visitor) const override; + void accept(criteria_visitor& visitor) const override; + + [[nodiscard]] const criteria_ptr& clause() const; private: - criteria_ptr criteria_; + criteria_ptr criteria_; }; } diff --git a/include/matador/query/criteria_evaluator.hpp b/include/matador/query/criteria_evaluator.hpp index ddc459c..374d640 100644 --- a/include/matador/query/criteria_evaluator.hpp +++ b/include/matador/query/criteria_evaluator.hpp @@ -1,7 +1,11 @@ #ifndef MATADOR_CRITERIA_EVALUATOR_HPP #define MATADOR_CRITERIA_EVALUATOR_HPP +#include "matador/query/criteria/abstract_criteria.hpp" #include "matador/query/criteria/criteria_visitor.hpp" +#include "matador/query/criteria/criteria_utils.hpp" + +#include namespace matador::sql { class dialect; @@ -13,16 +17,24 @@ namespace matador::query { class criteria_evaluator final : public criteria_visitor { public: criteria_evaluator(const sql::dialect &d, sql::query_context &query); + + std::string evaluate(const abstract_criteria &node); + void visit(const between_criteria &node) override; void visit(const binary_criteria &node) override; void visit(const collection_criteria &node) override; + void visit(const collection_query_criteria &node) override; void visit(const like_criteria &node) override; void visit(const logical_criteria &node) override; void visit(const not_criteria &node) override; +private: + void evaluate_value(const criteria_value &value); + private: const sql::dialect &dialect_; sql::query_context &query_; + std::string clause_; }; } #endif //MATADOR_CRITERIA_EVALUATOR_HPP \ No newline at end of file diff --git a/include/matador/sql/dialect.hpp b/include/matador/sql/dialect.hpp index a81d88f..1073a32 100644 --- a/include/matador/sql/dialect.hpp +++ b/include/matador/sql/dialect.hpp @@ -13,6 +13,10 @@ #include #include +namespace matador::utils { +class value; +} + namespace matador::sql { class connection_impl; @@ -53,6 +57,8 @@ public: [[nodiscard]] const std::string& to_string(bool val) const; + [[nodiscard]] std::string to_sql_string(const utils::value &val) const ; + void bool_strings(const std::string &true_string, const std::string &false_string); /** @@ -150,7 +156,10 @@ private: {dialect_token::WHERE, "WHERE"}, {dialect_token::AND, "AND"}, {dialect_token::OR, "OR"}, + {dialect_token::NOT, "NOT"}, {dialect_token::LIKE, "LIKE"}, + {dialect_token::BETWEEN, "BETWEEN"}, + {dialect_token::IN, "IN"}, {dialect_token::ORDER_BY, "ORDER BY"}, {dialect_token::GROUP_BY, "GROUP BY"}, {dialect_token::ASC, "ASC"}, @@ -171,6 +180,8 @@ private: {dialect_token::STRING_QUOTE, "'"}, {dialect_token::BEGIN_BINARY_DATA, "X'"}, {dialect_token::END_BINARY_DATA, "'"}, + {dialect_token::BEGIN_STRING_DATA, "'"}, + {dialect_token::END_STRING_DATA, "'"}, {dialect_token::NONE, ""} }; diff --git a/include/matador/sql/dialect_token.hpp b/include/matador/sql/dialect_token.hpp index be48a8d..92dffb3 100644 --- a/include/matador/sql/dialect_token.hpp +++ b/include/matador/sql/dialect_token.hpp @@ -28,7 +28,10 @@ enum class dialect_token : uint8_t { WHERE_CLAUSE, AND, OR, + NOT, LIKE, + BETWEEN, + IN, ORDER_BY, GROUP_BY, ASC, @@ -49,6 +52,8 @@ enum class dialect_token : uint8_t { STRING_QUOTE, BEGIN_BINARY_DATA, END_BINARY_DATA, + BEGIN_STRING_DATA, + END_STRING_DATA, NONE }; } diff --git a/include/matador/sql/interface/statement_impl.hpp b/include/matador/sql/interface/statement_impl.hpp index b168fe3..237178a 100644 --- a/include/matador/sql/interface/statement_impl.hpp +++ b/include/matador/sql/interface/statement_impl.hpp @@ -14,8 +14,7 @@ namespace matador::sql { class sql_error; -class statement_impl -{ +class statement_impl { protected: explicit statement_impl(query_context query, size_t start_bind_pos); diff --git a/source/orm/CMakeLists.txt b/source/orm/CMakeLists.txt index 39d106d..de3455e 100644 --- a/source/orm/CMakeLists.txt +++ b/source/orm/CMakeLists.txt @@ -153,6 +153,7 @@ add_library(matador-orm STATIC sql/table.cpp ../../include/matador/query/criteria_evaluator.hpp query/criteria_evaluator.cpp + ../../include/matador/query/criteria/criteria_utils.hpp ) target_include_directories(matador-orm diff --git a/source/orm/query/condition.cpp b/source/orm/query/condition.cpp index 8435325..f9c8c6e 100644 --- a/source/orm/query/condition.cpp +++ b/source/orm/query/condition.cpp @@ -28,12 +28,12 @@ std::string condition::evaluate(const sql::dialect &d, return d.prepare_condition(field_) + " " + operand + " " + d.prepare_condition(other_column_); } -condition in(const sql::column &col, sql::query_context &&q) +condition in_old(const sql::column &col, sql::query_context &&q) { return {col, basic_condition::operand_type::IN_LIST, q}; } -condition like(const sql::column &col, const std::string &val) { +condition like_old(const sql::column &col, const std::string &val) { return { col, basic_condition::operand_type::LIKE, val }; } diff --git a/source/orm/query/criteria/between_criteria.cpp b/source/orm/query/criteria/between_criteria.cpp index 92bf65c..726b0e3 100644 --- a/source/orm/query/criteria/between_criteria.cpp +++ b/source/orm/query/criteria/between_criteria.cpp @@ -3,13 +3,31 @@ #include "matador/query/criteria/criteria_visitor.hpp" namespace matador::query{ -between_criteria::between_criteria(sql::column column, utils::value min, utils::value max ) +between_criteria::between_criteria(sql::column column, const int64_t min, const int64_t max) : column_(std::move(column)) -, min_(std::move(min)) -, max_(std::move(max)) +, min_(utils::value{min}) +, max_(utils::value{max}) +{} + +between_criteria::between_criteria(sql::column column, utils::placeholder min, utils::placeholder max) +: column_(std::move(column)) +, min_(min) +, max_(max) {} void between_criteria::accept( criteria_visitor& visitor ) const { - visitor.visit(*this); + visitor.visit(*this); +} + +const sql::column & between_criteria::column() const { + return column_; +} + +const criteria_value &between_criteria::min() const { + return min_; +} + +const criteria_value &between_criteria::max() const { + return max_; +} } -} \ No newline at end of file diff --git a/source/orm/query/criteria/binary_criteria.cpp b/source/orm/query/criteria/binary_criteria.cpp index 0564057..58fc05e 100644 --- a/source/orm/query/criteria/binary_criteria.cpp +++ b/source/orm/query/criteria/binary_criteria.cpp @@ -3,13 +3,25 @@ #include "matador/query/criteria/criteria_visitor.hpp" namespace matador::query { -binary_criteria::binary_criteria(sql::column column, const binary_operator operator_, utils::value value) +binary_criteria::binary_criteria(sql::column column, const binary_operator operand, criteria_value value) : column_(std::move(column)) -, operator_(operator_) +, operator_(operand) , value_(std::move(value)) {} void binary_criteria::accept( criteria_visitor& visitor ) const { - visitor.visit(*this); + visitor.visit(*this); +} + +const sql::column & binary_criteria::column() const { + return column_; +} + +binary_operator binary_criteria::operand() const { + return operator_; +} + +const criteria_value& binary_criteria::value() const { + return value_; +} } -} \ No newline at end of file diff --git a/source/orm/query/criteria/collection_criteria.cpp b/source/orm/query/criteria/collection_criteria.cpp index 02490f9..38ba41f 100644 --- a/source/orm/query/criteria/collection_criteria.cpp +++ b/source/orm/query/criteria/collection_criteria.cpp @@ -3,19 +3,53 @@ #include "matador/query/criteria/criteria_visitor.hpp" namespace matador::query { -collection_criteria::collection_criteria(sql::column col, collection_operator operator_, std::vector values ) +collection_criteria::collection_criteria(sql::column col, const collection_operator operand_, std::vector values ) : column_(std::move(col)) -, operator_(operator_) +, operand_(operand_) , values_(std::move(values)) {} -collection_criteria::collection_criteria(sql::column col, const collection_operator operator_, const std::initializer_list values ) +collection_criteria::collection_criteria(sql::column col, const collection_operator operand_, const std::initializer_list values ) : column_(std::move(col)) -, operator_(operator_) +, operand_(operand_) , values_(values) {} -void collection_criteria::accept( criteria_visitor& visitor ) const { - visitor.visit(*this); +void collection_criteria::accept(criteria_visitor& visitor) const { + visitor.visit(*this); +} + +const sql::column & collection_criteria::column() const { + return column_; +} + +collection_operator collection_criteria::operand() const { + return operand_; +} + +const std::vector& collection_criteria::values() const { + return values_; +} + +collection_query_criteria::collection_query_criteria(sql::column col, collection_operator operand_, sql::query_context ctx) +: column_(std::move(col)) +, operand_(operand_) +, query_context_(std::move(ctx)){ +} + +void collection_query_criteria::accept(criteria_visitor &visitor) const { + visitor.visit(*this); +} + +const sql::column & collection_query_criteria::column() const { + return column_; +} + +collection_operator collection_query_criteria::operand() const { + return operand_; +} + +const sql::query_context & collection_query_criteria::context() const { + return query_context_; +} } -} \ No newline at end of file diff --git a/source/orm/query/criteria/criteria_operators.cpp b/source/orm/query/criteria/criteria_operators.cpp index a8aba16..0d9a68c 100644 --- a/source/orm/query/criteria/criteria_operators.cpp +++ b/source/orm/query/criteria/criteria_operators.cpp @@ -6,23 +6,78 @@ #include "matador/query/criteria/not_criteria.hpp" namespace matador::query { +criteria_ptr operator==(const sql::column &col, utils::placeholder p) { + return std::make_unique(col, binary_operator::EQUALS, p); +} + +criteria_ptr operator!=(const sql::column &col, utils::placeholder p) { + return std::make_unique(col, binary_operator::NOT_EQUALS, p); +} + +criteria_ptr operator>(const sql::column &col, utils::placeholder p) { + return std::make_unique(col, binary_operator::GREATER_THAN, p); +} + +criteria_ptr operator>=(const sql::column &col, utils::placeholder p) { + return std::make_unique(col, binary_operator::GREATER_THAN_OR_EQUAL, p); + +} + +criteria_ptr operator<(const sql::column &col, utils::placeholder p) { + return std::make_unique(col, binary_operator::LESS_THAN, p); +} + +criteria_ptr operator<=(const sql::column &col, utils::placeholder p) { + return std::make_unique(col, binary_operator::LESS_THAN_OR_EQUAL, p); +} + criteria_ptr operator&&(criteria_ptr left, criteria_ptr right) { - return std::make_unique(std::move(left), logical_operator::AND, std::move(right)); + return std::make_unique(std::move(left), logical_operator::AND, std::move(right)); } criteria_ptr operator||(criteria_ptr left, criteria_ptr right) { - return std::make_unique(std::move(left), logical_operator::OR, std::move(right)); + return std::make_unique(std::move(left), logical_operator::OR, std::move(right)); } criteria_ptr operator!(criteria_ptr clause) { - return std::make_unique(std::move(clause)); + return std::make_unique(std::move(clause)); } -criteria_ptr between(const sql::column &col, utils::value min, utils::value max) { - return std::make_unique(col, std::move(min), std::move(max)); +template <> +criteria_ptr in(const sql::column &col, const std::initializer_list args) { + std::vector values; + for ( auto &&arg : args ) { + values.emplace_back(arg); + } + return std::make_unique(col, collection_operator::IN, std::move(values)); +} + +criteria_ptr in(const sql::column &col, sql::query_context &&ctx) { + return std::make_unique(col, collection_operator::IN, std::move(ctx)); +} + +template <> +criteria_ptr out(const sql::column &col, const std::initializer_list args) { + std::vector values; + for ( auto &&arg : args ) { + values.emplace_back(arg); + } + return std::make_unique(col, collection_operator::OUT, values); +} + +criteria_ptr out(const sql::column &col, sql::query_context &&ctx) { + return std::make_unique(col, collection_operator::IN, std::move(ctx)); +} + +criteria_ptr between(const sql::column &col, const int64_t min, const int64_t max) { + return std::make_unique(col, min, max); +} + +criteria_ptr between(const sql::column &col, utils::placeholder min, utils::placeholder max) { + return std::make_unique(col, min, max); } criteria_ptr like(const sql::column &col, const std::string &pattern) { - return std::make_unique(col, pattern); + return std::make_unique(col, pattern); } } diff --git a/source/orm/query/criteria/like_criteria.cpp b/source/orm/query/criteria/like_criteria.cpp index 6ec85e4..ed1e62a 100644 --- a/source/orm/query/criteria/like_criteria.cpp +++ b/source/orm/query/criteria/like_criteria.cpp @@ -10,4 +10,12 @@ like_criteria::like_criteria(sql::column column, std::string pattern) void like_criteria::accept(criteria_visitor &visitor) const { visitor.visit(*this); } + +const sql::column & like_criteria::column() const { + return column_; +} + +const std::string & like_criteria::pattern() const { + return pattern_; +} } diff --git a/source/orm/query/criteria/logical_criteria.cpp b/source/orm/query/criteria/logical_criteria.cpp index 88e33a3..0f20c88 100644 --- a/source/orm/query/criteria/logical_criteria.cpp +++ b/source/orm/query/criteria/logical_criteria.cpp @@ -10,6 +10,18 @@ logical_criteria::logical_criteria(criteria_ptr left, const logical_operator op, {} void logical_criteria::accept(criteria_visitor& visitor) const { - visitor.visit(*this); + visitor.visit(*this); +} + +const criteria_ptr & logical_criteria::left_clause() const { + return left_criteria_; +} + +logical_operator logical_criteria::operand() const { + return operand_; +} + +const criteria_ptr & logical_criteria::right_clause() const { + return right_criteria_; +} } -} \ No newline at end of file diff --git a/source/orm/query/criteria/not_criteria.cpp b/source/orm/query/criteria/not_criteria.cpp index 5fb1156..4b45f6b 100644 --- a/source/orm/query/criteria/not_criteria.cpp +++ b/source/orm/query/criteria/not_criteria.cpp @@ -8,6 +8,10 @@ not_criteria::not_criteria(criteria_ptr clause) {} void not_criteria::accept(criteria_visitor& visitor) const { - visitor.visit(*this); + visitor.visit(*this); +} + +const criteria_ptr & not_criteria::clause() const { + return criteria_; +} } -} \ No newline at end of file diff --git a/source/orm/query/criteria_evaluator.cpp b/source/orm/query/criteria_evaluator.cpp index ed931a5..096ce3f 100644 --- a/source/orm/query/criteria_evaluator.cpp +++ b/source/orm/query/criteria_evaluator.cpp @@ -1,25 +1,117 @@ #include "matador/query/criteria_evaluator.hpp" -namespace matador::query { -criteria_evaluator::criteria_evaluator(const sql::dialect &d, sql::query_context &query) -: dialect_(d) -, query_(query) {} +#include "matador/query/criteria/between_criteria.hpp" +#include "matador/query/criteria/binary_criteria.hpp" +#include "matador/query/criteria/collection_criteria.hpp" +#include "matador/query/criteria/like_criteria.hpp" +#include "matador/query/criteria/logical_criteria.hpp" +#include "matador/query/criteria/not_criteria.hpp" -void criteria_evaluator::visit(const between_criteria &node) { +#include "matador/sql/dialect.hpp" +#include "matador/sql/query_context.hpp" + +#include "matador/utils/value.hpp" +#include "matador/utils/enum_mapper.hpp" + +namespace matador::query { +namespace detail { +static const utils::enum_mapper BinaryOperatorEnum({ + {binary_operator::EQUALS, "="}, + {binary_operator::NOT_EQUALS, "<>"}, + {binary_operator::GREATER_THAN, ">"}, + {binary_operator::GREATER_THAN_OR_EQUAL, ">="}, + {binary_operator::LESS_THAN, "<"}, + {binary_operator::LESS_THAN_OR_EQUAL, "<="}, +}); } +criteria_evaluator::criteria_evaluator(const sql::dialect &d, sql::query_context &query) + : dialect_(d) + , query_(query) { +} + +std::string criteria_evaluator::evaluate(const abstract_criteria &node) { + clause_.clear(); + node.accept(*this); + + return clause_; +} + +void criteria_evaluator::visit(const between_criteria &node) { + query_.bind_vars.emplace_back(node.column().name); + query_.bind_vars.emplace_back(node.column().name); + clause_ += dialect_.prepare_identifier(node.column()) + " " + dialect_.token_at(sql::dialect_token::BETWEEN) + " "; + evaluate_value(node.min()); + clause_ += " " + dialect_.token_at(sql::dialect_token::AND) + " "; + evaluate_value(node.max()); +} + +template struct overload : Ts... { using Ts::operator()...; }; +template overload(Ts...) -> overload; + void criteria_evaluator::visit(const binary_criteria &node) { + clause_ += dialect_.prepare_condition(node.column()) + " " + detail::BinaryOperatorEnum.to_string(node.operand()) + " "; + + evaluate_value(node.value()); } void criteria_evaluator::visit(const collection_criteria &node) { + const auto count = node.values().size(); + for (size_t i = 0; i < count; ++i) { + query_.bind_vars.emplace_back(node.column().name); + } + + clause_ += dialect_.prepare_identifier(node.column()) + + (node.operand() == collection_operator::OUT ? " " + dialect_.token_at(sql::dialect_token::NOT) + " " : " ") + + dialect_.token_at(sql::dialect_token::IN) + " ("; + if (node.values().size() < 2) { + for (const auto &val: node.values()) { + evaluate_value(val); + } + } else { + auto it = node.values().begin(); + evaluate_value(*it++); + for (; it != node.values().end(); ++it) { + clause_ += ", "; + evaluate_value(*it); + } + } + clause_ += ")"; +} + +void criteria_evaluator::visit(const collection_query_criteria &node) { + clause_ += dialect_.prepare_identifier(node.column()) + + (node.operand() == collection_operator::OUT ? " " + dialect_.token_at(sql::dialect_token::NOT) + " " : " ") + + dialect_.token_at(sql::dialect_token::IN) + " (" +node.context().sql + ")"; } void criteria_evaluator::visit(const like_criteria &node) { + clause_ += dialect_.prepare_condition(node.column()) + " " + dialect_.token_at(sql::dialect_token::LIKE) + + " " + dialect_.token_at(sql::dialect_token::BEGIN_STRING_DATA) + node.pattern() + dialect_.token_at( + sql::dialect_token::END_STRING_DATA); } void criteria_evaluator::visit(const logical_criteria &node) { + clause_ += "("; + node.left_clause()->accept(*this); + clause_ += " " + dialect_.token_at(node.operand() == logical_operator::AND + ? sql::dialect_token::AND + : sql::dialect_token::OR) + " "; + node.right_clause()->accept(*this); + clause_ += ")"; } void criteria_evaluator::visit(const not_criteria &node) { + clause_ += dialect_.token_at(sql::dialect_token::NOT) + " ("; + node.clause()->accept(*this); + clause_ += ")"; +} + +void criteria_evaluator::evaluate_value(const criteria_value &value) { + std::visit(overload{ + [this](const utils::value& val){ clause_ += dialect_.to_sql_string(val); }, + [this](const utils::placeholder&) { clause_ += dialect_.next_placeholder(query_.bind_vars); } + }, value); + } } diff --git a/source/orm/sql/dialect.cpp b/source/orm/sql/dialect.cpp index 96c5e57..994458b 100644 --- a/source/orm/sql/dialect.cpp +++ b/source/orm/sql/dialect.cpp @@ -57,6 +57,17 @@ const std::string& dialect::to_string(const bool val) const return bool_strings_[static_cast(val)]; } +std::string dialect::to_sql_string(const utils::value &val) const { + if (val.is_null()) { + return "NULL"; + } + if (val.is_string()) { + return token_at(dialect_token::BEGIN_STRING_DATA) + val.str() + token_at(dialect_token::BEGIN_STRING_DATA); + } + + return val.str(); +} + void dialect::bool_strings(const std::string &true_string, const std::string &false_string) { bool_strings_[0] = false_string; diff --git a/test/orm/CMakeLists.txt b/test/orm/CMakeLists.txt index 52e0bb0..ea400d4 100644 --- a/test/orm/CMakeLists.txt +++ b/test/orm/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable(OrmTests utils/auto_reset_event.hpp sql/StatementCacheTest.cpp sql/ConnectionPoolFixture.hpp + query/CriteriaTests.cpp ) target_link_libraries(OrmTests matador-orm matador-core Catch2::Catch2WithMain) diff --git a/test/orm/backend/test_statement.cpp b/test/orm/backend/test_statement.cpp index d01d93a..d7f1d7e 100644 --- a/test/orm/backend/test_statement.cpp +++ b/test/orm/backend/test_statement.cpp @@ -8,7 +8,7 @@ namespace matador::test::orm { test_statement::test_statement(const sql::query_context &query) -: statement_impl(query) {} +: statement_impl(query, 0) {} utils::result test_statement::execute(const sql::parameter_binder &/*bindings*/) { using namespace std::chrono_literals; diff --git a/test/orm/query/ConditionTests.cpp b/test/orm/query/ConditionTests.cpp index a52f920..0e6fa41 100644 --- a/test/orm/query/ConditionTests.cpp +++ b/test/orm/query/ConditionTests.cpp @@ -90,7 +90,7 @@ TEST_CASE_METHOD(ConditionFixture, "Test in query condition", "[condition][in qu query_context sub_ctx; sub_ctx.sql = R"(SELECT "name" FROM "test")"; - auto cond = age_col != 7 && in(name_col, std::move(sub_ctx)); + auto cond = age_col != 7 && in_old(name_col, std::move(sub_ctx)); auto clause = cond.evaluate(dlc, ctx); REQUIRE(clause == "(\"age\" <> 7 AND \"name\" IN (SELECT \"name\" FROM \"test\"))"); } @@ -106,7 +106,7 @@ TEST_CASE_METHOD(ConditionFixture, "Test between condition", "[condition][betwee TEST_CASE_METHOD(ConditionFixture, "Test like condition", "[condition][like]") { const auto name_col = "name"_col; - auto cond = like(name_col, "%er%"); + auto cond = like_old(name_col, "%er%"); auto clause = cond.evaluate(dlc, ctx); REQUIRE(clause == "\"name\" LIKE '%er%'"); } diff --git a/test/orm/query/CriteriaTests.cpp b/test/orm/query/CriteriaTests.cpp new file mode 100644 index 0000000..4953e7e --- /dev/null +++ b/test/orm/query/CriteriaTests.cpp @@ -0,0 +1,122 @@ +#include + +#include "matador/query/criteria.hpp" +#include "matador/query/criteria_evaluator.hpp" + +#include "matador/sql/dialect_builder.hpp" +#include "matador/sql/query_context.hpp" + +#include "matador/utils/placeholder.hpp" + +using namespace matador::sql; +using namespace matador::query; +using namespace matador::utils; + +class CriteriaFixture { +protected: + dialect dlc = dialect_builder::builder() + .create() + .build(); + query_context ctx; +}; + +TEST_CASE_METHOD(CriteriaFixture, "Test binary criteria", "[criteria][binary]") { + const auto name_col = "name"_col; + + REQUIRE(name_col.name == "name"); + + auto clause = name_col != "george"; + + criteria_evaluator evaluator(dlc, ctx); + auto str = evaluator.evaluate(*clause); + + REQUIRE(str == "\"name\" <> 'george'"); + + clause = "age"_col != 9; + str = evaluator.evaluate(*clause); + REQUIRE(str == "\"age\" <> 9"); + + clause = "age"_col != _; + str = evaluator.evaluate(*clause); + + REQUIRE(str == "\"age\" <> ?"); +} + +TEST_CASE_METHOD(CriteriaFixture, "Test not criteria", "[criteria][not]") { + const auto name_col = "name"_col; + auto clause = !(name_col == "Hans"); + + criteria_evaluator evaluator(dlc, ctx); + auto str = evaluator.evaluate(*clause); + + REQUIRE(str == "NOT (\"name\" = 'Hans')"); + + clause = !(name_col == _); + str = evaluator.evaluate(*clause); + + REQUIRE(str == "NOT (\"name\" = ?)"); +} + +TEST_CASE_METHOD(CriteriaFixture, "Test in criteria", "[criteria][in]") { + const auto age_col = "age"_col; + auto clause = age_col != 7 && in(age_col, {7,5,5,8}); + + criteria_evaluator evaluator(dlc, ctx); + auto str = evaluator.evaluate(*clause); + + REQUIRE(str == "(\"age\" <> 7 AND \"age\" IN (7, 5, 5, 8))"); + + clause = age_col != 7 && in(age_col, {_, _, _}); + str = evaluator.evaluate(*clause); + + REQUIRE(str == "(\"age\" <> 7 AND \"age\" IN (?, ?, ?))"); +} + +TEST_CASE_METHOD(CriteriaFixture, "Test in query criteria", "[criteria][in query]") { + const auto age_col = "age"_col; + const auto name_col = "name"_col; + + query_context sub_ctx; + sub_ctx.sql = R"(SELECT "name" FROM "test")"; + + auto clause = age_col != 7 && in(name_col, std::move(sub_ctx)); +} +TEST_CASE_METHOD(CriteriaFixture, "Test out criteria", "[criteria][out]") { + const auto age_col = "age"_col; + auto clause = age_col != 7 && out(age_col, {7,5,5,8}); + + criteria_evaluator evaluator(dlc, ctx); + auto str = evaluator.evaluate(*clause); + + REQUIRE(str == "(\"age\" <> 7 AND \"age\" NOT IN (7, 5, 5, 8))"); + + clause = age_col != 7 && out(age_col, {_, _, _}); + str = evaluator.evaluate(*clause); + + REQUIRE(str == "(\"age\" <> 7 AND \"age\" NOT IN (?, ?, ?))"); +} + +TEST_CASE_METHOD(CriteriaFixture, "Test between criteria", "[criteria][between]") { + const auto age_col = "age"_col; + auto clause = age_col != 7 || between(age_col, 21, 30); + + criteria_evaluator evaluator(dlc, ctx); + auto str = evaluator.evaluate(*clause); + + REQUIRE(str == "(\"age\" <> 7 OR \"age\" BETWEEN 21 AND 30)"); + + clause = age_col != 7 || between(age_col, _, _); + str = evaluator.evaluate(*clause); + + REQUIRE(str == "(\"age\" <> 7 OR \"age\" BETWEEN ? AND ?)"); +} + +TEST_CASE_METHOD(CriteriaFixture, "Test like criteria", "[criteria][like]") { + const auto name_col = "name"_col; + const auto clause = like(name_col, "%er%"); + + criteria_evaluator evaluator(dlc, ctx); + auto str = evaluator.evaluate(*clause); + + REQUIRE(str == "\"name\" LIKE '%er%'"); +} \ No newline at end of file