split column expression into separate classes

This commit is contained in:
sascha 2026-03-02 16:20:54 +01:00
parent 85c56584be
commit d9eaa2009f
31 changed files with 876 additions and 189 deletions

View File

@ -1,102 +0,0 @@
#ifndef MATADOR_COLUMN_EXPRESSION_HPP
#define MATADOR_COLUMN_EXPRESSION_HPP
#include "matador/query/table_column.hpp"
#include "matador/query/abstract_column_expression.hpp"
#include "matador/utils/placeholder.hpp"
#include "matador/utils/types.hpp"
namespace matador::query {
class binary_column_expression;
class table_column_expression;
class value_expression;
class placeholder_expression;
class expression_visitor {
public:
virtual ~expression_visitor() = default;
virtual void visit(const binary_column_expression& node) = 0;
virtual void visit(const table_column_expression& node) = 0;
virtual void visit(const value_expression& node) = 0;
virtual void visit(const placeholder_expression& node) = 0;
};
enum class binary_expression_operator {
Plus,
Minus,
Multiply,
Divide,
Modulo
};
class binary_column_expression : public abstract_column_expression {
public:
binary_column_expression() = delete;
binary_column_expression(column_expression_ptr left_column, binary_expression_operator operand, column_expression_ptr right_column);
void accept(expression_visitor& visitor) const override;
private:
column_expression_ptr left_column_;
binary_expression_operator operand_;
column_expression_ptr right_column_;
};
class table_column_expression : public abstract_column_expression {
public:
table_column_expression() = delete;
table_column_expression(table_column col);
void accept(expression_visitor& visitor) const override;
private:
table_column column_;
};
class value_expression : public abstract_column_expression {
public:
value_expression() = delete;
value_expression(utils::database_type value);
void accept(expression_visitor& visitor) const override;
private:
utils::database_type value_;
};
class placeholder_expression : public abstract_column_expression {
public:
placeholder_expression() = default;
void accept(expression_visitor& visitor) const override;
};
template<typename Type>
column_expression_ptr operator+(const table_column& col, Type val) {
return std::make_unique<binary_column_expression>(std::make_unique<table_column_expression>(col), binary_expression_operator::Plus, std::make_unique<value_expression>(val));
}
template<typename Type>
column_expression_ptr operator+(Type val, const table_column& col) {
return std::make_unique<binary_column_expression>(std::make_unique<value_expression>(val), binary_expression_operator::Plus, std::make_unique<table_column_expression>(col));
}
column_expression_ptr operator+(const table_column& col, utils::placeholder /*val*/);
column_expression_ptr operator+(utils::placeholder /*val*/, const table_column& col);
template<typename Type>
column_expression_ptr operator-(const table_column& col, Type val) {
return std::make_unique<binary_column_expression>(std::make_unique<table_column_expression>(col), binary_expression_operator::Minus, std::make_unique<value_expression>(val));
}
template<typename Type>
column_expression_ptr operator-(Type val, const table_column& col) {
return std::make_unique<binary_column_expression>(std::make_unique<value_expression>(val), binary_expression_operator::Minus, std::make_unique<table_column_expression>(col));
}
column_expression_ptr operator-(const table_column& col, utils::placeholder /*val*/);
column_expression_ptr operator-(utils::placeholder /*val*/, const table_column& col);
}
#endif //MATADOR_COLUMN_EXPRESSION_HPP

View File

@ -0,0 +1,31 @@
#ifndef MATADOR_BINARY_COLUMN_EXPRESSION_HPP
#define MATADOR_BINARY_COLUMN_EXPRESSION_HPP
#include "matador/query/expression/abstract_column_expression.hpp"
namespace matador::query {
enum class binary_expression_operator {
Plus,
Minus,
Multiply,
Divide,
Modulo
};
class binary_column_expression : public abstract_column_expression {
public:
binary_column_expression() = delete;
binary_column_expression(column_expression_ptr left_column, binary_expression_operator operand, column_expression_ptr right_column);
void accept(expression_visitor& visitor) const override;
[[nodiscard]] const column_expression_ptr& left_column() const;
[[nodiscard]] binary_expression_operator operand() const;
[[nodiscard]] const column_expression_ptr& right_column() const;
private:
column_expression_ptr left_column_;
binary_expression_operator operand_;
column_expression_ptr right_column_;
};
}
#endif //MATADOR_BINARY_COLUMN_EXPRESSION_HPP

View File

@ -0,0 +1,55 @@
#ifndef MATADOR_COLUMN_EXPRESSION_HPP
#define MATADOR_COLUMN_EXPRESSION_HPP
#include "matador/query/expression/table_column_expression.hpp"
#include "matador/query/expression/value_expression.hpp"
#include "matador/query/expression/placeholder_expression.hpp"
#include "matador/utils/placeholder.hpp"
#include "matador/utils/types.hpp"
namespace matador::query {
class column_expression {
public:
column_expression() = default;
explicit column_expression(column_expression_ptr expr) noexcept;
template<typename Type, typename = std::enable_if_t<std::is_convertible_v<Type, utils::database_type>>>
column_expression(Type&& value)
: column_expression(std::make_unique<value_expression>(std::forward<Type>(value))){
}
column_expression(const column_expression&) = delete;
column_expression& operator=(const column_expression&) = delete;
column_expression(column_expression&&) noexcept = default;
column_expression& operator=(column_expression&&) noexcept = default;
[[nodiscard]] bool empty() const noexcept;
[[nodiscard]] const abstract_column_expression* get() const noexcept;
[[nodiscard]] const column_expression_ptr& ptr() const noexcept;
column_expression_ptr release() noexcept;
// Move out for APIs that still take column_expression_ptr
operator column_expression_ptr() && noexcept;
// The feature you want: turn an expression into a SELECT-able column with alias
[[nodiscard]] table_column as(const std::string& alias) &&;
private:
column_expression_ptr expression_{};
};
// Helper constructors (optional but convenient)
inline column_expression make_column_expression(const table_column& col) {
return column_expression{std::make_unique<table_column_expression>(col)};
}
inline column_expression make_column_expression(utils::database_type v) {
return column_expression{std::make_unique<value_expression>(std::move(v))};
}
inline column_expression make_column_expression(utils::placeholder) {
return column_expression{std::make_unique<placeholder_expression>()};
}
}
#endif //MATADOR_COLUMN_EXPRESSION_HPP

View File

@ -0,0 +1,118 @@
#ifndef MATADOR_EXPRESSION_OPERATORS_HPP
#define MATADOR_EXPRESSION_OPERATORS_HPP
#include "matador/query/expression/binary_column_expression.hpp"
#include "matador/query/expression/table_column_expression.hpp"
#include "matador/query/expression/value_expression.hpp"
#include "matador/query/expression/column_expression.hpp"
#include "matador/utils/placeholder.hpp"
namespace matador::query {
template<typename Type>
column_expression operator+(const table_column& col, Type&& val) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<table_column_expression>(col),
binary_expression_operator::Plus,
std::make_unique<value_expression>(std::forward<Type>(val))
)};
}
template<typename Type>
column_expression operator+(Type&& val, const table_column& col) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<value_expression>(std::forward<Type>(val)),
binary_expression_operator::Plus,
std::make_unique<table_column_expression>(col)
)};
}
column_expression operator+(const table_column& col, utils::placeholder /*val*/);
column_expression operator+(utils::placeholder /*val*/, const table_column& col);
template<typename Type>
column_expression operator-(const table_column& col, Type&& val) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<table_column_expression>(col),
binary_expression_operator::Minus,
std::make_unique<value_expression>(std::forward<Type>(val))
)};
}
template<typename Type>
column_expression operator-(Type&& val, const table_column& col) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<value_expression>(std::forward<Type>(val)),
binary_expression_operator::Minus,
std::make_unique<table_column_expression>(col)
)};
}
column_expression operator-(const table_column& col, utils::placeholder /*val*/);
column_expression operator-(utils::placeholder /*val*/, const table_column& col);
template<typename Type>
column_expression operator*(const table_column& col, Type&& val) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<table_column_expression>(col),
binary_expression_operator::Multiply,
std::make_unique<value_expression>(std::forward<Type>(val))
)};
}
template<typename Type>
column_expression operator*(Type&& val, const table_column& col) {
return std::make_unique<binary_column_expression>(
std::make_unique<value_expression>(std::forward<Type>(val)),
binary_expression_operator::Multiply,
std::make_unique<table_column_expression>(col)
);
}
column_expression operator*(const table_column& col, utils::placeholder /*val*/);
column_expression operator*(utils::placeholder /*val*/, const table_column& col);
column_expression operator*(column_expression&& left, column_expression&& right);
template<typename Type>
column_expression operator/(const table_column& col, Type&& val) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<table_column_expression>(col),
binary_expression_operator::Divide,
std::make_unique<value_expression>(std::forward<Type>(val))
)};
}
template<typename Type>
column_expression operator/(Type&& val, const table_column& col) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<value_expression>(std::forward<Type>(val)),
binary_expression_operator::Divide,
std::make_unique<table_column_expression>(col)
)};
}
column_expression operator/(const table_column& col, utils::placeholder /*val*/);
column_expression operator/(utils::placeholder /*val*/, const table_column& col);
template<typename Type>
column_expression operator%(const table_column& col, Type&& val) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<table_column_expression>(col),
binary_expression_operator::Modulo,
std::make_unique<value_expression>(std::forward<Type>(val))
)};
}
template<typename Type>
column_expression operator%(Type&& val, const table_column& col) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<value_expression>(std::forward<Type>(val)),
binary_expression_operator::Modulo,
std::make_unique<table_column_expression>(col)
)};
}
column_expression operator%(const table_column& col, utils::placeholder /*val*/);
column_expression operator%(utils::placeholder /*val*/, const table_column& col);
}
#endif //MATADOR_EXPRESSION_OPERATORS_HPP

View File

@ -0,0 +1,20 @@
#ifndef MATADOR_EXPRESSION_VISITOR_HPP
#define MATADOR_EXPRESSION_VISITOR_HPP
namespace matador::query {
class binary_column_expression;
class table_column_expression;
class value_expression;
class placeholder_expression;
class expression_visitor {
public:
virtual ~expression_visitor() = default;
virtual void visit(const binary_column_expression& node) = 0;
virtual void visit(const table_column_expression& node) = 0;
virtual void visit(const value_expression& node) = 0;
virtual void visit(const placeholder_expression& node) = 0;
};
}
#endif //MATADOR_EXPRESSION_VISITOR_HPP

View File

@ -0,0 +1,14 @@
#ifndef MATADOR_PLACEHOLDER_EXPRESSION_HPP
#define MATADOR_PLACEHOLDER_EXPRESSION_HPP
#include "matador/query/expression/abstract_column_expression.hpp"
namespace matador::query {
class placeholder_expression : public abstract_column_expression {
public:
placeholder_expression() = default;
void accept(expression_visitor& visitor) const override;
};
}
#endif //MATADOR_PLACEHOLDER_EXPRESSION_HPP

View File

@ -0,0 +1,21 @@
#ifndef MATADOR_TABLE_COLUMN_EXPRESSION_HPP
#define MATADOR_TABLE_COLUMN_EXPRESSION_HPP
#include "matador/query/expression/abstract_column_expression.hpp"
#include "matador/query/table_column.hpp"
namespace matador::query {
class table_column_expression : public abstract_column_expression {
public:
table_column_expression() = delete;
explicit table_column_expression(table_column col);
void accept(expression_visitor& visitor) const override;
[[nodiscard]] const table_column& col() const;
private:
table_column column_;
};
}
#endif //MATADOR_TABLE_COLUMN_EXPRESSION_HPP

View File

@ -0,0 +1,22 @@
#ifndef MATADOR_VALUE_EXPRESSION_HPP
#define MATADOR_VALUE_EXPRESSION_HPP
#include "matador/query/expression/abstract_column_expression.hpp"
#include "matador/utils/types.hpp"
namespace matador::query {
class value_expression : public abstract_column_expression {
public:
value_expression() = delete;
explicit value_expression(utils::database_type value);
void accept(expression_visitor& visitor) const override;
[[nodiscard]] const utils::database_type& value() const;
private:
utils::database_type value_;
};
}
#endif //MATADOR_VALUE_EXPRESSION_HPP

View File

@ -0,0 +1,28 @@
#ifndef MATADOR_EXPRESSION_EVALUATOR_HPP
#define MATADOR_EXPRESSION_EVALUATOR_HPP
#include "matador/query/expression/expression_visitor.hpp"
#include <string>
namespace matador::sql {
class dialect;
struct query_context;
}
namespace matador::query {
class expression_evaluator final : public expression_visitor {
public:
expression_evaluator(const sql::dialect &d, sql::query_context &query);
void visit(const binary_column_expression& node) override;
void visit(const table_column_expression& node) override;
void visit(const value_expression& node) override;
void visit(const placeholder_expression& node) override;
private:
const sql::dialect &dialect_;
sql::query_context &query_;
std::string expression_;
};
}
#endif //MATADOR_EXPRESSION_EVALUATOR_HPP

View File

@ -4,20 +4,20 @@
#include "matador/query/intermediates/query_intermediate.hpp"
#include "matador/query/intermediates/query_set_intermediate.hpp"
#include "matador/query/column_expression.hpp"
#include "matador/query/expression/column_expression.hpp"
#include "matador/query/key_value_generator.hpp"
#include "matador/query/internal/column_value_pair.hpp"
namespace matador::query {
class query_update_intermediate : public query_intermediate {
class query_update_intermediate : public executable_query {
public:
explicit query_update_intermediate(const table& tab);
// query_set_intermediate set(std::initializer_list<internal::column_value_pair> columns);
// query_set_intermediate set(std::vector<internal::column_value_pair> &&columns);
query_update_intermediate& set(const table_column& col, column_expression_ptr expression);
query_update_intermediate& set(const table_column& col, column_expression&& expression);
template<class Type>
query_set_intermediate set(const Type &obj) {
return set(key_value_generator::generate(obj));

View File

@ -2,7 +2,7 @@
#define QUERY_KEY_VALUE_PAIR_HPP
#include "matador/query/table_column.hpp"
#include "matador/query/column_expression.hpp"
#include "../expression/column_expression.hpp"
#include "matador/utils/placeholder.hpp"
#include "matador/utils/types.hpp"

View File

@ -8,10 +8,12 @@
namespace matador::sql {
class dialect;
struct query_context;
}
namespace matador::query {
class table_column;
void prepare_column(sql::query_context& ctx, const sql::dialect& d, const table_column& col);
void prepare_column(std::string &out, const sql::dialect& d, const table_column &col);
[[nodiscard]] std::string prepare_identifier(const sql::dialect& d, const table_column &col);
[[nodiscard]] std::string prepare_criteria(const sql::dialect& d, const table_column &col);

View File

@ -1,7 +1,7 @@
#ifndef QUERY_COLUMN_HPP
#define QUERY_COLUMN_HPP
#include "matador/query/abstract_column_expression.hpp"
#include "expression/abstract_column_expression.hpp"
#include "matador/sql/sql_functions.hpp"
@ -94,6 +94,10 @@ public:
[[nodiscard]] const class table* table() const;
void table(const query::table* tab);
// New: expression-backed column support
[[nodiscard]] bool is_expression() const;
[[nodiscard]] const column_expression_ptr& expression() const;
// ReSharper disable once CppNonExplicitConversionOperator
operator const std::string&() const; // NOLINT(*-explicit-constructor)
@ -111,6 +115,8 @@ private:
utils::field_attributes attributes_{};
sql::sql_function_t function_{sql::sql_function_t::None};
column_expression_ptr expression_{};
};
table_column operator ""_col(const char *name, size_t len);

View File

@ -17,6 +17,17 @@ add_library(matador-orm STATIC
../../include/matador/query/criteria/like_criteria.hpp
../../include/matador/query/criteria/logical_criteria.hpp
../../include/matador/query/criteria_evaluator.hpp
../../include/matador/query/database.hpp
../../include/matador/query/error_code.hpp
../../include/matador/query/expression/abstract_column_expression.hpp
../../include/matador/query/expression/binary_column_expression.hpp
../../include/matador/query/expression/column_expression.hpp
../../include/matador/query/expression/expression_operators.hpp
../../include/matador/query/expression/expression_visitor.hpp
../../include/matador/query/expression/placeholder_expression.hpp
../../include/matador/query/expression/table_column_expression.hpp
../../include/matador/query/expression/value_expression.hpp
../../include/matador/query/expression_evaluator.hpp
../../include/matador/query/fk_value_extractor.hpp
../../include/matador/query/generator.hpp
../../include/matador/query/insert_query_builder.hpp
@ -47,6 +58,7 @@ add_library(matador-orm STATIC
../../include/matador/query/intermediates/query_select_intermediate.hpp
../../include/matador/query/intermediates/query_set_intermediate.hpp
../../include/matador/query/intermediates/query_update_intermediate.hpp
../../include/matador/query/intermediates/query_values_intermediate.hpp
../../include/matador/query/intermediates/query_where_intermediate.hpp
../../include/matador/query/internal/basic_type_to_string_visitor.hpp
../../include/matador/query/internal/column_value_pair.hpp
@ -54,11 +66,13 @@ add_library(matador-orm STATIC
../../include/matador/query/internal/string_builder_utils.hpp
../../include/matador/query/join_data.hpp
../../include/matador/query/key_value_generator.hpp
../../include/matador/query/manual_pk_generator.hpp
../../include/matador/query/meta_table_macro.hpp
../../include/matador/query/meta_table_macro.hpp
../../include/matador/query/query.hpp
../../include/matador/query/query_builder.hpp
../../include/matador/query/query_collection_resolver.hpp
../../include/matador/query/query_column.hpp
../../include/matador/query/query_data.hpp
../../include/matador/query/query_intermediates.hpp
../../include/matador/query/query_object_resolver.hpp
@ -70,6 +84,7 @@ add_library(matador-orm STATIC
../../include/matador/query/table.hpp
../../include/matador/query/table_column.hpp
../../include/matador/query/table_constraint.hpp
../../include/matador/query/table_pk_generator.hpp
../../include/matador/query/value_extractor.hpp
../../include/matador/sql/abstract_sql_logger.hpp
../../include/matador/sql/backend_provider.hpp
@ -106,6 +121,7 @@ add_library(matador-orm STATIC
../../include/matador/sql/statement_cache.hpp
orm/error_code.cpp
orm/session.cpp
query/abstract_pk_generator.cpp
query/attribute_string_writer.cpp
query/basic_schema.cpp
query/builder.cpp
@ -119,6 +135,14 @@ add_library(matador-orm STATIC
query/criteria/logical_criteria.cpp
query/criteria/not_criteria.cpp
query/criteria_evaluator.cpp
query/error_code.cpp
query/expression/binary_column_expression.cpp
query/expression/column_expression.cpp
query/expression/expression_operators.cpp
query/expression/placeholder_expression.cpp
query/expression/table_column_expression.cpp
query/expression/value_expression.cpp
query/expression_evaluator.cpp
query/generator.cpp
query/insert_query_builder.cpp
query/intermediates/executable_query.cpp
@ -147,6 +171,7 @@ add_library(matador-orm STATIC
query/intermediates/query_select_intermediate.cpp
query/intermediates/query_set_intermediate.cpp
query/intermediates/query_update_intermediate.cpp
query/intermediates/query_values_intermediate.cpp
query/intermediates/query_where_intermediate.cpp
query/internal/basic_type_to_string_visitor.cpp
query/internal/column_value_pair.cpp
@ -154,6 +179,7 @@ add_library(matador-orm STATIC
query/internal/query_result_impl.cpp
query/internal/string_builder_utils.cpp
query/key_value_generator.cpp
query/manual_pk_generator.cpp
query/query.cpp
query/query_builder.cpp
query/query_builder_exception.cpp
@ -166,6 +192,7 @@ add_library(matador-orm STATIC
query/table.cpp
query/table_column.cpp
query/table_constraint.cpp
query/table_pk_generator.cpp
query/value_extractor.cpp
sql/backend_provider.cpp
sql/connection.cpp
@ -192,20 +219,6 @@ add_library(matador-orm STATIC
sql/resolver_service.cpp
sql/statement.cpp
sql/statement_cache.cpp
../../include/matador/query/database.hpp
query/abstract_pk_generator.cpp
../../include/matador/query/manual_pk_generator.hpp
query/manual_pk_generator.cpp
../../include/matador/query/table_pk_generator.hpp
query/table_pk_generator.cpp
../../include/matador/query/intermediates/query_values_intermediate.hpp
query/intermediates/query_values_intermediate.cpp
../../include/matador/query/error_code.hpp
query/error_code.cpp
../../include/matador/query/column_expression.hpp
query/column_expression.cpp
../../include/matador/query/abstract_column_expression.hpp
../../include/matador/query/query_column.hpp
)
target_include_directories(matador-orm

View File

@ -1,49 +0,0 @@
#include "matador/query/column_expression.hpp"
namespace matador::query {
binary_column_expression::binary_column_expression(column_expression_ptr left_column, binary_expression_operator operand, column_expression_ptr right_column)
: left_column_(std::move(left_column))
, operand_(operand)
, right_column_(std::move(right_column)) {
}
void binary_column_expression::accept(expression_visitor &visitor) const {
visitor.visit(*this);
}
table_column_expression::table_column_expression(table_column col)
: column_(std::move(col)){
}
void table_column_expression::accept(expression_visitor &visitor) const {
visitor.visit(*this);
}
value_expression::value_expression(utils::database_type value)
: value_(std::move(value)) {
}
void value_expression::accept(expression_visitor &visitor) const {
visitor.visit(*this);
}
void placeholder_expression::accept(expression_visitor &visitor) const {
visitor.visit(*this);
}
column_expression_ptr operator+(const table_column &col, utils::placeholder /*placeholder*/) {
return std::make_unique<binary_column_expression>(std::make_unique<table_column_expression>(col), binary_expression_operator::Plus, std::make_unique<placeholder_expression>());
}
column_expression_ptr operator+(utils::placeholder /*placeholder*/, const table_column &col) {
return std::make_unique<binary_column_expression>(std::make_unique<placeholder_expression>(), binary_expression_operator::Plus, std::make_unique<table_column_expression>(col));
}
column_expression_ptr operator-(const table_column &col, utils::placeholder /*placeholder*/) {
return std::make_unique<binary_column_expression>(std::make_unique<table_column_expression>(col), binary_expression_operator::Minus, std::make_unique<placeholder_expression>());
}
column_expression_ptr operator-(utils::placeholder /*placeholder*/, const table_column &col) {
return std::make_unique<binary_column_expression>(std::make_unique<placeholder_expression>(), binary_expression_operator::Minus, std::make_unique<table_column_expression>(col));
}
}

View File

@ -8,8 +8,10 @@
#include "matador/query/criteria/logical_criteria.hpp"
#include "matador/query/criteria/not_criteria.hpp"
#include "matador/query/query_utils.hpp"
#include "matador/sql/dialect.hpp"
#include "matador/sql/query_context.hpp"
#include "matador/utils/enum_mapper.hpp"
#include "matador/utils/value.hpp"
@ -26,8 +28,8 @@ static const utils::enum_mapper<binary_operator> BinaryOperatorEnum({
}
criteria_evaluator::criteria_evaluator(const sql::dialect &d, sql::query_context &query)
: dialect_(d)
, query_(query) {
: dialect_(d)
, query_(query) {
}
std::string criteria_evaluator::evaluate(const abstract_criteria &node) {

View File

@ -0,0 +1,26 @@
#include "matador/query/expression/binary_column_expression.hpp"
#include "matador/query/expression/expression_visitor.hpp"
namespace matador::query {
binary_column_expression::binary_column_expression(column_expression_ptr left_column, const binary_expression_operator operand, column_expression_ptr right_column)
: left_column_(std::move(left_column))
, operand_(operand)
, right_column_(std::move(right_column)) {
}
void binary_column_expression::accept(expression_visitor &visitor) const {
visitor.visit(*this);
}
const column_expression_ptr& binary_column_expression::left_column() const {
return left_column_;
}
binary_expression_operator binary_column_expression::operand() const {
return operand_;
}
const column_expression_ptr& binary_column_expression::right_column() const {
return right_column_;
}
}

View File

@ -0,0 +1,31 @@
#include "matador/query/expression/column_expression.hpp"
namespace matador::query {
column_expression::column_expression(column_expression_ptr expr) noexcept
: expression_(std::move(expr)) {}
bool column_expression::empty() const noexcept {
return !expression_;
}
const abstract_column_expression* column_expression::get() const noexcept {
return expression_.get();
}
const column_expression_ptr& column_expression::ptr() const noexcept {
return expression_;
}
column_expression_ptr column_expression::release() noexcept {
return std::move(expression_);
}
column_expression::operator std::unique_ptr<abstract_column_expression>() && noexcept {
return std::move(expression_);
}
table_column column_expression::as(const std::string& alias) && {
const table_column col{std::move(expression_)};
return col.as(alias);
}
}

View File

@ -0,0 +1,90 @@
#include "matador/query/expression/expression_operators.hpp"
#include "matador/query/expression/placeholder_expression.hpp"
namespace matador::query {
column_expression operator+(const table_column &col, utils::placeholder /*placeholder*/) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<table_column_expression>(col),
binary_expression_operator::Plus,
std::make_unique<placeholder_expression>()
)};
}
column_expression operator+(utils::placeholder /*placeholder*/, const table_column &col) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<placeholder_expression>(),
binary_expression_operator::Plus,
std::make_unique<table_column_expression>(col)
)};
}
column_expression operator-(const table_column &col, utils::placeholder /*placeholder*/) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<table_column_expression>(col),
binary_expression_operator::Minus,
std::make_unique<placeholder_expression>()
)};
}
column_expression operator-(utils::placeholder /*placeholder*/, const table_column &col) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<placeholder_expression>(),
binary_expression_operator::Minus,
std::make_unique<table_column_expression>(col)
)};
}
column_expression operator*(const table_column& col, utils::placeholder /*placeholder*/) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<table_column_expression>(col),
binary_expression_operator::Multiply,
std::make_unique<placeholder_expression>()
)};
}
column_expression operator*(utils::placeholder /*placeholder*/, const table_column& col) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<placeholder_expression>(),
binary_expression_operator::Multiply,
std::make_unique<table_column_expression>(col)
)};
}
column_expression operator*(column_expression&& left, column_expression&& right) {
return column_expression{std::make_unique<binary_column_expression>(
left.release(), binary_expression_operator::Multiply, right.release()
)};
}
column_expression operator/(const table_column& col, utils::placeholder /*placeholder*/) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<table_column_expression>(col),
binary_expression_operator::Divide,
std::make_unique<placeholder_expression>()
)};
}
column_expression operator/(utils::placeholder /*placeholder*/, const table_column& col) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<placeholder_expression>(),
binary_expression_operator::Divide,
std::make_unique<table_column_expression>(col)
)};
}
column_expression operator%(const table_column& col, utils::placeholder /*placeholder*/) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<table_column_expression>(col),
binary_expression_operator::Modulo,
std::make_unique<placeholder_expression>()
)};
}
column_expression operator%(utils::placeholder /*placeholder*/, const table_column& col) {
return column_expression{std::make_unique<binary_column_expression>(
std::make_unique<placeholder_expression>(),
binary_expression_operator::Modulo,
std::make_unique<table_column_expression>(col)
)};
}
}

View File

@ -0,0 +1,9 @@
#include "matador/query/expression/placeholder_expression.hpp"
#include "matador/query/expression/expression_visitor.hpp"
namespace matador::query {
void placeholder_expression::accept(expression_visitor &visitor) const {
visitor.visit(*this);
}
}

View File

@ -0,0 +1,16 @@
#include "matador/query/expression/table_column_expression.hpp"
#include "matador/query/expression/expression_visitor.hpp"
namespace matador::query {
table_column_expression::table_column_expression(table_column col)
: column_(std::move(col)){
}
void table_column_expression::accept(expression_visitor &visitor) const {
visitor.visit(*this);
}
const table_column& table_column_expression::col() const {
return column_;
}
}

View File

@ -0,0 +1,16 @@
#include "matador/query/expression/value_expression.hpp"
#include "matador/query/expression/expression_visitor.hpp"
namespace matador::query {
value_expression::value_expression(utils::database_type value)
: value_(std::move(value)) {
}
void value_expression::accept(expression_visitor &visitor) const {
visitor.visit(*this);
}
const utils::database_type& value_expression::value() const {
return value_;
}
}

View File

@ -0,0 +1,57 @@
#include "matador/query/expression_evaluator.hpp"
#include "matador/query/expression/binary_column_expression.hpp"
#include "matador/query/expression/table_column_expression.hpp"
#include "matador/query/expression/placeholder_expression.hpp"
#include "matador/query/expression/value_expression.hpp"
#include "matador/query/internal/basic_type_to_string_visitor.hpp"
#include "matador/query/internal/string_builder_utils.hpp"
#include "matador/sql/dialect.hpp"
#include "matador/sql/query_context.hpp"
#include "matador/utils/enum_mapper.hpp"
namespace matador::query {
namespace detail {
static const utils::enum_mapper<binary_expression_operator> BinaryExpressionOperatorEnum({
{binary_expression_operator::Plus, "+"},
{binary_expression_operator::Minus, "-"},
{binary_expression_operator::Multiply, "*"},
{binary_expression_operator::Divide, "/"},
{binary_expression_operator::Modulo, "%"},
});
}
expression_evaluator::expression_evaluator(const sql::dialect &d, sql::query_context &query)
: dialect_(d)
, query_(query){
}
void expression_evaluator::visit(const binary_column_expression& node) {
expression_.append("(");
node.left_column()->accept(*this);
expression_.append(" ");
expression_.append(detail::BinaryExpressionOperatorEnum.to_string(node.operand()));
expression_.append(" ");
node.right_column()->accept(*this);
expression_.append(")");
}
void expression_evaluator::visit(const table_column_expression& node) {
prepare_identifier_string_append(expression_, node.col().name(), dialect_);
}
void expression_evaluator::visit(const value_expression& node) {
attribute_string_writer writer(dialect_, std::nullopt);
internal::basic_type_to_string_visitor v(writer, query_);
std::visit(v, node.value());
expression_.append(v.result);
}
void expression_evaluator::visit(const placeholder_expression& node) {
query_.bind_vars.emplace_back(std::string("value_") + std::to_string(query_.bind_vars.size() + 1));
expression_.append(dialect_.next_placeholder(query_.bind_vars));
}
}

View File

@ -17,7 +17,7 @@ query_update_intermediate::query_update_intermediate(const table& tab) {
// context_->parts.push_back(std::make_unique<internal::query_set_part>(std::move(columns)));
// return {context_};
// }
query_update_intermediate& query_update_intermediate::set(const table_column &col, column_expression_ptr expression) {
query_update_intermediate& query_update_intermediate::set(const table_column &col, column_expression&& expression) {
key_value_pairs_.emplace_back(col, std::move(expression));
return *this;
}

View File

@ -1,9 +1,39 @@
#include "matador/query/query_utils.hpp"
#include "matador/query/attribute_string_writer.hpp"
#include "matador/query/table.hpp"
#include "matador/query/internal/string_builder_utils.hpp"
#include "matador/query/expression_evaluator.hpp"
#include "matador/sql/dialect.hpp"
#include "matador/sql/query_context.hpp"
namespace matador::query {
void prepare_column(sql::query_context& ctx, const sql::dialect& d, const table_column& col) {
// Expression-backed select item: (<expr>) [AS alias]
if (col.is_expression()) {
// attribute_string_writer writer(d);
expression_evaluator v(d, ctx);
col.expression()->accept(v);
if (col.has_alias()) {
ctx.sql.append(" ").append(d.as()).append(" ").append(col.alias());
}
return;
}
// Existing behavior: plain column or function
if (!col.is_function()) {
prepare_identifier_string_append(ctx.sql, col.name(), d);
} else {
if (col.column_name() == d.asterisk()) {
ctx.sql += d.sql_function_at(col.function()) + "(" + col.column_name() + ")";
} else {
ctx.sql += d.sql_function_at(col.function()) + "(" + col.column_name() + ") " + d.as() + " " + col.alias();
}
}
}
void prepare_column(std::string &out, const sql::dialect &d, const table_column &col) {
if (!col.is_function()) {
prepare_identifier_string_append(out, col.name(), d);

View File

@ -144,6 +144,14 @@ void table_column::table(const query::table* tab) {
canonical_name_ = build_canonical_name(table_, column_name_);
}
bool table_column::is_expression() const {
return static_cast<bool>(expression_);
}
const column_expression_ptr& table_column::expression() const {
return expression_;
}
table_column::operator const std::string&() const {
return name();
}

View File

@ -2,7 +2,7 @@
#include "matador/query/query.hpp"
#include "matador/query/criteria.hpp"
#include "matador/query/error_code.hpp"
#include "matador/query/internal/column_value_pair.hpp"
#include "matador/query/expression/expression_operators.hpp"
namespace matador::query {
table_pk_generator::table_pk_generator(const std::string& table_name, const std::string &sequence_name)

View File

@ -31,6 +31,7 @@ add_executable(OrmTests
sql/StatementCacheTest.cpp
utils/auto_reset_event.cpp
utils/auto_reset_event.hpp
query/ColumnExpressionTest.cpp
)
target_link_libraries(OrmTests matador-orm matador-core Catch2::Catch2WithMain)

View File

@ -0,0 +1,222 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/query/expression/binary_column_expression.hpp"
#include "matador/query/expression/placeholder_expression.hpp"
#include "matador/query/expression/table_column_expression.hpp"
#include "matador/query/expression/value_expression.hpp"
#include "matador/query/expression/expression_visitor.hpp"
#include "matador/query/expression/expression_operators.hpp"
#include "matador/query/table_column.hpp"
#include "matador/utils/placeholder.hpp"
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
using matador::query::abstract_column_expression;
using matador::query::binary_column_expression;
using matador::query::binary_expression_operator;
using matador::query::column_expression_ptr;
using matador::query::expression_visitor;
using matador::query::operator""_col;
using matador::query::placeholder_expression;
using matador::query::table_column;
using matador::query::table_column_expression;
using matador::query::value_expression;
namespace {
template <typename...>
using void_t = void;
template <typename T, typename = void>
struct has_ptr_method : std::false_type {};
template <typename T>
struct has_ptr_method<T, void_t<decltype(std::declval<const T&>().ptr())>> : std::true_type {};
inline const column_expression_ptr& as_ptr_ref(const column_expression_ptr& e) {
return e;
}
template <typename Expr>
typename std::enable_if<has_ptr_method<Expr>::value, const column_expression_ptr&>::type
as_ptr_ref(const Expr& e) {
return e.ptr();
}
template <typename Expr>
const abstract_column_expression& as_expr_ref(const Expr& e) {
return *as_ptr_ref(e);
}
struct dispatch_probe_visitor final : expression_visitor {
void visit(const binary_column_expression& /*node*/) override { hits.emplace_back("binary"); }
void visit(const table_column_expression& /*node*/) override { hits.emplace_back("column"); }
void visit(const value_expression& /*node*/) override { hits.emplace_back("value"); }
void visit(const placeholder_expression& /*node*/) override { hits.emplace_back("placeholder"); }
std::vector<std::string> hits;
};
struct preorder_dump_visitor final : expression_visitor {
void visit(const binary_column_expression& node) override {
out.emplace_back("bin");
node.left_column()->accept(*this);
out.emplace_back(op_to_string(node.operand()));
node.right_column()->accept(*this);
}
void visit(const table_column_expression& node) override {
out.emplace_back(std::string("col:") + node.col().column_name());
}
void visit(const value_expression& node) override {
// We only assert this test for int literals below, so handle the int path explicitly.
// If utils::database_type evolves, this still stays a good smoke test.
std::string s = "val:";
bool appended = false;
std::visit([&](const auto& v) {
using V = std::decay_t<decltype(v)>;
if constexpr (std::is_same_v<V, int32_t> || std::is_same_v<V, int>) {
s += std::to_string(static_cast<long long>(v));
appended = true;
}
}, node.value());
if (!appended) {
s += "<non-int>";
}
out.emplace_back(std::move(s));
}
void visit(const placeholder_expression& /*node*/) override {
out.emplace_back("ph");
}
static std::string op_to_string(binary_expression_operator op) {
switch (op) {
case binary_expression_operator::Plus: return "+";
case binary_expression_operator::Minus: return "-";
case binary_expression_operator::Multiply: return "*";
case binary_expression_operator::Divide: return "/";
case binary_expression_operator::Modulo: return "%";
}
return "?";
}
std::vector<std::string> out;
};
} // namespace
TEST_CASE("column_expression nodes: accept dispatches to the correct visitor overload", "[query][expression][visitor]") {
dispatch_probe_visitor v;
{
auto n = std::make_unique<placeholder_expression>();
n->accept(v);
REQUIRE(v.hits.back() == "placeholder");
}
{
auto n = std::make_unique<value_expression>(int32_t{7});
n->accept(v);
REQUIRE(v.hits.back() == "value");
}
{
auto n = std::make_unique<table_column_expression>(table_column{"id"});
n->accept(v);
REQUIRE(v.hits.back() == "column");
}
{
auto n = std::make_unique<binary_column_expression>(
std::make_unique<table_column_expression>(table_column{"x"}),
binary_expression_operator::Plus,
std::make_unique<value_expression>(int32_t{1})
);
n->accept(v);
REQUIRE(v.hits.back() == "binary");
}
REQUIRE(v.hits.size() == 4);
}
TEST_CASE("binary_column_expression: accessors expose left/right and operand", "[query][expression]") {
auto left = std::make_unique<table_column_expression>(table_column{"x"});
auto right = std::make_unique<value_expression>(int32_t{123});
auto expr = binary_column_expression(std::move(left), binary_expression_operator::Minus, std::move(right));
REQUIRE(expr.operand() == binary_expression_operator::Minus);
REQUIRE(expr.left_column() != nullptr);
REQUIRE(expr.right_column() != nullptr);
dispatch_probe_visitor v;
expr.left_column()->accept(v);
expr.right_column()->accept(v);
REQUIRE(v.hits.size() == 2);
REQUIRE(v.hits[0] == "column");
REQUIRE(v.hits[1] == "value");
}
TEST_CASE("operator builders: build AST for column +/- value and column +/- placeholder", "[query][expression][operators]") {
using namespace matador::utils;
SECTION("column + int") {
auto e = "next_id"_col + 1;
const auto& root = dynamic_cast<const binary_column_expression&>(as_expr_ref(e));
REQUIRE(root.operand() == binary_expression_operator::Plus);
REQUIRE(dynamic_cast<const table_column_expression*>(root.left_column().get()) != nullptr);
REQUIRE(dynamic_cast<const value_expression*>(root.right_column().get()) != nullptr);
}
SECTION("column - placeholder") {
auto e = ("x"_col - _);
const auto& root = dynamic_cast<const binary_column_expression&>(as_expr_ref(e));
REQUIRE(root.operand() == binary_expression_operator::Minus);
REQUIRE(dynamic_cast<const table_column_expression*>(root.left_column().get()) != nullptr);
REQUIRE(dynamic_cast<const placeholder_expression*>(root.right_column().get()) != nullptr);
}
SECTION("placeholder + column") {
auto e = (_ + "x"_col);
const auto& root = dynamic_cast<const binary_column_expression&>(as_expr_ref(e));
REQUIRE(root.operand() == binary_expression_operator::Plus);
REQUIRE(dynamic_cast<const placeholder_expression*>(root.left_column().get()) != nullptr);
REQUIRE(dynamic_cast<const table_column_expression*>(root.right_column().get()) != nullptr);
}
}
TEST_CASE("expression visitor: can recursively traverse a nested arithmetic expression", "[query][expression][visitor]") {
using namespace matador::utils;
// (x + 1) * (y - ?)
auto e = ("x"_col + 1) * ("y"_col - _);
const auto& root = dynamic_cast<const binary_column_expression&>(as_expr_ref(e));
REQUIRE(root.operand() == binary_expression_operator::Multiply);
preorder_dump_visitor v;
root.accept(v);
// Expected preorder-ish dump:
// bin, (left subtree), *, (right subtree)
//
// left: bin col:x + val:1
// right: bin col:y - ph
const std::vector<std::string> expected = {
"bin",
"bin", "col:x", "+", "val:1",
"*",
"bin", "col:y", "-", "ph"
};
REQUIRE(v.out == expected);
}

View File

@ -15,21 +15,21 @@ TEST_CASE("Test placeholder generator", "[generator][placeholder]") {
}
TEST_CASE("Test column value generator", "[generator][column_value]") {
SECTION("Test column value generator for simple table") {
const airplane a380{1, "Airbus", "A380"};
const auto pairs = generator::column_value_pairs(a380);
const std::vector<internal::column_value_pair> expected_result{
{ "id", 1U },
{ "brand", std::string{"Airbus"} },
{ "model", std::string{"A380"} }
};
REQUIRE(pairs.size() == 3);
REQUIRE(pairs == expected_result);
}
SECTION("Test column value generator for table with foreign key") {
}
// SECTION("Test column value generator for simple table") {
// const airplane a380{1, "Airbus", "A380"};
// const auto pairs = generator::column_value_pairs(a380);
// const std::vector<internal::column_value_pair> expected_result{
// { "id", 1U },
// { "brand", std::string{"Airbus"} },
// { "model", std::string{"A380"} }
// };
// REQUIRE(pairs.size() == 3);
// REQUIRE(pairs == expected_result);
// }
//
// SECTION("Test column value generator for table with foreign key") {
//
// }
}
// TEST_CASE("Test column generator", "[generator][column]") {