#ifndef QUERY_CONDITION_HPP #define QUERY_CONDITION_HPP #include "matador/sql/basic_condition.hpp" #include "matador/sql/dialect.hpp" #include #include namespace matador::sql { /** * @class condition * @brief Represents a sql query condition * * @tparam L Left hand operator type * @tparam R Right hand operator type * This class represents a condition part * of a sql query or update statement. * Each compare method returns a reference to * the condition itself. That way one can * concatenate conditions together. */ /// @cond MATADOR_DEV class query; template class condition; template class condition::value && !std::is_same::value && !std::is_same::value>::type> : public basic_column_condition { public: condition(const column &fld, basic_condition::operand_t op, T val) : basic_column_condition(fld, op) , value(val) { } T value; 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(); } }; template class condition::value || std::is_same::value>::type> : public basic_column_condition { public: condition(const column &fld, basic_condition::operand_t op, T val) : basic_column_condition(fld, op) ,value(val) { } T value; 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(); } }; template class condition::value && !std::is_same::value && !std::is_same::value>::type> : public basic_column_condition { public: condition(T val, basic_condition::operand_t op, const column &fld) : basic_column_condition(fld, op) , value(val) { } T value; std::string evaluate(dialect &d) const override { std::stringstream str; str << value << " " << operand << " " << d.prepare_identifier(field_.name()); return str.str(); } }; template class condition::value || std::is_same::value>::type> : public basic_column_condition { public: condition(T val, basic_condition::operand_t op, const column &fld) : basic_column_condition(fld, op) , value(val) { } T value; std::string evaluate(dialect &d) const override { std::stringstream str; str << "'" << value << "' " << operand << " " << d.prepare_identifier(field_.name()); return str.str(); } }; /// @endcond /** * @brief Condition class representing an IN condition * * This class represents an query IN condition and evaluates to * this condition based on the current database d * * @code * WHERE age IN (29,34,56) * @endcode */ template < class V > 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 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() << ","; } } } 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(); } private: std::vector args_; }; /** * @brief Condition class representing an IN condition * * This class represents an query IN condition and evaluates to * this condition based on the current database d * * @code * WHERE age IN (select age_value from ) * @endcode */ template <> class condition : public basic_column_condition { public: /** * @brief Create a query IN condition * * Create an IN condition where the argument values come from * the given query. To evaluate the query a sql d must be * given. * * @param col Column for the IN condition * @param op Operand of the condition * @param q The query to be evaluated to the IN arguments */ condition(column col, basic_condition::operand_t op, query &q); /** * @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 { std::string result(d.prepare_identifier(field_.name()) + " " + operand + " ("); // result += d.continue_build(const_cast(query_), d.compile_type()); result += (")"); return result; } 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. * * The condition represents a between where clause * part. * * @tparam T The type of the boundary values */ template < class T > 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 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(); } private: column field_; std::pair range_; }; /** * @brief Logical binary condition * * This class represents a logical binary condition * - AND * - OR * * @tparam L1 The left hand type of the left operator * @tparam R1 The right hand type of the left operator * @tparam L2 The left hand type of the right operator * @tparam R2 The right hand type of the right operator */ template class condition, condition> : public basic_condition { public: /** * @brief Create a binary logical condition * @param l Left hand operator of the condition * @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) { } /** * @brief Evaluates the condition * * @param d The d used to evaluate * @return The evaluated string based on the compile type */ 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 << ")"; } else { str << cl << " " << basic_condition::operands[operand] << " " << cr; } return str.str(); } private: condition left; condition right; basic_condition::operand_t operand; }; /** * @brief Logical unary condition * * This class represents a logical unary NOT condition * * @tparam L Left hand type of the condition to be negated * @tparam R Right hand type of the condition to be negated */ template class condition, void> : public basic_condition { public: /** * @brief Create a logical unary condition * @param c The condition to be negated */ condition(const condition &c) // NOLINT(*-explicit-constructor) : cond(c), operand(basic_condition::operands[basic_condition::operand_t::NOT]) { } /** * @brief Evaluates the condition * * @param d The d used to evaluate * @return The evaluated string based on the compile type */ std::string evaluate(dialect &d) const override { std::stringstream str; str << operand << " (" << cond.evaluate(d) << ")"; return str.str(); } private: condition cond; std::string operand; }; /** * @file condition.hpp * @brief Contains functions to create query conditions * * This file contains some functions to create * query conditions. These functions wrap the * constructing of a concrete condition and allows * expression like condition programming. * * @code * cond1 == cond2 * cond1 < cond2 * cond2 != cond1 && cond3 < cond4 * @endcode */ /** * @brief Creates an IN condition for a given column and a list of values * * @tparam V The type of the list arguments * @param col The column to compare * @param args The list of values * @return The condition object */ template < class V > condition> in(const column &col, std::initializer_list args) { return condition>(col, args); } /** * @brief Creates an IN condition for a given column and a query to be executed * * @param col The column to compare * @param q The query to be executes as sub select * @return The condition object */ condition in(const column &col, query &&q); /** * @brief Creates a between condition. * * Creates a between condition for the given column with * the given range consisting of a low and high value * * @tparam T The type of the column and range * @param col The column for the between condition * @param low The low value of the range * @param high The high value of the range * @return The condition object */ template condition> between(const column &col, T low, T high) { return condition>(col, std::make_pair(low, high)); } /** * @brief Creates a like condition * * Creates a like condition for the given column * and the given value string. * * @param col The column for the like condition * @param val The value to the like operator * @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 * * Creates a condition object of a column and a value * checked on equality. * * @tparam T The type of the value * @param col The column object * @param val The value to compare with * @return The condition object representing the equality operation */ template condition operator==(const column &col, T val) { return condition(col, basic_condition::operand_t::EQUAL, val); } /** * @brief Condition equality method for a column and a query * * Creates a condition object of a column and a query * checked on equality. * * @param col The column object * @param q The query to compare with * @return The condition object representing the equality operation */ condition equals(const column &col, query &q); /** * @brief Condition inequality operator for a column and a value * * Creates a condition condition object of a column and a value * checked on inequality. * * @tparam T The type of the value * @param col The column object * @param val The value to compare with * @return The condition object representing the inequality operation */ template condition operator!=(const column &col, T val) { return condition(col, basic_condition::operand_t::NOT_EQUAL, val); } /** * @brief Condition less operator for a column and a value * * Creates a condition object checking if the value of the given column * is less than the given value. * * @tparam T The type of the value * @param col The column object * @param val The value to compare with * @return The condition object representing the less operation */ template condition operator<(const column &col, T val) { return condition(col, basic_condition::operand_t::LESS, val); } /** * @brief Condition less or equal operator for a column and a value * * Creates a condition object checking if the value of the given column * is less or equal than the given value. * * @tparam T The type of the value * @param col The column object * @param val The value to compare with * @return The condition object representing the less or equal operation */ template condition operator<=(const column &col, T val) { return condition(col, basic_condition::operand_t::LESS_EQUAL, val); } /** * @brief Condition greater operator for a column and a value * * Creates a condition object checking if the value of the given column * is greater than the given value. * * @tparam T The type of the value * @param col The column object * @param val The value to compare with * @return The condition object representing the greater operation */ template condition operator>(const column &col, T val) { return condition(col, basic_condition::operand_t::GREATER, val); } /** * @brief Condition greater or equal operator for a column and a value * * Creates a condition object checking if the value of the given column * is greater or equal than the given value. * * @tparam T The type of the value * @param col The column object * @param val The value to compare with * @return The condition object representing the greater or equal operation */ template condition operator>=(const column &col, T val) { return condition(col, basic_condition::operand_t::GREATER_EQUAL, val); } /** * @brief AND operation condition consisting of a left and right hand condition * * @tparam L1 Left hand type of left hand condition * @tparam R1 Right hand type of left hand condition * @tparam L2 Left hand type of right hand condition * @tparam R2 Right hand type of right hand condition * @param l Left hand condition * @param r Right hand condition * @return An condition object representing the AND operation */ template condition, condition> operator&&(condition l, condition r) { return condition, condition>(std::move(l), std::move(r), basic_condition::operand_t::AND); } /** * @brief OR operation condition consisting of a left and right hand condition * * @tparam L1 Left hand type of left hand condition * @tparam R1 Right hand type of left hand condition * @tparam L2 Left hand type of right hand condition * @tparam R2 Right hand type of right hand condition * @param l Left hand condition * @param r Right hand condition * @return An condition object representing the OR operation */ template condition, condition> operator||(condition l, condition r) { return condition, condition>(std::move(l), std::move(r), basic_condition::operand_t::OR); } /** * @brief Negates a given condition. * * @tparam L The left hand type of the condition * @tparam R The right hand type of the condition * @param c The condition to negated * @return An condition object representing the NOT operation */ template condition, void> operator!(condition c) { return condition, void>(std::move(c)); } /** * @brief Creates a unique condition from a given condition object * * @tparam L The left hand type of the condition * @tparam R The right hand type of the condition * @param cond The condition to be copied * @return A unique condition pointer representing the given condition */ template std::unique_ptr make_condition(const condition &cond) { return std::make_unique>(cond); } } #endif //QUERY_CONDITION_HPP