diff --git a/backends/postgres/test/CMakeLists.txt b/backends/postgres/test/CMakeLists.txt index a2f4a6a..a07d07f 100644 --- a/backends/postgres/test/CMakeLists.txt +++ b/backends/postgres/test/CMakeLists.txt @@ -12,7 +12,7 @@ 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) diff --git a/backends/tests/QueryTest.cpp b/backends/tests/QueryTest.cpp index c9bc514..7830510 100644 --- a/backends/tests/QueryTest.cpp +++ b/backends/tests/QueryTest.cpp @@ -21,7 +21,9 @@ public: { db.open(); } - ~QueryFixture() { + + ~QueryFixture() + { drop_table_if_exists("flight"); drop_table_if_exists("airplane"); drop_table_if_exists("person"); @@ -31,14 +33,16 @@ protected: matador::sql::connection db; 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)) { 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() .table("airplane") .execute(); @@ -58,26 +62,27 @@ TEST_CASE_METHOD(QueryFixture, " Create table with foreign key relation", "[sess 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() - .table("person") - .execute(); + .table("person") + .execute(); person george{7, "george", 45}; george.image.push_back(37); auto res = db.insert() - .into("person", george) - .execute(); - REQUIRE(res == 1); + .into("person", george) + .execute(); + REQUIRE(res == 1); // fetch person as record auto result_record = db.select() - .from("person") - .where("id"_col == 7) - .fetch_all(); + .from("person") + .where("id"_col == 7) + .fetch_all(); - for (const auto& i : result_record) { + for (const auto &i: result_record) { REQUIRE(i.size() == 4); REQUIRE(i.at(0).name() == "id"); REQUIRE(i.at(0).type() == data_type_t::type_unsigned_long); @@ -92,11 +97,11 @@ TEST_CASE_METHOD(QueryFixture, " Execute select statement with where clause", "[ // fetch person as person auto result_person = db.select() - .from("person") - .where("id"_col == 7) - .fetch_all(); + .from("person") + .where("id"_col == 7) + .fetch_all(); - for (const auto& i : result_person) { + for (const auto &i: result_person) { REQUIRE(i.id == 7); REQUIRE(i.name == "george"); REQUIRE(i.age == 45); @@ -105,29 +110,30 @@ TEST_CASE_METHOD(QueryFixture, " Execute select statement with where clause", "[ db.drop().table("person").execute(); } -TEST_CASE_METHOD(QueryFixture, " Execute insert statement", "[session]") { +TEST_CASE_METHOD(QueryFixture, " Execute insert statement", "[session]") +{ db.create() - .table("person", { - make_pk_column("id"), - make_column("name", 255), - make_column("color", 63) - }) - .execute(); + .table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("color", 63) + }) + .execute(); auto res = db.insert() - .into("person", {"id", "name", "color"}) - .values({7, "george", "green"}) - .execute(); + .into("person", {"id", "name", "color"}) + .values({7, "george", "green"}) + .execute(); REQUIRE(res == 1); // fetch person as record auto result_record = db.select({"id", "name", "color"}) - .from("person") - .where("id"_col == 7) - .fetch_all(); + .from("person") + .where("id"_col == 7) + .fetch_all(); - for (const auto& i : result_record) { + for (const auto &i: result_record) { REQUIRE(i.size() == 3); REQUIRE(i.at(0).name() == "id"); 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(); } -TEST_CASE_METHOD(QueryFixture, " Select statement with foreign key", "[session]") { +TEST_CASE_METHOD(QueryFixture, " Select statement with foreign key", "[session]") +{ db.create() .table("airplane") .execute(); @@ -152,13 +159,13 @@ TEST_CASE_METHOD(QueryFixture, " Select statement with foreign key", "[session]" .table("flight") .execute(); - std::vector> planes { - make_entity(1, "Airbus", "A380"), - make_entity(2, "Boeing", "707"), - make_entity(3, "Boeing", "747") + std::vector> planes{ + make_entity(1, "Airbus", "A380"), + make_entity(2, "Boeing", "707"), + make_entity(3, "Boeing", "747") }; - for (const auto &plane : planes) { + for (const auto &plane: planes) { auto res = db.insert().into("airplane").values(*plane).execute(); REQUIRE(res == 1); } @@ -181,22 +188,23 @@ TEST_CASE_METHOD(QueryFixture, " Select statement with foreign key", "[session]" 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() - .table("airplane") - .execute(); + .table("airplane") + .execute(); db.create() - .table("flight") - .execute(); + .table("flight") + .execute(); - std::vector> planes { - make_entity(1, "Airbus", "A380"), - make_entity(2, "Boeing", "707"), - make_entity(3, "Boeing", "747") + std::vector> planes{ + make_entity(1, "Airbus", "A380"), + make_entity(2, "Boeing", "707"), + make_entity(3, "Boeing", "747") }; - for (const auto &plane : planes) { + for (const auto &plane: planes) { auto res = db.insert().into("airplane").values(*plane).execute(); 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(); REQUIRE(count == 3); - std::vector> flights { - make_entity(4, planes.at(0), "hans"), - make_entity(5, planes.at(0), "otto"), - make_entity(6, planes.at(1), "george"), - make_entity(7, planes.at(2), "paul") + std::vector> flights{ + make_entity(4, planes.at(0), "hans"), + make_entity(5, planes.at(0), "otto"), + make_entity(6, planes.at(1), "george"), + make_entity(7, planes.at(2), "paul") }; - for (const auto &f : flights) { + for (const auto &f: flights) { auto res = db.insert().into("flight").values(*f).execute(); 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->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> 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() == expected_result[index].first); + REQUIRE(r.at(3).as() == expected_result[index++].second); + } + db.drop().table("flight").execute(); db.drop().table("airplane").execute(); diff --git a/include/matador/sql/dialect.hpp b/include/matador/sql/dialect.hpp index fb4cb61..57a528c 100644 --- a/include/matador/sql/dialect.hpp +++ b/include/matador/sql/dialect.hpp @@ -88,6 +88,7 @@ public: * @return The prepared string */ [[nodiscard]] std::string prepare_identifier(const column &col) const; + [[nodiscard]] std::string prepare_identifier_string(const std::string &col) const; /** * Prepare string literal diff --git a/include/matador/utils/string.hpp b/include/matador/utils/string.hpp index 4d82448..a27fab7 100644 --- a/include/matador/utils/string.hpp +++ b/include/matador/utils/string.hpp @@ -15,10 +15,9 @@ namespace matador::utils { * * @param str The string to split. * @param delim The delimiter character. - * @param values The result vector. - * @return The size of the vector. + * @return The the vector with split strings. */ -size_t split(const std::string &str, char delim, std::vector &values); +std::vector split(const std::string &str, char delim); /** * Replaces all occurrences of string from in given string diff --git a/src/sql/dialect.cpp b/src/sql/dialect.cpp index cecab65..dbd272b 100644 --- a/src/sql/dialect.cpp +++ b/src/sql/dialect.cpp @@ -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 result(col.name()); + std::string result; if (!col.is_function()) { - escape_quotes_in_identifier(result); - quote_identifier(result); + result = prepare_identifier_string(col.name()); } else { - result = sql_func_map_.at(col.function()) + "(" + result + ")"; + result = sql_func_map_.at(col.function()) + "(" + col.name() + ")"; } if (!col.alias().empty()) { result += " AS " + col.alias(); @@ -30,6 +29,17 @@ std::string dialect::prepare_identifier(const column &col) const 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 result(str); diff --git a/src/sql/query_intermediates.cpp b/src/sql/query_intermediates.cpp index a8ea2e0..8bedd6a 100644 --- a/src/sql/query_intermediates.cpp +++ b/src/sql/query_intermediates.cpp @@ -76,9 +76,34 @@ query_order_by_intermediate query_where_intermediate::order_by(const std::string 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) { - 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) diff --git a/src/utils/logger.cpp b/src/utils/logger.cpp index 8d36fa7..f360840 100644 --- a/src/utils/logger.cpp +++ b/src/utils/logger.cpp @@ -39,8 +39,8 @@ logger::logger(const std::string &path, std::string source) filename = (last + 1); } // extract base path and extension - std::vector result; - if (utils::split(filename, '.', result) != 2) { + const auto result = utils::split(filename, '.'); + if (result.size() != 2) { throw std::logic_error("split path must consists of two elements"); } // get current path diff --git a/src/utils/string.cpp b/src/utils/string.cpp index b3a3fd0..f72851e 100644 --- a/src/utils/string.cpp +++ b/src/utils/string.cpp @@ -36,14 +36,15 @@ std::string to_string(const blob &data) return str; } -size_t split(const std::string &str, char delim, std::vector &values) +std::vector split(const std::string &str, char delim) { std::stringstream ss(str); std::string item; + std::vector result; while (std::getline(ss, item, delim)) { - values.push_back(item); + result.push_back(item); } - return values.size(); + return result; } } \ No newline at end of file