From a79d50e6e8948e79a98f330910b09957eadcdcda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Thu, 1 Jan 2026 17:08:20 +0100 Subject: [PATCH] fixed PostgreSQL tests --- backends/postgres/src/postgres_connection.cpp | 7 +- include/matador/object/object_generator.hpp | 2 +- include/matador/orm/session.hpp | 43 ++-- include/matador/query/meta_table_macro.hpp | 9 +- include/matador/query/table.hpp | 8 +- include/matador/query/table_column.hpp | 7 +- source/orm/orm/session.cpp | 112 +---------- source/orm/query/query.cpp | 2 +- source/orm/query/query_compiler.cpp | 7 +- source/orm/query/table_column.cpp | 21 +- source/orm/sql/field.cpp | 36 ++-- source/orm/sql/record.cpp | 34 +--- test/backends/QueryTest.cpp | 187 ++++++++++-------- test/backends/SessionFixture.cpp | 7 +- test/backends/SessionFixture.hpp | 4 +- test/backends/SessionTest.cpp | 71 ++++--- test/core/object/ObjectTest.cpp | 2 +- 17 files changed, 230 insertions(+), 329 deletions(-) diff --git a/backends/postgres/src/postgres_connection.cpp b/backends/postgres/src/postgres_connection.cpp index 3a7346d..c8816d3 100644 --- a/backends/postgres/src/postgres_connection.cpp +++ b/backends/postgres/src/postgres_connection.cpp @@ -252,9 +252,10 @@ utils::result, utils::error> postgres_connection: } utils::result postgres_connection::exists(const std::string &schema_name, const std::string &table_name) { - const std::string stmt( - "SELECT 1 FROM information_schema.tables WHERE table_schema = '" + schema_name + "' AND table_name = '" + table_name - + "'"); + std::string stmt( "SELECT 1 FROM information_schema.tables WHERE table_name = '" + table_name + "'"); + if (!schema_name.empty()) { + stmt += " AND table_schema = '" + schema_name + "'"; + } PGresult *res = PQexec(conn_, stmt.c_str()); diff --git a/include/matador/object/object_generator.hpp b/include/matador/object/object_generator.hpp index a5847d0..0693590 100644 --- a/include/matador/object/object_generator.hpp +++ b/include/matador/object/object_generator.hpp @@ -137,7 +137,7 @@ template void object_generator::on_primary_key(const char *id, ValueType &x, const utils::primary_key_attribute& attr) { auto &ref = emplace_attribute(id, { attr.size(), utils::constraints::PrimaryKey }, null_option_type::NotNull); prepare_primary_key(ref, utils::identifier(x)); - create_pk_constraint(id); + // create_pk_constraint(id); } template diff --git a/include/matador/orm/session.hpp b/include/matador/orm/session.hpp index eacc8b7..a905148 100644 --- a/include/matador/orm/session.hpp +++ b/include/matador/orm/session.hpp @@ -84,15 +84,7 @@ private: }; class session final : public sql::executor { public: - explicit session(session_context &&ctx); - - template - [[nodiscard]] utils::result attach(const std::string &table_name) const; - template - [[nodiscard]] utils::result attach(const std::string &table_name) const; - - utils::result create_schema() const; - utils::result drop_schema() const; + session(session_context &&ctx, const query::schema &scm); /** * Insert the given object into the session. @@ -138,35 +130,24 @@ private: friend class query_select; static query::fetchable_query build_select_query(entity_query_data &&data); - static sql::query_context build_add_constraint_context(const std::string& table_name, const class object::restriction& cons, const sql::connection_ptr &conn) ; private: mutable sql::statement_cache cache_; const sql::dialect &dialect_; - std::unique_ptr schema_; + const query::schema& schema_; mutable std::unordered_map> prototypes_; }; -template -[[nodiscard]] utils::result session::attach(const std::string &table_name) const { - return schema_->attach(table_name); -} - -template -utils::result session::attach( const std::string& table_name ) const { - return schema_->attach(table_name); -} - template utils::result, utils::error> session::insert(Type *obj) { - auto info = schema_->repo().info(); + auto info = schema_.repo().info(); if (!info) { return utils::failure(info.err()); } auto res = query::query::insert() - .into(info->get().name(), query::generator::columns(*schema_)) + .into(info->get().name(), query::generator::columns(schema_)) .values(query::generator::placeholders()) .prepare(*this); if (!res) { @@ -233,7 +214,7 @@ private: template utils::result, utils::error> session::update( const object::object_ptr& obj ) { - auto info = schema_->repo().info(); + auto info = schema_.repo().info(); if (!info) { return utils::failure(info.err()); } @@ -259,7 +240,7 @@ utils::result, utils::error> session::update( const obj template utils::result session::remove( const object::object_ptr& obj ) { - auto info = schema_->repo().info(); + auto info = schema_.repo().info(); if (!info) { return utils::failure(info.err()); } @@ -284,8 +265,8 @@ utils::result session::remove( const object::object_ptr utils::result, utils::error> session::find(const PrimaryKeyType& pk) { - const auto it = schema_->find(typeid(Type)); - if (it == schema_->end()) { + const auto it = schema_.find(typeid(Type)); + if (it == schema_.end()) { return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); } const auto& info = it->second.node().info(); @@ -296,7 +277,7 @@ utils::result, utils::error> session::find(const Primar return utils::failure(make_error(error_code::NoPrimaryKey, "Type hasn't primary key.")); } - session_query_builder eqb(*schema_, *this); + session_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::_); @@ -318,12 +299,12 @@ utils::result, utils::error> session::find(const Primar template utils::result, utils::error> session::find(query::criteria_ptr clause) { - auto info = schema_->repo().info(); + auto info = schema_.repo().info(); if (!info) { return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); } - session_query_builder eqb(*schema_, *this); + session_query_builder eqb(schema_, *this); 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() + ".")); @@ -339,7 +320,7 @@ utils::result, utils::error> session::find(query::criter template utils::result session::drop_table() { - auto info = schema_->repo().info(); + auto info = schema_.repo().info(); if (info) { return drop_table(info->get().name()); } diff --git a/include/matador/query/meta_table_macro.hpp b/include/matador/query/meta_table_macro.hpp index f828a61..986753c 100644 --- a/include/matador/query/meta_table_macro.hpp +++ b/include/matador/query/meta_table_macro.hpp @@ -13,10 +13,13 @@ #define META_TABLE(TABLE_NAME, VARIABLE_NAME, ...) \ namespace matador::query::meta { \ namespace internal { \ -class TABLE_NAME##_table : public table { \ +class TABLE_NAME##_table : public typed_table { \ public: \ -TABLE_NAME##_table() \ -: table(#TABLE_NAME, {MAP(FIELD_STRING, __VA_ARGS__)}) \ +TABLE_NAME##_table()\ +: TABLE_NAME##_table("") \ +{} \ +TABLE_NAME##_table(const std::string& alias) \ +: typed_table(#TABLE_NAME, alias, {MAP(FIELD_STRING, __VA_ARGS__)}) \ MAP(INIT_FIELD, __VA_ARGS__) \ {} \ MAP(FIELD, __VA_ARGS__) \ diff --git a/include/matador/query/table.hpp b/include/matador/query/table.hpp index 35eb3ae..0778932 100644 --- a/include/matador/query/table.hpp +++ b/include/matador/query/table.hpp @@ -40,7 +40,6 @@ public: protected: static const table_column& create_column(class table& tab, const std::string& name); -private: table(std::string name, std::string alias, const std::vector& columns); private: @@ -55,6 +54,13 @@ private: table operator ""_tab(const char *name, size_t len); +template +class typed_table : public table { +public: + using table::table; + + Type as(std::string alias) const { return Type{std::move(alias)}; } +}; } #endif //QUERY_TABLE_HPP diff --git a/include/matador/query/table_column.hpp b/include/matador/query/table_column.hpp index c420062..8386c24 100644 --- a/include/matador/query/table_column.hpp +++ b/include/matador/query/table_column.hpp @@ -24,15 +24,18 @@ public: table_column(const class table* tab, std::string name); table_column(const class table* tab, std::string name, std::string alias); table_column(const class table* tab, std::string name, utils::basic_type type, const utils::field_attributes& attributes); - table_column(const class table*, std::string name, std::string alias, utils::basic_type type, const utils::field_attributes& attributes); + table_column(const class table*, std::string name, std::string alias, utils::basic_type type, const utils::field_attributes& attributes, sql::sql_function_t func = sql::sql_function_t::None); table_column& operator=(const table_column& other); - table_column(const table_column& other); + table_column(const table_column& other) = default; + table_column(table_column&& other) noexcept = default; + ~table_column() = default; [[nodiscard]] bool equals(const table_column &x) const; table_column as(std::string a); [[nodiscard]] const std::string& name() const; + [[nodiscard]] std::string canonical_name() const; [[nodiscard]] const std::string& alias() const; [[nodiscard]] utils::basic_type type() const; [[nodiscard]] utils::field_attributes attributes() const; diff --git a/source/orm/orm/session.cpp b/source/orm/orm/session.cpp index 4e95e82..b093a6a 100644 --- a/source/orm/orm/session.cpp +++ b/source/orm/orm/session.cpp @@ -12,116 +12,10 @@ utils::error make_error(const error_code ec, const std::string &msg) { return utils::error(ec, msg); } -session::session(session_context&& ctx) +session::session(session_context&& ctx, const query::schema &scm) : cache_(ctx.bus, ctx.pool, ctx.cache_size) , dialect_(sql::backend_provider::instance().connection_dialect(ctx.pool.info().type)) -, schema_(std::make_unique(dialect_.default_schema_name())) { -} - -utils::result session::create_schema() const { - // Step 1: Build dependency graph - // std::unordered_map > dependency_graph; - // std::unordered_map> in_degree; - - // for (const auto &node: *schema_) { - // for (auto it = node->info().endpoint_begin(); it != node->info().endpoint_end(); ++it) { - // dependency_graph[node->name()].push_back(it->second->node().name()); - // - // if (const auto dit = in_degree.find(it->second->node().name()); dit == in_degree.end()) { - // in_degree[it->second->node().name()] = std::make_pair(1, it->second->node_ptr()); - // } else { - // in_degree[it->second->node().name()].first++; - // } - // } - // - // // Ensure the current node exists in the graph representation - // if (in_degree.find(node->name()) == in_degree.end()) { - // in_degree[node->name()] = std::make_pair(0, node); - // } - // } - // - // for (const auto &it : dependency_graph) { - // std::cout << "Dependency graph " << it.first << std::endl; - // for (const auto &neighbor: it.second) { - // std::cout << " " << neighbor << std::endl; - // } - // std::cout << std::endl; - // } - - auto c = cache_.pool().acquire(); - for (const auto &node: schema_->repo()) { - auto ctx = query::query::create() - .table(node->name()) - .columns(node->info().attributes()) - .compile(*c); - - std::cout << ctx.sql << std::endl; - if (auto result = c->execute(ctx.sql); !result) { - return utils::failure(result.err()); - } - } - - // create table constraints - for (const auto &node: schema_->repo()) { - for (const auto& cons : node->info().constraints()) { - auto ctx = build_add_constraint_context(node->name(), cons, c); - - std::cout << ctx.sql << std::endl; - if (auto result = c->execute(ctx.sql); !result) { - return utils::failure(result.err()); - } - } - } - - return utils::ok(); -} - -sql::query_context session::build_add_constraint_context(const std::string& table_name, const class object::restriction& cons, const sql::connection_ptr &conn) { - if (cons.is_foreign_key_constraint()) { - return query::query::alter() - .table(table_name) - .add_constraint(cons) - .compile(*conn); - } - if (cons.is_primary_key_constraint()) { - return query::query::alter() - .table(table_name) - .add_constraint(cons) - .compile(*conn); - } - return {}; -} - -utils::result session::drop_schema() const { - auto c = cache_.pool().acquire(); - // drop table constraints - for (const auto &node: schema_->repo()) { - for (const auto& cons : node->info().constraints()) { - auto ctx = query::query::alter() - .table(node->name()) - .drop_constraint(cons) - .compile(*c); - - std::cout << ctx.sql << std::endl; - if (auto result = c->execute(ctx.sql); !result) { - return utils::failure(result.err()); - } - } - } - - // drop table - for (const auto &node: schema_->repo()) { - auto ctx = query::query::drop() - .table(node->name()) - .compile(*c); - - std::cout << ctx.sql << std::endl; - if (auto result = c->execute(ctx.sql); !result) { - return utils::failure(result.err()); - } - } - - return utils::ok(); +, schema_(scm) { } utils::result session::drop_table(const std::string &table_name) const { @@ -191,7 +85,7 @@ const class sql::dialect &session::dialect() const { } void session::dump_schema(std::ostream &os) const { - schema_->repo().dump(os); + schema_.repo().dump(os); } utils::result, utils::error> session::fetch(const sql::query_context& ctx) const { diff --git a/source/orm/query/query.cpp b/source/orm/query/query.cpp index b25772d..65ee7eb 100644 --- a/source/orm/query/query.cpp +++ b/source/orm/query/query.cpp @@ -8,7 +8,7 @@ table_column alias(const std::string &column, const std::string &as) { table_column alias(table_column &&col, const std::string &as) { col.as(as); - return std::move(col); + return col; } table_column count(const std::string &column) { diff --git a/source/orm/query/query_compiler.cpp b/source/orm/query/query_compiler.cpp index b3a66c6..09738e6 100644 --- a/source/orm/query/query_compiler.cpp +++ b/source/orm/query/query_compiler.cpp @@ -39,10 +39,10 @@ sql::query_context query_compiler::compile(const query_data &data, 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.has_alias() ? col.alias() : col.name()); + ctx.prototype.emplace_back(col.has_alias() ? col.alias() : col.canonical_name()); ctx.prototype.back().change_type(utils::basic_type::Int32); } else { - ctx.prototype.emplace_back(col.name()); + ctx.prototype.emplace_back(col.canonical_name()); } @@ -128,7 +128,8 @@ void query_compiler::visit(internal::query_drop_key_constraint_part_by_name& par query_.sql += " " + dialect_->token_at(part.token()) + " " + part.name(); } -void query_compiler::visit(internal::query_drop_key_constraint_part_by_constraint& /*part*/) { +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) { diff --git a/source/orm/query/table_column.cpp b/source/orm/query/table_column.cpp index 57d3670..e0deed0 100644 --- a/source/orm/query/table_column.cpp +++ b/source/orm/query/table_column.cpp @@ -56,12 +56,14 @@ table_column::table_column(const class table* tab, std::string name, std::string alias, utils::basic_type type, - const utils::field_attributes &attributes) + const utils::field_attributes &attributes, + const sql::sql_function_t func) : table_(tab) , name_(std::move(name)) , alias_(std::move(alias)) , type_(type) -, attributes_(attributes) {} +, attributes_(attributes) +, function_(func) {} table_column & table_column::operator=(const table_column &other) { if (this == &other) { @@ -72,17 +74,10 @@ table_column & table_column::operator=(const table_column &other) { alias_ = other.alias_; type_ = other.type_; attributes_ = other.attributes_; + function_ = other.function_; return *this; } -table_column::table_column(const table_column &other) -: table_(other.table_) -, name_(other.name_) -, alias_(other.alias_) -, type_(other.type_) -, attributes_(other.attributes_) { -} - bool table_column::equals(const table_column &x) const { if (table_ != nullptr && x.table_ != nullptr) { return *table_ == *x.table_ && @@ -98,13 +93,17 @@ bool table_column::equals(const table_column &x) const { table_column table_column::as(std::string a) { alias_ = std::move(a); - return {table_, name_, alias_, type_, attributes_}; + return {table_, name_, alias_, type_, attributes_, function_}; } const std::string& table_column::name() const { return name_; } +std::string table_column::canonical_name() const { + return table_ ? table_->name() + "." + name_ : name_; +} + const std::string& table_column::alias() const { return alias_; } diff --git a/source/orm/sql/field.cpp b/source/orm/sql/field.cpp index 3020d0b..d561cef 100644 --- a/source/orm/sql/field.cpp +++ b/source/orm/sql/field.cpp @@ -35,8 +35,7 @@ field &field::operator=(field &&x) noexcept { return *this; } -const std::string &field::name() const -{ +const std::string &field::name() const { return name_; } @@ -44,59 +43,48 @@ utils::constraints field::type() const { return type_; } -size_t field::size() const -{ +size_t field::size() const { return value_.size(); } -int field::index() const -{ +int field::index() const { return index_; } -std::ostream &operator<<(std::ostream &out, const field &col) -{ +std::ostream &operator<<(std::ostream &out, const field &col) { out << col.str(); return out; } -std::string field::str() const -{ +std::string field::str() const { return as().value_or(""); } -bool field::is_integer() const -{ +bool field::is_integer() const { return value_.is_integer(); } -bool field::is_floating_point() const -{ +bool field::is_floating_point() const { return value_.is_floating_point(); } -bool field::is_bool() const -{ +bool field::is_bool() const { return value_.is_bool(); } -bool field::is_string() const -{ +bool field::is_string() const { return value_.is_string(); } -bool field::is_varchar() const -{ +bool field::is_varchar() const { return value_.is_varchar(); } -bool field::is_blob() const -{ +bool field::is_blob() const { return value_.is_blob(); } -bool field::is_null() const -{ +bool field::is_null() const { return value_.is_null(); } diff --git a/source/orm/sql/record.cpp b/source/orm/sql/record.cpp index 7a4fa4b..78c5aaf 100644 --- a/source/orm/sql/record.cpp +++ b/source/orm/sql/record.cpp @@ -5,16 +5,14 @@ namespace matador::sql { -record::record(std::initializer_list columns) -{ +record::record(const std::initializer_list columns) { for (auto &&col :columns) { const auto it = fields_by_name_.emplace(col.name(), field_index_pair {col, fields_.size()}); fields_.push_back(std::ref(it.first->second.first)); } } -record::record(const std::vector &columns) -{ +record::record(const std::vector &columns) { for (auto &&col :columns) { const auto it = fields_by_name_.emplace(col.name(), field_index_pair {col, fields_.size()}); fields_.push_back(std::ref(it.first->second.first)); @@ -23,7 +21,6 @@ record::record(const std::vector &columns) record::record(const record &x) : fields_by_name_(x.fields_by_name_) -//, pk_index_(x.pk_index_) { for (auto& col : x.fields_) { auto &it = fields_by_name_.at(col.get().name()); @@ -47,25 +44,10 @@ record &record::operator=(const record &x) return *this; } -const std::vector &record::columns() const -{ +const std::vector &record::columns() const { return fields_; } -//bool record::has_primary_key() const -//{ -// return pk_index_ > -1; -//} -// -//std::optional record::primary_key() const -//{ -// if (!has_primary_key()) { -// return std::nullopt; -// } -// -// return columns_[pk_index_]; -//} - const field &record::at(const std::string &name) const { const auto &res = fields_by_name_.at(name); const auto &f = res.first; @@ -88,8 +70,7 @@ record::const_iterator record::find(const std::string &column_name) const { return it != fields_by_name_.end() ? fields_.begin() + it->second.second : fields_.end(); } -void record::append(const field &col) -{ +void record::append(const field &col) { const auto it = fields_by_name_.emplace(col.name(), field_index_pair {col, fields_.size()}); fields_.push_back(std::ref(it.first->second.first)); } @@ -140,13 +121,6 @@ void record::clear() fields_by_name_.clear(); } -//bool record::unknown() const -//{ -// return std::all_of(std::begin(columns_), std::end(columns_), [](const auto &col) { -// return col.type() == data_type_t::type_unknown; -// }); -//} - void record::init() { size_t index{0}; diff --git a/test/backends/QueryTest.cpp b/test/backends/QueryTest.cpp index 6324e91..ef61b5e 100644 --- a/test/backends/QueryTest.cpp +++ b/test/backends/QueryTest.cpp @@ -4,6 +4,7 @@ #include "matador/query/criteria.hpp" #include "matador/query/generator.hpp" #include "matador/query/query.hpp" +#include "matador/query/meta_table_macro.hpp" #include "QueryFixture.hpp" @@ -17,6 +18,12 @@ using namespace matador::query; using namespace matador::sql; using namespace matador::test; +META_TABLE(recipes, RECIPE, id, name); +META_TABLE(ingredients, INGREDIENT, id, name); +META_TABLE(recipe_ingredients, RECIPE_INGREDIENT, recipe_id, ingredient_id); +META_TABLE(airplanes, AIRPLANE, id, brand, model); +META_TABLE(flights, FLIGHT, id, airplane_id, pilot_name); + TEST_CASE_METHOD(QueryFixture, "Create table with foreign key relation", "[query][foreign][relation]") { auto result = repo.attach("airplane") .and_then( [this] { return repo.attach("flight");} ); @@ -80,13 +87,13 @@ TEST_CASE_METHOD(QueryFixture, "Execute select statement with where clause", "[q for (const auto &i: *result_record) { REQUIRE(i.size() == 4); - REQUIRE(i.at(0).name() == "id"); + REQUIRE(i.at(0).name() == "person.id"); REQUIRE(i.at(0).is_integer()); REQUIRE(i.at(0).as() == george.id); - REQUIRE(i.at(1).name() == "name"); + REQUIRE(i.at(1).name() == "person.name"); REQUIRE(i.at(1).is_varchar()); REQUIRE(i.at(1).as() == george.name); - REQUIRE(i.at(2).name() == "age"); + REQUIRE(i.at(2).name() == "person.age"); REQUIRE(i.at(2).is_integer()); REQUIRE(i.at(2).as() == george.age); } @@ -221,11 +228,11 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key", "[query][for } TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left", "[query][foreign][join_left]") { - auto result = repo.attach("airplane") - .and_then( [this] { return repo.attach("flight");} ); + auto result = repo.attach("airplanes") + .and_then( [this] { return repo.attach("flights");} ); REQUIRE(result.is_ok()); - auto obj = object_generator::generate(repo.repo(), "airplane"); + auto obj = object_generator::generate(repo.repo(), "airplanes"); auto res = query::create() .table(obj->name()) .columns(obj->attributes()) @@ -234,10 +241,10 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left" REQUIRE(res.is_ok()); REQUIRE(*res == 0); - REQUIRE(db.exists("airplane")); - tables_to_drop.emplace("airplane"); + REQUIRE(db.exists("airplanes")); + tables_to_drop.emplace("airplanes"); - obj = object_generator::generate(repo.repo(), "flight"); + obj = object_generator::generate(repo.repo(), "flights"); res = query::create() .table(obj->name()) .columns(obj->attributes()) @@ -246,8 +253,8 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left" REQUIRE(res.is_ok()); REQUIRE(*res == 0); - REQUIRE(db.exists("flight")); - tables_to_drop.emplace("flight"); + REQUIRE(db.exists("flights")); + tables_to_drop.emplace("flights"); std::vector planes{ object_ptr(new airplane{1, "Airbus", "A380"}), @@ -257,7 +264,7 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left" for (const auto &plane: planes) { res = query::insert() - .into("airplane", generator::columns(repo)) + .into("airplanes", generator::columns(repo)) .values(*plane) .execute(db); REQUIRE(res.is_ok()); @@ -265,7 +272,7 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left" } auto count = query::select({count_all()}) - .from("airplane") + .from("airplanes") .fetch_value(db).value(); REQUIRE(count == 3); @@ -278,27 +285,31 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left" for (const auto &f: flights) { res = query::insert() - .into("flight", {"id", "airplane_id", "pilot_name"}) + .into("flights", {"id", "airplane_id", "pilot_name"}) .values(*f) .execute(db); REQUIRE(res.is_ok()); REQUIRE(*res == 1); } - auto f = query::select(generator::columns(repo)) - .from("flight") + auto flight_result = query::select(generator::columns(repo)) + .from("flights") .fetch_one(db); - REQUIRE(f.is_ok()); - REQUIRE(f->has_value()); - REQUIRE(f.value()->at(0).as() == 4); - REQUIRE(f.value()->at(1).as() == 1); - REQUIRE(f.value()->at(2).as() == "hans"); + REQUIRE(flight_result.is_ok()); + REQUIRE(flight_result->has_value()); + REQUIRE(flight_result.value()->at(0).as() == 4); + REQUIRE(flight_result.value()->at(1).as() == 1); + REQUIRE(flight_result.value()->at(2).as() == "hans"); - auto select_result = query::select({"f.id", "ap.brand", "ap.model", "f.pilot_name"}) - .from("flight"_tab.as("f")) - .join_left("airplane"_tab.as("ap")) - .on("f.airplane_id"_col == "ap.id"_col) - .order_by("f.id").asc() + using namespace matador::query::meta; + const auto f = FLIGHT.as("f"); + const auto ap = AIRPLANE.as("ap"); + + auto select_result = query::select({f.id, ap.brand, ap.model, f.pilot_name}) + .from(f) + .join_left(ap) + .on(f.airplane_id == ap.id) + .order_by(f.id).asc() .fetch_all(db); REQUIRE(select_result.is_ok()); @@ -317,11 +328,11 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left" } TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single entity", "[query][join_left][find]") { - auto result = repo.attach("airplane") - .and_then( [this] { return repo.attach("flight");} ); + auto result = repo.attach("airplanes") + .and_then( [this] { return repo.attach("flights");} ); REQUIRE(result.is_ok()); - auto obj = object_generator::generate(repo.repo(), "airplane"); + auto obj = object_generator::generate(repo.repo(), "airplanes"); auto res = query::create() .table(obj->name()) .columns(obj->attributes()) @@ -330,10 +341,10 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single REQUIRE(res.is_ok()); REQUIRE(*res == 0); - REQUIRE(db.exists("airplane")); - tables_to_drop.emplace("airplane"); + REQUIRE(db.exists("airplanes")); + tables_to_drop.emplace("airplanes"); - obj = object_generator::generate(repo.repo(), "flight"); + obj = object_generator::generate(repo.repo(), "flights"); res = query::create() .table(obj->name()) .columns(obj->attributes()) @@ -342,8 +353,8 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single REQUIRE(res.is_ok()); REQUIRE(*res == 0); - REQUIRE(db.exists("flight")); - tables_to_drop.emplace("flight"); + REQUIRE(db.exists("flights")); + tables_to_drop.emplace("flights"); std::vector planes{ object_ptr(new airplane{1, "Airbus", "A380"}), @@ -353,7 +364,7 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single for (const auto &plane: planes) { res = query::insert() - .into("airplane", generator::columns(repo)) + .into("airplanes", generator::columns(repo)) .values(*plane) .execute(db); REQUIRE(res.is_ok()); @@ -361,7 +372,7 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single } auto count = query::select({count_all()}) - .from("airplane") + .from("airplanes") .fetch_value(db); REQUIRE(count.is_ok()); REQUIRE(count->has_value()); @@ -376,27 +387,31 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single for (const auto &f: flights) { res = query::insert() - .into("flight", generator::columns(repo)) + .into("flights", generator::columns(repo)) .values(*f) .execute(db); REQUIRE(res.is_ok()); REQUIRE(*res == 1); } - auto f = query::select(generator::columns(repo)) - .from("flight") + auto flight_result = query::select(generator::columns(repo)) + .from("flights") .fetch_one(db); - REQUIRE(f.is_ok()); - REQUIRE(f->has_value()); - REQUIRE(f.value()->at(0).as() == 4); - REQUIRE(f.value()->at(1).as() == 1); - REQUIRE(f.value()->at(2).as() == "hans"); + REQUIRE(flight_result.is_ok()); + REQUIRE(flight_result->has_value()); + REQUIRE(flight_result.value()->at(0).as() == 4); + REQUIRE(flight_result.value()->at(1).as() == 1); + REQUIRE(flight_result.value()->at(2).as() == "hans"); - auto select_result = query::select({"f.id", "f.airplane_id", "ap.brand", "ap.model", "f.pilot_name"}) - .from("flight"_tab.as("f")) - .join_left("airplane"_tab.as("ap")) - .on("f.airplane_id"_col == "ap.id"_col) - .where("f.id"_col == 4) + using namespace matador::query::meta; + const auto f = FLIGHT.as("f"); + const auto ap = AIRPLANE.as("ap"); + + auto select_result = query::select({f.id, f.airplane_id, ap.brand, ap.model, f.pilot_name}) + .from(f) + .join_left(ap) + .on(f.airplane_id == ap.id) + .where(f.id == 4) .fetch_one(db); auto expected_flight = flights[0]; @@ -506,10 +521,16 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with many to many relationship" REQUIRE(*res == 1); } - auto select_result = query::select({"r.id", "r.name", "ri.ingredient_id"}) - .from("recipes"_tab.as("r")) - .join_left("recipe_ingredients"_tab.as("ri")) - .on("r.id"_col == "ri.recipe_id"_col) + using namespace matador::query::meta; + + const auto r = RECIPE.as("r"); + const auto ri= RECIPE_INGREDIENT.as("ri"); + const auto i = INGREDIENT.as("i"); + + auto select_result = query::select({ r.id, r.name, ri.ingredient_id}) + .from(r) + .join_left(ri) + .on(r.id == ri.recipe_id) .fetch_all(db); REQUIRE(select_result.is_ok()); @@ -524,20 +545,20 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with many to many relationship" {9, "Fruit Salad", 3} }; size_t index{0}; - for (const auto &r: *select_result) { - REQUIRE(r.size() == 3); - REQUIRE(r.at(0).as().value() == std::get<0>(expected_result_one_join[index])); - REQUIRE(r.at(1).as().value() == std::get<1>(expected_result_one_join[index])); - REQUIRE(r.at(2).as().value() == std::get<2>(expected_result_one_join[index])); + for (const auto &row: *select_result) { + REQUIRE(row.size() == 3); + REQUIRE(row.at(0).as().value() == std::get<0>(expected_result_one_join[index])); + REQUIRE(row.at(1).as().value() == std::get<1>(expected_result_one_join[index])); + REQUIRE(row.at(2).as().value() == std::get<2>(expected_result_one_join[index])); ++index; } - select_result = query::select({"r.id", "r.name", "ri.ingredient_id", "i.name"}) - .from("recipes"_tab.as("r")) - .join_left("recipe_ingredients"_tab.as("ri")) - .on("r.id"_col == "ri.recipe_id"_col) - .join_left("ingredients"_tab.as("i")) - .on("ri.ingredient_id"_col == "i.id"_col) + select_result = query::select({r.id, r.name, ri.ingredient_id, i.name}) + .from(r) + .join_left(ri) + .on(r.id == ri.recipe_id) + .join_left(i) + .on(ri.ingredient_id == i.id) .fetch_all(db); REQUIRE(result.is_ok()); @@ -552,32 +573,32 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with many to many relationship" {9, "Fruit Salad", 3, "Pineapple"} }; index = 0; - for (const auto &r: *select_result) { - REQUIRE(r.size() == 4); - REQUIRE(r.at(0).as().value() == std::get<0>(expected_result_two_joins[index])); - REQUIRE(r.at(1).as().value() == std::get<1>(expected_result_two_joins[index])); - REQUIRE(r.at(2).as().value() == std::get<2>(expected_result_two_joins[index])); - REQUIRE(r.at(3).as().value() == std::get<3>(expected_result_two_joins[index])); + for (const auto &row: *select_result) { + REQUIRE(row.size() == 4); + REQUIRE(row.at(0).as().value() == std::get<0>(expected_result_two_joins[index])); + REQUIRE(row.at(1).as().value() == std::get<1>(expected_result_two_joins[index])); + REQUIRE(row.at(2).as().value() == std::get<2>(expected_result_two_joins[index])); + REQUIRE(row.at(3).as().value() == std::get<3>(expected_result_two_joins[index])); ++index; } - select_result = query::select({"r.id", "r.name", "ri.ingredient_id", "i.name"}) - .from("recipes"_tab.as("r")) - .join_left("recipe_ingredients"_tab.as("ri")) - .on("r.id"_col == "ri.recipe_id"_col) - .join_left("ingredients"_tab.as("i")) - .on("ri.ingredient_id"_col == "i.id"_col) - .where("r.id"_col == 8) + select_result = query::select({r.id, r.name, ri.ingredient_id, i.name}) + .from(r) + .join_left(ri) + .on(r.id == ri.recipe_id) + .join_left(i) + .on(ri.ingredient_id == i.id) + .where(r.id == 8) .fetch_all(db); REQUIRE(result.is_ok()); index = 3; - for (const auto &r: *select_result) { - REQUIRE(r.size() == 4); - REQUIRE(r.at(0).as().value() == std::get<0>(expected_result_two_joins[index])); - REQUIRE(r.at(1).as().value() == std::get<1>(expected_result_two_joins[index])); - REQUIRE(r.at(2).as().value() == std::get<2>(expected_result_two_joins[index])); - REQUIRE(r.at(3).as().value() == std::get<3>(expected_result_two_joins[index])); + for (const auto &row: *select_result) { + REQUIRE(row.size() == 4); + REQUIRE(row.at(0).as().value() == std::get<0>(expected_result_two_joins[index])); + REQUIRE(row.at(1).as().value() == std::get<1>(expected_result_two_joins[index])); + REQUIRE(row.at(2).as().value() == std::get<2>(expected_result_two_joins[index])); + REQUIRE(row.at(3).as().value() == std::get<3>(expected_result_two_joins[index])); ++index; } } \ No newline at end of file diff --git a/test/backends/SessionFixture.cpp b/test/backends/SessionFixture.cpp index 67b4005..8ac004f 100644 --- a/test/backends/SessionFixture.cpp +++ b/test/backends/SessionFixture.cpp @@ -1,15 +1,18 @@ #include "SessionFixture.hpp" +#include "connection.hpp" + #include "catch2/catch_test_macros.hpp" namespace matador::test { SessionFixture::SessionFixture() : pool(connection::dns, 4) -, ses({bus, pool}) {} +, ses({bus, pool}, schema) {} SessionFixture::~SessionFixture() { - const auto result = ses.drop_schema(); + const auto conn = pool.acquire(); + const auto result = schema.drop(*conn); REQUIRE(result.is_ok()); } diff --git a/test/backends/SessionFixture.hpp b/test/backends/SessionFixture.hpp index 4da3120..b1d0261 100644 --- a/test/backends/SessionFixture.hpp +++ b/test/backends/SessionFixture.hpp @@ -5,8 +5,6 @@ #include "matador/utils/message_bus.hpp" -#include "connection.hpp" - #include namespace matador::test { @@ -21,6 +19,8 @@ protected: orm::session ses; utils::message_bus bus; + query::schema schema; + private: void drop_table_if_exists(const std::string &table_name) const; diff --git a/test/backends/SessionTest.cpp b/test/backends/SessionTest.cpp index 83ca9bb..1fc02fc 100644 --- a/test/backends/SessionTest.cpp +++ b/test/backends/SessionTest.cpp @@ -16,8 +16,11 @@ using namespace matador::object; using namespace matador::test; TEST_CASE_METHOD(SessionFixture, "Session insert test", "[session][insert]") { - const auto result = ses.attach("airplanes") - .and_then([this] { return ses.create_schema(); } ); + const auto result = schema.attach("airplanes") + .and_then([this] { + const auto conn = pool.acquire(); + return schema.create(*conn); + } ); REQUIRE(result.is_ok()); auto plane = ses.insert(1, "Boeing", "A380"); @@ -32,8 +35,11 @@ TEST_CASE_METHOD(SessionFixture, "Session insert test", "[session][insert]") { } TEST_CASE_METHOD(SessionFixture, "Session update test", "[session][update]") { - const auto res = ses.attach("airplanes") - .and_then([this] { return ses.create_schema(); } ); + const auto res = schema.attach("airplanes") + .and_then([this] { + const auto conn = pool.acquire(); + return schema.create(*conn); + } ); REQUIRE(res.is_ok()); auto result = ses.insert(1, "Boeing", "747"); @@ -62,8 +68,11 @@ TEST_CASE_METHOD(SessionFixture, "Session update test", "[session][update]") { } TEST_CASE_METHOD(SessionFixture, "Session delete test", "[session][delete]") { - const auto res = ses.attach("airplanes") - .and_then([this] { return ses.create_schema(); } ); + const auto res = schema.attach("airplanes") + .and_then([this] { + const auto conn = pool.acquire(); + return schema.create(*conn); + } ); REQUIRE(res.is_ok()); auto result = ses.insert(1, "Boeing", "747"); @@ -83,9 +92,12 @@ TEST_CASE_METHOD(SessionFixture, "Session delete test", "[session][delete]") { } TEST_CASE_METHOD(SessionFixture, "Session relation test", "[session][relation]") { - const auto result = ses.attach("airplanes") - .and_then([this] { return ses.attach("flights"); } ) - .and_then([this] { return ses.create_schema(); } ); + const auto result = schema.attach("airplanes") + .and_then([this] { return schema.attach("flights"); } ) + .and_then([this] { + const auto conn = pool.acquire(); + return schema.create(*conn); + } ); REQUIRE(result.is_ok()); auto plane = ses.insert(1, "Boeing", "A380"); @@ -105,8 +117,11 @@ TEST_CASE_METHOD(SessionFixture, "Session relation test", "[session][relation]") } TEST_CASE_METHOD(SessionFixture, "Use session to find object with id", "[session][find]") { - const auto result = ses.attach("airplanes") - .and_then([this] { return ses.create_schema(); } ); + const auto result = schema.attach("airplanes") + .and_then([this] { + const auto conn = pool.acquire(); + return schema.create(*conn); + } ); REQUIRE(result.is_ok()); auto a380 = ses.insert(1, "Boeing", "A380"); @@ -124,8 +139,11 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find object with id", "[session } TEST_CASE_METHOD(SessionFixture, "Use session to find all objects", "[session][find]") { - const auto result = ses.attach("airplanes") - .and_then([this] { return ses.create_schema(); } ); + const auto result = schema.attach("airplanes") + .and_then([this] { + const auto conn = pool.acquire(); + return schema.create(*conn); + } ); REQUIRE(result.is_ok()); std::vector> planes; @@ -157,9 +175,12 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find all objects", "[session][f } TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with one-to-many lazy relation", "[session][find][one-to-many][eager]") { - auto result = ses.attach("authors") - .and_then( [this] { return ses.attach("books"); } ) - .and_then( [this] { return ses.create_schema(); } ); + auto result = schema.attach("authors") + .and_then( [this] { return schema.attach("books"); } ) + .and_then([this] { + const auto conn = pool.acquire(); + return schema.create(*conn); + } ); std::vector> authors; authors.emplace_back(new author{1, "Michael", "Crichton", "23.10.1942", 1975, true, {}}); @@ -207,9 +228,12 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with one-to-ma } TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with one-to-many eager relation", "[session][find][one-to-many][eager]") { - auto result = ses.attach("departments") - .and_then( [this] { return ses.attach("employees"); } ) - .and_then( [this] { return ses.create_schema(); } ); + auto result = schema.attach("departments") + .and_then( [this] { return schema.attach("employees"); } ) + .and_then([this] { + const auto conn = pool.acquire(); + return schema.create(*conn); + } ); std::vector> departments; departments.emplace_back(new department{1, "Insurance", {}}); @@ -260,9 +284,12 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with one-to-ma } TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with many-to-many eager relation", "[session][find][many-to-many][eager]") { - auto result = ses.attach("recipes") - .and_then( [this] { return ses.attach("ingredients"); } ) - .and_then( [this] { return ses.create_schema(); } ); + auto result = schema.attach("recipes") + .and_then( [this] { return schema.attach("ingredients"); } ) + .and_then([this] { + const auto conn = pool.acquire(); + return schema.create(*conn); + } ); std::vector> ingredients; ingredients.push_back(std::make_unique(1, "Apple")); diff --git a/test/core/object/ObjectTest.cpp b/test/core/object/ObjectTest.cpp index d07c8e5..1cf6050 100644 --- a/test/core/object/ObjectTest.cpp +++ b/test/core/object/ObjectTest.cpp @@ -16,6 +16,6 @@ TEST_CASE("Generate object from type", "[object][generate]") { const auto obj = object::object_generator::generate(repo, "books"); REQUIRE(obj->name() == "books"); REQUIRE(obj->attributes().size() == 4); - REQUIRE(obj->constraints().size() == 2); + REQUIRE(obj->constraints().size() == 1); REQUIRE(obj->has_primary_key()); } \ No newline at end of file