added where condition
This commit is contained in:
parent
b9d9609c28
commit
487dfb2eb4
|
|
@ -6,6 +6,7 @@ set(CMAKE_CXX_STANDARD 17)
|
||||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
add_subdirectory(test)
|
||||||
|
|
||||||
add_executable(query main.cpp)
|
add_executable(query main.cpp)
|
||||||
target_link_libraries(query matador)
|
target_link_libraries(query matador)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
#ifndef QUERY_BASIC_CONDITION_HPP
|
||||||
|
#define QUERY_BASIC_CONDITION_HPP
|
||||||
|
|
||||||
|
#include "matador/sql/column.hpp"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace matador::sql {
|
||||||
|
|
||||||
|
class dialect;
|
||||||
|
|
||||||
|
class basic_condition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
basic_condition() = default;
|
||||||
|
virtual ~basic_condition() = default;
|
||||||
|
|
||||||
|
enum class operand_t : uint8_t
|
||||||
|
{
|
||||||
|
EQUAL = 0,
|
||||||
|
NOT_EQUAL,
|
||||||
|
LESS,
|
||||||
|
LESS_EQUAL,
|
||||||
|
GREATER,
|
||||||
|
GREATER_EQUAL,
|
||||||
|
OR,
|
||||||
|
AND,
|
||||||
|
NOT,
|
||||||
|
IN_LIST,
|
||||||
|
LIKE
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual std::string evaluate(dialect &dialect) const = 0;
|
||||||
|
|
||||||
|
static std::unordered_map<operand_t, std::string> operands;
|
||||||
|
};
|
||||||
|
|
||||||
|
class basic_column_condition : public basic_condition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
column field_;
|
||||||
|
std::string operand;
|
||||||
|
|
||||||
|
basic_column_condition(column fld, basic_condition::operand_t op);
|
||||||
|
};
|
||||||
|
|
||||||
|
class basic_in_condition : public basic_condition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
column field_;
|
||||||
|
|
||||||
|
explicit basic_in_condition(column fld);
|
||||||
|
|
||||||
|
[[nodiscard]] virtual size_t size() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //QUERY_BASIC_CONDITION_HPP
|
||||||
|
|
@ -11,6 +11,10 @@ namespace matador::sql {
|
||||||
|
|
||||||
class column {
|
class column {
|
||||||
public:
|
public:
|
||||||
|
explicit column(std::string name)
|
||||||
|
: name_(std::move(name))
|
||||||
|
, attributes_(utils::null_attributes) {}
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
explicit column(std::string name, utils::field_attributes attr = utils::null_attributes)
|
explicit column(std::string name, utils::field_attributes attr = utils::null_attributes)
|
||||||
: column(std::move(name), data_type_traits<Type>::data_type(), attr)
|
: column(std::move(name), data_type_traits<Type>::data_type(), attr)
|
||||||
|
|
@ -27,5 +31,42 @@ private:
|
||||||
data_type_t type_{};
|
data_type_t type_{};
|
||||||
std::any value_;
|
std::any value_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User defined literal to have a shortcut creating a column object
|
||||||
|
* @param name Name of the column
|
||||||
|
* @param len Length of the column name
|
||||||
|
* @return A column object with given name
|
||||||
|
*/
|
||||||
|
column operator "" _col(const char *name, size_t len);
|
||||||
|
|
||||||
|
column make_column(const std::string &name, data_type_t type, utils::field_attributes attr = utils::null_attributes);
|
||||||
|
|
||||||
|
template < typename Type >
|
||||||
|
column make_column(const std::string &name, utils::field_attributes attr = utils::null_attributes)
|
||||||
|
{
|
||||||
|
return make_column(name, data_type_traits<Type>::builtin_type(0), attr);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
column make_column<std::string>(const std::string &name, utils::field_attributes attr);
|
||||||
|
|
||||||
|
template < typename Type >
|
||||||
|
column make_pk_column(const std::string &name, size_t size = 0)
|
||||||
|
{
|
||||||
|
return make_column<Type>(name, { size, utils::constraints::PRIMARY_KEY | utils::constraints::NOT_NULL});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
column make_pk_column<std::string>(const std::string &name, size_t size);
|
||||||
|
|
||||||
|
template < typename Type >
|
||||||
|
column make_fk_column(const std::string &name, size_t size = 0)
|
||||||
|
{
|
||||||
|
return make_column<Type>(name, { size, utils::constraints::FOREIGN_KEY });
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
column make_fk_column<std::string>(const std::string &name, size_t size);
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif //QUERY_COLUMN_HPP
|
#endif //QUERY_COLUMN_HPP
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,649 @@
|
||||||
|
#ifndef QUERY_CONDITION_HPP
|
||||||
|
#define QUERY_CONDITION_HPP
|
||||||
|
|
||||||
|
#include "matador/sql/basic_condition.hpp"
|
||||||
|
#include "matador/sql/dialect.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
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 L, class R, class Enabled = void>
|
||||||
|
class condition;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class condition<column, T, typename std::enable_if<
|
||||||
|
std::is_scalar<T>::value &&
|
||||||
|
!std::is_same<std::string, T>::value &&
|
||||||
|
!std::is_same<const char*, T>::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 T>
|
||||||
|
class condition<column, T, typename std::enable_if<
|
||||||
|
std::is_same<std::string, T>::value ||
|
||||||
|
std::is_same<const char*, T>::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 T>
|
||||||
|
class condition<T, column, typename std::enable_if<
|
||||||
|
std::is_scalar<T>::value &&
|
||||||
|
!std::is_same<std::string, T>::value &&
|
||||||
|
!std::is_same<const char*, T>::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 T>
|
||||||
|
class condition<T, column, typename std::enable_if<
|
||||||
|
std::is_same<std::string, T>::value ||
|
||||||
|
std::is_same<const char*, T>::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<column, std::initializer_list<V>> : 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<V> &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<V> 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 <table>)
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
class condition<column, query> : 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&>(query_), d.compile_type());
|
||||||
|
result += (")");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
query &query_;
|
||||||
|
};
|
||||||
|
|
||||||
|
condition<column, query>::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<column, std::pair<T, T>> : 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<T, T> &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<T, T> 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 L1, class R1, class L2, class R2>
|
||||||
|
class condition<condition<L1, R1>, condition<L2, R2>> : 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<L1, R1> &l, const condition<L2, R2> &r, basic_condition::operand_t op)
|
||||||
|
condition(condition<L1, R1> &&l, condition<L2, R2> &&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<L1, R1> left;
|
||||||
|
condition<L2, R2> 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 L, class R>
|
||||||
|
class condition<condition<L, R>, void> : public basic_condition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Create a logical unary condition
|
||||||
|
* @param c The condition to be negated
|
||||||
|
*/
|
||||||
|
condition(const condition<L, R> &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<L, R> 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<column, std::initializer_list<V>> in(const column &col, std::initializer_list<V> args)
|
||||||
|
{
|
||||||
|
return condition<column, std::initializer_list<V>>(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<column, query> 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<class T>
|
||||||
|
condition<column, std::pair<T, T>> between(const column &col, T low, T high)
|
||||||
|
{
|
||||||
|
return condition<column, std::pair<T, T>>(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<column, std::string> like(const column &col, const std::string &val);
|
||||||
|
//OOS_SQL_API condition<column, std::string> 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<class T>
|
||||||
|
condition<column, T> operator==(const column &col, T val)
|
||||||
|
{
|
||||||
|
return condition<column, T>(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<column, query> 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<class T>
|
||||||
|
condition<column, T> operator!=(const column &col, T val)
|
||||||
|
{
|
||||||
|
return condition<column, T>(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<class T>
|
||||||
|
condition<column, T> operator<(const column &col, T val)
|
||||||
|
{
|
||||||
|
return condition<column, T>(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<class T>
|
||||||
|
condition<column, T> operator<=(const column &col, T val)
|
||||||
|
{
|
||||||
|
return condition<column, T>(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<class T>
|
||||||
|
condition<column, T> operator>(const column &col, T val)
|
||||||
|
{
|
||||||
|
return condition<column, T>(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<class T>
|
||||||
|
condition<column, T> operator>=(const column &col, T val)
|
||||||
|
{
|
||||||
|
return condition<column, T>(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<class L1, class R1, class L2, class R2>
|
||||||
|
condition<condition<L1, R1>, condition<L2, R2>> operator&&(condition<L1, R1> l, condition<L2, R2> r)
|
||||||
|
{
|
||||||
|
return condition<condition<L1, R1>, condition<L2, R2>>(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<class L1, class R1, class L2, class R2>
|
||||||
|
condition<condition<L1, R1>, condition<L2, R2>> operator||(condition<L1, R1> l, condition<L2, R2> r)
|
||||||
|
{
|
||||||
|
return condition<condition<L1, R1>, condition<L2, R2>>(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<class L, class R>
|
||||||
|
condition<condition<L, R>, void> operator!(condition<L, R> c)
|
||||||
|
{
|
||||||
|
return condition<condition<L, R>, 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<class L, class R>
|
||||||
|
std::unique_ptr<basic_condition> make_condition(const condition<L, R> &cond)
|
||||||
|
{
|
||||||
|
return std::make_unique<condition<L, R>>(cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //QUERY_CONDITION_HPP
|
||||||
|
|
@ -6,10 +6,11 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace matador::sql {
|
namespace matador::sql {
|
||||||
|
|
||||||
class dialect
|
class [[nodiscard]] dialect
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class token_t : uint8_t
|
enum class token_t : uint8_t
|
||||||
|
|
@ -52,6 +53,14 @@ public:
|
||||||
NONE
|
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
|
* Holding enums concerning escaping identifiers
|
||||||
*/
|
*/
|
||||||
|
|
@ -110,9 +119,28 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual escape_identifier_t identifier_escape_type() const;
|
virtual escape_identifier_t identifier_escape_type() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a next placeholder string. default is
|
||||||
|
* question mark '?'
|
||||||
|
*
|
||||||
|
* @return Placeholder string
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
const std::vector<std::string>& host_vars() const;
|
||||||
|
const std::vector<std::string>& columns() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using token_to_string_map = std::unordered_map<token_t, std::string>;
|
using token_to_string_map = std::unordered_map<token_t, std::string>;
|
||||||
|
|
||||||
|
compile_type_t compile_type_ = compile_type_t::DIRECT;
|
||||||
|
std::vector<std::string> host_vars_;
|
||||||
|
std::vector<std::string> columns_;
|
||||||
|
|
||||||
token_to_string_map tokens_ {
|
token_to_string_map tokens_ {
|
||||||
{token_t::CREATE, "CREATE"},
|
{token_t::CREATE, "CREATE"},
|
||||||
{token_t::DROP, "DROP"},
|
{token_t::DROP, "DROP"},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef QUERY_KEY_VALUE_PAIR_HPP
|
||||||
|
#define QUERY_KEY_VALUE_PAIR_HPP
|
||||||
|
|
||||||
|
#include <any>
|
||||||
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
namespace matador::sql {
|
||||||
|
|
||||||
|
using any_type = std::variant<
|
||||||
|
char, short, int, long, long long,
|
||||||
|
unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long,
|
||||||
|
bool,
|
||||||
|
float, double,
|
||||||
|
const char*,
|
||||||
|
std::string>;
|
||||||
|
|
||||||
|
class key_value_pair
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
key_value_pair(const std::string &name, any_type value);
|
||||||
|
key_value_pair(const char *name, any_type value);
|
||||||
|
|
||||||
|
const std::string& name() const;
|
||||||
|
const any_type& value() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string name_;
|
||||||
|
any_type value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //QUERY_KEY_VALUE_PAIR_HPP
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
#ifndef QUERY_QUERY_BUILDER_HPP
|
#ifndef QUERY_QUERY_BUILDER_HPP
|
||||||
#define QUERY_QUERY_BUILDER_HPP
|
#define QUERY_QUERY_BUILDER_HPP
|
||||||
|
|
||||||
|
#include "matador/sql/basic_condition.hpp"
|
||||||
#include "matador/sql/column.hpp"
|
#include "matador/sql/column.hpp"
|
||||||
|
#include "matador/sql/key_value_pair.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
@ -12,13 +14,49 @@ namespace matador::sql {
|
||||||
|
|
||||||
class dialect;
|
class dialect;
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
struct any_type_to_string_visitor
|
||||||
|
{
|
||||||
|
any_type_to_string_visitor(const dialect &d) : d(d) {}
|
||||||
|
|
||||||
|
void operator()(char &x) { to_string(x); }
|
||||||
|
void operator()(short &x) { to_string(x); }
|
||||||
|
void operator()(int &x) { to_string(x); }
|
||||||
|
void operator()(long &x) { to_string(x); }
|
||||||
|
void operator()(long long &x) { to_string(x); }
|
||||||
|
void operator()(unsigned char &x) { to_string(x); }
|
||||||
|
void operator()(unsigned short &x) { to_string(x); }
|
||||||
|
void operator()(unsigned int &x) { to_string(x); }
|
||||||
|
void operator()(unsigned long &x) { to_string(x); }
|
||||||
|
void operator()(unsigned long long &x) { to_string(x); }
|
||||||
|
void operator()(bool &x) { to_string(x); }
|
||||||
|
void operator()(float &x) { to_string(x); }
|
||||||
|
void operator()(double &x) { to_string(x); }
|
||||||
|
void operator()(const char *x) { to_string(x); }
|
||||||
|
void operator()(std::string &x) { to_string(x); }
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
void to_string(Type &val)
|
||||||
|
{
|
||||||
|
result = std::to_string(val);
|
||||||
|
}
|
||||||
|
void to_string(const char *val);
|
||||||
|
void to_string(std::string &val);
|
||||||
|
|
||||||
|
const dialect &d;
|
||||||
|
std::string result;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
class query_builder
|
class query_builder
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
enum class state_t {
|
enum class state_t {
|
||||||
QUERY_INIT,
|
QUERY_INIT,
|
||||||
QUERY_CREATE,
|
QUERY_CREATE,
|
||||||
QUERY_TABLE,
|
QUERY_TABLE_CREATE,
|
||||||
|
QUERY_TABLE_DROP,
|
||||||
QUERY_DROP,
|
QUERY_DROP,
|
||||||
QUERY_SELECT,
|
QUERY_SELECT,
|
||||||
QUERY_INSERT,
|
QUERY_INSERT,
|
||||||
|
|
@ -49,12 +87,20 @@ private:
|
||||||
public:
|
public:
|
||||||
explicit query_builder(const dialect &d);
|
explicit query_builder(const dialect &d);
|
||||||
|
|
||||||
query_builder& create();
|
query_builder create();
|
||||||
|
query_builder drop();
|
||||||
|
query_builder select(std::initializer_list<std::string> 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<column> columns);
|
query_builder& table(const std::string &table, std::initializer_list<column> columns);
|
||||||
|
query_builder& table(const std::string &table);
|
||||||
query_builder& select(std::initializer_list<std::string> column_names);
|
query_builder& into(const std::string &table, std::initializer_list<std::string> column_names);
|
||||||
|
query_builder& values(std::initializer_list<any_type> values);
|
||||||
query_builder& from(const std::string &table, const std::string &as = "");
|
query_builder& from(const std::string &table, const std::string &as = "");
|
||||||
|
query_builder& set(std::initializer_list<key_value_pair> key_values);
|
||||||
|
query_builder& where(const basic_condition &cond);
|
||||||
|
|
||||||
std::string compile();
|
std::string compile();
|
||||||
|
|
||||||
|
|
@ -70,6 +116,8 @@ private:
|
||||||
|
|
||||||
std::vector<std::string> query_parts_;
|
std::vector<std::string> query_parts_;
|
||||||
|
|
||||||
|
detail::any_type_to_string_visitor value_to_string_;
|
||||||
|
|
||||||
using query_state_set = std::unordered_set<state_t>;
|
using query_state_set = std::unordered_set<state_t>;
|
||||||
using query_state_transition_map = std::unordered_map<state_t, query_state_set>;
|
using query_state_transition_map = std::unordered_map<state_t, query_state_set>;
|
||||||
using query_state_to_string_map = std::unordered_map<state_t, std::string>;
|
using query_state_to_string_map = std::unordered_map<state_t, std::string>;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,18 @@
|
||||||
set(SQL_SOURCES
|
set(SQL_SOURCES
|
||||||
sql/dialect.cpp
|
sql/dialect.cpp
|
||||||
sql/query_builder.cpp
|
sql/query_builder.cpp
|
||||||
sql/column.cpp)
|
sql/column.cpp
|
||||||
|
sql/key_value_pair.cpp
|
||||||
|
sql/basic_condition.cpp)
|
||||||
|
|
||||||
set(SQL_HEADER
|
set(SQL_HEADER
|
||||||
../include/matador/sql/dialect.hpp
|
../include/matador/sql/dialect.hpp
|
||||||
../include/matador/sql/query_builder.hpp
|
../include/matador/sql/query_builder.hpp
|
||||||
../include/matador/sql/column.hpp
|
../include/matador/sql/column.hpp
|
||||||
../include/matador/sql/types.hpp)
|
../include/matador/sql/types.hpp
|
||||||
|
../include/matador/sql/key_value_pair.hpp
|
||||||
|
../include/matador/sql/basic_condition.hpp
|
||||||
|
../include/matador/sql/condition.hpp)
|
||||||
|
|
||||||
set(UTILS_HEADER
|
set(UTILS_HEADER
|
||||||
../include/matador/utils/field_attributes.hpp
|
../include/matador/utils/field_attributes.hpp
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include "matador/sql/basic_condition.hpp"
|
||||||
|
|
||||||
|
namespace matador::sql {
|
||||||
|
|
||||||
|
std::unordered_map<basic_condition::operand_t, std::string> basic_condition::operands{
|
||||||
|
{operand_t::EQUAL, "="},
|
||||||
|
{operand_t::NOT_EQUAL, "<>"},
|
||||||
|
{operand_t::LESS, "<"},
|
||||||
|
{operand_t::LESS_EQUAL, "<="},
|
||||||
|
{operand_t::GREATER, ">"},
|
||||||
|
{operand_t::GREATER_EQUAL, ">="},
|
||||||
|
{operand_t::OR, "OR"},
|
||||||
|
{operand_t::AND, "AND"},
|
||||||
|
{operand_t::NOT, "NOT"},
|
||||||
|
{operand_t::IN_LIST, "IN"},
|
||||||
|
{operand_t::LIKE, "LIKE"}
|
||||||
|
};
|
||||||
|
|
||||||
|
basic_column_condition::basic_column_condition(column fld, basic_condition::operand_t op)
|
||||||
|
: field_(std::move(fld)), operand(basic_condition::operands[op])
|
||||||
|
{ }
|
||||||
|
|
||||||
|
basic_in_condition::basic_in_condition(column fld)
|
||||||
|
: field_(std::move(fld))
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
|
@ -20,4 +20,28 @@ data_type_t column::type() const {
|
||||||
return type_;
|
return type_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
column operator "" _col(const char *name, size_t len)
|
||||||
|
{
|
||||||
|
return column(std::string(name, len));
|
||||||
|
}
|
||||||
|
|
||||||
|
column make_column( const std::string& name, data_type_t type, utils::field_attributes attr )
|
||||||
|
{
|
||||||
|
return {name, type, attr};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
column make_column<std::string>( const std::string& name, utils::field_attributes attr ) {
|
||||||
|
return make_column(name, data_type_traits<std::string>::builtin_type(attr.size()), attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
column make_fk_column<std::string>( const std::string& name, size_t size ) {
|
||||||
|
return make_column<std::string>(name, { size, utils::constraints::FOREIGN_KEY });
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
column make_pk_column<std::string>( const std::string& name, size_t size ) {
|
||||||
|
return make_column<std::string>(name, { size, utils::constraints::PRIMARY_KEY | utils::constraints::NOT_NULL});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -52,8 +52,39 @@ void dialect::escape_quotes_in_literals(std::string &str) const
|
||||||
utils::replace_all(str, single_quote, double_quote);
|
utils::replace_all(str, single_quote, double_quote);
|
||||||
}
|
}
|
||||||
|
|
||||||
dialect::escape_identifier_t dialect::identifier_escape_type() const {
|
dialect::escape_identifier_t dialect::identifier_escape_type() const
|
||||||
|
{
|
||||||
return dialect::escape_identifier_t::ESCAPE_BOTH_SAME;
|
return dialect::escape_identifier_t::ESCAPE_BOTH_SAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dialect::add_column(const std::string &column)
|
||||||
|
{
|
||||||
|
columns_.emplace_back(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> &dialect::host_vars() const
|
||||||
|
{
|
||||||
|
return host_vars_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> &dialect::columns() const
|
||||||
|
{
|
||||||
|
return columns_;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#include "matador/sql/key_value_pair.hpp"
|
||||||
|
|
||||||
|
namespace matador::sql {
|
||||||
|
key_value_pair::key_value_pair(const std::string &name, any_type value)
|
||||||
|
: name_(name)
|
||||||
|
, value_(std::move(value)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
key_value_pair::key_value_pair(const char *name, any_type value)
|
||||||
|
: name_(name)
|
||||||
|
, value_(std::move(value)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &key_value_pair::name() const {
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const any_type& key_value_pair::value() const {
|
||||||
|
return value_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,17 +5,33 @@
|
||||||
|
|
||||||
namespace matador::sql {
|
namespace matador::sql {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
void any_type_to_string_visitor::to_string(const char *val)
|
||||||
|
{
|
||||||
|
result = "'" + d.prepare_literal(val) + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
void any_type_to_string_visitor::to_string(std::string &val)
|
||||||
|
{
|
||||||
|
result = "'" + d.prepare_literal(val) + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
std::string build_create_column(const column &col, const dialect &d);
|
||||||
|
|
||||||
// poor mens state machine
|
// poor mens state machine
|
||||||
// but does the job for query
|
// but does the job for query
|
||||||
query_builder::query_state_transition_map query_builder::transitions_{
|
query_builder::query_state_transition_map query_builder::transitions_{
|
||||||
{state_t::QUERY_INIT, {state_t::QUERY_CREATE, state_t::QUERY_DROP, state_t::QUERY_SELECT, state_t::QUERY_INSERT, state_t::QUERY_UPDATE, state_t::QUERY_DELETE}},
|
{state_t::QUERY_INIT, {state_t::QUERY_CREATE, state_t::QUERY_DROP, state_t::QUERY_SELECT, state_t::QUERY_INSERT, state_t::QUERY_UPDATE, state_t::QUERY_DELETE}},
|
||||||
{state_t::QUERY_CREATE, {state_t::QUERY_TABLE}},
|
{state_t::QUERY_CREATE, {state_t::QUERY_TABLE_CREATE}},
|
||||||
{state_t::QUERY_DROP, {state_t::QUERY_TABLE}},
|
{state_t::QUERY_DROP, {state_t::QUERY_TABLE_DROP}},
|
||||||
{state_t::QUERY_SELECT, {state_t::QUERY_FROM}},
|
{state_t::QUERY_SELECT, {state_t::QUERY_FROM}},
|
||||||
{state_t::QUERY_INSERT, {state_t::QUERY_INTO}},
|
{state_t::QUERY_INSERT, {state_t::QUERY_INTO}},
|
||||||
{state_t::QUERY_UPDATE, {state_t::QUERY_SET}},
|
{state_t::QUERY_UPDATE, {state_t::QUERY_SET}},
|
||||||
{state_t::QUERY_DELETE, {state_t::QUERY_FROM}},
|
{state_t::QUERY_DELETE, {state_t::QUERY_FROM}},
|
||||||
{state_t::QUERY_TABLE, {state_t::QUERY_FINISH}},
|
{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_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_SET, {state_t::QUERY_WHERE, state_t::QUERY_FINISH}},
|
||||||
{state_t::QUERY_INTO, {state_t::QUERY_VALUES}},
|
{state_t::QUERY_INTO, {state_t::QUERY_VALUES}},
|
||||||
|
|
@ -36,7 +52,8 @@ query_builder::query_state_to_string_map query_builder::state_strings_ {
|
||||||
{ state_t::QUERY_INSERT, "insert" },
|
{ state_t::QUERY_INSERT, "insert" },
|
||||||
{ state_t::QUERY_UPDATE, "update" },
|
{ state_t::QUERY_UPDATE, "update" },
|
||||||
{ state_t::QUERY_DELETE, "delete" },
|
{ state_t::QUERY_DELETE, "delete" },
|
||||||
{ state_t::QUERY_TABLE, "table" },
|
{ state_t::QUERY_TABLE_CREATE, "table" },
|
||||||
|
{ state_t::QUERY_TABLE_DROP, "table" },
|
||||||
{ state_t::QUERY_FROM, "from" },
|
{ state_t::QUERY_FROM, "from" },
|
||||||
{ state_t::QUERY_SET, "set" },
|
{ state_t::QUERY_SET, "set" },
|
||||||
{ state_t::QUERY_INTO, "into" },
|
{ state_t::QUERY_INTO, "into" },
|
||||||
|
|
@ -58,35 +75,90 @@ query_builder::query_command_to_string_map query_builder::command_strings_ {
|
||||||
};
|
};
|
||||||
|
|
||||||
query_builder::query_builder(const dialect &d)
|
query_builder::query_builder(const dialect &d)
|
||||||
: 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);
|
initialize(command_t::CREATE, state_t::QUERY_CREATE);
|
||||||
|
|
||||||
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::CREATE) + " ");
|
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::CREATE));
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder query_builder::drop() {
|
||||||
|
initialize(command_t::DROP, state_t::QUERY_DROP);
|
||||||
|
|
||||||
|
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::DROP));
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder query_builder::select(std::initializer_list<std::string> column_names) {
|
||||||
|
initialize(command_t::SELECT, state_t::QUERY_SELECT);
|
||||||
|
|
||||||
|
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::SELECT) + " ");
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
if (column_names.size() < 2) {
|
||||||
|
for (const auto &col : column_names) {
|
||||||
|
result.append(dialect_.prepare_identifier(col));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto it = column_names.begin();
|
||||||
|
result.append(dialect_.prepare_identifier(*it++));
|
||||||
|
for (it; it != column_names.end(); ++it) {
|
||||||
|
result.append(", ");
|
||||||
|
result.append(dialect_.prepare_identifier(*it));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query_parts_.emplace_back(result);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder query_builder::insert() {
|
||||||
|
initialize(command_t::INSERT, state_t::QUERY_INSERT);
|
||||||
|
|
||||||
|
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::INSERT));
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder query_builder::remove() {
|
||||||
|
initialize(command_t::REMOVE, state_t::QUERY_DELETE);
|
||||||
|
|
||||||
|
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::REMOVE));
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
query_builder &query_builder::table(const std::string &table, std::initializer_list<column> columns) {
|
query_builder &query_builder::table(const std::string &table, std::initializer_list<column> columns) {
|
||||||
transition_to(state_t::QUERY_TABLE);
|
transition_to(state_t::QUERY_TABLE_CREATE);
|
||||||
|
|
||||||
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table) + " ");
|
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table) + " ");
|
||||||
|
|
||||||
std::string result = "(";
|
std::string result = "(";
|
||||||
|
|
||||||
for (const auto &col : columns) {
|
if (columns.size() < 2) {
|
||||||
result.append(dialect_.prepare_identifier(col.name()) + " " + dialect_.data_type_at(col.type()));
|
for (const auto &col : columns) {
|
||||||
if (col.attributes().size() > 0) {
|
result.append(build_create_column(col, dialect_));
|
||||||
result.append("(" + std::to_string(col.attributes().size()) +")");
|
}
|
||||||
}
|
} else {
|
||||||
if (is_constraint_set(col.attributes().options(), utils::constraints::NOT_NULL)) {
|
auto it = columns.begin();
|
||||||
result.append(" NOT NULL");
|
result.append(build_create_column(*it++, dialect_));
|
||||||
}
|
for (it; it != columns.end(); ++it) {
|
||||||
if (is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) {
|
result.append(", ");
|
||||||
result.append(" PRIMARY KEY");
|
result.append(build_create_column(*it, dialect_));
|
||||||
}
|
}
|
||||||
result.append(", ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result += ")";
|
result += ")";
|
||||||
|
|
@ -94,22 +166,117 @@ query_builder &query_builder::table(const std::string &table, std::initializer_l
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
query_builder &query_builder::select(std::initializer_list<std::string> column_names) {
|
query_builder &query_builder::table(const std::string &table) {
|
||||||
initialize(command_t::SELECT, state_t::QUERY_SELECT);
|
transition_to(state_t::QUERY_TABLE_DROP);
|
||||||
|
|
||||||
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::SELECT) + " ");
|
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table));
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query_builder &query_builder::into(const std::string &table, std::initializer_list<std::string> column_names) {
|
||||||
|
transition_to(state_t::QUERY_INTO);
|
||||||
|
|
||||||
|
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::INTO) + " " + dialect_.prepare_identifier(table) + " ");
|
||||||
|
|
||||||
|
std::string result{"("};
|
||||||
|
if (column_names.size() < 2) {
|
||||||
|
for (const auto &col : column_names) {
|
||||||
|
result.append(dialect_.prepare_identifier(col));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto it = column_names.begin();
|
||||||
|
result.append(dialect_.prepare_identifier(*it++));
|
||||||
|
for (it; it != column_names.end(); ++it) {
|
||||||
|
result.append(", ");
|
||||||
|
result.append(dialect_.prepare_identifier(*it));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += (")");
|
||||||
|
|
||||||
|
query_parts_.emplace_back(result);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder &query_builder::values(std::initializer_list<any_type> values) {
|
||||||
|
transition_to(state_t::QUERY_VALUES);
|
||||||
|
|
||||||
|
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::VALUES) + " ");
|
||||||
|
|
||||||
|
std::string result{"("};
|
||||||
|
if (values.size() < 2) {
|
||||||
|
for (auto val : values) {
|
||||||
|
std::visit(value_to_string_, val);
|
||||||
|
result.append(value_to_string_.result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto it = values.begin();
|
||||||
|
auto val = *it++;
|
||||||
|
std::visit(value_to_string_, val);
|
||||||
|
result.append(value_to_string_.result);
|
||||||
|
for (; it != values.end(); ++it) {
|
||||||
|
result.append(", ");
|
||||||
|
val = *it;
|
||||||
|
std::visit(value_to_string_, val);
|
||||||
|
result.append(value_to_string_.result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += (")");
|
||||||
|
|
||||||
|
query_parts_.emplace_back(result);
|
||||||
|
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);
|
transition_to(state_t::QUERY_FROM);
|
||||||
|
|
||||||
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::TABLE) + " " + (as.empty() ? "" : as + " "));
|
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::FROM) + " " + dialect_.prepare_identifier(table) + (as.empty() ? "" : " " + as));
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query_builder &query_builder::set(std::initializer_list<key_value_pair> key_values) {
|
||||||
|
transition_to(state_t::QUERY_SET);
|
||||||
|
|
||||||
|
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::SET) + " ");
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
if (key_values.size() < 2) {
|
||||||
|
for (const auto &col : key_values) {
|
||||||
|
result.append(dialect_.prepare_identifier(col.name()) + "=");
|
||||||
|
auto var = col.value();
|
||||||
|
std::visit(value_to_string_, var);
|
||||||
|
result.append(value_to_string_.result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto it = key_values.begin();
|
||||||
|
result.append(dialect_.prepare_identifier(it->name()) + "=");
|
||||||
|
auto var = (it++)->value();
|
||||||
|
std::visit(value_to_string_, var);
|
||||||
|
result.append(value_to_string_.result);
|
||||||
|
for (; it != key_values.end(); ++it) {
|
||||||
|
result.append(", ");
|
||||||
|
result.append(dialect_.prepare_identifier((*it).name()) + "=");
|
||||||
|
var = it->value();
|
||||||
|
std::visit(value_to_string_, var);
|
||||||
|
result.append(value_to_string_.result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query_parts_.emplace_back(result);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) + " ");
|
||||||
|
query_parts_.emplace_back(cond.evaluate(const_cast<dialect &>(dialect_)));
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string query_builder::compile() {
|
std::string query_builder::compile() {
|
||||||
std::string result;
|
std::string result;
|
||||||
for (const auto &part : query_parts_) {
|
for (const auto &part : query_parts_) {
|
||||||
|
|
@ -133,4 +300,19 @@ void query_builder::initialize(query_builder::command_t cmd, query_builder::stat
|
||||||
query_parts_.clear();
|
query_parts_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string build_create_column(const column &col, const dialect &d) {
|
||||||
|
std::string result = d.prepare_identifier(col.name()) + " " + d.data_type_at(col.type());
|
||||||
|
if (col.attributes().size() > 0) {
|
||||||
|
result.append("(" + std::to_string(col.attributes().size()) +")");
|
||||||
|
}
|
||||||
|
if (is_constraint_set(col.attributes().options(), utils::constraints::NOT_NULL)) {
|
||||||
|
result.append(" NOT NULL");
|
||||||
|
}
|
||||||
|
if (is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) {
|
||||||
|
result.append(" PRIMARY KEY");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
Include(FetchContent)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
Catch2
|
||||||
|
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
||||||
|
GIT_TAG v3.4.0 # or a later release
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(Catch2)
|
||||||
|
|
||||||
|
add_executable(tests builder.cpp)
|
||||||
|
target_link_libraries(tests PRIVATE Catch2::Catch2WithMain matador)
|
||||||
|
target_include_directories(tests PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>/include)
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
#include <matador/sql/column.hpp>
|
||||||
|
#include <matador/sql/condition.hpp>
|
||||||
|
#include <matador/sql/dialect.hpp>
|
||||||
|
#include <matador/sql/query_builder.hpp>
|
||||||
|
|
||||||
|
using namespace matador::sql;
|
||||||
|
|
||||||
|
TEST_CASE("Create table", "[query]") {
|
||||||
|
dialect d;
|
||||||
|
query_builder query(d);
|
||||||
|
const auto sql = query.create().table("person", {
|
||||||
|
make_pk_column<unsigned long>("id"),
|
||||||
|
make_column<std::string>("name", 255),
|
||||||
|
make_column<unsigned short>("age")
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
REQUIRE(sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL PRIMARY KEY, "name" VARCHAR(255), "age" INTEGER))##");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Drop table", "[query]") {
|
||||||
|
dialect d;
|
||||||
|
query_builder query(d);
|
||||||
|
const auto sql = query.drop().table("person").compile();
|
||||||
|
|
||||||
|
REQUIRE(sql == R"(DROP TABLE "person")");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Select", "[query]") {
|
||||||
|
dialect d;
|
||||||
|
query_builder query(d);
|
||||||
|
const auto sql = query.select({"id", "name", "age"}).from("person").compile();
|
||||||
|
|
||||||
|
REQUIRE(sql == R"(SELECT "id", "name", "age" FROM "person")");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Insert", "[query]") {
|
||||||
|
dialect d;
|
||||||
|
query_builder query(d);
|
||||||
|
const auto sql = query.insert().into("person", {
|
||||||
|
"id", "name", "age"
|
||||||
|
}).values({7UL, "george", 65U}).compile();
|
||||||
|
|
||||||
|
REQUIRE(sql == R"(INSERT INTO "person" ("id", "name", "age") VALUES (7, 'george', 65))");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Update", "[query]") {
|
||||||
|
dialect d;
|
||||||
|
query_builder query(d);
|
||||||
|
const auto sql = query.update("person").set({
|
||||||
|
{"id", 7UL},
|
||||||
|
{"name", "george"},
|
||||||
|
{"age", 65U}
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
REQUIRE(sql == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Delete", "[query]") {
|
||||||
|
dialect d;
|
||||||
|
query_builder query(d);
|
||||||
|
const auto sql = query.remove().from("person").compile();
|
||||||
|
|
||||||
|
REQUIRE(sql == R"(DELETE FROM "person")");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Where", "[query]") {
|
||||||
|
dialect d;
|
||||||
|
query_builder query(d);
|
||||||
|
const auto sql = query.select({"id", "name", "age"})
|
||||||
|
.from("person")
|
||||||
|
.where("id"_col == 8)
|
||||||
|
.compile();
|
||||||
|
|
||||||
|
REQUIRE(sql == R"(SELECT "id", "name", "age" FROM "person" WHERE "id" = 8)");
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue