sql select join progress

This commit is contained in:
Sascha Kuehl 2024-02-02 19:30:16 +01:00
parent e8b0f0e802
commit aa221aa571
8 changed files with 131 additions and 68 deletions

View File

@ -12,7 +12,7 @@ list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest) include(CTest)
include(Catch) 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) configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE)

View File

@ -21,7 +21,9 @@ public:
{ {
db.open(); db.open();
} }
~QueryFixture() {
~QueryFixture()
{
drop_table_if_exists("flight"); drop_table_if_exists("flight");
drop_table_if_exists("airplane"); drop_table_if_exists("airplane");
drop_table_if_exists("person"); drop_table_if_exists("person");
@ -31,14 +33,16 @@ protected:
matador::sql::connection db; matador::sql::connection db;
private: private:
void drop_table_if_exists(const std::string &table_name) { void drop_table_if_exists(const std::string &table_name)
{
if (db.exists(table_name)) { if (db.exists(table_name)) {
db.drop().table(table_name).execute(); db.drop().table(table_name).execute();
} }
} }
}; };
TEST_CASE_METHOD(QueryFixture, " Create table with foreign key relation", "[session]") { TEST_CASE_METHOD(QueryFixture, " Create table with foreign key relation", "[session]")
{
db.create() db.create()
.table<airplane>("airplane") .table<airplane>("airplane")
.execute(); .execute();
@ -58,7 +62,8 @@ TEST_CASE_METHOD(QueryFixture, " Create table with foreign key relation", "[sess
REQUIRE(!db.exists("airplane")); REQUIRE(!db.exists("airplane"));
} }
TEST_CASE_METHOD(QueryFixture, " Execute select statement with where clause", "[session]") { TEST_CASE_METHOD(QueryFixture, " Execute select statement with where clause", "[session]")
{
db.create() db.create()
.table<person>("person") .table<person>("person")
.execute(); .execute();
@ -77,7 +82,7 @@ TEST_CASE_METHOD(QueryFixture, " Execute select statement with where clause", "[
.where("id"_col == 7) .where("id"_col == 7)
.fetch_all(); .fetch_all();
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() == "id"); REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).type() == data_type_t::type_unsigned_long); REQUIRE(i.at(0).type() == data_type_t::type_unsigned_long);
@ -96,7 +101,7 @@ TEST_CASE_METHOD(QueryFixture, " Execute select statement with where clause", "[
.where("id"_col == 7) .where("id"_col == 7)
.fetch_all<person>(); .fetch_all<person>();
for (const auto& i : result_person) { for (const auto &i: result_person) {
REQUIRE(i.id == 7); REQUIRE(i.id == 7);
REQUIRE(i.name == "george"); REQUIRE(i.name == "george");
REQUIRE(i.age == 45); REQUIRE(i.age == 45);
@ -105,7 +110,8 @@ TEST_CASE_METHOD(QueryFixture, " Execute select statement with where clause", "[
db.drop().table("person").execute(); db.drop().table("person").execute();
} }
TEST_CASE_METHOD(QueryFixture, " Execute insert statement", "[session]") { TEST_CASE_METHOD(QueryFixture, " Execute insert statement", "[session]")
{
db.create() db.create()
.table("person", { .table("person", {
make_pk_column<unsigned long>("id"), make_pk_column<unsigned long>("id"),
@ -127,7 +133,7 @@ TEST_CASE_METHOD(QueryFixture, " Execute insert statement", "[session]") {
.where("id"_col == 7) .where("id"_col == 7)
.fetch_all(); .fetch_all();
for (const auto& i : result_record) { for (const auto &i: result_record) {
REQUIRE(i.size() == 3); REQUIRE(i.size() == 3);
REQUIRE(i.at(0).name() == "id"); REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).type() == data_type_t::type_long_long); REQUIRE(i.at(0).type() == data_type_t::type_long_long);
@ -143,7 +149,8 @@ TEST_CASE_METHOD(QueryFixture, " Execute insert statement", "[session]") {
db.drop().table("person").execute(); db.drop().table("person").execute();
} }
TEST_CASE_METHOD(QueryFixture, " Select statement with foreign key", "[session]") { TEST_CASE_METHOD(QueryFixture, " Select statement with foreign key", "[session]")
{
db.create() db.create()
.table<airplane>("airplane") .table<airplane>("airplane")
.execute(); .execute();
@ -152,13 +159,13 @@ TEST_CASE_METHOD(QueryFixture, " Select statement with foreign key", "[session]"
.table<flight>("flight") .table<flight>("flight")
.execute(); .execute();
std::vector<entity<airplane>> planes { std::vector<entity<airplane>> planes{
make_entity<airplane>(1, "Airbus", "A380"), make_entity<airplane>(1, "Airbus", "A380"),
make_entity<airplane>(2, "Boeing", "707"), make_entity<airplane>(2, "Boeing", "707"),
make_entity<airplane>(3, "Boeing", "747") make_entity<airplane>(3, "Boeing", "747")
}; };
for (const auto &plane : planes) { for (const auto &plane: planes) {
auto res = db.insert().into<airplane>("airplane").values(*plane).execute(); auto res = db.insert().into<airplane>("airplane").values(*plane).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
} }
@ -181,7 +188,8 @@ TEST_CASE_METHOD(QueryFixture, " Select statement with foreign key", "[session]"
db.drop().table("airplane").execute(); db.drop().table("airplane").execute();
} }
TEST_CASE_METHOD(QueryFixture, " Select statement with foreign key and join", "[session][join]") { TEST_CASE_METHOD(QueryFixture, " Select statement with foreign key and join", "[session][join]")
{
db.create() db.create()
.table<airplane>("airplane") .table<airplane>("airplane")
.execute(); .execute();
@ -190,13 +198,13 @@ TEST_CASE_METHOD(QueryFixture, " Select statement with foreign key and join", "[
.table<flight>("flight") .table<flight>("flight")
.execute(); .execute();
std::vector<entity<airplane>> planes { std::vector<entity<airplane>> planes{
make_entity<airplane>(1, "Airbus", "A380"), make_entity<airplane>(1, "Airbus", "A380"),
make_entity<airplane>(2, "Boeing", "707"), make_entity<airplane>(2, "Boeing", "707"),
make_entity<airplane>(3, "Boeing", "747") make_entity<airplane>(3, "Boeing", "747")
}; };
for (const auto &plane : planes) { for (const auto &plane: planes) {
auto res = db.insert().into<airplane>("airplane").values(*plane).execute(); auto res = db.insert().into<airplane>("airplane").values(*plane).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
} }
@ -204,14 +212,14 @@ TEST_CASE_METHOD(QueryFixture, " Select statement with foreign key and join", "[
auto count = db.select({count_all()}).from("airplane").fetch_value<int>(); auto count = db.select({count_all()}).from("airplane").fetch_value<int>();
REQUIRE(count == 3); REQUIRE(count == 3);
std::vector<entity<flight>> flights { std::vector<entity<flight>> flights{
make_entity<flight>(4, planes.at(0), "hans"), make_entity<flight>(4, planes.at(0), "hans"),
make_entity<flight>(5, planes.at(0), "otto"), make_entity<flight>(5, planes.at(0), "otto"),
make_entity<flight>(6, planes.at(1), "george"), make_entity<flight>(6, planes.at(1), "george"),
make_entity<flight>(7, planes.at(2), "paul") make_entity<flight>(7, planes.at(2), "paul")
}; };
for (const auto &f : flights) { for (const auto &f: flights) {
auto res = db.insert().into<flight>("flight").values(*f).execute(); auto res = db.insert().into<flight>("flight").values(*f).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
} }
@ -222,7 +230,26 @@ TEST_CASE_METHOD(QueryFixture, " Select statement with foreign key and join", "[
REQUIRE(f.airplane.get() != nullptr); REQUIRE(f.airplane.get() != nullptr);
REQUIRE(f.airplane->id == 1); REQUIRE(f.airplane->id == 1);
db.select({"f.id", "ap.brand", "f.pilot_name"}).from("flight", "f").join("airplane", join_type_t::INNER, "ap").on("f.airplane_id", "ap.id"); auto result = db.select({"f.id", "ap.brand", "ap.model", "f.pilot_name"})
.from("flight", "f")
.join("airplane", "ap")
.on("f.airplane_id", "ap.id")
.order_by("f.id").asc()
.fetch_all();
std::vector<std::pair<unsigned long, std::string>> expected_result {
{4, "hans"},
{5, "otto"},
{6, "george"},
{7, "paul"}
};
size_t index{0};
for (const auto &r: result) {
REQUIRE(r.size() == 4);
REQUIRE(r.at(0).as<unsigned long>() == expected_result[index].first);
REQUIRE(r.at(3).as<std::string>() == expected_result[index++].second);
}
db.drop().table("flight").execute(); db.drop().table("flight").execute();
db.drop().table("airplane").execute(); db.drop().table("airplane").execute();

View File

@ -88,6 +88,7 @@ public:
* @return The prepared string * @return The prepared string
*/ */
[[nodiscard]] std::string prepare_identifier(const column &col) const; [[nodiscard]] std::string prepare_identifier(const column &col) const;
[[nodiscard]] std::string prepare_identifier_string(const std::string &col) const;
/** /**
* Prepare string literal * Prepare string literal

View File

@ -15,10 +15,9 @@ namespace matador::utils {
* *
* @param str The string to split. * @param str The string to split.
* @param delim The delimiter character. * @param delim The delimiter character.
* @param values The result vector. * @return The the vector with split strings.
* @return The size of the vector.
*/ */
size_t split(const std::string &str, char delim, std::vector<std::string> &values); std::vector<std::string> split(const std::string &str, char delim);
/** /**
* Replaces all occurrences of string from in given string * Replaces all occurrences of string from in given string

View File

@ -17,12 +17,11 @@ const std::string &dialect::data_type_at(data_type_t type) const
std::string dialect::prepare_identifier(const column &col) const std::string dialect::prepare_identifier(const column &col) const
{ {
std::string result(col.name()); std::string result;
if (!col.is_function()) { if (!col.is_function()) {
escape_quotes_in_identifier(result); result = prepare_identifier_string(col.name());
quote_identifier(result);
} else { } else {
result = sql_func_map_.at(col.function()) + "(" + result + ")"; result = sql_func_map_.at(col.function()) + "(" + col.name() + ")";
} }
if (!col.alias().empty()) { if (!col.alias().empty()) {
result += " AS " + col.alias(); result += " AS " + col.alias();
@ -30,6 +29,17 @@ std::string dialect::prepare_identifier(const column &col) const
return result; return result;
} }
std::string dialect::prepare_identifier_string(const std::string &col) const
{
auto parts = utils::split(col, '.');
for (auto &part : parts) {
escape_quotes_in_identifier(part);
quote_identifier(part);
}
return utils::join(parts, ".");
}
std::string dialect::prepare_literal(const std::string &str) const std::string dialect::prepare_literal(const std::string &str) const
{ {
std::string result(str); std::string result(str);

View File

@ -76,9 +76,34 @@ query_order_by_intermediate query_where_intermediate::order_by(const std::string
return {connection_, builder_.order_by(name)}; return {connection_, builder_.order_by(name)};
} }
query_join_intermediate query_on_intermediate::join(const std::string &join_table_name, const std::string &as)
{
return {connection_, builder_.join(join_table_name, join_type_t::INNER, as)};
}
query_where_intermediate query_on_intermediate::where(const basic_condition &cond)
{
return {connection_, builder_.where(cond)};
}
query_group_by_intermediate query_on_intermediate::group_by(const std::string &name)
{
return {connection_, builder_.group_by(name)};
}
query_order_by_intermediate query_on_intermediate::order_by(const std::string &name)
{
return {connection_, builder_.order_by(name)};
}
query_on_intermediate query_join_intermediate::on(const std::string &column, const std::string &join_column)
{
return {connection_, builder_.on(column, join_column)};
}
query_join_intermediate query_from_intermediate::join(const std::string &join_table_name, const std::string &as) query_join_intermediate query_from_intermediate::join(const std::string &join_table_name, const std::string &as)
{ {
return query_join_intermediate(); return {connection_, builder_.join(join_table_name, join_type_t::INNER, as)};
} }
query_where_intermediate query_from_intermediate::where(const basic_condition &cond) query_where_intermediate query_from_intermediate::where(const basic_condition &cond)

View File

@ -39,8 +39,8 @@ logger::logger(const std::string &path, std::string source)
filename = (last + 1); filename = (last + 1);
} }
// extract base path and extension // extract base path and extension
std::vector<std::string> result; const auto result = utils::split(filename, '.');
if (utils::split(filename, '.', result) != 2) { if (result.size() != 2) {
throw std::logic_error("split path must consists of two elements"); throw std::logic_error("split path must consists of two elements");
} }
// get current path // get current path

View File

@ -36,14 +36,15 @@ std::string to_string(const blob &data)
return str; return str;
} }
size_t split(const std::string &str, char delim, std::vector<std::string> &values) std::vector<std::string> split(const std::string &str, char delim)
{ {
std::stringstream ss(str); std::stringstream ss(str);
std::string item; std::string item;
std::vector<std::string> result;
while (std::getline(ss, item, delim)) { while (std::getline(ss, item, delim)) {
values.push_back(item); result.push_back(item);
} }
return values.size(); return result;
} }
} }