diff --git a/include/matador/orm/session.hpp b/include/matador/orm/session.hpp index f0be5d5..67e44e4 100644 --- a/include/matador/orm/session.hpp +++ b/include/matador/orm/session.hpp @@ -3,7 +3,7 @@ #include "matador/orm/error_code.hpp" -#include "matador/query/query_builder.hpp" +#include "matador/query/select_query_builder.hpp" #include "matador/query/criteria.hpp" #include "matador/query/query.hpp" #include "matador/query/generator.hpp" @@ -320,7 +320,7 @@ utils::result, utils::error> session::find(const Primar return utils::failure(make_error(error_code::NoPrimaryKey, "Type hasn't primary key.")); } - query::query_builder eqb(schema_, *this); + query::select_query_builder eqb(schema_, *this); const query::table_column c(&it->second.table(), info.primary_key_attribute()->name()); using namespace matador::query; auto data = eqb.build(c == utils::_); @@ -351,7 +351,7 @@ utils::result, utils::error> session::find(query::criter return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); } - query::query_builder eqb(schema_, *this); + query::select_query_builder eqb(schema_, *this); auto data = eqb.build(std::move(clause)); if (!data.is_ok()) { return utils::failure(make_error(error_code::FailedToBuildQuery, diff --git a/include/matador/query/query_builder.hpp b/include/matador/query/query_builder.hpp index d2e7a54..6f682f5 100644 --- a/include/matador/query/query_builder.hpp +++ b/include/matador/query/query_builder.hpp @@ -1,316 +1,100 @@ -#ifndef QUERY_ENTITY_QUERY_BUILDER_HPP -#define QUERY_ENTITY_QUERY_BUILDER_HPP +#ifndef QUERY_QUERY_COMPILER_HPP +#define QUERY_QUERY_COMPILER_HPP -#include "matador/query/query_builder_exception.hpp" +#include "matador/query/query_part_visitor.hpp" +#include "matador/query/query_data.hpp" -#include "matador/query/criteria.hpp" -#include "matador/query/query.hpp" +#include "matador/sql/query_context.hpp" -#include "matador/sql/statement.hpp" +#include "matador/utils/placeholder.hpp" -#include "matador/object/join_columns_collector.hpp" -#include "matador/object/repository.hpp" -#include "matador/query/criteria/criteria_visitor.hpp" +#include +#include -#include "matador/utils/primary_key_attribute.hpp" -#include "matador/utils/result.hpp" -#include "matador/utils/value.hpp" - -#include -#include +namespace matador::sql { +class connection_impl; +class dialect; +} namespace matador::query { -struct entity_query_data { - const table* root_table{nullptr}; - std::string pk_column_name{}; - std::vector columns{}; - std::unordered_map lazy_loading_statements{}; - std::vector joins{}; - criteria_ptr where_clause{}; -}; - -class criteria_transformer final : public criteria_visitor { -public: - criteria_transformer(const basic_schema &repo, const std::unordered_map& tables_by_name); - void visit( const between_criteria& node ) override; - void visit( const binary_criteria& node ) override; - void visit( const binary_column_criteria& node ) override; - void visit( const collection_criteria& node ) override; - void visit( const check_null_criteria& node ) override; - void visit( const collection_query_criteria& node ) override; - void visit( const like_criteria& node ) override; - void visit( const logical_criteria& node ) override; - void visit( const not_criteria& node ) override; - -private: - void update_criteria_column(const abstract_column_criteria& node) const; - -private: - const basic_schema &repo_; - const std::unordered_map& tables_by_name_; -}; - -class query_builder final { -public: - query_builder(const basic_schema &scm, sql::executor &exec) - : schema_(scm) - , executor_(exec){} - - template - utils::result build(criteria_ptr clause = {}) { - const auto it = schema_.find(typeid(EntityType)); - if (it == schema_.end()) { - return utils::failure(query_build_error::UnknownType); - } - table_info_stack_.push({it->second.node().info(), it->second.table().as(build_alias('t', ++table_index))}); - entity_query_data_ = { &table_info_stack_.top().table }; - processed_tables_.insert({it->second.name(), *entity_query_data_.root_table}); - try { - EntityType obj; - access::process(*this, obj); - - 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())}; - } catch (...) { - return {utils::failure(query_build_error::UnexpectedError)}; - } - } - - template < class V > - 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; - } - - void on_revision(const char *id, uint64_t &/*rev*/); - - template - void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) - { - push(id); - } - - template - void on_belongs_to(const char *id, Pointer &obj, const utils::foreign_attributes &attr) { - on_foreign_object(id, obj, attr); - } - - template - void on_has_one(const char *id, Pointer &obj, const utils::foreign_attributes &attr) { - on_foreign_object(id, obj, attr); - } - - template - struct NoopDeleter { - void operator()(const T*) const {} - }; - - template - void on_has_many(const char * /*id*/, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t::value> * = nullptr) { - if (attr.fetch() != utils::fetch_type::Eager) { - return; - } - - const auto it = schema_.find(typeid(typename CollectionType::value_type::value_type)); - if (it == schema_.end()) { - throw query_builder_exception{query_build_error::UnknownType}; - } - - auto next = processed_tables_.find(it->second.name()); - if (next != processed_tables_.end()) { - // node already processed - return; - } - - table_info_stack_.push({it->second.node().info(), it->second.table().as(build_alias('t', ++table_index))}); - next = processed_tables_.insert({it->second.name(), table_info_stack_.top().table}).first; - typename CollectionType::value_type::value_type obj; - access::process(*this , obj); - table_info_stack_.pop(); - - if (!it->second.node().info().has_primary_key()) { - throw query_builder_exception{query_build_error::MissingPrimaryKey}; - } - - append_join( - table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()}, - table_column{&next->second, join_column} - ); - } - - template - void on_has_many(const char * /*id*/, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t::value> * = nullptr) { - } - - template - void on_has_many_to_many(const char *id, ContainerType &/*cont*/, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &attr) { - if (attr.fetch() != utils::fetch_type::Eager) { - return; - } - const auto result = schema_.find(typeid(typename ContainerType::value_type::value_type)); - if (result == schema_.end()) { - throw query_builder_exception{query_build_error::UnknownType}; - } - - auto next = processed_tables_.find(result->second.name()); - if (next != processed_tables_.end()) { - // attribute was already processed - return; - } - - auto relation = processed_tables_.find(id); - if (relation == processed_tables_.end()) { - const auto it = schema_.find(id); - if (it == schema_.end()) { - throw query_builder_exception{query_build_error::UnknownType}; - } - relation = processed_tables_.emplace(id, it->second.table().as(build_alias('t', ++table_index))).first; - } - table_info_stack_.push({result->second.node().info(), result->second.table().as(build_alias('t', ++table_index))}); - next = processed_tables_.insert({result->second.name(), table_info_stack_.top().table}).first; - typename ContainerType::value_type::value_type obj; - access::process(*this , obj); - table_info_stack_.pop(); - - if (!result->second.node().info().has_primary_key()) { - throw query_builder_exception{query_build_error::MissingPrimaryKey}; - } - - append_join( - table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()}, - table_column{&relation->second, join_column} - ); - append_join( - table_column{&relation->second, inverse_join_column}, - table_column{&next->second, result->second.node().info().primary_key_attribute()->name()} - ); - } - - template - void on_has_many_to_many(const char *id, ContainerType &/*cont*/, const utils::foreign_attributes &attr) { - if (attr.fetch() != utils::fetch_type::Eager) { - return; - } - const auto result = schema_.find(typeid(typename ContainerType::value_type::value_type)); - if (result == schema_.end()) { - throw query_builder_exception{query_build_error::UnknownType}; - } - - auto next = processed_tables_.find(result->second.name()); - if (next != processed_tables_.end()) { - // attribute was already processed - return; - } - - auto relation = processed_tables_.find(id); - if (relation == processed_tables_.end()) { - const auto it = schema_.find(id); - if (it == schema_.end()) { - throw query_builder_exception{query_build_error::UnknownType}; - } - const auto t = it->second.table().as(build_alias('t', ++table_index)); - relation = processed_tables_.insert({id, t}).first; - } - table_info_stack_.push({result->second.node().info(), result->second.table().as(build_alias('t', ++table_index))}); - next = processed_tables_.insert({result->second.name(), table_info_stack_.top().table}).first; - typename ContainerType::value_type::value_type obj; - access::process(*this , obj); - table_info_stack_.pop(); - - if (!result->second.node().info().has_primary_key()) { - throw query_builder_exception{query_build_error::MissingPrimaryKey}; - } - - const auto join_columns = join_columns_collector_.collect(); - - append_join( - table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()}, - table_column{&relation->second, join_columns.inverse_join_column} - ); - append_join( - table_column{&relation->second, join_columns.join_column}, - table_column{&next->second, result->second.node().info().primary_key_attribute()->name()} - ); - } - -private: - template - void on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr); - void push(const std::string &column_name); - static std::string build_alias(char prefix, unsigned int count); - [[nodiscard]] bool is_root_entity() const; - void append_join(const table_column &left, const table_column &right); - -private: - struct table_info { - const object::basic_object_info &info; - class table table; - }; - - std::stack table_info_stack_{}; - std::unordered_map processed_tables_{}; - const basic_schema &schema_; - entity_query_data entity_query_data_{}; - unsigned int column_index{0}; - unsigned int table_index{0}; - object::join_columns_collector join_columns_collector_{}; - sql::executor &executor_; -}; - -template -void query_builder::on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr) { - const auto it = schema_.find(typeid(typename Pointer::value_type)); - if (it == schema_.end()) { - throw query_builder_exception{query_build_error::UnknownType}; - } - if (!it->second.node().info().has_primary_key()) { - throw query_builder_exception{query_build_error::MissingPrimaryKey}; - } - - const auto& info = it->second.node().info(); - auto foreign_table = it->second.table().as(build_alias('t', ++table_index)); - if (attr.fetch() == utils::fetch_type::Eager) { - - auto next = processed_tables_.find(info.name()); - if (next != processed_tables_.end()) { - return; - } - table_info_stack_.push({info, std::move(foreign_table)}); - next = processed_tables_.insert({info.name(), table_info_stack_.top().table}).first; - typename Pointer::value_type obj; - access::process(*this, obj); - table_info_stack_.pop(); - - append_join( - table_column{&table_info_stack_.top().table, id}, - table_column{&next->second, info.primary_key_attribute()->name()} - ); - } else { - push(id); - using namespace matador::utils; - using namespace matador::query; - // create select query - auto result = query::query::select(generator::columns(schema_, foreign_table, generator::column_generator_options::ForceLazy)) - .from(foreign_table) - .where(table_column(&foreign_table, info.primary_key_attribute()->name(), "") == _) - .prepare(executor_); - if (!result) { - throw query_builder_exception(query_build_error::QueryError, result.release_error()); - } - - entity_query_data_.lazy_loading_statements.emplace(id, std::move(result.release())); - } +namespace internal { +struct basic_type_to_string_visitor; } +class table_constraint; + +struct query_data; +struct value_visitor; + +class query_builder final : public query_part_visitor { +public: + sql::query_context compile(const query_data &data, + const sql::dialect &d, + std::optional> conn); + +protected: + void visit(internal::query_alter_part& part) override; + void visit(internal::query_alter_table_part& part) override; + void visit(internal::query_add_key_constraint_part& part) override; + void visit(internal::query_add_foreign_key_constraint_part& part) override; + void visit(internal::query_add_primary_key_constraint_part& part) override; + void visit(internal::query_add_foreign_key_reference_part& part) override; + void visit(internal::query_add_constraint_part_by_constraint &part) override; + void visit(internal::query_drop_key_constraint_part_by_name &part) override; + void visit(internal::query_drop_key_constraint_part_by_constraint &part) override; + +protected: + void visit(internal::query_select_part &part) override; + void visit(internal::query_from_part &part) override; + void visit(internal::query_join_table_part &part) override; + void visit(internal::query_join_query_part &part) override; + void visit(internal::query_on_part &part) override; + void visit(internal::query_where_part &part) override; + void visit(internal::query_group_by_part &part) override; + void visit(internal::query_order_by_part &part) override; + void visit(internal::query_order_by_asc_part &part) override; + void visit(internal::query_order_by_desc_part &part) override; + void visit(internal::query_offset_part &part) override; + void visit(internal::query_limit_part &part) override; + void visit(internal::query_insert_part &part) override; + void visit(internal::query_into_part &part) override; + void visit(internal::query_values_part &part) override; + + void visit(internal::query_update_part &part) override; + void visit(internal::query_set_part &part) override; + + void visit(internal::query_delete_part &part) override; + void visit(internal::query_delete_from_part &part) override; + + void visit(internal::query_create_part &part) override; + void visit(internal::query_create_table_part &part) override; + void visit(internal::query_create_table_columns_part& part) override; + void visit(internal::query_create_table_constraints_part& part) override; + void visit(internal::query_create_schema_part& part) override; + + void visit(internal::query_drop_part &part) override; + void visit(internal::query_drop_table_part &part) override; + void visit(internal::query_drop_schema_part& part) override; + + static std::string build_table_name(sql::dialect_token token, const sql::dialect &d, const table& t); + static std::string build_table_name(const sql::dialect &d, const table& t); + static std::string determine_value(value_visitor &visitor, const std::variant &val); + + [[nodiscard]] std::string build_add_constraint_string(const table_constraint& c) const; + [[nodiscard]] std::string build_drop_constraint_string(const table_constraint& c) const; + static std::string build_constraint_name(const table_constraint& c); + +protected: + const query_data *data_{}; + sql::query_context query_; + size_t table_index{0}; + const sql::dialect *dialect_{nullptr}; + std::optional> connection_{}; + + std::function finisher_ = [](sql::query_context&) {}; +}; + } -#endif //QUERY_ENTITY_QUERY_BUILDER_HPP + +#endif //QUERY_QUERY_COMPILER_HPP diff --git a/include/matador/query/query_compiler.hpp b/include/matador/query/query_compiler.hpp deleted file mode 100644 index baa2a9a..0000000 --- a/include/matador/query/query_compiler.hpp +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef QUERY_QUERY_COMPILER_HPP -#define QUERY_QUERY_COMPILER_HPP - -#include "matador/query/query_part_visitor.hpp" -#include "matador/query/query_data.hpp" - -#include "matador/sql/query_context.hpp" - -#include "matador/utils/placeholder.hpp" - -#include -#include - -namespace matador::sql { -class connection_impl; -class dialect; -} - -namespace matador::query { - -namespace internal { -struct basic_type_to_string_visitor; -} - -class table_constraint; - -struct query_data; -struct value_visitor; - -class query_compiler final : public query_part_visitor { -public: - sql::query_context compile(const query_data &data, - const sql::dialect &d, - std::optional> conn); - -protected: - void visit(internal::query_alter_part& part) override; - void visit(internal::query_alter_table_part& part) override; - void visit(internal::query_add_key_constraint_part& part) override; - void visit(internal::query_add_foreign_key_constraint_part& part) override; - void visit(internal::query_add_primary_key_constraint_part& part) override; - void visit(internal::query_add_foreign_key_reference_part& part) override; - void visit(internal::query_add_constraint_part_by_constraint &part) override; - void visit(internal::query_drop_key_constraint_part_by_name &part) override; - void visit(internal::query_drop_key_constraint_part_by_constraint &part) override; - -protected: - void visit(internal::query_select_part &part) override; - void visit(internal::query_from_part &part) override; - void visit(internal::query_join_table_part &part) override; - void visit(internal::query_join_query_part &part) override; - void visit(internal::query_on_part &part) override; - void visit(internal::query_where_part &part) override; - void visit(internal::query_group_by_part &part) override; - void visit(internal::query_order_by_part &part) override; - void visit(internal::query_order_by_asc_part &part) override; - void visit(internal::query_order_by_desc_part &part) override; - void visit(internal::query_offset_part &part) override; - void visit(internal::query_limit_part &part) override; - void visit(internal::query_insert_part &part) override; - void visit(internal::query_into_part &part) override; - void visit(internal::query_values_part &part) override; - - void visit(internal::query_update_part &part) override; - void visit(internal::query_set_part &part) override; - - void visit(internal::query_delete_part &part) override; - void visit(internal::query_delete_from_part &part) override; - - void visit(internal::query_create_part &part) override; - void visit(internal::query_create_table_part &part) override; - void visit(internal::query_create_table_columns_part& part) override; - void visit(internal::query_create_table_constraints_part& part) override; - void visit(internal::query_create_schema_part& part) override; - - void visit(internal::query_drop_part &part) override; - void visit(internal::query_drop_table_part &part) override; - void visit(internal::query_drop_schema_part& part) override; - - static std::string build_table_name(sql::dialect_token token, const sql::dialect &d, const table& t); - static std::string build_table_name(const sql::dialect &d, const table& t); - static std::string determine_value(value_visitor &visitor, const std::variant &val); - - [[nodiscard]] std::string build_add_constraint_string(const table_constraint& c) const; - [[nodiscard]] std::string build_drop_constraint_string(const table_constraint& c) const; - static std::string build_constraint_name(const table_constraint& c); - -protected: - const query_data *data_{}; - sql::query_context query_; - size_t table_index{0}; - const sql::dialect *dialect_{nullptr}; - std::optional> connection_{}; - - std::function finisher_ = [](sql::query_context&) {}; -}; - -} - -#endif //QUERY_QUERY_COMPILER_HPP diff --git a/include/matador/query/query_object_resolver.hpp b/include/matador/query/query_object_resolver.hpp index 360d348..c186dd0 100644 --- a/include/matador/query/query_object_resolver.hpp +++ b/include/matador/query/query_object_resolver.hpp @@ -7,7 +7,7 @@ #include "matador/object/object_resolver.hpp" #include "matador/query/table.hpp" -#include "matador/query/query_builder.hpp" +#include "matador/query/select_query_builder.hpp" namespace matador::sql { class executor; @@ -36,7 +36,7 @@ utils::result prepare_statement(sql::executor& exe template std::shared_ptr query_object_resolver::resolve(const utils::identifier &id) { - query_builder qb(schema_, executor_); + select_query_builder qb(schema_, executor_); const auto *pk_column = table_[pk_name_]; auto builder_result = qb.build(*pk_column == utils::_); diff --git a/include/matador/query/select_query_builder.hpp b/include/matador/query/select_query_builder.hpp new file mode 100644 index 0000000..1d33f2f --- /dev/null +++ b/include/matador/query/select_query_builder.hpp @@ -0,0 +1,316 @@ +#ifndef QUERY_ENTITY_QUERY_BUILDER_HPP +#define QUERY_ENTITY_QUERY_BUILDER_HPP + +#include "matador/query/query_builder_exception.hpp" + +#include "matador/query/criteria.hpp" +#include "matador/query/query.hpp" + +#include "matador/sql/statement.hpp" + +#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" +#include "matador/utils/value.hpp" + +#include +#include + +namespace matador::query { + +struct entity_query_data { + const table* root_table{nullptr}; + std::string pk_column_name{}; + std::vector columns{}; + std::unordered_map lazy_loading_statements{}; + std::vector joins{}; + criteria_ptr where_clause{}; +}; + +class criteria_transformer final : public criteria_visitor { +public: + criteria_transformer(const basic_schema &repo, const std::unordered_map& tables_by_name); + void visit( const between_criteria& node ) override; + void visit( const binary_criteria& node ) override; + void visit( const binary_column_criteria& node ) override; + void visit( const collection_criteria& node ) override; + void visit( const check_null_criteria& node ) override; + void visit( const collection_query_criteria& node ) override; + void visit( const like_criteria& node ) override; + void visit( const logical_criteria& node ) override; + void visit( const not_criteria& node ) override; + +private: + void update_criteria_column(const abstract_column_criteria& node) const; + +private: + const basic_schema &repo_; + const std::unordered_map& tables_by_name_; +}; + +class select_query_builder final { +public: + select_query_builder(const basic_schema &scm, sql::executor &exec) + : schema_(scm) + , executor_(exec){} + + template + utils::result build(criteria_ptr clause = {}) { + const auto it = schema_.find(typeid(EntityType)); + if (it == schema_.end()) { + return utils::failure(query_build_error::UnknownType); + } + table_info_stack_.push({it->second.node().info(), it->second.table().as(build_alias('t', ++table_index))}); + entity_query_data_ = { &table_info_stack_.top().table }; + processed_tables_.insert({it->second.name(), *entity_query_data_.root_table}); + try { + EntityType obj; + access::process(*this, obj); + + 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())}; + } catch (...) { + return {utils::failure(query_build_error::UnexpectedError)}; + } + } + + template < class V > + 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; + } + + void on_revision(const char *id, uint64_t &/*rev*/); + + template + void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) + { + push(id); + } + + template + void on_belongs_to(const char *id, Pointer &obj, const utils::foreign_attributes &attr) { + on_foreign_object(id, obj, attr); + } + + template + void on_has_one(const char *id, Pointer &obj, const utils::foreign_attributes &attr) { + on_foreign_object(id, obj, attr); + } + + template + struct NoopDeleter { + void operator()(const T*) const {} + }; + + template + void on_has_many(const char * /*id*/, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t::value> * = nullptr) { + if (attr.fetch() != utils::fetch_type::Eager) { + return; + } + + const auto it = schema_.find(typeid(typename CollectionType::value_type::value_type)); + if (it == schema_.end()) { + throw query_builder_exception{query_build_error::UnknownType}; + } + + auto next = processed_tables_.find(it->second.name()); + if (next != processed_tables_.end()) { + // node already processed + return; + } + + table_info_stack_.push({it->second.node().info(), it->second.table().as(build_alias('t', ++table_index))}); + next = processed_tables_.insert({it->second.name(), table_info_stack_.top().table}).first; + typename CollectionType::value_type::value_type obj; + access::process(*this , obj); + table_info_stack_.pop(); + + if (!it->second.node().info().has_primary_key()) { + throw query_builder_exception{query_build_error::MissingPrimaryKey}; + } + + append_join( + table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()}, + table_column{&next->second, join_column} + ); + } + + template + void on_has_many(const char * /*id*/, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t::value> * = nullptr) { + } + + template + void on_has_many_to_many(const char *id, ContainerType &/*cont*/, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &attr) { + if (attr.fetch() != utils::fetch_type::Eager) { + return; + } + const auto result = schema_.find(typeid(typename ContainerType::value_type::value_type)); + if (result == schema_.end()) { + throw query_builder_exception{query_build_error::UnknownType}; + } + + auto next = processed_tables_.find(result->second.name()); + if (next != processed_tables_.end()) { + // attribute was already processed + return; + } + + auto relation = processed_tables_.find(id); + if (relation == processed_tables_.end()) { + const auto it = schema_.find(id); + if (it == schema_.end()) { + throw query_builder_exception{query_build_error::UnknownType}; + } + relation = processed_tables_.emplace(id, it->second.table().as(build_alias('t', ++table_index))).first; + } + table_info_stack_.push({result->second.node().info(), result->second.table().as(build_alias('t', ++table_index))}); + next = processed_tables_.insert({result->second.name(), table_info_stack_.top().table}).first; + typename ContainerType::value_type::value_type obj; + access::process(*this , obj); + table_info_stack_.pop(); + + if (!result->second.node().info().has_primary_key()) { + throw query_builder_exception{query_build_error::MissingPrimaryKey}; + } + + append_join( + table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()}, + table_column{&relation->second, join_column} + ); + append_join( + table_column{&relation->second, inverse_join_column}, + table_column{&next->second, result->second.node().info().primary_key_attribute()->name()} + ); + } + + template + void on_has_many_to_many(const char *id, ContainerType &/*cont*/, const utils::foreign_attributes &attr) { + if (attr.fetch() != utils::fetch_type::Eager) { + return; + } + const auto result = schema_.find(typeid(typename ContainerType::value_type::value_type)); + if (result == schema_.end()) { + throw query_builder_exception{query_build_error::UnknownType}; + } + + auto next = processed_tables_.find(result->second.name()); + if (next != processed_tables_.end()) { + // attribute was already processed + return; + } + + auto relation = processed_tables_.find(id); + if (relation == processed_tables_.end()) { + const auto it = schema_.find(id); + if (it == schema_.end()) { + throw query_builder_exception{query_build_error::UnknownType}; + } + const auto t = it->second.table().as(build_alias('t', ++table_index)); + relation = processed_tables_.insert({id, t}).first; + } + table_info_stack_.push({result->second.node().info(), result->second.table().as(build_alias('t', ++table_index))}); + next = processed_tables_.insert({result->second.name(), table_info_stack_.top().table}).first; + typename ContainerType::value_type::value_type obj; + access::process(*this , obj); + table_info_stack_.pop(); + + if (!result->second.node().info().has_primary_key()) { + throw query_builder_exception{query_build_error::MissingPrimaryKey}; + } + + const auto join_columns = join_columns_collector_.collect(); + + append_join( + table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()}, + table_column{&relation->second, join_columns.inverse_join_column} + ); + append_join( + table_column{&relation->second, join_columns.join_column}, + table_column{&next->second, result->second.node().info().primary_key_attribute()->name()} + ); + } + +private: + template + void on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr); + void push(const std::string &column_name); + static std::string build_alias(char prefix, unsigned int count); + [[nodiscard]] bool is_root_entity() const; + void append_join(const table_column &left, const table_column &right); + +private: + struct table_info { + const object::basic_object_info &info; + class table table; + }; + + std::stack table_info_stack_{}; + std::unordered_map processed_tables_{}; + const basic_schema &schema_; + entity_query_data entity_query_data_{}; + unsigned int column_index{0}; + unsigned int table_index{0}; + object::join_columns_collector join_columns_collector_{}; + sql::executor &executor_; +}; + +template +void select_query_builder::on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr) { + const auto it = schema_.find(typeid(typename Pointer::value_type)); + if (it == schema_.end()) { + throw query_builder_exception{query_build_error::UnknownType}; + } + if (!it->second.node().info().has_primary_key()) { + throw query_builder_exception{query_build_error::MissingPrimaryKey}; + } + + const auto& info = it->second.node().info(); + auto foreign_table = it->second.table().as(build_alias('t', ++table_index)); + if (attr.fetch() == utils::fetch_type::Eager) { + + auto next = processed_tables_.find(info.name()); + if (next != processed_tables_.end()) { + return; + } + table_info_stack_.push({info, std::move(foreign_table)}); + next = processed_tables_.insert({info.name(), table_info_stack_.top().table}).first; + typename Pointer::value_type obj; + access::process(*this, obj); + table_info_stack_.pop(); + + append_join( + table_column{&table_info_stack_.top().table, id}, + table_column{&next->second, info.primary_key_attribute()->name()} + ); + } else { + push(id); + using namespace matador::utils; + using namespace matador::query; + // create select query + auto result = query::query::select(generator::columns(schema_, foreign_table, generator::column_generator_options::ForceLazy)) + .from(foreign_table) + .where(table_column(&foreign_table, info.primary_key_attribute()->name(), "") == _) + .prepare(executor_); + if (!result) { + throw query_builder_exception(query_build_error::QueryError, result.release_error()); + } + + entity_query_data_.lazy_loading_statements.emplace(id, std::move(result.release())); + } +} + +} +#endif //QUERY_ENTITY_QUERY_BUILDER_HPP diff --git a/source/orm/CMakeLists.txt b/source/orm/CMakeLists.txt index 052bfb1..b3363c7 100644 --- a/source/orm/CMakeLists.txt +++ b/source/orm/CMakeLists.txt @@ -54,8 +54,8 @@ add_library(matador-orm STATIC ../../include/matador/query/meta_table_macro.hpp ../../include/matador/query/meta_table_macro.hpp ../../include/matador/query/query.hpp + ../../include/matador/query/select_query_builder.hpp ../../include/matador/query/query_builder.hpp - ../../include/matador/query/query_compiler.hpp ../../include/matador/query/query_data.hpp ../../include/matador/query/query_intermediates.hpp ../../include/matador/query/query_object_resolver.hpp @@ -146,9 +146,9 @@ add_library(matador-orm STATIC query/internal/string_builder_utils.cpp query/key_value_generator.cpp query/query.cpp - query/query_builder.cpp + query/select_query_builder.cpp query/query_builder_exception.cpp - query/query_compiler.cpp + query/query_builder.cpp query/query_object_resolver.cpp query/query_part.cpp query/query_utils.cpp diff --git a/source/orm/query/intermediates/executable_query.cpp b/source/orm/query/intermediates/executable_query.cpp index 98b4175..cb3d072 100644 --- a/source/orm/query/intermediates/executable_query.cpp +++ b/source/orm/query/intermediates/executable_query.cpp @@ -1,5 +1,5 @@ #include "matador/query/intermediates/executable_query.hpp" -#include "matador/query/query_compiler.hpp" +#include "matador/query/query_builder.hpp" #include "matador/sql/executor.hpp" #include "matador/sql/statement.hpp" @@ -7,22 +7,22 @@ namespace matador::query { utils::result executable_query::execute(const sql::executor &exec) const { - query_compiler compiler; + query_builder compiler; return exec.execute(compiler.compile(*context_, exec.dialect(), std::nullopt)); } utils::result executable_query::prepare(sql::executor &exec) const { - query_compiler compiler; + query_builder compiler; return exec.prepare(compiler.compile(*context_, exec.dialect(), std::nullopt)); } sql::query_context executable_query::compile( const sql::executor& exec ) const { - query_compiler compiler; + query_builder compiler; return compiler.compile(*context_, exec.dialect(), std::nullopt); } std::string executable_query::str(const sql::executor &exec) const { - query_compiler compiler; + query_builder compiler; return exec.str(compiler.compile(*context_, exec.dialect(), std::nullopt)); } diff --git a/source/orm/query/intermediates/fetchable_query.cpp b/source/orm/query/intermediates/fetchable_query.cpp index ccd4cc1..8d26fad 100644 --- a/source/orm/query/intermediates/fetchable_query.cpp +++ b/source/orm/query/intermediates/fetchable_query.cpp @@ -1,6 +1,6 @@ #include "matador/query/intermediates/fetchable_query.hpp" -#include "matador/query/query_compiler.hpp" +#include "matador/query/query_builder.hpp" #include "matador/sql/executor.hpp" #include "matador/sql/field.hpp" @@ -27,7 +27,7 @@ sql::record *create_prototype(const std::vector &prototype) { } utils::result, utils::error> fetchable_query::fetch_all(const sql::executor &exec) const { - query_compiler compiler; + query_builder compiler; const auto ctx = compiler.compile(*context_, exec.dialect(), std::nullopt); return exec.fetch(ctx) .and_then([](auto &&res) { @@ -37,7 +37,7 @@ utils::result, utils::error> fetchable_query::fet } utils::result, utils::error> fetchable_query::fetch_one(const sql::executor &exec) const { - query_compiler compiler; + query_builder compiler; auto ctx = compiler.compile(*context_, exec.dialect(), std::nullopt); ctx.resolver_factory = exec.resolver_factory(); auto result = exec.fetch(ctx); @@ -63,7 +63,7 @@ std::string fetchable_query::str(const sql::dialect &d) const { } sql::query_context fetchable_query::compile(const sql::dialect &d) const { - query_compiler compiler; + query_builder compiler; return compiler.compile(*context_, d, std::nullopt); } diff --git a/source/orm/query/query_builder.cpp b/source/orm/query/query_builder.cpp index 9cb0ae2..c1f6636 100644 --- a/source/orm/query/query_builder.cpp +++ b/source/orm/query/query_builder.cpp @@ -1,85 +1,481 @@ #include "matador/query/query_builder.hpp" +#include "matador/query/attribute_string_writer.hpp" +#include "matador/query/query_data.hpp" +#include "matador/query/criteria_evaluator.hpp" +#include "matador/query/query_utils.hpp" +#include "matador/query/table_constraint.hpp" +#include "matador/query/table_column.hpp" + +#include "matador/query/internal/basic_type_to_string_visitor.hpp" +#include "matador/query/internal/string_builder_utils.hpp" +#include "matador/query/internal/query_parts.hpp" + +#include "matador/sql/query_context.hpp" +#include "matador/sql/connection.hpp" +#include "matador/sql/dialect.hpp" + namespace matador::query { -criteria_transformer::criteria_transformer(const basic_schema& repo, const std::unordered_map& tables_by_name) -: repo_(repo) -, tables_by_name_(tables_by_name) {} -void criteria_transformer::visit( const between_criteria& node ) { - update_criteria_column(node); -} - -void criteria_transformer::visit( const binary_criteria& node ) { - update_criteria_column(node); -} - -void criteria_transformer::visit( const binary_column_criteria& /*node*/ ) {} - -void criteria_transformer::visit(const check_null_criteria& node) { - update_criteria_column(node); -} - -void criteria_transformer::visit( const collection_criteria& node ) { - update_criteria_column(node); -} - -void criteria_transformer::visit( const collection_query_criteria& node ) { - update_criteria_column(node); -} - -void criteria_transformer::visit( const like_criteria& node ) { - update_criteria_column(node); -} - -void criteria_transformer::visit( const logical_criteria& node ) { - node.left_clause()->accept(*this); - node.right_clause()->accept(*this); -} - -void criteria_transformer::visit( const not_criteria& node ) { - node.clause()->accept(*this); -} - -void criteria_transformer::update_criteria_column(const abstract_column_criteria& node) const { - if (node.col().table() == nullptr) { - return; +sql::query_context query_builder::compile(const query_data &data, + const sql::dialect &d, + const std::optional> conn) +{ + data_ = &data; + dialect_ = &d; + connection_ = conn; + query_ = {}; + for (const auto &part: data.parts) { + part->accept(*this); } - const auto it = tables_by_name_.find(node.col().table()->name()); - if (it == tables_by_name_.end()) { - return; + finisher_(query_); + connection_ = std::nullopt; + dialect_ = nullptr; + data_ = nullptr; + + return {query_}; +} + +std::string handle_column(sql::query_context &ctx, const sql::dialect *d, const query_data &data, const table_column &col) { + if (col.is_function()) { + ctx.prototype.emplace_back(col.name()); + ctx.prototype.back().change_type(utils::basic_type::Int32); + } else { + ctx.prototype.emplace_back(col.name()); } - const_cast(node.col()).table(&it->second); + + // if (col.table() != nullptr) { + // if (const auto it = data.tables.find(col.table()->name()); it != data.tables.end()) { + // return prepare_identifier(*d,col); + // } + // } + + return prepare_identifier(*d, col); } -void query_builder::on_revision(const char *id, uint64_t &/*rev*/) { - push(id); +void query_builder::visit(internal::query_alter_part& part) { + query_.sql = dialect_->token_at(part.token()); } -void query_builder::push(const std::string &column_name) { - const auto it = processed_tables_.find(table_info_stack_.top().info.name()); - if (it == processed_tables_.end()) { - throw query_builder_exception{query_build_error::UnexpectedError}; +void query_builder::visit(internal::query_alter_table_part& part) { + query_.command = sql::sql_command::SQL_ALTER_TABLE; + query_.sql += " " + dialect_->token_at(part.token()) + " " + + dialect_->prepare_identifier_string(part.table().name()); +} + +void query_builder::visit(internal::query_add_key_constraint_part& part) { + query_.sql += " " + dialect_->add_constraint() + " " + part.name(); +} + +void build_columns_with_name_only(std::string &out, const std::vector &cols, const sql::dialect &d); +void build_columns(std::string &out, const std::vector &cols, const sql::dialect &d); + +void query_builder::visit(internal::query_add_foreign_key_constraint_part& part) { + query_.sql += " " + dialect_->token_at(part.token()) + " ("; + build_columns(query_.sql, part.columns(), *dialect_); + query_.sql += ")"; +} + +void query_builder::visit(internal::query_add_primary_key_constraint_part& part) { + query_.sql += " " + dialect_->primary_key() + " ("; + build_columns(query_.sql, part.columns(), *dialect_); + query_.sql += ")"; +} + +void query_builder::visit(internal::query_add_foreign_key_reference_part& part) { + query_.sql += " " + dialect_->token_at(part.token()) + " " + part.table().name() + " ("; + build_columns(query_.sql, part.columns(), *dialect_); + query_.sql += ")"; +} + +void query_builder::visit(internal::query_add_constraint_part_by_constraint &part) { + query_.sql += build_add_constraint_string(part.constraint()); +} + +void query_builder::visit(internal::query_drop_key_constraint_part_by_name& part) { + query_.sql += " " + dialect_->token_at(part.token()) + " " + part.name(); +} + +void query_builder::visit(internal::query_drop_key_constraint_part_by_constraint& part) { + query_.sql += " " + build_drop_constraint_string(part.constraint()); +} + +void query_builder::visit(internal::query_select_part &part) { + query_.command = sql::sql_command::SQL_SELECT; + query_.sql = dialect_->select() + " "; + + query_.prototype.clear(); + + std::string result; + if (part.columns().empty()) { + result = dialect_->asterisk(); + } else if (const auto &columns = part.columns(); columns.size() < 2) { + for (const auto &col: columns) { + result.append(handle_column(query_, dialect_, *data_, col )); + } + } else { + auto it = columns.begin(); + result.append(handle_column(query_, dialect_, *data_, *it++)); + for (; it != columns.end(); ++it) { + result.append(", "); + result.append(handle_column(query_, dialect_, *data_, *it)); + } } - entity_query_data_.columns.emplace_back(&it->second, column_name, build_alias('c', ++column_index)); + + query_.sql += result; } -std::string query_builder::build_alias(const char prefix, const unsigned int count) { - char str[4]; - snprintf(str, 4, "%c%02d", prefix, count); - - return str; +void query_builder::visit(internal::query_from_part &part) { + query_.table_name = part.tables().front().name(); + query_.sql += " " + dialect_->from() + " "; + if (const auto &tables = part.tables(); tables.size() < 2) { + for (const auto &tab: tables) { + query_.sql += build_table_name(*dialect_, tab); + } + } else { + auto it = tables.begin(); + query_.sql.append(build_table_name(*dialect_, *it++)); + for (; it != tables.end(); ++it) { + query_.sql.append(", "); + query_.sql.append(build_table_name(*dialect_, *it)); + } + } } -[[nodiscard]] bool query_builder::is_root_entity() const { - return table_info_stack_.size() == 1; +void query_builder::visit(internal::query_join_table_part &part) { + query_.sql += " " + build_table_name(part.token(), *dialect_, part.table()); } -void query_builder::append_join(const table_column &left, const table_column &right) { - using namespace matador::query; - entity_query_data_.joins.push_back({ - right.table(), - std::make_unique(left, binary_operator::EQUALS, right) - }); +void query_builder::visit(internal::query_join_query_part &part) { + query_.sql += " " + dialect_->join() + " (" + part.query().str(*dialect_) + ")"; +} + +void query_builder::visit(internal::query_on_part &part) { + criteria_evaluator evaluator(*dialect_, query_); + query_.sql += " " + dialect_->on() + + " " + evaluator.evaluate(part.condition()); +} + +void query_builder::visit(internal::query_where_part &part) { + criteria_evaluator evaluator(*dialect_, query_); + query_.sql += " " + dialect_->where() + + " " + evaluator.evaluate(part.condition()); +} + +void query_builder::visit(internal::query_group_by_part &part) { + query_.sql += " " + dialect_->group_by() + " "; + if (part.columns().size() < 2) { + for (const auto &col: part.columns()) { + query_.sql.append(dialect_->prepare_identifier_string(col.name())); + } + } else { + auto it = part.columns().begin(); + query_.sql.append(dialect_->prepare_identifier_string((it++)->canonical_name())); + for (; it != part.columns().end(); ++it) { + query_.sql.append(", "); + query_.sql.append(dialect_->prepare_identifier_string(it->canonical_name())); + } + } +} + +void query_builder::visit(internal::query_order_by_part &part) { + query_.sql += " " + dialect_->order_by() + " "; + if (part.columns().size() < 2) { + for (const auto &col: part.columns()) { + query_.sql.append(dialect_->prepare_identifier_string(col.canonical_name())); + } + } else { + auto it = part.columns().begin(); + query_.sql.append(dialect_->prepare_identifier_string((it++)->canonical_name())); + for (; it != part.columns().end(); ++it) { + query_.sql.append(", "); + query_.sql.append(dialect_->prepare_identifier_string(it->canonical_name())); + } + } +} + +void query_builder::visit(internal::query_order_by_asc_part &/*order_by_asc_part*/) { + query_.sql += " " + dialect_->asc(); +} + +void query_builder::visit(internal::query_order_by_desc_part &/*order_by_desc_part*/) { + query_.sql += " " + dialect_->desc(); +} + +void query_builder::visit(internal::query_offset_part &part) { + query_.sql += " " + dialect_->offset() + " " + std::to_string(part.offset()); +} + +void query_builder::visit(internal::query_limit_part &part) { + query_.sql += " " + dialect_->limit() + " " + std::to_string(part.limit()); +} + +void query_builder::visit(internal::query_insert_part &/*insert_part*/) { + query_.command = sql::sql_command::SQL_INSERT; + query_.sql = dialect_->insert(); +} + +void query_builder::visit(internal::query_into_part &part) { + query_.table_name = part.table().name(); + query_.sql += " " + dialect_->into() + + " " + dialect_->prepare_identifier_string(part.table().name()) + " ("; + + build_columns_with_name_only(query_.sql, part.columns(), *dialect_); + query_.sql += ")"/* + result*/; +} + +struct value_visitor { + value_visitor(attribute_string_writer &w, sql::query_context &ctx) + : value_to_string_visitor(w, ctx) {} + + void operator()(const utils::database_type &val) { + std::visit(value_to_string_visitor, val); + + } + void operator()(const utils::placeholder &/*val*/) { + value_to_string_visitor.query.bind_vars.emplace_back(std::string("value_") + std::to_string(value_to_string_visitor.query.bind_vars.size() + 1)); + value_to_string_visitor.result = value_to_string_visitor.writer->dialect().next_placeholder(value_to_string_visitor.query.bind_vars); + } + + internal::basic_type_to_string_visitor value_to_string_visitor; +}; + +std::string query_builder::determine_value(value_visitor &visitor, const std::variant &val) { + std::visit(visitor, val); + return visitor.value_to_string_visitor.result; +} + +void query_builder::visit(internal::query_values_part &part) { + query_.sql += " " + dialect_->values(); + + attribute_string_writer writer(*dialect_, connection_); + + value_visitor visitor(writer, query_); + std::string result{"("}; + if (part.values().size() < 2) { + for (const auto& val: part.values()) { + result.append(determine_value(visitor, val)); + } + } else { + auto it = part.values().begin(); + auto val = *it++; + result.append(determine_value(visitor, val)); + for (; it != part.values().end(); ++it) { + result.append(", "); + val = *it; + result.append(determine_value(visitor, val)); + } + } + result += (")"); + + query_.sql += " " + result; +} + +void query_builder::visit(internal::query_update_part &part) +{ + query_.command = sql::sql_command::SQL_UPDATE; + query_.table_name = part.table().name(); + query_.sql += query_builder::build_table_name(part.token(), *dialect_, query_.table_name); +} + +void query_builder::visit(internal::query_delete_part &/*delete_part*/) +{ + query_.command = sql::sql_command::SQL_DELETE; + query_.sql = dialect_->remove(); +} + +void query_builder::visit(internal::query_delete_from_part &part) +{ + query_.table_name = part.table().name(); + query_.sql += " " + build_table_name(part.token(), *dialect_, query_.table_name); +} + +void query_builder::visit(internal::query_create_part &/*create_part*/) +{ + query_.command = sql::sql_command::SQL_CREATE_TABLE; + query_.sql = dialect_->create(); +} + +void build_create_column(std::string &out, const table_column &col, const sql::dialect &d); +std::string build_constraint(const table_constraint &cons, const sql::dialect &d); + +void query_builder::visit(internal::query_create_table_part &part) +{ + query_.sql += " " + dialect_->table() + " " + dialect_->prepare_identifier_string(part.table().name()) + " ("; + query_.table_name = part.table().name(); + + finisher_ = [](sql::query_context &ctx) { ctx.sql += ")"; }; +} + +void query_builder::visit(internal::query_create_table_columns_part& part) { + bool first = true; + for (const auto& col : part.columns()) { + if (!first) { + query_.sql.append(", "); + } + build_create_column(query_.sql, col, *dialect_); + first = false; + } +} + +void query_builder::visit(internal::query_create_table_constraints_part& part) { + std::string result; + for (const auto& c : part.constraints()) { + result.append(", "); + result.append(build_constraint(c, *dialect_)); + } + query_.sql += result; +} + +void query_builder::visit( internal::query_create_schema_part& part ) { + query_.command = sql::sql_command::SQL_CREATE_SCHEMA; + query_.sql += " " + dialect_->schema() + " " + dialect_->prepare_identifier_string(part.schema()); +} + +void query_builder::visit(internal::query_drop_part &part) { + query_.command = sql::sql_command::SQL_DROP_TABLE; + query_.sql = dialect_->token_at(part.token()); +} + +void query_builder::visit( internal::query_drop_schema_part& part ) { + query_.sql += " " + dialect_->drop() + " " + + dialect_->schema() + " " + dialect_->prepare_identifier_string(part.schema()); +} + +void query_builder::visit(internal::query_set_part &part) { + query_.sql += " " + dialect_->set() + " "; + + attribute_string_writer writer(*dialect_, connection_); + std::string result; + + value_visitor visitor(writer, query_); if (part.column_values().size() < 2) { + for (const auto &column_value: part.column_values()) { + result.append(dialect_->prepare_identifier_string(column_value.col().name()) + "="); + result.append(determine_value(visitor, column_value.value())); + } + } else { + auto it = part.column_values().begin(); + result.append(dialect_->prepare_identifier_string(it->col().name()) + "="); + result.append(determine_value(visitor, (it++)->value())); + for (; it != part.column_values().end(); ++it) { + result.append(", "); + result.append(dialect_->prepare_identifier_string(it->col().name()) + "="); + result.append(determine_value(visitor, it->value())); + } + } + + query_.sql += result; +} + +void query_builder::visit(internal::query_drop_table_part &part) { + query_.table_name = part.table().name(); + query_.sql += " " + build_table_name(part.token(), *dialect_, query_.table_name); +} + +void build_create_column(std::string &out, const table_column &col, const sql::dialect &d) { + prepare_identifier_string_append(out, col.canonical_name(), d); + out += " " + d.data_type_at(col.type()); + if (col.attributes().size() > 0) { + out.append("(" + std::to_string(col.attributes().size()) + ")"); + } + if (!col.is_nullable()) { + out.append(" ").append(d.not_null()); + } + if (is_constraint_set(col.attributes().options(), utils::constraints::Unique)) { + out.append(" ").append(d.unique()); + } + if (is_constraint_set(col.attributes().options(), utils::constraints::PrimaryKey)) { + out.append(" ").append(d.primary_key()); + } +} + +void build_columns_with_name_only(std::string &out, const std::vector &cols, const sql::dialect &d) { + bool first = true; + for (const auto& col : cols) { + if (!first) { + out.append(", "); + } + prepare_identifier_string_append(out, col.column_name(), d); + first = false; + } +} + +void build_columns(std::string &out, const std::vector &cols, const sql::dialect &d) { + bool first = true; + for (const auto& col : cols) { + if (!first) { + out.append(", "); + } + prepare_identifier_string_append(out, col.name(), d); + first = false; + } +} + +std::string build_constraint(const table_constraint& cons, const sql::dialect& d) { + std::string result; + if (!cons.name().empty()) { + result.append(d.constraint()).append(" ").append(cons.name()).append(" "); + } + if (cons.is_primary_key_constraint()) { + result + .append(d.primary_key()) + .append(" (") + .append(cons.column_name()) + .append(")"); + } else if (cons.is_foreign_key_constraint()) { + result + .append(d.foreign_key()) + .append(" (") + .append(cons.column_name()) + .append(") ") + .append(d.references()).append(" ") + .append(cons.referenced_table()) + .append(" (") + .append(cons.referenced_column()) + .append(")"); + } else { + // handle error + } + return result; +} + +std::string query_builder::build_table_name(const sql::dialect_token token, const sql::dialect &d, const table& t) { + return d.token_at(token) + " " + build_table_name(d, t); +} + +std::string query_builder::build_table_name(const sql::dialect &d, const table& t) { + return (!d.default_schema_name().empty() ? d.prepare_identifier_string(d.default_schema_name()) + "." : "") + + d.prepare_identifier_string(t.table_name()) + + (!t.has_alias() ? "" : " " + d.prepare_identifier_string(t.name())); +} + +std::string query_builder::build_add_constraint_string(const table_constraint &c) const { + std::string result = " " + dialect_->add_constraint() + " " + build_constraint_name(c) + " "; + if (c.is_primary_key_constraint()) { + result.append(dialect_->primary_key()).append(" (").append(c.column_name()).append(")"); + } else if (c.is_foreign_key_constraint()) { + result.append(dialect_->foreign_key()).append(" (").append(c.column_name()).append(") ").append(dialect_->references()).append(" ").append(c.referenced_table()).append(" (").append(c.referenced_column()).append(")"); + } else if (c.is_unique_constraint()) { + result.append(dialect_->unique()).append(" (").append(c.column_name()).append(")"); + } + return result; +} + +std::string query_builder::build_drop_constraint_string(const table_constraint &c) const { + return dialect_->drop_constraint() + " " + build_constraint_name(c); +} + +std::string query_builder::build_constraint_name(const table_constraint &c) { + if (c.is_primary_key_constraint()) { + return "PK_" + c.table_name(); + } + if (c.is_foreign_key_constraint()) { + return "FK_" + c.column_name() + "_" + c.table_name(); + } + if (c.is_unique_constraint()) { + return "UK_" + c.column_name() + "_" + c.table_name(); + } + return ""; } } diff --git a/source/orm/query/query_compiler.cpp b/source/orm/query/query_compiler.cpp deleted file mode 100644 index 17dc1be..0000000 --- a/source/orm/query/query_compiler.cpp +++ /dev/null @@ -1,468 +0,0 @@ -#include "matador/query/query_compiler.hpp" - -#include "matador/query/attribute_string_writer.hpp" -#include "matador/query/query_data.hpp" -#include "matador/query/criteria_evaluator.hpp" -#include "matador/query/query_utils.hpp" -#include "matador/query/table_constraint.hpp" -#include "matador/query/table_column.hpp" - -#include "matador/query/internal/basic_type_to_string_visitor.hpp" -#include "matador/query/internal/string_builder_utils.hpp" -#include "matador/query/internal/query_parts.hpp" - -#include "matador/sql/query_context.hpp" -#include "matador/sql/connection.hpp" -#include "matador/sql/dialect.hpp" - -namespace matador::query { - -sql::query_context query_compiler::compile(const query_data &data, - const sql::dialect &d, - const std::optional> conn) -{ - data_ = &data; - dialect_ = &d; - connection_ = conn; - query_ = {}; - for (const auto &part: data.parts) { - part->accept(*this); - } - finisher_(query_); - connection_ = std::nullopt; - dialect_ = nullptr; - data_ = nullptr; - - return {query_}; -} - -std::string handle_column(sql::query_context &ctx, const sql::dialect *d, const query_data &data, const table_column &col) { - if (col.is_function()) { - ctx.prototype.emplace_back(col.name()); - ctx.prototype.back().change_type(utils::basic_type::Int32); - } else { - ctx.prototype.emplace_back(col.name()); - } - - - if (col.table() != nullptr) { - if (const auto it = data.tables.find(col.table()->name()); it != data.tables.end()) { - return prepare_identifier(*d,{&it->second, col.name(), col.alias()}); - } - } - - return prepare_identifier(*d, col); -} - -void query_compiler::visit(internal::query_alter_part& part) { - query_.sql = dialect_->token_at(part.token()); -} - -void query_compiler::visit(internal::query_alter_table_part& part) { - query_.command = sql::sql_command::SQL_ALTER_TABLE; - query_.sql += " " + dialect_->token_at(part.token()) + " " + - dialect_->prepare_identifier_string(part.table().name()); -} - -void query_compiler::visit(internal::query_add_key_constraint_part& part) { - query_.sql += " " + dialect_->add_constraint() + " " + part.name(); -} - -void build_columns(std::string &out, const std::vector &cols, const sql::dialect &d); - -void query_compiler::visit(internal::query_add_foreign_key_constraint_part& part) { - query_.sql += " " + dialect_->token_at(part.token()) + " ("; - build_columns(query_.sql, part.columns(), *dialect_); - query_.sql += ")"; -} - -void query_compiler::visit(internal::query_add_primary_key_constraint_part& part) { - query_.sql += " " + dialect_->primary_key() + " ("; - build_columns(query_.sql, part.columns(), *dialect_); - query_.sql += ")"; -} - -void query_compiler::visit(internal::query_add_foreign_key_reference_part& part) { - query_.sql += " " + dialect_->token_at(part.token()) + " " + part.table().name() + " ("; - build_columns(query_.sql, part.columns(), *dialect_); - query_.sql += ")"; -} - -void query_compiler::visit(internal::query_add_constraint_part_by_constraint &part) { - query_.sql += build_add_constraint_string(part.constraint()); -} - -void query_compiler::visit(internal::query_drop_key_constraint_part_by_name& part) { - query_.sql += " " + dialect_->token_at(part.token()) + " " + part.name(); -} - -void query_compiler::visit(internal::query_drop_key_constraint_part_by_constraint& part) { - query_.sql += " " + build_drop_constraint_string(part.constraint()); -} - -void query_compiler::visit(internal::query_select_part &part) { - query_.command = sql::sql_command::SQL_SELECT; - query_.sql = dialect_->select() + " "; - - query_.prototype.clear(); - - std::string result; - if (part.columns().empty()) { - result = dialect_->asterisk(); - } else if (const auto &columns = part.columns(); columns.size() < 2) { - for (const auto &col: columns) { - result.append(handle_column(query_, dialect_, *data_, col )); - } - } else { - auto it = columns.begin(); - result.append(handle_column(query_, dialect_, *data_, *it++)); - for (; it != columns.end(); ++it) { - result.append(", "); - result.append(handle_column(query_, dialect_, *data_, *it)); - } - } - - query_.sql += result; -} - -void query_compiler::visit(internal::query_from_part &part) { - query_.table_name = part.tables().front().name(); - query_.sql += " " + dialect_->from() + " "; - if (const auto &tables = part.tables(); tables.size() < 2) { - for (const auto &tab: tables) { - query_.sql += build_table_name(*dialect_, tab); - } - } else { - auto it = tables.begin(); - query_.sql.append(build_table_name(*dialect_, *it++)); - for (; it != tables.end(); ++it) { - query_.sql.append(", "); - query_.sql.append(build_table_name(*dialect_, *it)); - } - } -} - -void query_compiler::visit(internal::query_join_table_part &part) { - query_.sql += " " + build_table_name(part.token(), *dialect_, part.table()); -} - -void query_compiler::visit(internal::query_join_query_part &part) { - query_.sql += " " + dialect_->join() + " (" + part.query().str(*dialect_) + ")"; -} - -void query_compiler::visit(internal::query_on_part &part) { - criteria_evaluator evaluator(*dialect_, query_); - query_.sql += " " + dialect_->on() + - " " + evaluator.evaluate(part.condition()); -} - -void query_compiler::visit(internal::query_where_part &part) { - criteria_evaluator evaluator(*dialect_, query_); - query_.sql += " " + dialect_->where() + - " " + evaluator.evaluate(part.condition()); -} - -void query_compiler::visit(internal::query_group_by_part &part) { - query_.sql += " " + dialect_->group_by() + " "; - if (part.columns().size() < 2) { - for (const auto &col: part.columns()) { - query_.sql.append(dialect_->prepare_identifier_string(col.name())); - } - } else { - auto it = part.columns().begin(); - query_.sql.append(dialect_->prepare_identifier_string((it++)->canonical_name())); - for (; it != part.columns().end(); ++it) { - query_.sql.append(", "); - query_.sql.append(dialect_->prepare_identifier_string(it->canonical_name())); - } - } -} - -void query_compiler::visit(internal::query_order_by_part &part) { - query_.sql += " " + dialect_->order_by() + " "; - if (part.columns().size() < 2) { - for (const auto &col: part.columns()) { - query_.sql.append(dialect_->prepare_identifier_string(col.canonical_name())); - } - } else { - auto it = part.columns().begin(); - query_.sql.append(dialect_->prepare_identifier_string((it++)->canonical_name())); - for (; it != part.columns().end(); ++it) { - query_.sql.append(", "); - query_.sql.append(dialect_->prepare_identifier_string(it->canonical_name())); - } - } -} - -void query_compiler::visit(internal::query_order_by_asc_part &/*order_by_asc_part*/) { - query_.sql += " " + dialect_->asc(); -} - -void query_compiler::visit(internal::query_order_by_desc_part &/*order_by_desc_part*/) { - query_.sql += " " + dialect_->desc(); -} - -void query_compiler::visit(internal::query_offset_part &part) { - query_.sql += " " + dialect_->offset() + " " + std::to_string(part.offset()); -} - -void query_compiler::visit(internal::query_limit_part &part) { - query_.sql += " " + dialect_->limit() + " " + std::to_string(part.limit()); -} - -void query_compiler::visit(internal::query_insert_part &/*insert_part*/) { - query_.command = sql::sql_command::SQL_INSERT; - query_.sql = dialect_->insert(); -} - -void query_compiler::visit(internal::query_into_part &part) { - query_.table_name = part.table().name(); - query_.sql += " " + dialect_->into() + - " " + dialect_->prepare_identifier_string(part.table().name()) + " ("; - - build_columns(query_.sql, part.columns(), *dialect_); - query_.sql += ")"/* + result*/; -} - -struct value_visitor { - value_visitor(attribute_string_writer &w, sql::query_context &ctx) - : value_to_string_visitor(w, ctx) {} - - void operator()(const utils::database_type &val) { - std::visit(value_to_string_visitor, val); - - } - void operator()(const utils::placeholder &/*val*/) { - value_to_string_visitor.query.bind_vars.emplace_back(std::string("value_") + std::to_string(value_to_string_visitor.query.bind_vars.size() + 1)); - value_to_string_visitor.result = value_to_string_visitor.writer->dialect().next_placeholder(value_to_string_visitor.query.bind_vars); - } - - internal::basic_type_to_string_visitor value_to_string_visitor; -}; - -std::string query_compiler::determine_value(value_visitor &visitor, const std::variant &val) { - std::visit(visitor, val); - return visitor.value_to_string_visitor.result; -} - -void query_compiler::visit(internal::query_values_part &part) { - query_.sql += " " + dialect_->values(); - - attribute_string_writer writer(*dialect_, connection_); - - value_visitor visitor(writer, query_); - std::string result{"("}; - if (part.values().size() < 2) { - for (const auto& val: part.values()) { - result.append(determine_value(visitor, val)); - } - } else { - auto it = part.values().begin(); - auto val = *it++; - result.append(determine_value(visitor, val)); - for (; it != part.values().end(); ++it) { - result.append(", "); - val = *it; - result.append(determine_value(visitor, val)); - } - } - result += (")"); - - query_.sql += " " + result; -} - -void query_compiler::visit(internal::query_update_part &part) -{ - query_.command = sql::sql_command::SQL_UPDATE; - query_.table_name = part.table().name(); - query_.sql += query_compiler::build_table_name(part.token(), *dialect_, query_.table_name); -} - -void query_compiler::visit(internal::query_delete_part &/*delete_part*/) -{ - query_.command = sql::sql_command::SQL_DELETE; - query_.sql = dialect_->remove(); -} - -void query_compiler::visit(internal::query_delete_from_part &part) -{ - query_.table_name = part.table().name(); - query_.sql += " " + build_table_name(part.token(), *dialect_, query_.table_name); -} - -void query_compiler::visit(internal::query_create_part &/*create_part*/) -{ - query_.command = sql::sql_command::SQL_CREATE_TABLE; - query_.sql = dialect_->create(); -} - -void build_create_column(std::string &out, const table_column &col, const sql::dialect &d); -std::string build_constraint(const table_constraint &cons, const sql::dialect &d); - -void query_compiler::visit(internal::query_create_table_part &part) -{ - query_.sql += " " + dialect_->table() + " " + dialect_->prepare_identifier_string(part.table().name()) + " ("; - query_.table_name = part.table().name(); - - finisher_ = [](sql::query_context &ctx) { ctx.sql += ")"; }; -} - -void query_compiler::visit(internal::query_create_table_columns_part& part) { - bool first = true; - for (const auto& col : part.columns()) { - if (!first) { - query_.sql.append(", "); - } - build_create_column(query_.sql, col, *dialect_); - first = false; - } -} - -void query_compiler::visit(internal::query_create_table_constraints_part& part) { - std::string result; - for (const auto& c : part.constraints()) { - result.append(", "); - result.append(build_constraint(c, *dialect_)); - } - query_.sql += result; -} - -void query_compiler::visit( internal::query_create_schema_part& part ) { - query_.command = sql::sql_command::SQL_CREATE_SCHEMA; - query_.sql += " " + dialect_->schema() + " " + dialect_->prepare_identifier_string(part.schema()); -} - -void query_compiler::visit(internal::query_drop_part &part) { - query_.command = sql::sql_command::SQL_DROP_TABLE; - query_.sql = dialect_->token_at(part.token()); -} - -void query_compiler::visit( internal::query_drop_schema_part& part ) { - query_.sql += " " + dialect_->drop() + " " + - dialect_->schema() + " " + dialect_->prepare_identifier_string(part.schema()); -} - -void query_compiler::visit(internal::query_set_part &part) { - query_.sql += " " + dialect_->set() + " "; - - attribute_string_writer writer(*dialect_, connection_); - std::string result; - - value_visitor visitor(writer, query_); if (part.column_values().size() < 2) { - for (const auto &column_value: part.column_values()) { - result.append(dialect_->prepare_identifier_string(column_value.col().name()) + "="); - result.append(determine_value(visitor, column_value.value())); - } - } else { - auto it = part.column_values().begin(); - result.append(dialect_->prepare_identifier_string(it->col().name()) + "="); - result.append(determine_value(visitor, (it++)->value())); - for (; it != part.column_values().end(); ++it) { - result.append(", "); - result.append(dialect_->prepare_identifier_string(it->col().name()) + "="); - result.append(determine_value(visitor, it->value())); - } - } - - query_.sql += result; -} - -void query_compiler::visit(internal::query_drop_table_part &part) { - query_.table_name = part.table().name(); - query_.sql += " " + build_table_name(part.token(), *dialect_, query_.table_name); -} - -void build_create_column(std::string &out, const table_column &col, const sql::dialect &d) { - prepare_identifier_string_append(out, col.canonical_name(), d); - out += " " + d.data_type_at(col.type()); - if (col.attributes().size() > 0) { - out.append("(" + std::to_string(col.attributes().size()) + ")"); - } - if (!col.is_nullable()) { - out.append(" ").append(d.not_null()); - } - if (is_constraint_set(col.attributes().options(), utils::constraints::Unique)) { - out.append(" ").append(d.unique()); - } - if (is_constraint_set(col.attributes().options(), utils::constraints::PrimaryKey)) { - out.append(" ").append(d.primary_key()); - } -} -void build_columns(std::string &out, const std::vector &cols, const sql::dialect &d) { - bool first = true; - for (const auto& col : cols) { - if (!first) { - out.append(", "); - } - prepare_identifier_string_append(out, col.name(), d); - first = false; - } -} - -std::string build_constraint(const table_constraint& cons, const sql::dialect& d) { - std::string result; - if (!cons.name().empty()) { - result.append(d.constraint()).append(" ").append(cons.name()).append(" "); - } - if (cons.is_primary_key_constraint()) { - result - .append(d.primary_key()) - .append(" (") - .append(cons.column_name()) - .append(")"); - } else if (cons.is_foreign_key_constraint()) { - result - .append(d.foreign_key()) - .append(" (") - .append(cons.column_name()) - .append(") ") - .append(d.references()).append(" ") - .append(cons.referenced_table()) - .append(" (") - .append(cons.referenced_column()) - .append(")"); - } else { - // handle error - } - return result; -} - -std::string query_compiler::build_table_name(const sql::dialect_token token, const sql::dialect &d, const table& t) { - return d.token_at(token) + " " + build_table_name(d, t); -} - -std::string query_compiler::build_table_name(const sql::dialect &d, const table& t) { - return (!d.default_schema_name().empty() ? d.prepare_identifier_string(d.default_schema_name()) + "." : "") + - d.prepare_identifier_string(t.table_name()) + - (!t.has_alias() ? "" : " " + d.prepare_identifier_string(t.name())); -} - -std::string query_compiler::build_add_constraint_string(const table_constraint &c) const { - std::string result = " " + dialect_->add_constraint() + " " + build_constraint_name(c) + " "; - if (c.is_primary_key_constraint()) { - result.append(dialect_->primary_key()).append(" (").append(c.column_name()).append(")"); - } else if (c.is_foreign_key_constraint()) { - result.append(dialect_->foreign_key()).append(" (").append(c.column_name()).append(") ").append(dialect_->references()).append(" ").append(c.referenced_table()).append(" (").append(c.referenced_column()).append(")"); - } else if (c.is_unique_constraint()) { - result.append(dialect_->unique()).append(" (").append(c.column_name()).append(")"); - } - return result; -} - -std::string query_compiler::build_drop_constraint_string(const table_constraint &c) const { - return dialect_->drop_constraint() + " " + build_constraint_name(c); -} - -std::string query_compiler::build_constraint_name(const table_constraint &c) { - if (c.is_primary_key_constraint()) { - return "PK_" + c.table_name(); - } - if (c.is_foreign_key_constraint()) { - return "FK_" + c.column_name() + "_" + c.table_name(); - } - if (c.is_unique_constraint()) { - return "UK_" + c.column_name() + "_" + c.table_name(); - } - return ""; -} -} diff --git a/source/orm/query/query_utils.cpp b/source/orm/query/query_utils.cpp index de89994..0302fe8 100644 --- a/source/orm/query/query_utils.cpp +++ b/source/orm/query/query_utils.cpp @@ -9,7 +9,11 @@ std::string prepare_identifier(const sql::dialect& d, const table_column& col) { if (!col.is_function()) { prepare_identifier_string_append(result, col.name(), d); } else { - result = d.sql_function_at(col.function()) + "(" + col.name() + ")"; + if (col.column_name() == d.asterisk()) { + result = d.sql_function_at(col.function()) + "(" + col.column_name() + ")"; + } else { + result = d.sql_function_at(col.function()) + "(" + col.column_name() + ") " + d.as() + " " + col.alias(); + } } return result; } @@ -19,7 +23,11 @@ std::string prepare_criteria(const sql::dialect& d, const table_column& col) { if (!col.is_function()) { prepare_identifier_string_append(result, col.name(), d); } else { - result = d.sql_function_at(col.function()) + "(" + col.name() + ")"; + if (col.column_name() == d.asterisk()) { + result = d.sql_function_at(col.function()) + "(" + col.column_name() + ")"; + } else { + result = d.sql_function_at(col.function()) + "(" + col.column_name() + ") " + d.as() + " " + col.alias(); + } } return result; diff --git a/source/orm/query/select_query_builder.cpp b/source/orm/query/select_query_builder.cpp new file mode 100644 index 0000000..b4b2b90 --- /dev/null +++ b/source/orm/query/select_query_builder.cpp @@ -0,0 +1,85 @@ +#include "matador/query/select_query_builder.hpp" + +namespace matador::query { +criteria_transformer::criteria_transformer(const basic_schema& repo, const std::unordered_map& tables_by_name) +: repo_(repo) +, tables_by_name_(tables_by_name) {} + +void criteria_transformer::visit( const between_criteria& node ) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const binary_criteria& node ) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const binary_column_criteria& /*node*/ ) {} + +void criteria_transformer::visit(const check_null_criteria& node) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const collection_criteria& node ) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const collection_query_criteria& node ) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const like_criteria& node ) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const logical_criteria& node ) { + node.left_clause()->accept(*this); + node.right_clause()->accept(*this); +} + +void criteria_transformer::visit( const not_criteria& node ) { + node.clause()->accept(*this); +} + +void criteria_transformer::update_criteria_column(const abstract_column_criteria& node) const { + if (node.col().table() == nullptr) { + return; + } + const auto it = tables_by_name_.find(node.col().table()->name()); + if (it == tables_by_name_.end()) { + return; + } + + const_cast(node.col()).table(&it->second); +} + +void select_query_builder::on_revision(const char *id, uint64_t &/*rev*/) { + push(id); +} + +void select_query_builder::push(const std::string &column_name) { + const auto it = processed_tables_.find(table_info_stack_.top().info.name()); + if (it == processed_tables_.end()) { + throw query_builder_exception{query_build_error::UnexpectedError}; + } + entity_query_data_.columns.emplace_back(&it->second, column_name, build_alias('c', ++column_index)); +} + +std::string select_query_builder::build_alias(const char prefix, const unsigned int count) { + char str[4]; + snprintf(str, 4, "%c%02d", prefix, count); + + return str; +} + +[[nodiscard]] bool select_query_builder::is_root_entity() const { + return table_info_stack_.size() == 1; +} + +void select_query_builder::append_join(const table_column &left, const table_column &right) { + using namespace matador::query; + entity_query_data_.joins.push_back({ + right.table(), + std::make_unique(left, binary_operator::EQUALS, right) + }); +} +} diff --git a/test/orm/orm/SessionQueryBuilderTest.cpp b/test/orm/orm/SessionQueryBuilderTest.cpp index d584f14..aa50ae2 100644 --- a/test/orm/orm/SessionQueryBuilderTest.cpp +++ b/test/orm/orm/SessionQueryBuilderTest.cpp @@ -12,7 +12,7 @@ #include "matador/query/table.hpp" #include "matador/query/schema.hpp" -#include "matador/query/query_builder.hpp" +#include "matador/query/select_query_builder.hpp" #include "../backend/test_backend_service.hpp" @@ -44,7 +44,7 @@ TEST_CASE("Create sql query data for entity with eager has one", "[query][entity .and_then( [&scm] { return scm.attach("flights"); } ); REQUIRE(result); - query_builder eqb(scm, db); + select_query_builder eqb(scm, db); const auto it = scm.find(typeid(flight)); REQUIRE(it != scm.end()); @@ -98,7 +98,7 @@ TEST_CASE("Create sql query data for entity with eager belongs to", "[query][ent .and_then( [&scm] { return scm.attach("books"); } ); REQUIRE(result); - query_builder eqb(scm, db); + select_query_builder eqb(scm, db); const auto it = scm.find(typeid(book)); REQUIRE(it != scm.end()); @@ -170,7 +170,7 @@ TEST_CASE("Create sql query data for entity with eager has many belongs to", "[q .and_then( [&scm] { return scm.attach("orders"); } ); REQUIRE(result); - query_builder eqb(scm, db); + select_query_builder eqb(scm, db); const auto it = scm.find(typeid(order)); REQUIRE(it != scm.end()); @@ -234,7 +234,7 @@ TEST_CASE("Create sql query data for entity with eager many to many", "[query][e .and_then( [&scm] { return scm.attach("ingredients"); } ); REQUIRE(result); - query_builder eqb(scm, db); + select_query_builder eqb(scm, db); const auto it = scm.find(typeid(ingredient)); REQUIRE(it != scm.end()); @@ -288,7 +288,7 @@ TEST_CASE("Create sql query data for entity with eager many to many (inverse par .and_then( [&scm] { return scm.attach("courses"); } ); REQUIRE(result); - query_builder eqb(scm, db); + select_query_builder eqb(scm, db); const auto it = scm.find(typeid(course)); REQUIRE(it != scm.end()); @@ -342,7 +342,7 @@ TEST_CASE("Test eager relationship", "[session][eager]") { .and_then( [&scm] { return scm.attach("employees"); } ); REQUIRE(result); - query_builder eqb(scm, db); + select_query_builder eqb(scm, db); auto data = eqb.build(); REQUIRE(data.is_ok());