sql select join progress
This commit is contained in:
parent
e8b0f0e802
commit
aa221aa571
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,26 +62,27 @@ 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();
|
||||||
|
|
||||||
person george{7, "george", 45};
|
person george{7, "george", 45};
|
||||||
george.image.push_back(37);
|
george.image.push_back(37);
|
||||||
|
|
||||||
auto res = db.insert()
|
auto res = db.insert()
|
||||||
.into("person", george)
|
.into("person", george)
|
||||||
.execute();
|
.execute();
|
||||||
REQUIRE(res == 1);
|
REQUIRE(res == 1);
|
||||||
|
|
||||||
// fetch person as record
|
// fetch person as record
|
||||||
auto result_record = db.select<person>()
|
auto result_record = db.select<person>()
|
||||||
.from("person")
|
.from("person")
|
||||||
.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);
|
||||||
|
|
@ -92,11 +97,11 @@ TEST_CASE_METHOD(QueryFixture, " Execute select statement with where clause", "[
|
||||||
|
|
||||||
// fetch person as person
|
// fetch person as person
|
||||||
auto result_person = db.select<person>()
|
auto result_person = db.select<person>()
|
||||||
.from("person")
|
.from("person")
|
||||||
.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,29 +110,30 @@ 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"),
|
||||||
make_column<std::string>("name", 255),
|
make_column<std::string>("name", 255),
|
||||||
make_column<std::string>("color", 63)
|
make_column<std::string>("color", 63)
|
||||||
})
|
})
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
auto res = db.insert()
|
auto res = db.insert()
|
||||||
.into("person", {"id", "name", "color"})
|
.into("person", {"id", "name", "color"})
|
||||||
.values({7, "george", "green"})
|
.values({7, "george", "green"})
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
REQUIRE(res == 1);
|
REQUIRE(res == 1);
|
||||||
|
|
||||||
// fetch person as record
|
// fetch person as record
|
||||||
auto result_record = db.select({"id", "name", "color"})
|
auto result_record = db.select({"id", "name", "color"})
|
||||||
.from("person")
|
.from("person")
|
||||||
.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,22 +188,23 @@ 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();
|
||||||
|
|
||||||
db.create()
|
db.create()
|
||||||
.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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue