Compare commits

..

4 Commits

7 changed files with 104 additions and 14 deletions

View File

@ -456,43 +456,77 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with many to many relationship"
} }
auto result = db auto result = db
.query(schema) .query(schema)
.select({"r.id", "r.name", "ri.recipe_id"}) .select({"r.id", "r.name", "ri.ingredient_id"})
.from({"recipes", "r"}) .from({"recipes", "r"})
.join_left({"recipe_ingredients", "ri"}) .join_left({"recipe_ingredients", "ri"})
.on("r.id"_col == "ri.recipe_id"_col) .on("r.id"_col == "ri.recipe_id"_col)
.fetch_all(); .fetch_all();
std::vector<std::tuple<unsigned long, std::string, unsigned long>> expected_result_one_join {
{7, "Apple Crumble", 1},
{7, "Apple Crumble", 4},
{7, "Apple Crumble", 5},
{8, "Beans Chili", 6},
{8, "Beans Chili", 7},
{9, "Fruit Salad", 1},
{9, "Fruit Salad", 2},
{9, "Fruit Salad", 3}
};
size_t index{0};
for (const auto &r: result) { for (const auto &r: result) {
REQUIRE(r.size() == 3); REQUIRE(r.size() == 3);
std::cout << "record r.id " << r.at(0).as<unsigned long>().value() << " r.name " << r.at(1).as<std::string>().value() << " ri.id " << r.at(2).as<unsigned long>().value() << "\n"; REQUIRE(r.at(0).as<unsigned long>().value() == std::get<0>(expected_result_one_join[index]));
REQUIRE(r.at(1).as<std::string>().value() == std::get<1>(expected_result_one_join[index]));
REQUIRE(r.at(2).as<unsigned long>().value() == std::get<2>(expected_result_one_join[index]));
++index;
} }
result = db result = db
.query(schema) .query(schema)
.select({"r.id", "r.name", "ri.recipe_id", "i.name"}) .select({"r.id", "r.name", "ri.ingredient_id", "i.name"})
.from({"recipes", "r"}) .from({"recipes", "r"})
.join_left({"recipe_ingredients", "ri"}).on("r.id"_col == "ri.recipe_id"_col) .join_left({"recipe_ingredients", "ri"}).on("r.id"_col == "ri.recipe_id"_col)
.join_left({"ingredients", "i"}).on("ri.ingredient_id"_col == "i.id"_col) .join_left({"ingredients", "i"}).on("ri.ingredient_id"_col == "i.id"_col)
.fetch_all(); .fetch_all();
std::vector<std::tuple<unsigned long, std::string, unsigned long, std::string>> expected_result_two_joins {
{7, "Apple Crumble", 1, "Apple"},
{7, "Apple Crumble", 4, "Sugar"},
{7, "Apple Crumble", 5, "Flour"},
{8, "Beans Chili", 6, "Butter"},
{8, "Beans Chili", 7, "Beans"},
{9, "Fruit Salad", 1, "Apple"},
{9, "Fruit Salad", 2, "Strawberry"},
{9, "Fruit Salad", 3, "Pineapple"}
};
index = 0;
for (const auto &r: result) { for (const auto &r: result) {
REQUIRE(r.size() == 4); REQUIRE(r.size() == 4);
std::cout << "record r.id " << r.at(0).as<unsigned long>().value() << " r.name " << r.at(1).as<std::string>().value() << " ri.id " << r.at(2).as<unsigned long>().value() << " i.name " << r.at(3).as<std::string>().value() << "\n"; REQUIRE(r.at(0).as<unsigned long>().value() == std::get<0>(expected_result_two_joins[index]));
REQUIRE(r.at(1).as<std::string>().value() == std::get<1>(expected_result_two_joins[index]));
REQUIRE(r.at(2).as<unsigned long>().value() == std::get<2>(expected_result_two_joins[index]));
REQUIRE(r.at(3).as<std::string>().value() == std::get<3>(expected_result_two_joins[index]));
++index;
} }
result = db result = db
.query(schema) .query(schema)
.select({"r.id", "r.name", "ri.recipe_id", "i.name"}) .select({"r.id", "r.name", "ri.ingredient_id", "i.name"})
.from({"recipes", "r"}) .from({"recipes", "r"})
.join_left({"recipe_ingredients", "ri"}).on("r.id"_col == "ri.recipe_id"_col) .join_left({"recipe_ingredients", "ri"}).on("r.id"_col == "ri.recipe_id"_col)
.join_left({"ingredients", "i"}).on("ri.ingredient_id"_col == "i.id"_col) .join_left({"ingredients", "i"}).on("ri.ingredient_id"_col == "i.id"_col)
.where("r.id"_col == 8) .where("r.id"_col == 8)
.fetch_all(); .fetch_all();
index = 3;
for (const auto &r: result) { for (const auto &r: result) {
REQUIRE(r.size() == 4); REQUIRE(r.size() == 4);
std::cout << "record r.id " << r.at(0).as<unsigned long>().value() << " r.name " << r.at(1).as<std::string>().value() << " ri.id " << r.at(2).as<unsigned long>().value() << " i.name " << r.at(3).as<std::string>().value() << "\n"; REQUIRE(r.at(0).as<unsigned long>().value() == std::get<0>(expected_result_two_joins[index]));
REQUIRE(r.at(1).as<std::string>().value() == std::get<1>(expected_result_two_joins[index]));
REQUIRE(r.at(2).as<unsigned long>().value() == std::get<2>(expected_result_two_joins[index]));
REQUIRE(r.at(3).as<std::string>().value() == std::get<3>(expected_result_two_joins[index]));
++index;
} }
db.query(schema).drop().table("recipe_ingredients").execute(); db.query(schema).drop().table("recipe_ingredients").execute();

View File

@ -23,9 +23,9 @@ struct column
column(sql_function_t func, std::string name); // NOLINT(*-explicit-constructor) column(sql_function_t func, std::string name); // NOLINT(*-explicit-constructor)
column(std::string table_name, std::string name, std::string as); column(std::string table_name, std::string name, std::string as);
column(std::string table_name, const char* name, std::string as); column(std::string table_name, const char* name, std::string as);
column(table &t, const char* name, std::string as); column(struct table &t, const char* name, std::string as);
bool equals(const column &x) const; [[nodiscard]] bool equals(const column &x) const;
column& as(std::string a); column& as(std::string a);

View File

@ -53,6 +53,15 @@ public:
[[nodiscard]] const std::string& ref_table() const; [[nodiscard]] const std::string& ref_table() const;
[[nodiscard]] const std::string& ref_column() const; [[nodiscard]] const std::string& ref_column() const;
[[nodiscard]] bool is_integer() const;
[[nodiscard]] bool is_floating_point() const;
[[nodiscard]] bool is_bool() const;
[[nodiscard]] bool is_string() const;
[[nodiscard]] bool is_varchar() const;
[[nodiscard]] bool is_blob() const;
[[nodiscard]] bool is_null() const;
[[nodiscard]] bool is_unknown() const;
void type(data_type_t type); void type(data_type_t type);
template< typename Type > template< typename Type >

View File

@ -63,6 +63,46 @@ const std::string &column_definition::ref_column() const
return ref_column_; return ref_column_;
} }
bool column_definition::is_integer() const
{
return type_ >= data_type_t::type_char && type_ <= data_type_t::type_unsigned_long_long;
}
bool column_definition::is_floating_point() const
{
return type_ == data_type_t::type_float || type_ == data_type_t::type_double;
}
bool column_definition::is_bool() const
{
return type_ == data_type_t::type_bool;
}
bool column_definition::is_string() const
{
return type_ == data_type_t::type_text;
}
bool column_definition::is_varchar() const
{
return type_ == data_type_t::type_varchar;
}
bool column_definition::is_blob() const
{
return type_ == data_type_t::type_blob;
}
bool column_definition::is_null() const
{
return type_ == data_type_t::type_null;
}
bool column_definition::is_unknown() const
{
return type_ == data_type_t::type_unknown;
}
void column_definition::type(data_type_t type) void column_definition::type(data_type_t type)
{ {
type_ = type; type_ = type;

View File

@ -15,6 +15,7 @@ struct author
std::string date_of_birth; std::string date_of_birth;
unsigned short year_of_birth{}; unsigned short year_of_birth{};
bool distinguished{false}; bool distinguished{false};
std::vector<matador::sql::entity<book>> books;
template<typename Operator> template<typename Operator>
void process(Operator &op) void process(Operator &op)
@ -26,6 +27,7 @@ struct author
field::attribute(op, "date_of_birth", date_of_birth, 31); field::attribute(op, "date_of_birth", date_of_birth, 31);
field::attribute(op, "year_of_birth", year_of_birth); field::attribute(op, "year_of_birth", year_of_birth);
field::attribute(op, "distinguished", distinguished); field::attribute(op, "distinguished", distinguished);
field::has_many(op, "books", books, "author_id", "id", utils::fetch_type::LAZY);
} }
}; };

View File

@ -25,7 +25,7 @@ struct book
namespace field = matador::utils::access; namespace field = matador::utils::access;
field::primary_key(op, "id", id); field::primary_key(op, "id", id);
field::attribute(op, "title", title, 511); field::attribute(op, "title", title, 511);
field::has_one(op, "author_id", book_author, utils::fetch_type::EAGER); field::belongs_to(op, "author_id", book_author, utils::fetch_type::EAGER);
field::attribute(op, "published_in", published_in); field::attribute(op, "published_in", published_in);
} }
}; };

View File

@ -11,16 +11,19 @@
namespace matador::test { namespace matador::test {
struct recipe;
struct ingredient struct ingredient
{ {
unsigned long id{}; unsigned long id{};
std::string name; std::string name;
std::vector<matador::sql::entity<recipe>> recipes;
template<class Operator> template<class Operator>
void process(Operator &op) { void process(Operator &op) {
namespace field = matador::utils::access; namespace field = matador::utils::access;
field::primary_key(op, "id", id); field::primary_key(op, "id", id);
field::attribute(op, "name", name, 255); field::attribute(op, "name", name, 255);
field::has_many(op, "recipes", recipes, "ingredient_id", "recipe_id", utils::fetch_type::EAGER);
} }
}; };
@ -28,12 +31,14 @@ struct recipe
{ {
unsigned long id{}; unsigned long id{};
std::string name; std::string name;
std::vector<matador::sql::entity<ingredient>> ingredients;
template<class Operator> template<class Operator>
void process(Operator &op) { void process(Operator &op) {
namespace field = matador::utils::access; namespace field = matador::utils::access;
field::primary_key(op, "id", id); field::primary_key(op, "id", id);
field::attribute(op, "name", name, 255); field::attribute(op, "name", name, 255);
field::has_many(op, "ingredients", ingredients, "recipe_id", "ingredient_id", utils::fetch_type::EAGER);
} }
}; };