use table metas where possible

This commit is contained in:
Sascha Kühl 2026-01-09 18:10:23 +01:00
parent ac53526a27
commit b9f5819be5
9 changed files with 173 additions and 404 deletions

View File

@ -33,6 +33,7 @@ set(TEST_SOURCES
../../../test/backends/TypeTraitsTest.cpp ../../../test/backends/TypeTraitsTest.cpp
../../../test/utils/record_printer.hpp ../../../test/utils/record_printer.hpp
../../../test/utils/record_printer.cpp ../../../test/utils/record_printer.cpp
../../../test/models/model_metas.hpp
) )
set(LIBRARY_TEST_TARGET PostgresTests) set(LIBRARY_TEST_TARGET PostgresTests)

View File

@ -22,18 +22,12 @@ using namespace matador::test;
using namespace matador::sql; using namespace matador::sql;
using namespace matador::object; using namespace matador::object;
using namespace matador::query; using namespace matador::query;
using namespace matador::utils;
TEST_CASE_METHOD(QueryFixture, "Insert and select basic datatypes", "[query][datatypes]") { TEST_CASE_METHOD(QueryFixture, "Insert and select basic datatypes", "[query][datatypes]") {
REQUIRE(repo.attach<types>("types")); REQUIRE(repo.attach<types>("types")
auto obj = object_generator::generate<types>(repo.repo(), "types"); .and_then([this] {return repo.create(db); }));
auto res = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
tables_to_drop.emplace("types");
float float_value = 2.44557f; float float_value = 2.44557f;
double double_value = 11111.23433345; double double_value = 11111.23433345;
int8_t cval = 'c'; int8_t cval = 'c';
@ -74,7 +68,7 @@ TEST_CASE_METHOD(QueryFixture, "Insert and select basic datatypes", "[query][dat
blob_val blob_val
}; };
res = query::insert() auto res = query::insert()
.into("types", generator::columns<types>(repo)) .into("types", generator::columns<types>(repo))
.values(t) .values(t)
.execute(db); .execute(db);
@ -108,9 +102,6 @@ TEST_CASE_METHOD(QueryFixture, "Insert and select basic datatypes", "[query][dat
} }
TEST_CASE_METHOD( QueryFixture, "Test quoted identifier", "[query][quotes][identifier]" ) { TEST_CASE_METHOD( QueryFixture, "Test quoted identifier", "[query][quotes][identifier]" ) {
using namespace matador::sql;
using namespace matador::utils;
auto res = query::create() auto res = query::create()
.table("quotes") .table("quotes")
.columns({ .columns({
@ -169,9 +160,6 @@ TEST_CASE_METHOD( QueryFixture, "Test quoted identifier", "[query][quotes][ident
} }
TEST_CASE_METHOD( QueryFixture, "Test quoted column names", "[query][quotes][column]" ) { TEST_CASE_METHOD( QueryFixture, "Test quoted column names", "[query][quotes][column]" ) {
using namespace matador::sql;
using namespace matador::utils;
const auto start_quote = db.dialect().token_at(dialect_token::StartQuote); const auto start_quote = db.dialect().token_at(dialect_token::StartQuote);
const auto end_quote = db.dialect().token_at(dialect_token::EndQuote); const auto end_quote = db.dialect().token_at(dialect_token::EndQuote);
@ -213,9 +201,6 @@ TEST_CASE_METHOD( QueryFixture, "Test quoted column names", "[query][quotes][col
} }
TEST_CASE_METHOD(QueryFixture, "Test quoted literals", "[query][quotes][literals]") { TEST_CASE_METHOD(QueryFixture, "Test quoted literals", "[query][quotes][literals]") {
using namespace matador::sql;
using namespace matador::utils;
auto res = query::create() auto res = query::create()
.table("escapes") .table("escapes")
.columns({ .columns({
@ -285,18 +270,8 @@ TEST_CASE_METHOD(QueryFixture, "Test quoted literals", "[query][quotes][literals
} }
TEST_CASE_METHOD(QueryFixture, "Test describe table", "[query][describe][table]") { TEST_CASE_METHOD(QueryFixture, "Test describe table", "[query][describe][table]") {
using namespace matador::sql; REQUIRE(repo.attach<types>("types")
.and_then([this] { return repo.create(db); }));
REQUIRE(repo.attach<types>("types"));
auto obj = object_generator::generate<types>(repo.repo(), "types");
auto res = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
tables_to_drop.emplace("types");
const auto columns = db.describe("types"); const auto columns = db.describe("types");
REQUIRE(columns.is_ok()); REQUIRE(columns.is_ok());
@ -365,20 +340,12 @@ TEST_CASE_METHOD(QueryFixture, "Test primary key", "[query][primary key]") {
using namespace matador::test::temporary; using namespace matador::test::temporary;
using namespace matador::sql; using namespace matador::sql;
REQUIRE(repo.attach<pk>("pk")); REQUIRE(repo.attach<pk>("pk")
auto obj = object_generator::generate<pk>(repo.repo(), "pk"); .and_then([this] { return repo.create(db); }));
auto res = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
tables_to_drop.emplace("pk");
pk pk1{ 7, "george" }; pk pk1{ 7, "george" };
res = query::insert() auto res = query::insert()
.into("pk", generator::columns<pk>(repo)) .into("pk", generator::columns<pk>(repo))
.values(pk1) .values(pk1)
.execute(db); .execute(db);
@ -395,18 +362,9 @@ TEST_CASE_METHOD(QueryFixture, "Test primary key", "[query][primary key]") {
TEST_CASE_METHOD(QueryFixture, "Test primary key prepared", "[query][primary key][prepared]") { TEST_CASE_METHOD(QueryFixture, "Test primary key prepared", "[query][primary key][prepared]") {
using namespace matador::test::temporary; using namespace matador::test::temporary;
using namespace matador::sql;
REQUIRE(repo.attach<pk>("pk")); REQUIRE(repo.attach<pk>("pk")
auto obj = object_generator::generate<pk>(repo.repo(), "pk"); .and_then([this] { return repo.create(db); }));
auto res = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
tables_to_drop.emplace("pk");
pk pk1{ 7, "george" }; pk pk1{ 7, "george" };
@ -416,7 +374,7 @@ TEST_CASE_METHOD(QueryFixture, "Test primary key prepared", "[query][primary key
.prepare(db); .prepare(db);
REQUIRE(stmt); REQUIRE(stmt);
res = stmt->bind(pk1) auto res = stmt->bind(pk1)
.execute(); .execute();
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
REQUIRE(*res == 1); REQUIRE(*res == 1);

View File

@ -9,11 +9,9 @@
#include "catch2/catch_test_macros.hpp" #include "catch2/catch_test_macros.hpp"
namespace matador::test { namespace matador::test {
QueryFixture::QueryFixture() QueryFixture::QueryFixture()
: db(connection::dns) : db(connection::dns)
, repo(db.dialect().default_schema_name()) , repo(db.dialect().default_schema_name()) {
{
REQUIRE(db.open()); REQUIRE(db.open());
} }
@ -22,17 +20,17 @@ QueryFixture::~QueryFixture() {
drop_table_if_exists(tables_to_drop.top()); drop_table_if_exists(tables_to_drop.top());
tables_to_drop.pop(); tables_to_drop.pop();
} }
REQUIRE(repo.drop(db));
REQUIRE(db.close());
} }
void QueryFixture::check_table_exists(const std::string &table_name) const void QueryFixture::check_table_exists(const std::string &table_name) const {
{
auto result = db.exists(table_name); auto result = db.exists(table_name);
REQUIRE(result.is_ok()); REQUIRE(result.is_ok());
REQUIRE(*result); REQUIRE(*result);
} }
void QueryFixture::check_table_not_exists(const std::string &table_name) const void QueryFixture::check_table_not_exists(const std::string &table_name) const {
{
auto result = db.exists(table_name); auto result = db.exists(table_name);
REQUIRE(result.is_ok()); REQUIRE(result.is_ok());
REQUIRE(!*result); REQUIRE(!*result);
@ -50,5 +48,4 @@ void QueryFixture::drop_table_if_exists(const std::string &table_name) const {
return utils::ok(true); return utils::ok(true);
}); });
} }
} }

View File

@ -207,8 +207,7 @@ TEST_CASE_METHOD(QueryFixture, "Create and drop table statement with foreign key
check_table_not_exists("airplane"); check_table_not_exists("airplane");
} }
TEST_CASE_METHOD(QueryFixture, "Execute insert record statement", "[query][record]") TEST_CASE_METHOD(QueryFixture, "Execute insert record statement", "[query][record]") {
{
auto res = query::create() auto res = query::create()
.table("person") .table("person")
.columns({ .columns({

View File

@ -7,6 +7,7 @@
#include "matador/query/table_column.hpp" #include "matador/query/table_column.hpp"
#include "models/person.hpp" #include "models/person.hpp"
#include "models/model_metas.hpp"
#include "QueryFixture.hpp" #include "QueryFixture.hpp"
@ -16,11 +17,12 @@
using namespace matador::sql; using namespace matador::sql;
using namespace matador::object; using namespace matador::object;
using namespace matador::query; using namespace matador::query;
using namespace matador::query::meta;
using namespace matador::utils;
using namespace matador::test; using namespace matador::test;
TEST_CASE_METHOD(QueryFixture, "Test create statement", "[query][statement][create]") { TEST_CASE_METHOD(QueryFixture, "Test create statement", "[query][statement][create]") {
REQUIRE(repo.attach<matador::test::person>("person")); const auto obj = object_generator::generate<person>(repo.repo(), "persons");
const auto obj = object_generator::generate<person>(repo.repo(), "person");
auto stmt = query::create() auto stmt = query::create()
.table(obj->name()) .table(obj->name())
.columns(obj->attributes()) .columns(obj->attributes())
@ -31,11 +33,11 @@ TEST_CASE_METHOD(QueryFixture, "Test create statement", "[query][statement][crea
auto res = stmt->execute(); auto res = stmt->execute();
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
REQUIRE(*res == 0); REQUIRE(*res == 0);
tables_to_drop.emplace("person"); tables_to_drop.emplace("persons");
check_table_exists("person"); check_table_exists("persons");
const std::vector<std::string> cols = {"id", "name", "age", "image"}; const std::vector<std::string> cols = {"id", "name", "age", "image"};
const auto fields = db.describe("person"); const auto fields = db.describe("persons");
REQUIRE(fields.is_ok()); REQUIRE(fields.is_ok());
for (const auto &fld : *fields) { for (const auto &fld : *fields) {
@ -44,39 +46,27 @@ TEST_CASE_METHOD(QueryFixture, "Test create statement", "[query][statement][crea
} }
TEST_CASE_METHOD(QueryFixture, "Test insert statement", "[query][statement][insert]") { TEST_CASE_METHOD(QueryFixture, "Test insert statement", "[query][statement][insert]") {
using namespace matador::test;
REQUIRE(repo.attach<person>("person")); REQUIRE(repo.attach<person>("persons")
const auto obj = object_generator::generate<person>(repo.repo(), "person"); .and_then([this] { return repo.create(db);}));
auto stmt = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.prepare(db);
REQUIRE(stmt);
auto res = stmt->execute(); check_table_exists(PERSON.table_name());
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
tables_to_drop.emplace("person");
check_table_exists("person");
person george{1, "george", 45, {1,2,3,4}}; person george{1, "george", 45, {1,2,3,4}};
stmt = query::insert() auto stmt = query::insert()
.into<person>("person", repo) .into(PERSON, {PERSON.id, PERSON.name, PERSON.age, PERSON.image})
.values(generator::placeholders<person>()) .values(generator::placeholders<person>())
.prepare(db); .prepare(db);
REQUIRE(stmt); REQUIRE(stmt);
res = stmt->bind(george) auto res = stmt->bind(george)
.execute(); .execute();
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
REQUIRE(*res == 1); REQUIRE(*res == 1);
auto row = query::select<person>(repo) auto row = query::select({PERSON.id, PERSON.name, PERSON.age, PERSON.image})
.from("person") .from(PERSON)
.fetch_one<person>(db); .fetch_one<person>(db);
REQUIRE(row.is_ok()); REQUIRE(row.is_ok());
@ -88,40 +78,26 @@ TEST_CASE_METHOD(QueryFixture, "Test insert statement", "[query][statement][inse
} }
TEST_CASE_METHOD(QueryFixture, "Test update statement", "[query][statement][update]") { TEST_CASE_METHOD(QueryFixture, "Test update statement", "[query][statement][update]") {
using namespace matador::test; REQUIRE(repo.attach<person>("persons")
using namespace matador::utils; .and_then([this] { return repo.create(db);}));
REQUIRE(repo.attach<matador::test::person>("person")); check_table_exists(PERSON.table_name());
const auto obj = object_generator::generate<person>(repo.repo(), "person");
auto stmt = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.prepare(db);
REQUIRE(stmt);
auto res = stmt->execute();
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
tables_to_drop.emplace("person");
check_table_exists("person");
person george{1, "george", 45, {1,2,3,4}}; person george{1, "george", 45, {1,2,3,4}};
stmt = query::insert() auto stmt = query::insert()
.into<person>("person", repo) .into(PERSON, {PERSON.id, PERSON.name, PERSON.age, PERSON.image})
.values(generator::placeholders<person>()) .values(generator::placeholders<person>())
.prepare(db); .prepare(db);
REQUIRE(stmt); REQUIRE(stmt);
res = stmt->bind(george) auto res = stmt->bind(george)
.execute(); .execute();
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
REQUIRE(*res == 1); REQUIRE(*res == 1);
auto row = query::select<person>(repo) auto row = query::select({PERSON.id, PERSON.name, PERSON.age, PERSON.image})
.from("person") .from(PERSON)
.fetch_one<person>(db); .fetch_one<person>(db);
REQUIRE(row.is_ok()); REQUIRE(row.is_ok());
@ -133,9 +109,9 @@ TEST_CASE_METHOD(QueryFixture, "Test update statement", "[query][statement][upda
george.age = 36; george.age = 36;
george.image = {5,6,7,8}; george.image = {5,6,7,8};
stmt = query::update("person") stmt = query::update(PERSON)
.set(generator::column_value_pairs<person>()) .set(generator::column_value_pairs<person>())
.where("id"_col == _) .where(PERSON.id == _)
.prepare(db); .prepare(db);
REQUIRE(stmt); REQUIRE(stmt);
@ -145,8 +121,8 @@ TEST_CASE_METHOD(QueryFixture, "Test update statement", "[query][statement][upda
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
REQUIRE(*res == 1); REQUIRE(*res == 1);
row = query::select<person>(repo) row = query::select({PERSON.id, PERSON.name, PERSON.age, PERSON.image})
.from("person") .from(PERSON)
.fetch_one<person>(db); .fetch_one<person>(db);
REQUIRE(row.is_ok()); REQUIRE(row.is_ok());
@ -158,26 +134,13 @@ TEST_CASE_METHOD(QueryFixture, "Test update statement", "[query][statement][upda
} }
TEST_CASE_METHOD(QueryFixture, "Test delete statement", "[query][statement][delete]") { TEST_CASE_METHOD(QueryFixture, "Test delete statement", "[query][statement][delete]") {
using namespace matador::test; REQUIRE(repo.attach<person>("persons")
.and_then([this] { return repo.create(db);}));
REQUIRE(repo.attach<matador::test::person>("person")); check_table_exists(PERSON.table_name());
const auto obj = object_generator::generate<person>(repo.repo(), "person");
auto stmt = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.prepare(db);
REQUIRE(stmt);
auto res = stmt->execute(); auto stmt = query::insert()
REQUIRE(res.is_ok()); .into(PERSON, {PERSON.id, PERSON.name, PERSON.age, PERSON.image})
REQUIRE(*res == 0);
tables_to_drop.emplace("person");
check_table_exists("person");
stmt = query::insert()
.into<person>("person", repo)
.values(generator::placeholders<person>()) .values(generator::placeholders<person>())
.prepare(db); .prepare(db);
REQUIRE(stmt); REQUIRE(stmt);
@ -190,16 +153,16 @@ TEST_CASE_METHOD(QueryFixture, "Test delete statement", "[query][statement][dele
}; };
for (const auto &p : peoples) { for (const auto &p : peoples) {
res = stmt->bind(p) auto res = stmt->bind(p)
.execute(); .execute();
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
REQUIRE(*res == 1); REQUIRE(*res == 1);
stmt->reset(); stmt->reset();
} }
auto select_stmt = query::select<person>(repo) auto select_stmt = query::select({PERSON.id, PERSON.name, PERSON.age, PERSON.image})
.from("person") .from(PERSON)
.where("name"_col == matador::utils::_) .where(PERSON.name ==_)
.prepare(db); .prepare(db);
REQUIRE(select_stmt); REQUIRE(select_stmt);
@ -216,12 +179,12 @@ TEST_CASE_METHOD(QueryFixture, "Test delete statement", "[query][statement][dele
} }
stmt = query::remove() stmt = query::remove()
.from("person") .from(PERSON)
.where("name"_col == matador::utils::_) .where(PERSON.name == _)
.prepare(db); .prepare(db);
REQUIRE(stmt); REQUIRE(stmt);
res = stmt->bind(0, "jane") auto res = stmt->bind(0, "jane")
.execute(); .execute();
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
REQUIRE(*res == 1); REQUIRE(*res == 1);
@ -248,26 +211,13 @@ TEST_CASE_METHOD(QueryFixture, "Test delete statement", "[query][statement][dele
} }
TEST_CASE_METHOD(QueryFixture, "Test reuse prepared statement", "[query][statement][reuse]") { TEST_CASE_METHOD(QueryFixture, "Test reuse prepared statement", "[query][statement][reuse]") {
using namespace matador::test; REQUIRE(repo.attach<matador::test::person>("persons")
.and_then([this] { return repo.create(db);}));
REQUIRE(repo.attach<matador::test::person>("person")); check_table_exists(PERSON.table_name());
const auto obj = object_generator::generate<person>(repo.repo(), "person");
auto stmt = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.prepare(db);
REQUIRE(stmt);
auto res = stmt->execute(); auto stmt = query::insert()
REQUIRE(res.is_ok()); .into(PERSON, {PERSON.id, PERSON.name, PERSON.age, PERSON.image})
REQUIRE(*res == 0);
tables_to_drop.emplace("person");
check_table_exists("person");
stmt = query::insert()
.into<person>("person", repo)
.values(generator::placeholders<person>()) .values(generator::placeholders<person>())
.prepare(db); .prepare(db);
REQUIRE(stmt); REQUIRE(stmt);
@ -280,15 +230,15 @@ TEST_CASE_METHOD(QueryFixture, "Test reuse prepared statement", "[query][stateme
}; };
for (const auto &p : peoples) { for (const auto &p : peoples) {
res = stmt->bind(p) auto res = stmt->bind(p)
.execute(); .execute();
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
REQUIRE(*res == 1); REQUIRE(*res == 1);
stmt->reset(); stmt->reset();
} }
stmt = query::select<person>(repo) stmt = query::select({PERSON.id, PERSON.name, PERSON.age, PERSON.image})
.from("person") .from(PERSON)
.prepare(db); .prepare(db);
REQUIRE(stmt); REQUIRE(stmt);

View File

@ -13,6 +13,7 @@
#include "models/person.hpp" #include "models/person.hpp"
#include "models/recipe.hpp" #include "models/recipe.hpp"
#include "models/shipment.hpp" #include "models/shipment.hpp"
#include "models/model_metas.hpp"
#include "../test/utils/record_printer.hpp" #include "../test/utils/record_printer.hpp"
@ -20,93 +21,59 @@ using namespace matador::object;
using namespace matador::query; using namespace matador::query;
using namespace matador::sql; using namespace matador::sql;
using namespace matador::test; using namespace matador::test;
using namespace matador::query::meta;
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);
META_TABLE(shipments, SHIPMENT, id, tracking_number)
META_TABLE(packages, PACKAGE, id, weight, shipment_id)
TEST_CASE_METHOD(QueryFixture, "Create table with foreign key relation", "[query][foreign][relation]") { TEST_CASE_METHOD(QueryFixture, "Create table with foreign key relation", "[query][foreign][relation]") {
auto result = repo.attach<airplane>("airplane") auto result = repo.attach<airplane>("airplane")
.and_then( [this] { return repo.attach<flight>("flight");} ); .and_then([this] { return repo.attach<flight>("flight");})
.and_then([this] {return repo.create(db); });
REQUIRE(result.is_ok()); REQUIRE(result.is_ok());
auto obj = object_generator::generate<airplane>(repo.repo(), "airplane");
auto res = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
check_table_exists("airplane"); check_table_exists("airplane");
tables_to_drop.emplace("airplane");
obj = object_generator::generate<flight>(repo.repo(), "flight");
res = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
check_table_exists("flight"); check_table_exists("flight");
tables_to_drop.emplace("flight");
} }
TEST_CASE_METHOD(QueryFixture, "Execute select statement with where clause", "[query][where]") { TEST_CASE_METHOD(QueryFixture, "Execute select statement with where clause", "[query][where]") {
REQUIRE(repo.attach<person>("person")); REQUIRE(repo.attach<person>("persons"));
const auto obj = object_generator::generate<person>(repo.repo(), "person"); auto result = repo.create(db);
auto res = query::create() REQUIRE(result.is_ok());
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
check_table_exists("person"); check_table_exists(PERSON.table_name());
tables_to_drop.emplace("person");
person george{7, "george", 45}; person george{7, "george", 45};
george.image.emplace_back(37); george.image.emplace_back(37);
res = query::insert() auto res = query::insert()
.into("person", generator::columns<person>(repo)) .into(PERSON, {PERSON.id, PERSON.name, PERSON.age, PERSON.image})
.values(george) .values(george)
.execute(db); .execute(db);
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
REQUIRE(*res == 1); REQUIRE(*res == 1);
// fetch person as record // fetch person as record
auto result_record = query::select(generator::columns<person>(repo)) auto result_record = query::select({PERSON.id, PERSON.name, PERSON.age, PERSON.image})
.from("person") .from(PERSON)
.where("id"_col == 7) .where(PERSON.id == 7)
.fetch_all(db); .fetch_all(db);
REQUIRE(result_record.is_ok()); REQUIRE(result_record.is_ok());
for (const auto &i: *result_record) { for (const auto &i: *result_record) {
REQUIRE(i.size() == 4); REQUIRE(i.size() == 4);
REQUIRE(i.at(0).name() == "person.id"); REQUIRE(i.at(0).name() == "persons.id");
REQUIRE(i.at(0).is_integer()); REQUIRE(i.at(0).is_integer());
REQUIRE(i.at(0).as<uint32_t>() == george.id); REQUIRE(i.at(0).as<uint32_t>() == george.id);
REQUIRE(i.at(1).name() == "person.name"); REQUIRE(i.at(1).name() == "persons.name");
REQUIRE(i.at(1).is_varchar()); REQUIRE(i.at(1).is_varchar());
REQUIRE(i.at(1).as<std::string>() == george.name); REQUIRE(i.at(1).as<std::string>() == george.name);
REQUIRE(i.at(2).name() == "person.age"); REQUIRE(i.at(2).name() == "persons.age");
REQUIRE(i.at(2).is_integer()); REQUIRE(i.at(2).is_integer());
REQUIRE(i.at(2).as<uint32_t>() == george.age); REQUIRE(i.at(2).as<uint32_t>() == george.age);
} }
// fetch person as person // fetch person as person
auto result_person = query::select(generator::columns<person>(repo)) auto result_person = query::select({PERSON.id, PERSON.name, PERSON.age, PERSON.image})
.from("person") .from(PERSON)
.where("id"_col == 7) .where(PERSON.id == 7)
.fetch_all<person>(db); .fetch_all<person>(db);
REQUIRE(result_person.is_ok()); REQUIRE(result_person.is_ok());
@ -164,33 +131,13 @@ TEST_CASE_METHOD(QueryFixture, "Execute insert statement", "[query][insert]") {
} }
TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key", "[query][foreign]") { TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key", "[query][foreign]") {
auto result = repo.attach<airplane>("airplane") auto result = repo.attach<airplane>("airplanes")
.and_then( [this] { return repo.attach<flight>("flight");} ); .and_then( [this] { return repo.attach<flight>("flights");} )
.and_then([this] {return repo.create(db); });
REQUIRE(result.is_ok()); REQUIRE(result.is_ok());
auto obj = object_generator::generate<airplane>(repo.repo(), "airplane"); REQUIRE(db.exists(AIRPLANE.table_name()));
auto res = query::create() REQUIRE(db.exists(FLIGHT.table_name()));
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
REQUIRE(db.exists("airplane"));
tables_to_drop.emplace("airplane");
obj = object_generator::generate<flight>(repo.repo(), "flight");
res = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
REQUIRE(db.exists("flight"));
tables_to_drop.emplace("flight");
std::vector planes{ std::vector planes{
object_ptr(new airplane{1, "Airbus", "A380"}), object_ptr(new airplane{1, "Airbus", "A380"}),
@ -199,8 +146,8 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key", "[query][for
}; };
for (const auto &plane: planes) { for (const auto &plane: planes) {
res = query::insert() auto res = query::insert()
.into("airplane", generator::columns<airplane>(repo)) .into(AIRPLANE, {AIRPLANE.id, AIRPLANE.brand, AIRPLANE.model})
.values(*plane) .values(*plane)
.execute(db); .execute(db);
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
@ -208,22 +155,22 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key", "[query][for
} }
auto count = query::select({count_all()}) auto count = query::select({count_all()})
.from("airplane") .from(AIRPLANE)
.fetch_value<int>(db); .fetch_value<int>(db);
REQUIRE(count.is_ok()); REQUIRE(count.is_ok());
REQUIRE(*count == 3); REQUIRE(*count == 3);
flight f4711{4, planes.at(1), "hans"}; flight f4711{4, planes.at(1), "hans"};
res = query::insert() auto res = query::insert()
.into("flight", generator::columns<flight>(repo)) .into(FLIGHT, {FLIGHT.id, FLIGHT.airplane_id, FLIGHT.pilot_name})
.values(f4711) .values(f4711)
.execute(db); .execute(db);
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
REQUIRE(*res == 1); REQUIRE(*res == 1);
auto f = query::select(generator::columns<flight>(repo)) auto f = query::select({FLIGHT.id, FLIGHT.airplane_id, FLIGHT.pilot_name})
.from("flight") .from(FLIGHT)
.fetch_one(db); .fetch_one(db);
REQUIRE(f.is_ok()); REQUIRE(f.is_ok());
@ -234,32 +181,12 @@ 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]") { TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left", "[query][foreign][join_left]") {
auto result = repo.attach<airplane>("airplanes") auto result = repo.attach<airplane>("airplanes")
.and_then( [this] { return repo.attach<flight>("flights");} ); .and_then( [this] { return repo.attach<flight>("flights");} )
.and_then([this] {return repo.create(db); });
REQUIRE(result.is_ok()); REQUIRE(result.is_ok());
auto obj = object_generator::generate<airplane>(repo.repo(), "airplanes"); REQUIRE(db.exists(AIRPLANE.table_name()));
auto res = query::create() REQUIRE(db.exists(FLIGHT.table_name()));
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
REQUIRE(db.exists("airplanes"));
tables_to_drop.emplace("airplanes");
obj = object_generator::generate<flight>(repo.repo(), "flights");
res = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
REQUIRE(db.exists("flights"));
tables_to_drop.emplace("flights");
std::vector planes{ std::vector planes{
object_ptr(new airplane{1, "Airbus", "A380"}), object_ptr(new airplane{1, "Airbus", "A380"}),
@ -268,8 +195,8 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left"
}; };
for (const auto &plane: planes) { for (const auto &plane: planes) {
res = query::insert() auto res = query::insert()
.into("airplanes", generator::columns<airplane>(repo)) .into(AIRPLANE, {AIRPLANE.id, AIRPLANE.brand, AIRPLANE.model})
.values(*plane) .values(*plane)
.execute(db); .execute(db);
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
@ -277,7 +204,7 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left"
} }
auto count = query::select({count_all()}) auto count = query::select({count_all()})
.from("airplanes") .from(AIRPLANE)
.fetch_value<int>(db).value(); .fetch_value<int>(db).value();
REQUIRE(count == 3); REQUIRE(count == 3);
@ -289,16 +216,16 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left"
}; };
for (const auto &f: flights) { for (const auto &f: flights) {
res = query::insert() auto res = query::insert()
.into("flights", {"id", "airplane_id", "pilot_name"}) .into(FLIGHT, {FLIGHT.id, FLIGHT.airplane_id, FLIGHT.pilot_name})
.values(*f) .values(*f)
.execute(db); .execute(db);
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
REQUIRE(*res == 1); REQUIRE(*res == 1);
} }
auto flight_result = query::select(generator::columns<flight>(repo)) auto flight_result = query::select({FLIGHT.id, FLIGHT.airplane_id, FLIGHT.pilot_name})
.from("flights") .from(FLIGHT)
.fetch_one(db); .fetch_one(db);
REQUIRE(flight_result.is_ok()); REQUIRE(flight_result.is_ok());
REQUIRE(flight_result->has_value()); REQUIRE(flight_result->has_value());
@ -306,7 +233,6 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left"
REQUIRE(flight_result.value()->at(1).as<uint32_t>() == 1); REQUIRE(flight_result.value()->at(1).as<uint32_t>() == 1);
REQUIRE(flight_result.value()->at(2).as<std::string>() == "hans"); REQUIRE(flight_result.value()->at(2).as<std::string>() == "hans");
using namespace matador::query::meta;
const auto f = FLIGHT.as("f"); const auto f = FLIGHT.as("f");
const auto ap = AIRPLANE.as("ap"); const auto ap = AIRPLANE.as("ap");
@ -334,32 +260,12 @@ 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]") { TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single entity", "[query][join_left][find]") {
auto result = repo.attach<airplane>("airplanes") auto result = repo.attach<airplane>("airplanes")
.and_then( [this] { return repo.attach<flight>("flights");} ); .and_then([this] { return repo.attach<flight>("flights");})
.and_then([this] {return repo.create(db); });
REQUIRE(result.is_ok()); REQUIRE(result.is_ok());
auto obj = object_generator::generate<airplane>(repo.repo(), "airplanes"); REQUIRE(db.exists(AIRPLANE.table_name()));
auto res = query::create() REQUIRE(db.exists(FLIGHT.table_name()));
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
REQUIRE(db.exists("airplanes"));
tables_to_drop.emplace("airplanes");
obj = object_generator::generate<flight>(repo.repo(), "flights");
res = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
REQUIRE(db.exists("flights"));
tables_to_drop.emplace("flights");
std::vector planes{ std::vector planes{
object_ptr(new airplane{1, "Airbus", "A380"}), object_ptr(new airplane{1, "Airbus", "A380"}),
@ -368,8 +274,8 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single
}; };
for (const auto &plane: planes) { for (const auto &plane: planes) {
res = query::insert() auto res = query::insert()
.into("airplanes", generator::columns<airplane>(repo)) .into(AIRPLANE, {AIRPLANE.id, AIRPLANE.brand, AIRPLANE.model})
.values(*plane) .values(*plane)
.execute(db); .execute(db);
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
@ -377,7 +283,7 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single
} }
auto count = query::select({count_all()}) auto count = query::select({count_all()})
.from("airplanes") .from(AIRPLANE)
.fetch_value<int>(db); .fetch_value<int>(db);
REQUIRE(count.is_ok()); REQUIRE(count.is_ok());
REQUIRE(count->has_value()); REQUIRE(count->has_value());
@ -391,16 +297,16 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single
}; };
for (const auto &f: flights) { for (const auto &f: flights) {
res = query::insert() auto res = query::insert()
.into("flights", generator::columns<flight>(repo)) .into(FLIGHT, {FLIGHT.id, FLIGHT.airplane_id, FLIGHT.pilot_name})
.values(*f) .values(*f)
.execute(db); .execute(db);
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
REQUIRE(*res == 1); REQUIRE(*res == 1);
} }
auto flight_result = query::select(generator::columns<flight>(repo)) auto flight_result = query::select({FLIGHT.id, FLIGHT.airplane_id, FLIGHT.pilot_name})
.from("flights") .from(FLIGHT)
.fetch_one(db); .fetch_one(db);
REQUIRE(flight_result.is_ok()); REQUIRE(flight_result.is_ok());
REQUIRE(flight_result->has_value()); REQUIRE(flight_result->has_value());
@ -408,7 +314,6 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single
REQUIRE(flight_result.value()->at(1).as<uint32_t>() == 1); REQUIRE(flight_result.value()->at(1).as<uint32_t>() == 1);
REQUIRE(flight_result.value()->at(2).as<std::string>() == "hans"); REQUIRE(flight_result.value()->at(2).as<std::string>() == "hans");
using namespace matador::query::meta;
const auto f = FLIGHT.as("f"); const auto f = FLIGHT.as("f");
const auto ap = AIRPLANE.as("ap"); const auto ap = AIRPLANE.as("ap");
@ -434,46 +339,12 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single
TEST_CASE_METHOD(QueryFixture, "Select statement with many to many relationship", "[query][join][many_to_many]") { TEST_CASE_METHOD(QueryFixture, "Select statement with many to many relationship", "[query][join][many_to_many]") {
auto result = repo.attach<ingredient>("ingredients") auto result = repo.attach<ingredient>("ingredients")
.and_then( [this] { return repo.attach<recipe>("recipes"); } ); .and_then( [this] { return repo.attach<recipe>("recipes"); } )
.and_then([this] {return repo.create(db); });
auto obj = object_generator::generate<recipe>(repo.repo(), "recipes"); REQUIRE(db.exists(RECIPE.table_name()));
auto res = query::create() REQUIRE(db.exists(INGREDIENT.table_name()));
.table(obj->name()) REQUIRE(db.exists(RECIPE_INGREDIENT.table_name()));
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
REQUIRE(db.exists("recipes"));
tables_to_drop.emplace("recipes");
obj = object_generator::generate<ingredient>(repo.repo(), "ingredients");
res = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
REQUIRE(db.exists("ingredients"));
tables_to_drop.emplace("ingredients");
const auto it = repo.find("recipe_ingredients");
REQUIRE(it != repo.end());
obj = it->second.node().info().object();
// obj = object_generator::generate<recipe_ingredient>(repo.repo(), "recipe_ingredients");
res = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 0);
REQUIRE(db.exists("recipe_ingredients"));
tables_to_drop.emplace("recipe_ingredients");
std::vector<ingredient> ingredients { std::vector<ingredient> ingredients {
{1, "Apple"}, {1, "Apple"},
@ -486,8 +357,8 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with many to many relationship"
}; };
for (const auto &i: ingredients) { for (const auto &i: ingredients) {
res = query::insert() auto res = query::insert()
.into("ingredients", generator::columns<ingredient>(repo)) .into(INGREDIENT, {INGREDIENT.id, INGREDIENT.name})
.values(i) .values(i)
.execute(db); .execute(db);
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
@ -501,8 +372,8 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with many to many relationship"
}; };
for (const auto &r: recipes) { for (const auto &r: recipes) {
res = query::insert() auto res = query::insert()
.into("recipes", generator::columns<recipe>(repo)) .into(RECIPE, {RECIPE.id, RECIPE.name})
.values(r) .values(r)
.execute(db); .execute(db);
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
@ -520,11 +391,9 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with many to many relationship"
{ 9, 3 } { 9, 3 }
}; };
using namespace matador::query::meta;
for (const auto & [recipe_id, ingredient_id]: recipe_ingredients) { for (const auto & [recipe_id, ingredient_id]: recipe_ingredients) {
res = query::insert() auto res = query::insert()
.into("recipe_ingredients", RECIPE_INGREDIENT) .into(RECIPE_INGREDIENT, {RECIPE_INGREDIENT.recipe_id, RECIPE_INGREDIENT.ingredient_id})
.values({recipe_id, ingredient_id}) .values({recipe_id, ingredient_id})
.execute(db); .execute(db);
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
@ -618,12 +487,8 @@ void print(std::ostream& out, const record& row) {
TEST_CASE_METHOD(QueryFixture, "Test load entity with eager has many relation", "[query][has_many][eager]") { TEST_CASE_METHOD(QueryFixture, "Test load entity with eager has many relation", "[query][has_many][eager]") {
auto result = repo.attach<package>("packages") auto result = repo.attach<package>("packages")
.and_then( [this] { return repo.attach<shipment>("shipments"); } ); .and_then([this] { return repo.attach<shipment>("shipments"); })
REQUIRE(result.is_ok()); .and_then([this] {return repo.create(db); });
tables_to_drop.emplace("shipments");
tables_to_drop.emplace("packages");
result = repo.create(db);
REQUIRE(result.is_ok()); REQUIRE(result.is_ok());
const std::vector shipments { const std::vector shipments {
@ -631,8 +496,6 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with eager has many relation",
object_ptr{new shipment{2, "0815"}} object_ptr{new shipment{2, "0815"}}
}; };
using namespace matador::query::meta;
for (const auto &sh: shipments) { for (const auto &sh: shipments) {
auto res = query::insert() auto res = query::insert()
.into(SHIPMENT, SHIPMENT) .into(SHIPMENT, SHIPMENT)

View File

@ -1,6 +1,5 @@
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include "matador/object/attribute.hpp"
#include "matador/object/object_generator.hpp" #include "matador/object/object_generator.hpp"
#include "matador/sql/connection.hpp" #include "matador/sql/connection.hpp"
@ -14,12 +13,13 @@
#include "QueryFixture.hpp" #include "QueryFixture.hpp"
#include "models/airplane.hpp" #include "models/airplane.hpp"
#include "models/location.hpp" #include "models/model_metas.hpp"
using namespace matador::sql; using namespace matador::sql;
using namespace matador::object; using namespace matador::object;
using namespace matador::query; using namespace matador::query;
using namespace matador::test; using namespace matador::test;
using namespace matador::query::meta;
namespace matador::test::detail { namespace matador::test::detail {
template<class Type, typename... Args> template<class Type, typename... Args>
@ -31,14 +31,7 @@ template<class Type, typename... Args>
class StatementTestFixture : public QueryFixture { class StatementTestFixture : public QueryFixture {
public: public:
StatementTestFixture() { StatementTestFixture() {
const auto obj = object_generator::generate<airplane>(repo.repo(), "airplane"); REQUIRE(repo.attach<airplane>("airplanes"));
const auto res = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
REQUIRE(res.is_ok());
tables_to_drop.emplace("airplane");
} }
protected: protected:
@ -51,11 +44,10 @@ protected:
TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement]") { TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement]") {
using namespace matador::utils; using namespace matador::utils;
REQUIRE(repo.attach<airplane>("airplane")); REQUIRE(repo.create(db));
table ap{"airplane"};
SECTION("Insert with prepared statement and placeholder") { SECTION("Insert with prepared statement and placeholder") {
auto stmt = query::insert() auto stmt = query::insert()
.into("airplane", generator::columns<airplane>(repo)) .into(AIRPLANE, {AIRPLANE.id, AIRPLANE.brand, AIRPLANE.model})
.values(generator::placeholders<airplane>()) .values(generator::placeholders<airplane>())
.prepare(db); .prepare(db);
REQUIRE(stmt.is_ok()); REQUIRE(stmt.is_ok());
@ -67,8 +59,8 @@ TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement]
stmt->reset(); stmt->reset();
} }
auto result = query::select(generator::columns<airplane>(repo)) auto result = query::select({AIRPLANE.id, AIRPLANE.brand, AIRPLANE.model})
.from(ap) .from(AIRPLANE)
.fetch_all<airplane>(db); .fetch_all<airplane>(db);
REQUIRE(result.is_ok()); REQUIRE(result.is_ok());
@ -83,7 +75,7 @@ TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement]
SECTION("Select with prepared statement") { SECTION("Select with prepared statement") {
for (const auto &plane: planes) { for (const auto &plane: planes) {
auto res = query::insert() auto res = query::insert()
.into("airplane", generator::columns<airplane>(repo)) .into(AIRPLANE, {AIRPLANE.id, AIRPLANE.brand, AIRPLANE.model})
.values(*plane) .values(*plane)
.execute(db); .execute(db);
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
@ -91,8 +83,8 @@ TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement]
} }
auto stmt = query::select(generator::columns<airplane>(repo)) auto stmt = query::select(generator::columns<airplane>(repo))
.from(ap) .from(AIRPLANE)
.where("brand"_col == _) .where(AIRPLANE.brand == _)
.prepare(db); .prepare(db);
REQUIRE(stmt.is_ok()); REQUIRE(stmt.is_ok());

View File

@ -22,18 +22,12 @@ class TypeTraitsTestFixture : public QueryFixture
public: public:
TypeTraitsTestFixture() { TypeTraitsTestFixture() {
REQUIRE(db.open()); REQUIRE(db.open());
const auto obj = matador::object::object_generator::generate<location>(repo.repo(), "location"); REQUIRE(repo.attach<location>("location"));
const auto res = query::create()
.table(obj->name())
.columns(obj->attributes())
.constraints(obj->constraints())
.execute(db);
tables_to_drop.emplace("location");
} }
}; };
TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with type traits", "[typetraits]") { TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with type traits", "[typetraits]") {
REQUIRE(repo.attach<location>("location")); REQUIRE(repo.create(db));
SECTION("Insert and select with direct execution") { SECTION("Insert and select with direct execution") {
location loc{1, "center", {1, 2, 3}, Color::Black}; location loc{1, "center", {1, 2, 3}, Color::Black};

View File

@ -0,0 +1,15 @@
#ifndef MATADOR_MODEL_METAS_HPP
#define MATADOR_MODEL_METAS_HPP
#include "matador/query/meta_table_macro.hpp"
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);
META_TABLE(shipments, SHIPMENT, id, tracking_number)
META_TABLE(packages, PACKAGE, id, weight, shipment_id)
META_TABLE(persons, PERSON, id, name, age, image)
#endif //MATADOR_MODEL_METAS_HPP