From e72733d37ef7f066f2e6f33202afa70b89512fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Tue, 24 Jun 2025 16:16:28 +0200 Subject: [PATCH] progress on session_insert_builder --- include/matador/object/schema.hpp | 4 +- .../matador/orm/query_builder_exception.hpp | 29 ++ .../matador/orm/session_insert_builder.hpp | 25 +- include/matador/orm/session_query_builder.hpp | 20 +- include/matador/utils/cascade_type.hpp | 2 + include/matador/utils/constraints.hpp | 17 +- test/orm/CMakeLists.txt | 1 + test/orm/orm/SessionInsertBuilderTest.cpp | 272 +----------------- 8 files changed, 68 insertions(+), 302 deletions(-) create mode 100644 include/matador/orm/query_builder_exception.hpp diff --git a/include/matador/object/schema.hpp b/include/matador/object/schema.hpp index 3269e40..f324b22 100644 --- a/include/matador/object/schema.hpp +++ b/include/matador/object/schema.hpp @@ -136,12 +136,12 @@ public: * Returns true if the schema contains * no schema nodes. * - * @return True if schema is empty + * @return True if the schema is empty */ [[nodiscard]] bool empty() const; /** - * Returns the current number of schema node. + * Returns the current number of the schema node. * * @return Number of schema nodes */ diff --git a/include/matador/orm/query_builder_exception.hpp b/include/matador/orm/query_builder_exception.hpp new file mode 100644 index 0000000..50f527c --- /dev/null +++ b/include/matador/orm/query_builder_exception.hpp @@ -0,0 +1,29 @@ +#ifndef QUERY_BUILDER_EXCEPTION_HPP +#define QUERY_BUILDER_EXCEPTION_HPP + +#include +#include + +namespace matador::orm { + +enum class query_build_error : std::uint8_t { + Ok = 0, + UnknownType, + MissingPrimaryKey, + UnexpectedError +}; + + +class query_builder_exception final : public std::exception { +public: + explicit query_builder_exception(const query_build_error error) : error_(error) {} + + [[nodiscard]] query_build_error error() const { return error_; } + +private: + const query_build_error error_; +}; + +} + +#endif //QUERY_BUILDER_EXCEPTION_HPP diff --git a/include/matador/orm/session_insert_builder.hpp b/include/matador/orm/session_insert_builder.hpp index c3a9675..03497c8 100644 --- a/include/matador/orm/session_insert_builder.hpp +++ b/include/matador/orm/session_insert_builder.hpp @@ -1,11 +1,15 @@ #ifndef SESSION_INSERT_BUILDER_HPP #define SESSION_INSERT_BUILDER_HPP +#include "matador/orm/query_builder_exception.hpp" + #include "matador/query/condition.hpp" #include "matador/query/query_intermediates.hpp" #include "matador/object/schema.hpp" +#include "matador/utils/cascade_type.hpp" + namespace matador::orm { struct entity_insert_data { sql::table table; @@ -58,8 +62,7 @@ public: } template - void on_primary_key(const char *id, V &x, - std::enable_if_t && !std::is_same_v> * = nullptr) { + void on_primary_key(const char *id, V &x, std::enable_if_t && !std::is_same_v> * = nullptr) { push(id, x); } @@ -73,10 +76,27 @@ public: template void on_belongs_to(const char *id, Pointer &obj, const utils::foreign_attributes &attr) { + if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::INSERT)) { + return; + } + + const auto info = schema_.info(); + if (!info) { + throw query_builder_exception{query_build_error::UnknownType}; + } } template void on_has_one(const char *id, Pointer &obj, const utils::foreign_attributes &attr) { + if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::INSERT)) { + return; + } + + const auto info = schema_.info(); + if (!info) { + throw query_builder_exception{query_build_error::UnknownType}; + } + } template @@ -94,7 +114,6 @@ public: } private: - [[nodiscard]] bool is_root_entity() const; void push(const std::string &column_name, const utils::database_type &value); private: diff --git a/include/matador/orm/session_query_builder.hpp b/include/matador/orm/session_query_builder.hpp index ef3db8e..430d79a 100644 --- a/include/matador/orm/session_query_builder.hpp +++ b/include/matador/orm/session_query_builder.hpp @@ -1,6 +1,8 @@ #ifndef QUERY_ENTITY_QUERY_BUILDER_HPP #define QUERY_ENTITY_QUERY_BUILDER_HPP +#include "matador/orm/query_builder_exception.hpp" + #include "matador/query/condition.hpp" #include "matador/query/query_intermediates.hpp" @@ -66,24 +68,6 @@ struct entity_query_data { std::unique_ptr where_clause{}; }; -enum class query_build_error : std::uint8_t { - Ok = 0, - UnknownType, - MissingPrimaryKey, - UnexpectedError -}; - -class query_builder_exception final : public std::exception -{ -public: - explicit query_builder_exception(const query_build_error error) : error_(error) {} - - [[nodiscard]] query_build_error error() const { return error_; } - -private: - const query_build_error error_; -}; - class session_query_builder final { public: diff --git a/include/matador/utils/cascade_type.hpp b/include/matador/utils/cascade_type.hpp index 47bd64a..d28f398 100644 --- a/include/matador/utils/cascade_type.hpp +++ b/include/matador/utils/cascade_type.hpp @@ -23,5 +23,7 @@ inline cascade_type& operator|= (cascade_type& a, cascade_type b) { return reint inline cascade_type& operator&= (cascade_type& a, cascade_type b) { return reinterpret_cast(reinterpret_cast(a) &= static_cast(b)); } inline cascade_type& operator^= (cascade_type& a, cascade_type b) { return reinterpret_cast(reinterpret_cast(a) ^= static_cast(b)); } +inline bool is_cascade_type_set(const cascade_type source, const cascade_type needle) { return static_cast(source & needle) > 0; } + } #endif //OOS_CASCADE_TYPE_HPP diff --git a/include/matador/utils/constraints.hpp b/include/matador/utils/constraints.hpp index a0c8a60..e824d6d 100644 --- a/include/matador/utils/constraints.hpp +++ b/include/matador/utils/constraints.hpp @@ -14,23 +14,12 @@ enum class constraints : unsigned char { // UNIQUE_NOT_NULL = UNIQUE | NOT_NULL }; -inline constraints operator|(constraints a, constraints b) -{ - return static_cast(static_cast(a) | static_cast(b)); -} - -inline constraints operator&(constraints a, constraints b) -{ - return static_cast(static_cast(a) & static_cast(b)); -} - +inline constraints operator|(constraints a, constraints b) { return static_cast(static_cast(a) | static_cast(b)); } +inline constraints operator&(constraints a, constraints b) { return static_cast(static_cast(a) & static_cast(b)); } inline constraints& operator|= (constraints& a, constraints b) { return (constraints&)((int&)a |= (int)b); } inline constraints& operator&= (constraints& a, constraints b) { return (constraints&)((int&)a &= (int)b); } -inline bool is_constraint_set(const constraints source, const constraints needle) -{ - return static_cast(source & needle) > 0; -} +inline bool is_constraint_set(const constraints source, const constraints needle) { return static_cast(source & needle) > 0; } } diff --git a/test/orm/CMakeLists.txt b/test/orm/CMakeLists.txt index 9c32c38..9d152a4 100644 --- a/test/orm/CMakeLists.txt +++ b/test/orm/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable(OrmTests backend/test_result_reader.hpp backend/test_statement.cpp backend/test_statement.hpp + orm/SessionInsertBuilderTest.cpp orm/SessionQueryBuilderTest.cpp query/ConditionTests.cpp query/QueryBuilderTest.cpp diff --git a/test/orm/orm/SessionInsertBuilderTest.cpp b/test/orm/orm/SessionInsertBuilderTest.cpp index 024f56b..b605cc6 100644 --- a/test/orm/orm/SessionInsertBuilderTest.cpp +++ b/test/orm/orm/SessionInsertBuilderTest.cpp @@ -8,7 +8,7 @@ #include "matador/query/query.hpp" -#include "matador/orm/session_query_builder.hpp" +#include "matador/orm/session_insert_builder.hpp" #include "../backend/test_connection.hpp" #include "../backend/test_backend_service.hpp" @@ -28,7 +28,7 @@ using namespace matador::query; using namespace matador::sql; using namespace matador::test; -TEST_CASE("Create sql query data for entity with eager has one", "[query][entity][builder]") { +TEST_CASE("Create sql insert for entity with eager has one", "[query][entity][insert][builder]") { using namespace matador::test; backend_provider::instance().register_backend("noop", std::make_unique()); connection db("noop://noop.db"); @@ -37,270 +37,12 @@ TEST_CASE("Create sql query data for entity with eager has one", "[query][entity .and_then( [&scm] { return scm.attach("flights"); } ); REQUIRE(result); - session_query_builder eqb(scm); + session_insert_builder eib(scm); - auto data = eqb.build(17U); + auto b737 = object_ptr(new airplane{0, "Boeing", "737"}); + flight f1{0, b737, "F828"}; + + auto data = eib.build(f1); REQUIRE(data.is_ok()); - REQUIRE(data->root_table->name == "flights"); - REQUIRE(data->joins.size() == 1); - const std::vector expected_columns { - { "flights", "id", "c01" }, - { "airplanes", "id", "c02" }, - { "airplanes", "brand", "c03" }, - { "airplanes", "model", "c04" }, - { "flights", "pilot_name", "c05" }, - }; - REQUIRE(data->columns.size() == expected_columns.size()); - for (size_t i = 0; i != expected_columns.size(); ++i) { - REQUIRE(expected_columns[i].equals(data->columns[i])); - } - - std::vector> expected_join_data { - { "airplanes", R"("t01"."airplane_id" = "t02"."id")"} - }; - - query_context qc; - size_t index{0}; - for (const auto &jd : data->joins) { - REQUIRE(jd.join_table->name == expected_join_data[index].first); - REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second); - ++index; - } - - REQUIRE(data->where_clause); - auto cond = data->where_clause->evaluate(db.dialect(), qc); - REQUIRE(cond == R"("t01"."id" = 17)"); } - -TEST_CASE("Create sql query data for entity with eager belongs to", "[query][entity][builder]") { - using namespace matador::test; - backend_provider::instance().register_backend("noop", std::make_unique()); - connection db("noop://noop.db"); - schema scm("noop"); - auto result = scm.attach("authors") - .and_then( [&scm] { return scm.attach("books"); } ); - REQUIRE(result); - - session_query_builder eqb(scm); - - auto data = eqb.build(17); - - REQUIRE(data.is_ok()); - REQUIRE(data->root_table->name == "books"); - REQUIRE(data->joins.size() == 1); - const std::vector expected_columns { - { "books", "id", "c01" }, - { "books", "title", "c02" }, - { "authors", "id", "c03" }, - { "authors", "first_name", "c04" }, - { "authors", "last_name", "c05" }, - { "authors", "date_of_birth", "c06" }, - { "authors", "year_of_birth", "c07" }, - { "authors", "distinguished", "c08" }, - { "books", "published_in", "c09" } - }; - REQUIRE(data->columns.size() == expected_columns.size()); - for (size_t i = 0; i != expected_columns.size(); ++i) { - REQUIRE(expected_columns[i].equals(data->columns[i])); - } - - std::vector> expected_join_data { - { "authors", R"("t01"."author_id" = "t02"."id")"} - }; - - query_context qc; - size_t index{0}; - for (const auto &jd : data->joins) { - REQUIRE(jd.join_table->name == expected_join_data[index].first); - REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second); - ++index; - } - - REQUIRE(data->where_clause); - auto cond = data->where_clause->evaluate(db.dialect(), qc); - REQUIRE(cond == R"("t01"."id" = 17)"); - - auto q = matador::query::query::select(data->columns) - .from(data->root_table->name); - - for (auto &jd : data->joins) { - q.join_left(*jd.join_table) - .on(std::move(jd.condition)); - } - auto context = q - .where(std::move(data->where_clause)) - .str(db); -} - -TEST_CASE("Create sql query data for entity with eager has many belongs to", "[query][entity][builder]") { - using namespace matador::test; - backend_provider::instance().register_backend("noop", std::make_unique()); - connection db("noop://noop.db"); - schema scm("noop"); - auto result = scm.attach("products") - .and_then( [&scm] { return scm.attach("order_details"); } ) - .and_then( [&scm] { return scm.attach("suppliers"); } ) - .and_then( [&scm] { return scm.attach("categories"); } ) - .and_then( [&scm] { return scm.attach("orders"); } ); - REQUIRE(result); - - session_query_builder eqb(scm); - - auto data = eqb.build(17); - - REQUIRE(data.is_ok()); - REQUIRE(data->root_table->name == "orders"); - REQUIRE(data->joins.size() == 1); - const std::vector expected_columns = { - { "orders", "order_id", "c01" }, - { "orders", "order_date", "c02" }, - { "orders", "required_date", "c03" }, - { "orders", "shipped_date", "c04" }, - { "orders", "ship_via", "c05" }, - { "orders", "freight", "c06" }, - { "orders", "ship_name", "c07" }, - { "orders", "ship_address", "c08" }, - { "orders", "ship_city", "c09" }, - { "orders", "ship_region", "c10" }, - { "orders", "ship_postal_code", "c11" }, - { "orders", "ship_country", "c12" }, - { "order_details", "order_details_id", "c13" }, - { "order_details", "order_id", "c14" }, - { "order_details", "product_id", "c15" } - }; - REQUIRE(data->columns.size() == expected_columns.size()); - for (size_t i = 0; i != expected_columns.size(); ++i) { - REQUIRE(expected_columns[i].equals(data->columns[i])); - } - - std::vector> expected_join_data { - { "order_details", R"("t01"."order_id" = "t02"."order_id")"} - }; - - query_context qc; - size_t index{0}; - for (const auto &jd : data->joins) { - REQUIRE(jd.join_table->name == expected_join_data[index].first); - REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second); - ++index; - } - - REQUIRE(data->where_clause); - auto cond = data->where_clause->evaluate(db.dialect(), qc); - REQUIRE(cond == R"("t01"."order_id" = 17)"); -} - -TEST_CASE("Create sql query data for entity with eager many to many", "[query][entity][builder]") { - using namespace matador::test; - backend_provider::instance().register_backend("noop", std::make_unique()); - connection db("noop://noop.db"); - schema scm("noop"); - auto result = scm.attach("recipes") - .and_then( [&scm] { return scm.attach("ingredients"); } ) - .and_then( [&scm] { return scm.attach("recipe_ingredients"); } ); - - session_query_builder eqb(scm); - - auto data = eqb.build(17); - - REQUIRE(data.is_ok()); - REQUIRE(data->root_table->name == "ingredients"); - REQUIRE(data->joins.size() == 2); - const std::vector expected_columns { - { "ingredients", "id", "c01" }, - { "ingredients", "name", "c02" }, - { "recipes", "id", "c03" }, - { "recipes", "name", "c04" } - }; - REQUIRE(data->columns.size() == expected_columns.size()); - for (size_t i = 0; i != expected_columns.size(); ++i) { - REQUIRE(expected_columns[i].equals(data->columns[i])); - } - - std::vector> expected_join_data { - { "recipe_ingredients", R"("t01"."id" = "t02"."ingredient_id")"}, - { "recipes", R"("t02"."recipe_id" = "t03"."id")"} - }; - - query_context qc; - size_t index{0}; - for (const auto &jd : data->joins) { - REQUIRE(jd.join_table->name == expected_join_data[index].first); - REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second); - ++index; - } - - REQUIRE(data->where_clause); - auto cond = data->where_clause->evaluate(db.dialect(), qc); - REQUIRE(cond == R"("t01"."id" = 17)"); -} - -TEST_CASE("Create sql query data for entity with eager many to many (inverse part)", "[query][entity][builder]") { - using namespace matador::test; - backend_provider::instance().register_backend("noop", std::make_unique()); - connection db("noop://noop.db"); - schema scm("noop"); - auto result = scm.attach("students") - .and_then( [&scm] { return scm.attach("courses"); } ) - .and_then( [&scm] { return scm.attach("student_courses"); } ); - - session_query_builder eqb(scm); - - auto data = eqb.build(17); - - REQUIRE(data.is_ok()); - REQUIRE(data->root_table->name == "courses"); - REQUIRE(data->joins.size() == 2); - const std::vector expected_columns { - { "courses", "id", "c01" }, - { "courses", "title", "c02" }, - { "students", "id", "c03" }, - { "students", "name", "c04" } - }; - REQUIRE(data->columns.size() == expected_columns.size()); - for (size_t i = 0; i != expected_columns.size(); ++i) { - REQUIRE(expected_columns[i].equals(data->columns[i])); - } - - std::vector> expected_join_data { - { "student_courses", R"("t01"."id" = "t02"."course_id")"}, - { "students", R"("t02"."student_id" = "t03"."id")"} - }; - - query_context qc; - size_t index{0}; - for (const auto &jd : data->joins) { - REQUIRE(jd.join_table->name == expected_join_data[index].first); - REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second); - ++index; - } - - REQUIRE(data->where_clause); - auto cond = data->where_clause->evaluate(db.dialect(), qc); - REQUIRE(cond == R"("t01"."id" = 17)"); -} - -TEST_CASE("Test eager relationship", "[session][eager]") { - using namespace matador::test; - backend_provider::instance().register_backend("noop", std::make_unique()); - connection db("noop://noop.db"); - schema scm("noop"); - auto result = scm.attach("departments") - .and_then( [&scm] { return scm.attach("employees"); } ); - - session_query_builder eqb(scm); - - auto data = eqb.build(); - REQUIRE(data.is_ok()); - - auto ctx = query::select(data->columns) - .from(*data->root_table) - .join_left(data->joins) - .where(std::move(data->where_clause)) - .order_by(column{data->root_table, data->pk_column_name}) - .asc() - .str(db); - - std::cout << ctx << std::endl; -} \ No newline at end of file