diff --git a/include/matador/orm/error_code.hpp b/include/matador/orm/error_code.hpp index ab3461c..7bfc679 100644 --- a/include/matador/orm/error_code.hpp +++ b/include/matador/orm/error_code.hpp @@ -10,6 +10,7 @@ enum class error_code : uint8_t { Ok = 0, NoConnectionAvailable, UnknownType, + NoPrimaryKey, FailedToBuildQuery, FailedToFindObject, Failed diff --git a/include/matador/orm/session.hpp b/include/matador/orm/session.hpp index 8d9ba78..90489ab 100644 --- a/include/matador/orm/session.hpp +++ b/include/matador/orm/session.hpp @@ -121,9 +121,7 @@ public: template utils::result, utils::error> find(const PrimaryKeyType &pk); template - utils::result, utils::error> find(); - // template - // utils::result, utils::error> find(const Condition &cond); + utils::result, utils::error> find(query::criteria_ptr clause = {}); template utils::result drop_table(); @@ -291,16 +289,23 @@ utils::result session::remove( const object::object_ptr -utils::result, utils::error> session::find( const PrimaryKeyType& pk ) { +utils::result, utils::error> session::find(const PrimaryKeyType& pk) { auto info = schema_->info(); if (!info) { return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); } + if (!info.value().get().definition().has_primary_key()) { + return utils::failure(make_error(error_code::NoPrimaryKey, "Type hasn't primary key.")); + } + + const auto& type_info = info.value().get(); session_query_builder eqb(*schema_, *this); - auto data = eqb.build(pk); + const sql::column col(sql::table{type_info.reference_column()->table_name()}, type_info.reference_column()->name()); + using namespace matador::query; + auto data = eqb.build(col == utils::_); if (!data.is_ok()) { - return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info->get().name() + ".")); + return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + type_info.name() + ".")); } auto res = build_select_query(data.release()).prepare(*this); @@ -310,20 +315,20 @@ utils::result, utils::error> session::find( const Prima } auto stmt_result = res->bind(0, const_cast(pk)).template fetch_one(); if (stmt_result && !stmt_result.value()) { - return utils::failure(make_error(error_code::FailedToFindObject, "Failed to find object of type " + info->get().name() + " with primary key " + std::to_string(pk) + ".")); + return utils::failure(make_error(error_code::FailedToFindObject, "Failed to find object of type " + type_info.name() + " with primary key " + std::to_string(pk) + ".")); } return utils::ok(object::object_ptr{ stmt_result->release() }); } template -utils::result, utils::error> session::find() { +utils::result, utils::error> session::find(query::criteria_ptr clause) { auto info = schema_->info(); if (!info) { return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); } session_query_builder eqb(*schema_, *this); - auto data = eqb.build(); + auto data = eqb.build(std::move(clause)); if (!data.is_ok()) { return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info->get().name() + ".")); } @@ -336,27 +341,6 @@ utils::result, utils::error> session::find() { return result->template fetch(); } -// template -// utils::result, utils::error> session::find(const Condition &cond) { -// auto info = schema_->info(); -// if (!info) { -// return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); -// } -// -// session_query_builder eqb(*schema_, *this); -// auto data = eqb.build(); -// if (!data.is_ok()) { -// return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info->get().name() + ".")); -// } -// -// auto result = build_select_query(data.release()).prepare(*this); -// if (!result.is_ok()) { -// return utils::failure(result.err()); -// } -// -// return result->template fetch(); -// } -// template utils::result session::drop_table() { auto info = schema_->info(); diff --git a/include/matador/orm/session_query_builder.hpp b/include/matador/orm/session_query_builder.hpp index bab87eb..ae3e45b 100644 --- a/include/matador/orm/session_query_builder.hpp +++ b/include/matador/orm/session_query_builder.hpp @@ -11,6 +11,7 @@ #include "matador/object/join_columns_collector.hpp" #include "matador/object/repository.hpp" +#include "matador/query/criteria/criteria_visitor.hpp" #include "matador/utils/primary_key_attribute.hpp" #include "matador/utils/result.hpp" @@ -30,46 +31,49 @@ struct entity_query_data { query::criteria_ptr where_clause; }; +class criteria_transformer final : public query::criteria_visitor { +public: + criteria_transformer(const object::repository &repo, const std::unordered_map>& tables_by_name); + void visit( const query::between_criteria& node ) override; + void visit( const query::binary_criteria& node ) override; + void visit( const query::binary_column_criteria& node ) override; + void visit( const query::collection_criteria& node ) override; + void visit( const query::collection_query_criteria& node ) override; + void visit( const query::like_criteria& node ) override; + void visit( const query::logical_criteria& node ) override; + void visit( const query::not_criteria& node ) override; + +private: + void update_criteria_column(const query::abstract_column_criteria& node) const; + +private: + const object::repository &repo_; + const std::unordered_map>& tables_by_name_; +}; + class session_query_builder final { public: session_query_builder(const object::repository &scm, sql::executor &exec) : schema_(scm) , executor_(exec){} - template - utils::result build(const PrimaryKeyType &pk) { - auto info = schema_.info(); - if (!info) { - return utils::failure(query_build_error::UnknownType); - } - pk_ = pk; - table_info_stack_.push({info.value(), std::make_shared(info.value().get().name(), build_alias('t', ++table_index))}); - entity_query_data_ = { table_info_stack_.top().table }; - processed_tables_.insert({info->get().name(), entity_query_data_.root_table}); - try { - access::process(*this, info->get().prototype()); - - return {utils::ok(std::move(entity_query_data_))}; - } catch (const query_builder_exception &ex) { - return {utils::failure(ex.error_type())}; - } catch (...) { - return {utils::failure(query_build_error::UnexpectedError)}; - } - } - template - utils::result build() { + utils::result build(query::criteria_ptr clause = {}) { const auto info = schema_.info(); if (!info) { return utils::failure(query_build_error::UnknownType); } - pk_ = nullptr; table_info_stack_.push({info.value(), std::make_shared(info.value().get().name(), build_alias('t', ++table_index))}); entity_query_data_ = { table_info_stack_.top().table }; processed_tables_.insert({info->get().name(), entity_query_data_.root_table}); try { access::process(*this, info->get().prototype()); + if (clause) { + criteria_transformer transformer{schema_, processed_tables_}; + clause->accept(transformer); + entity_query_data_.where_clause = std::move(clause); + } return {utils::ok(std::move(entity_query_data_))}; } catch (const query_builder_exception &ex) { return {utils::failure(ex.error_type())}; @@ -79,19 +83,12 @@ public: } template < class V > - void on_primary_key(const char *id, V &, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) - { + void on_primary_key(const char *id, V &, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) { push(id); if (!is_root_entity()) { return; } entity_query_data_.pk_column_name = id; - if (!pk_.is_null()) { - const auto c = sql::column{table_info_stack_.top().table, id, ""}; - using namespace matador::query; - auto co = c == utils::_; - entity_query_data_.where_clause = std::move(co); - } } void on_revision(const char *id, uint64_t &/*rev*/); @@ -235,7 +232,6 @@ private: void append_join(const sql::column &left, const sql::column &right); private: - utils::value pk_; struct table_info { std::reference_wrapper info; std::shared_ptr table; diff --git a/include/matador/query/criteria/abstract_column_criteria.hpp b/include/matador/query/criteria/abstract_column_criteria.hpp new file mode 100644 index 0000000..263c115 --- /dev/null +++ b/include/matador/query/criteria/abstract_column_criteria.hpp @@ -0,0 +1,22 @@ +#ifndef MATADOR_ABSTRACT_COLUMN_CRITERIA_HPP +#define MATADOR_ABSTRACT_COLUMN_CRITERIA_HPP + +#include "matador/query/criteria/abstract_criteria.hpp" + +#include "matador/sql/column.hpp" + +namespace matador::query { + +class abstract_column_criteria : public abstract_criteria { +public: + abstract_column_criteria() = delete; + explicit abstract_column_criteria(sql::column column); + + [[nodiscard]] const sql::column& column() const; + +protected: + sql::column column_; +}; + +} +#endif //MATADOR_ABSTRACT_COLUMN_CRITERIA_HPP \ No newline at end of file diff --git a/include/matador/query/criteria/between_criteria.hpp b/include/matador/query/criteria/between_criteria.hpp index 3cf6ead..107b3c9 100644 --- a/include/matador/query/criteria/between_criteria.hpp +++ b/include/matador/query/criteria/between_criteria.hpp @@ -1,16 +1,14 @@ #ifndef CRITERIA_BETWEEN_CRITERIA_NODE_HPP #define CRITERIA_BETWEEN_CRITERIA_NODE_HPP -#include "matador/query/criteria/abstract_criteria.hpp" +#include "matador/query/criteria/abstract_column_criteria.hpp" #include "matador/query/criteria/criteria_utils.hpp" -#include "matador/sql/column.hpp" - #include "matador/utils/value.hpp" namespace matador::query { -class between_criteria final : public abstract_criteria { +class between_criteria final : public abstract_column_criteria { public: between_criteria() = delete; between_criteria(sql::column column, int64_t min, int64_t max); @@ -18,12 +16,10 @@ public: void accept(criteria_visitor& visitor) const override; - [[nodiscard]] const sql::column& column() const; [[nodiscard]] const criteria_value &minimum() const; [[nodiscard]] const criteria_value &maximum() const; private: - sql::column column_; criteria_value min_; criteria_value max_; }; diff --git a/include/matador/query/criteria/binary_criteria.hpp b/include/matador/query/criteria/binary_criteria.hpp index 1dafa1c..3670692 100644 --- a/include/matador/query/criteria/binary_criteria.hpp +++ b/include/matador/query/criteria/binary_criteria.hpp @@ -1,7 +1,7 @@ #ifndef CRITERIA_BINARY_CRITERIA_NODE_HPP #define CRITERIA_BINARY_CRITERIA_NODE_HPP -#include "matador/query/criteria/abstract_criteria.hpp" +#include "matador/query/criteria/abstract_column_criteria.hpp" #include "matador/query/criteria/criteria_utils.hpp" #include "matador/sql/column.hpp" @@ -16,19 +16,17 @@ enum class binary_operator { LESS_THAN_OR_EQUAL, }; -class binary_criteria final : public abstract_criteria { +class binary_criteria final : public abstract_column_criteria { public: binary_criteria() = delete; binary_criteria(sql::column column, binary_operator operand, criteria_value value); void accept( criteria_visitor& visitor ) const override; - [[nodiscard]] const sql::column& column() const; [[nodiscard]] binary_operator operand() const; [[nodiscard]] const criteria_value& value() const; private: - sql::column column_; binary_operator operator_{}; criteria_value value_; }; diff --git a/include/matador/query/criteria/collection_criteria.hpp b/include/matador/query/criteria/collection_criteria.hpp index 9a3cf21..8db7bbf 100644 --- a/include/matador/query/criteria/collection_criteria.hpp +++ b/include/matador/query/criteria/collection_criteria.hpp @@ -1,7 +1,7 @@ #ifndef CRITERIA_COLLECTION_CRITERIA_NODE_HPP #define CRITERIA_COLLECTION_CRITERIA_NODE_HPP -#include "matador/query/criteria/abstract_criteria.hpp" +#include "matador/query/criteria/abstract_column_criteria.hpp" #include "matador/query/criteria/criteria_utils.hpp" #include "matador/sql/column.hpp" @@ -23,7 +23,7 @@ enum class collection_operator { * WHERE age IN (29,34,56) * @endcode */ -class collection_criteria final : public abstract_criteria { +class collection_criteria final : public abstract_column_criteria { public: collection_criteria() = delete; /** @@ -41,29 +41,25 @@ public: void accept(criteria_visitor& visitor) const override; - [[nodiscard]] const sql::column& column() const; [[nodiscard]] collection_operator operand() const; [[nodiscard]] const std::vector& values() const; private: - sql::column column_; collection_operator operand_; std::vector values_; }; -class collection_query_criteria final : public abstract_criteria { +class collection_query_criteria final : public abstract_column_criteria { public: collection_query_criteria() = delete; collection_query_criteria(sql::column col, collection_operator operand_, sql::query_context ctx); void accept(criteria_visitor& visitor) const override; - [[nodiscard]] const sql::column& column() const; [[nodiscard]] collection_operator operand() const; [[nodiscard]] const sql::query_context& context() const; private: - sql::column column_; collection_operator operand_; sql::query_context query_context_; }; diff --git a/include/matador/query/criteria/criteria_operators.hpp b/include/matador/query/criteria/criteria_operators.hpp index eb9ec2b..49e5e89 100644 --- a/include/matador/query/criteria/criteria_operators.hpp +++ b/include/matador/query/criteria/criteria_operators.hpp @@ -19,8 +19,6 @@ criteria_ptr operator==(const sql::column &col, Type val) { return std::make_unique(col, binary_operator::EQUALS, utils::value(val)); } -criteria_ptr operator==(const sql::column &col_left, const sql::column &col_right); - template criteria_ptr operator!=(const sql::column &col, Type val) { return std::make_unique(col, binary_operator::NOT_EQUALS, utils::value(val)); @@ -46,6 +44,13 @@ criteria_ptr operator<=(const sql::column &col, Type val) { return std::make_unique(col, binary_operator::LESS_THAN_OR_EQUAL, utils::value(val)); } +criteria_ptr operator==(const sql::column &col_left, const sql::column &col_right); +criteria_ptr operator!=(const sql::column &col_left, const sql::column &col_right); +criteria_ptr operator>(const sql::column &col_left, const sql::column &col_right); +criteria_ptr operator>=(const sql::column &col_left, const sql::column &col_right); +criteria_ptr operator<(const sql::column &col_left, const sql::column &col_right); +criteria_ptr operator<=(const sql::column &col_left, const sql::column &col_right); + criteria_ptr operator==(const sql::column &col, utils::placeholder p); criteria_ptr operator!=(const sql::column &col, utils::placeholder p); criteria_ptr operator>(const sql::column &col, utils::placeholder p); diff --git a/include/matador/query/criteria/like_criteria.hpp b/include/matador/query/criteria/like_criteria.hpp index 724dd9d..53f0f44 100644 --- a/include/matador/query/criteria/like_criteria.hpp +++ b/include/matador/query/criteria/like_criteria.hpp @@ -1,23 +1,21 @@ #ifndef CRITERIA_LIKE_CRITERIA_NODE_HPP #define CRITERIA_LIKE_CRITERIA_NODE_HPP -#include "matador/query/criteria/abstract_criteria.hpp" +#include "matador/query/criteria/abstract_column_criteria.hpp" #include "matador/sql/column.hpp" namespace matador::query { -class like_criteria final : public abstract_criteria { +class like_criteria final : public abstract_column_criteria { public: like_criteria() = delete; like_criteria(sql::column column, std::string pattern); void accept(criteria_visitor &visitor) const override; - [[nodiscard]] const sql::column& column() const; [[nodiscard]] const std::string& pattern() const; private: - sql::column column_; std::string pattern_; }; } diff --git a/include/matador/query/criteria/logical_criteria.hpp b/include/matador/query/criteria/logical_criteria.hpp index af4e9c0..4cbdf38 100644 --- a/include/matador/query/criteria/logical_criteria.hpp +++ b/include/matador/query/criteria/logical_criteria.hpp @@ -21,9 +21,9 @@ public: [[nodiscard]] const criteria_ptr& right_clause() const; private: - std::unique_ptr left_criteria_; + criteria_ptr left_criteria_; logical_operator operand_; - std::unique_ptr right_criteria_; + criteria_ptr right_criteria_; }; } #endif //CRITERIA_LOGICAL_CRITERIA_NODE_HPP \ No newline at end of file diff --git a/include/matador/sql/column.hpp b/include/matador/sql/column.hpp index 937a311..2fdfba3 100644 --- a/include/matador/sql/column.hpp +++ b/include/matador/sql/column.hpp @@ -31,11 +31,12 @@ struct column { [[nodiscard]] const std::string& column_name() const; [[nodiscard]] std::string full_name() const; + [[nodiscard]] const std::string& alias_name() const; [[nodiscard]] bool is_function() const; [[nodiscard]] bool has_alias() const; + [[nodiscard]] std::shared_ptr table() const; - std::shared_ptr table_; - using table_ref = std::reference_wrapper; + std::shared_ptr table_; std::string name; std::string alias; sql_function_t function_{sql_function_t::NONE}; diff --git a/source/orm/CMakeLists.txt b/source/orm/CMakeLists.txt index c238b0c..39497db 100644 --- a/source/orm/CMakeLists.txt +++ b/source/orm/CMakeLists.txt @@ -150,6 +150,8 @@ add_library(matador-orm STATIC ../../include/matador/query/criteria_evaluator.hpp query/criteria_evaluator.cpp ../../include/matador/query/criteria/criteria_utils.hpp + ../../include/matador/query/criteria/abstract_column_criteria.hpp + query/criteria/abstract_column_criteria.cpp ) target_include_directories(matador-orm diff --git a/source/orm/orm/error_code.cpp b/source/orm/orm/error_code.cpp index 0044642..26a911b 100644 --- a/source/orm/orm/error_code.cpp +++ b/source/orm/orm/error_code.cpp @@ -14,6 +14,8 @@ std::string orm_category_impl::message(const int ev) const { return "No connection available"; case error_code::UnknownType: return "Unknown type"; + case error_code::NoPrimaryKey: + return "No primary key"; case error_code::FailedToBuildQuery: return "Failed to build query"; case error_code::FailedToFindObject: diff --git a/source/orm/orm/session_query_builder.cpp b/source/orm/orm/session_query_builder.cpp index 6cdc3d2..c84287f 100644 --- a/source/orm/orm/session_query_builder.cpp +++ b/source/orm/orm/session_query_builder.cpp @@ -3,6 +3,50 @@ #include namespace matador::orm { +criteria_transformer::criteria_transformer(const object::repository& repo, const std::unordered_map>& tables_by_name) +: repo_(repo) +, tables_by_name_(tables_by_name) {} + +void criteria_transformer::visit( const query::between_criteria& node ) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const query::binary_criteria& node ) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const query::binary_column_criteria& node ) {} + +void criteria_transformer::visit( const query::collection_criteria& node ) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const query::collection_query_criteria& node ) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const query::like_criteria& node ) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const query::logical_criteria& node ) { + node.left_clause()->accept(*this); + node.right_clause()->accept(*this); +} + +void criteria_transformer::visit( const query::not_criteria& node ) { + node.clause()->accept(*this); +} + +void criteria_transformer::update_criteria_column(const query::abstract_column_criteria& node) const { + const auto it = tables_by_name_.find(node.column().table()->name); + if (it == tables_by_name_.end()) { + return; + } + + const_cast(node.column()).table_ = it->second; +} + void session_query_builder::on_revision(const char *id, uint64_t &/*rev*/) { push(id); } diff --git a/source/orm/query/criteria/abstract_column_criteria.cpp b/source/orm/query/criteria/abstract_column_criteria.cpp new file mode 100644 index 0000000..6686217 --- /dev/null +++ b/source/orm/query/criteria/abstract_column_criteria.cpp @@ -0,0 +1,10 @@ +#include "matador/query/criteria/abstract_column_criteria.hpp" + +namespace matador::query { +abstract_column_criteria::abstract_column_criteria(sql::column column) +: column_(std::move(column)) {} + +const sql::column& abstract_column_criteria::column() const { + return column_; +} +} \ No newline at end of file diff --git a/source/orm/query/criteria/between_criteria.cpp b/source/orm/query/criteria/between_criteria.cpp index 73e36a0..6058faa 100644 --- a/source/orm/query/criteria/between_criteria.cpp +++ b/source/orm/query/criteria/between_criteria.cpp @@ -4,13 +4,13 @@ namespace matador::query{ between_criteria::between_criteria(sql::column column, const int64_t min, const int64_t max) -: column_(std::move(column)) +: abstract_column_criteria(std::move(column)) , min_(utils::value{min}) , max_(utils::value{max}) {} between_criteria::between_criteria(sql::column column, utils::placeholder min, utils::placeholder max) -: column_(std::move(column)) +: abstract_column_criteria(std::move(column)) , min_(min) , max_(max) {} @@ -19,10 +19,6 @@ void between_criteria::accept( criteria_visitor& visitor ) const { visitor.visit(*this); } -const sql::column & between_criteria::column() const { - return column_; -} - const criteria_value &between_criteria::minimum() const { return min_; } diff --git a/source/orm/query/criteria/binary_criteria.cpp b/source/orm/query/criteria/binary_criteria.cpp index 4a8fa61..65e05f9 100644 --- a/source/orm/query/criteria/binary_criteria.cpp +++ b/source/orm/query/criteria/binary_criteria.cpp @@ -4,7 +4,7 @@ namespace matador::query { binary_criteria::binary_criteria(sql::column column, const binary_operator operand, criteria_value value) -: column_(std::move(column)) +: abstract_column_criteria(std::move(column)) , operator_(operand) , value_(std::move(value)) {} @@ -13,10 +13,6 @@ void binary_criteria::accept( criteria_visitor& visitor ) const { visitor.visit(*this); } -const sql::column & binary_criteria::column() const { - return column_; -} - binary_operator binary_criteria::operand() const { return operator_; } diff --git a/source/orm/query/criteria/collection_criteria.cpp b/source/orm/query/criteria/collection_criteria.cpp index 38ba41f..dcc57f3 100644 --- a/source/orm/query/criteria/collection_criteria.cpp +++ b/source/orm/query/criteria/collection_criteria.cpp @@ -4,13 +4,13 @@ namespace matador::query { collection_criteria::collection_criteria(sql::column col, const collection_operator operand_, std::vector values ) -: column_(std::move(col)) +: abstract_column_criteria(std::move(col)) , operand_(operand_) , values_(std::move(values)) {} collection_criteria::collection_criteria(sql::column col, const collection_operator operand_, const std::initializer_list values ) -: column_(std::move(col)) +: abstract_column_criteria(std::move(col)) , operand_(operand_) , values_(values) {} @@ -19,10 +19,6 @@ void collection_criteria::accept(criteria_visitor& visitor) const { visitor.visit(*this); } -const sql::column & collection_criteria::column() const { - return column_; -} - collection_operator collection_criteria::operand() const { return operand_; } @@ -32,7 +28,7 @@ const std::vector& collection_criteria::values() const { } collection_query_criteria::collection_query_criteria(sql::column col, collection_operator operand_, sql::query_context ctx) -: column_(std::move(col)) +: abstract_column_criteria(std::move(col)) , operand_(operand_) , query_context_(std::move(ctx)){ } @@ -41,10 +37,6 @@ void collection_query_criteria::accept(criteria_visitor &visitor) const { visitor.visit(*this); } -const sql::column & collection_query_criteria::column() const { - return column_; -} - collection_operator collection_query_criteria::operand() const { return operand_; } diff --git a/source/orm/query/criteria/criteria_operators.cpp b/source/orm/query/criteria/criteria_operators.cpp index b791b2b..d7de339 100644 --- a/source/orm/query/criteria/criteria_operators.cpp +++ b/source/orm/query/criteria/criteria_operators.cpp @@ -6,10 +6,6 @@ #include "matador/query/criteria/not_criteria.hpp" namespace matador::query { -criteria_ptr operator==( const sql::column& col_left, const sql::column& col_right ) { - return std::make_unique(col_left, binary_operator::EQUALS, col_right); -} - criteria_ptr operator==(const sql::column &col, utils::placeholder p) { return std::make_unique(col, binary_operator::EQUALS, p); } @@ -24,7 +20,6 @@ criteria_ptr operator>(const sql::column &col, utils::placeholder p) { criteria_ptr operator>=(const sql::column &col, utils::placeholder p) { return std::make_unique(col, binary_operator::GREATER_THAN_OR_EQUAL, p); - } criteria_ptr operator<(const sql::column &col, utils::placeholder p) { @@ -35,6 +30,30 @@ criteria_ptr operator<=(const sql::column &col, utils::placeholder p) { return std::make_unique(col, binary_operator::LESS_THAN_OR_EQUAL, p); } +criteria_ptr operator==( const sql::column& col_left, const sql::column& col_right ) { + return std::make_unique(col_left, binary_operator::EQUALS, col_right); +} + +criteria_ptr operator!=( const sql::column& col_left, const sql::column& col_right ) { + return std::make_unique(col_left, binary_operator::NOT_EQUALS, col_right); +} + +criteria_ptr operator>( const sql::column& col_left, const sql::column& col_right ) { + return std::make_unique(col_left, binary_operator::GREATER_THAN, col_right); +} + +criteria_ptr operator>=( const sql::column& col_left, const sql::column& col_right ) { + return std::make_unique(col_left, binary_operator::GREATER_THAN_OR_EQUAL, col_right); +} + +criteria_ptr operator<( const sql::column& col_left, const sql::column& col_right ) { + return std::make_unique(col_left, binary_operator::LESS_THAN, col_right); +} + +criteria_ptr operator<=( const sql::column& col_left, const sql::column& col_right ) { + return std::make_unique(col_left, binary_operator::LESS_THAN_OR_EQUAL, col_right); +} + criteria_ptr operator&&(criteria_ptr left, criteria_ptr right) { return std::make_unique(std::move(left), logical_operator::AND, std::move(right)); } diff --git a/source/orm/query/criteria/like_criteria.cpp b/source/orm/query/criteria/like_criteria.cpp index ed1e62a..28a512c 100644 --- a/source/orm/query/criteria/like_criteria.cpp +++ b/source/orm/query/criteria/like_criteria.cpp @@ -4,17 +4,13 @@ namespace matador::query { like_criteria::like_criteria(sql::column column, std::string pattern) -: column_(std::move(column)) +: abstract_column_criteria(std::move(column)) , pattern_(std::move(pattern)){} void like_criteria::accept(criteria_visitor &visitor) const { visitor.visit(*this); } -const sql::column & like_criteria::column() const { - return column_; -} - const std::string & like_criteria::pattern() const { return pattern_; } diff --git a/source/orm/sql/column.cpp b/source/orm/sql/column.cpp index ee48f16..f5a6345 100644 --- a/source/orm/sql/column.cpp +++ b/source/orm/sql/column.cpp @@ -36,29 +36,27 @@ column::column(const sql_function_t func, std::string name) , name(std::move(name)) , function_(func) {} -column::column(const table& tab, std::string name, std::string as) -: table_(std::make_shared
(tab)) +column::column(const sql::table& tab, std::string name, std::string as) +: table_(std::make_shared(tab)) , name(std::move(name)) , alias(std::move(as)) { table_->columns.push_back(*this); } -column::column(const std::shared_ptr
& t, std::string name, std::string as) +column::column(const std::shared_ptr& t, std::string name, std::string as) : table_(t) , name(std::move(name)) , alias(std::move(as)) { } -bool column::equals(const column &x) const -{ +bool column::equals(const column &x) const { return *table_ == *x.table_ && name == x.name && alias == x.alias && function_ == x.function_; } -column &column::as(std::string a) -{ +column &column::as(std::string a) { alias = std::move(a); return *this; } @@ -74,8 +72,11 @@ std::string column::full_name() const { return name; } -bool column::is_function() const -{ +const std::string& column::alias_name() const { + return alias; +} + +bool column::is_function() const { return function_ != sql_function_t::NONE; } @@ -83,4 +84,7 @@ bool column::has_alias() const { return !alias.empty(); } +std::shared_ptr
column::table() const { + return table_; +} } \ No newline at end of file diff --git a/test/orm/orm/SessionQueryBuilderTest.cpp b/test/orm/orm/SessionQueryBuilderTest.cpp index 21a6cc9..e505dac 100644 --- a/test/orm/orm/SessionQueryBuilderTest.cpp +++ b/test/orm/orm/SessionQueryBuilderTest.cpp @@ -26,6 +26,7 @@ using namespace matador::object; using namespace matador::orm; using namespace matador::query; +using namespace matador::utils; using namespace matador::sql; using namespace matador::test; @@ -40,7 +41,7 @@ TEST_CASE("Create sql query data for entity with eager has one", "[query][entity session_query_builder eqb(scm, db); - auto data = eqb.build(17U); + auto data = eqb.build("flights.id"_col == _); REQUIRE(data.is_ok()); REQUIRE(data->root_table->name == "flights"); @@ -86,7 +87,7 @@ TEST_CASE("Create sql query data for entity with eager belongs to", "[query][ent session_query_builder eqb(scm, db); - auto data = eqb.build(17); + auto data = eqb.build("books.id"_col == _); REQUIRE(data.is_ok()); REQUIRE(data->root_table->name == "books"); @@ -114,9 +115,9 @@ TEST_CASE("Create sql query data for entity with eager belongs to", "[query][ent query_context qc; size_t index{0}; criteria_evaluator evaluator(db.dialect(), qc); - for (const auto &jd : data->joins) { - REQUIRE(jd.join_table->name == expected_join_data[index].first); - REQUIRE(evaluator.evaluate(*jd.condition) == expected_join_data[index].second); + for (const auto & [join_table, clause] : data->joins) { + REQUIRE(join_table->name == expected_join_data[index].first); + REQUIRE(evaluator.evaluate(*clause) == expected_join_data[index].second); ++index; } @@ -150,7 +151,7 @@ TEST_CASE("Create sql query data for entity with eager has many belongs to", "[q session_query_builder eqb(scm, db); - auto data = eqb.build(17); + auto data = eqb.build("orders.order_id"_col == _); REQUIRE(data.is_ok()); REQUIRE(data->root_table->name == "orders"); @@ -206,7 +207,7 @@ TEST_CASE("Create sql query data for entity with eager many to many", "[query][e session_query_builder eqb(scm, db); - auto data = eqb.build(17); + auto data = eqb.build("ingredients.id"_col == _); REQUIRE(data.is_ok()); REQUIRE(data->root_table->name == "ingredients"); @@ -252,7 +253,7 @@ TEST_CASE("Create sql query data for entity with eager many to many (inverse par session_query_builder eqb(scm, db); - auto data = eqb.build(17); + auto data = eqb.build("courses.id"_col == _); REQUIRE(data.is_ok()); REQUIRE(data->root_table->name == "courses"); diff --git a/test/orm/sql/StatementCacheTest.cpp b/test/orm/sql/StatementCacheTest.cpp index 51b0951..d2cf60e 100644 --- a/test/orm/sql/StatementCacheTest.cpp +++ b/test/orm/sql/StatementCacheTest.cpp @@ -222,76 +222,76 @@ TEST_CASE("Test statement reuse avoids reprepare", "[statement][cache][prepare]" auto stmt2 = result.value(); } -TEST_CASE("Multithreaded stress test", "[statement][cache][stress]") { - backend_provider::instance().register_backend("noop", std::make_unique()); - - constexpr int thread_count = 16; - constexpr int iterations = 1000; - constexpr int sql_pool_size = 10; - - std::vector sqls; - for (int i = 0; i < sql_pool_size; ++i) { - sqls.push_back("SELECT " + std::to_string(i)); - } - - connection_pool pool("noop://noop.db", 4); - message_bus bus; - statement_cache cache(bus, pool, 5); - RecordingObserver observer(bus); - MetricsObserver metrics(bus); - - auto start_time = std::chrono::steady_clock::now(); - - std::atomic_int lock_failed_count{0}; - std::atomic_int exec_failed_count{0}; - - auto worker = [&](const int tid) { - std::mt19937 rng(tid); - std::uniform_int_distribution dist(0, sql_pool_size - 1); - - for (int i = 0; i < iterations; ++i) { - const auto& sql = sqls[dist(rng)]; - if (const auto result = cache.acquire({sql}); !result) { - FAIL("Failed to acquire statement"); - } else { - if (const auto exec_result = result->execute(); !exec_result) { - if (exec_result.err().ec() == error_code::STATEMENT_LOCKED) { - ++lock_failed_count; - } else { - ++exec_failed_count; - } - } - } - } - }; - - std::vector threads; - for (int i = 0; i < thread_count; ++i) { - threads.emplace_back(worker, i); - } - - for (auto& t : threads) { - t.join(); - } - - auto end_time = std::chrono::steady_clock::now(); - auto duration = std::chrono::duration_cast(end_time - start_time); - - std::cout << "[Performance] Executed " << (thread_count * iterations) << " statements in " << duration.count() << " ms (lock failed: " << lock_failed_count << ", execute failed: " << exec_failed_count << ")\n"; - - std::cout << "Average lock wait time: " << metrics.get_average_lock_wait_time().count() << "ms\n"; - std::cout << "Total lock wait time: " << metrics.get_total_lock_wait_time().count() << "ms\n"; - std::cout << "Average execution time: " << metrics.get_average_execution_time().count() << "ms\n"; - std::cout << "Total execution time: " << metrics.get_total_execution_time().count() << "ms\n"; - std::cout << "Number of lock failures: " << metrics.get_lock_failure_count() << "\n"; - - // Some events should be generated - int accessed = 0; - while (auto e = observer.poll()) { - if (e->is()) accessed++; - } - REQUIRE(accessed > 0); -} +// TEST_CASE("Multithreaded stress test", "[statement][cache][stress]") { +// backend_provider::instance().register_backend("noop", std::make_unique()); +// +// constexpr int thread_count = 16; +// constexpr int iterations = 1000; +// constexpr int sql_pool_size = 10; +// +// std::vector sqls; +// for (int i = 0; i < sql_pool_size; ++i) { +// sqls.push_back("SELECT " + std::to_string(i)); +// } +// +// connection_pool pool("noop://noop.db", 4); +// message_bus bus; +// statement_cache cache(bus, pool, 5); +// RecordingObserver observer(bus); +// MetricsObserver metrics(bus); +// +// auto start_time = std::chrono::steady_clock::now(); +// +// std::atomic_int lock_failed_count{0}; +// std::atomic_int exec_failed_count{0}; +// +// auto worker = [&](const int tid) { +// std::mt19937 rng(tid); +// std::uniform_int_distribution dist(0, sql_pool_size - 1); +// +// for (int i = 0; i < iterations; ++i) { +// const auto& sql = sqls[dist(rng)]; +// if (const auto result = cache.acquire({sql}); !result) { +// FAIL("Failed to acquire statement"); +// } else { +// if (const auto exec_result = result->execute(); !exec_result) { +// if (exec_result.err().ec() == error_code::STATEMENT_LOCKED) { +// ++lock_failed_count; +// } else { +// ++exec_failed_count; +// } +// } +// } +// } +// }; +// +// std::vector threads; +// for (int i = 0; i < thread_count; ++i) { +// threads.emplace_back(worker, i); +// } +// +// for (auto& t : threads) { +// t.join(); +// } +// +// auto end_time = std::chrono::steady_clock::now(); +// auto duration = std::chrono::duration_cast(end_time - start_time); +// +// std::cout << "[Performance] Executed " << (thread_count * iterations) << " statements in " << duration.count() << " ms (lock failed: " << lock_failed_count << ", execute failed: " << exec_failed_count << ")\n"; +// +// std::cout << "Average lock wait time: " << metrics.get_average_lock_wait_time().count() << "ms\n"; +// std::cout << "Total lock wait time: " << metrics.get_total_lock_wait_time().count() << "ms\n"; +// std::cout << "Average execution time: " << metrics.get_average_execution_time().count() << "ms\n"; +// std::cout << "Total execution time: " << metrics.get_total_execution_time().count() << "ms\n"; +// std::cout << "Number of lock failures: " << metrics.get_lock_failure_count() << "\n"; +// +// // Some events should be generated +// int accessed = 0; +// while (auto e = observer.poll()) { +// if (e->is()) accessed++; +// } +// REQUIRE(accessed > 0); +// } TEST_CASE("Race condition simulation with mixed access", "[statement_cache][race]") { backend_provider::instance().register_backend("noop", std::make_unique());