query select changes

This commit is contained in:
Sascha Kühl 2024-03-26 16:05:04 +01:00
parent b15b8da31f
commit 4a04a678f9
9 changed files with 193 additions and 32 deletions

View File

@ -12,14 +12,13 @@ list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest)
include(Catch)
set(POSTGRES_CONNECTION_STRING "postgres://test:test123@127.0.0.1:5432/matador_test")
set(POSTGRES_CONNECTION_STRING "postgres://test:test123@127.0.0.1:15432/test")
configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE)
message(STATUS "postgresql connection string: ${POSTGRES_CONNECTION_STRING}")
set(TEST_SOURCES
../../tests/QueryTest.cpp
../../tests/QueryTest.cpp
../../tests/ConnectionTest.cpp
../../tests/QueryRecordTest.cpp

View File

@ -252,7 +252,8 @@ TEST_CASE_METHOD(QueryRecordFixture, "Execute select statement", "[session][reco
auto rec = db.query(schema).select({"id", "name", "age"})
.from("person")
.fetch_one();
REQUIRE(rec.at(1).str() == "george");
REQUIRE(rec.has_value());
REQUIRE(rec->at(1).str() == "george");
auto name = db.query(schema).select({"name"})
.from("person")
@ -386,15 +387,16 @@ TEST_CASE_METHOD(QueryRecordFixture, "Test quoted identifier", "[session][record
auto res = db.query(schema).select({"from", "to"}).from("quotes").fetch_one();
REQUIRE("Berlin" == res.at("from").str());
REQUIRE("London" == res.at("to").str());
REQUIRE(res.has_value());
REQUIRE("Berlin" == res->at("from").str());
REQUIRE("London" == res->at("to").str());
db.query(schema).update("quotes").set({{"from", "Hamburg"}, {"to", "New York"}}).where("from"_col == "Berlin").execute();
res = db.query(schema).select({"from", "to"}).from("quotes").fetch_one();
REQUIRE("Hamburg" == res.at("from").str());
REQUIRE("New York" == res.at("to").str());
REQUIRE("Hamburg" == res->at("from").str());
REQUIRE("New York" == res->at("to").str());
db.query(schema).drop().table("quotes").execute();
}

View File

@ -188,11 +188,13 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key", "[session]")
auto res = db.query(schema).insert().into<flight>("flight").values(f4711).execute();
REQUIRE(res == 1);
auto f = *db.query(schema).select<flight>().from("flight").fetch_all<flight>().begin();
REQUIRE(f.id == 4);
REQUIRE(f.pilot_name == "hans");
REQUIRE(f.airplane.get() != nullptr);
REQUIRE(f.airplane->id == 2);
auto f = *db.query(schema)
.select<flight>()
.from("flight")
.fetch_all().begin();
REQUIRE(f.at(0).as<unsigned long>() == 4);
REQUIRE(f.at(1).as<unsigned long>() == 2);
REQUIRE(f.at(2).as<std::string>() == "hans");
db.query(schema).drop().table("flight").execute();
db.query(schema).drop().table("airplane").execute();
@ -236,11 +238,13 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left"
REQUIRE(res == 1);
}
auto f = *db.query(schema).select<flight>().from("flight").fetch_all<flight>().begin();
REQUIRE(f.id == 4);
REQUIRE(f.pilot_name == "hans");
REQUIRE(f.airplane.get() != nullptr);
REQUIRE(f.airplane->id == 1);
auto f = *db.query(schema)
.select<flight>()
.from("flight")
.fetch_all().begin();
REQUIRE(f.at(0).as<unsigned long>() == 4);
REQUIRE(f.at(1).as<unsigned long>() == 1);
REQUIRE(f.at(2).as<std::string>() == "hans");
auto result = db.query(schema).select({"f.id", "ap.brand", "ap.model", "f.pilot_name"})
.from({"flight", "f"})
@ -264,5 +268,81 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left"
db.query(schema).drop().table("flight").execute();
db.query(schema).drop().table("airplane").execute();
}
TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single entity", "[session][join_left][find]") {
schema.attach<airplane>("airplane");
schema.attach<flight>("flight");
db.query(schema).create()
.table<airplane>("airplane")
.execute();
db.query(schema).create()
.table<flight>("flight")
.execute();
std::vector<entity<airplane>> planes{
make_entity<airplane>(1, "Airbus", "A380"),
make_entity<airplane>(2, "Boeing", "707"),
make_entity<airplane>(3, "Boeing", "747")
};
for (const auto &plane: planes) {
auto res = db
.query(schema)
.insert()
.into<airplane>("airplane")
.values(*plane)
.execute();
REQUIRE(res == 1);
}
auto count = db
.query(schema)
.select({count_all()})
.from("airplane")
.fetch_value<int>();
REQUIRE(count == 3);
std::vector<entity<flight>> flights{
make_entity<flight>(4, planes.at(0), "hans"),
make_entity<flight>(5, planes.at(0), "otto"),
make_entity<flight>(6, planes.at(1), "george"),
make_entity<flight>(7, planes.at(2), "paul")
};
for (const auto &f: flights) {
auto res = db.query(schema).insert().into<flight>("flight").values(*f).execute();
REQUIRE(res == 1);
}
auto f = *db.query(schema)
.select<flight>()
.from("flight")
.fetch_all().begin();
REQUIRE(f.at(0).as<unsigned long>() == 4);
REQUIRE(f.at(1).as<unsigned long>() == 1);
REQUIRE(f.at(2).as<std::string>() == "hans");
auto result = db
.query(schema)
.select({"f.id", "f.airplane_id", "ap.brand", "ap.model", "f.pilot_name"})
.from({"flight", "f"})
.join_left({"airplane", "ap"})
.on("f.airplane_id"_col == "ap.id"_col)
.where("f.id"_col == 4)
.fetch_one<flight>();
auto expected_flight = flights[0];
REQUIRE(result.has_value());
REQUIRE(result->id == expected_flight->id);
REQUIRE(result->pilot_name == expected_flight->pilot_name);
REQUIRE(result->airplane.get());
REQUIRE(result->airplane->id == 1);
REQUIRE(result->airplane->model == "A380");
REQUIRE(result->airplane->brand == "Airbus");
db.query(schema).drop().table("flight").execute();
db.query(schema).drop().table("airplane").execute();
}

View File

@ -46,10 +46,11 @@ TEST_CASE_METHOD(SessionFixture, "Session relation test", "[session][relation]")
}
TEST_CASE_METHOD(SessionFixture, "Find object with id", "[session][find]") {
ses.attach<matador::test::airplane>("airplane");
using namespace matador::test;
ses.attach<airplane>("airplane");
ses.create_schema();
auto a380 = ses.insert<test::airplane>(1, "Boeing", "A380");
auto a380 = ses.insert<airplane>(1, "Boeing", "A380");
ses.find<test::airplane>(1);
ses.find<airplane>(1);
}

27
demo/author.json Normal file
View File

@ -0,0 +1,27 @@
{
"name": "book",
"fields": {
"id": {
"type": "unsigned long",
"constraint": "primary_key"
},
"first_name": {
"type": "string",
"size": 63
},
"last_name": {
"type": "string",
"size": 63
},
"date_of_birth": {
"type": "string",
"size": 31
},
"year_of_birth": {
"type": "unsigned short"
},
"distinguished": {
"type": "boolean"
}
}
}

23
demo/book.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "book",
"fields": {
"id": {
"type": "unsigned long",
"constraint": "primary_key"
},
"title": {
"type": "string",
"size": 511
},
"book_author": {
"type": "author",
"constraint": {
"type": "foreign_key",
"references": "id"
}
},
"published_in": {
"type": "unsigned_short"
}
}
}

View File

@ -61,13 +61,29 @@ public:
{
return query_result<Type>(fetch());
}
query_result<record> fetch_all();
record fetch_one();
template<typename Type>
Type fetch_value()
template < class Type >
std::optional<Type> fetch_one()
{
return fetch_one().at(0).as<Type>().value();
auto result = query_result<Type>(fetch());
auto first = result.begin();
if (first == result.end()) {
return std::nullopt;
}
return *first.get();
}
std::optional<record> fetch_one();
template<typename Type>
std::optional<Type> fetch_value()
{
const auto result = fetch_one();
if (result.has_value()) {
return result.value().at(0).as<Type>().value();
}
return std::nullopt;
}
statement prepare();

View File

@ -73,23 +73,30 @@ public:
void on_attribute(const char *id, char *value, const utils::field_attributes &attr = utils::null_attributes);
void on_attribute(const char *id, std::string &value, const utils::field_attributes &attr = utils::null_attributes);
void on_attribute(const char *id, any_type &value, data_type_t type, const utils::field_attributes &attr = utils::null_attributes);
// void on_attribute(const char *id, any_type &value, data_type_t type, const utils::field_attributes &attr = utils::null_attributes);
template < class Pointer >
void on_belongs_to(const char * /*id*/, Pointer &x, const utils::foreign_attributes &/*attr*/)
void on_belongs_to(const char * /*id*/, Pointer &x, const utils::foreign_attributes &attr)
{
if (!x.get()) {
x.reset(new typename Pointer::value_type);
}
if (attr.fetch() == utils::fetch_type::LAZY) {
pk_reader_.read(*x, column_index_++);
} else {
utils::access::process(*this, *x);
}
}
template < class Pointer >
void on_has_one(const char * /*id*/, Pointer &x, const utils::foreign_attributes &/*attr*/)
void on_has_one(const char * /*id*/, Pointer &x, const utils::foreign_attributes &attr)
{
if (!x.get()) {
x.reset(new typename Pointer::value_type);
}
if (attr.fetch() == utils::fetch_type::LAZY) {
pk_reader_.read(*x, column_index_++);
} else {
utils::access::process(*this, *x);
}
}
template<class ContainerType>

View File

@ -14,10 +14,16 @@ query_result<record> query_select_finish::fetch_all()
return connection_.fetch(compiler.compile(&data_));
}
record query_select_finish::fetch_one()
std::optional<record> query_select_finish::fetch_one()
{
query_compiler compiler(connection_.dialect());
return *connection_.fetch(compiler.compile(&data_)).begin().get();
auto result = connection_.fetch(compiler.compile(&data_));
auto first = result.begin();
if (first == result.end()) {
return std::nullopt;
}
return *first.get();
}
query_context query_select_finish::build() const