initial matador ng commit
This commit is contained in:
@@ -1,78 +0,0 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/sql/any_type.hpp"
|
||||
#include "matador/sql/any_type_to_visitor.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
|
||||
TEST_CASE("Convert any type to string", "[any type visitor]") {
|
||||
any_type_to_visitor<std::string> to_string_visitor;
|
||||
|
||||
any_type value = 6;
|
||||
std::visit(to_string_visitor, value);
|
||||
REQUIRE(to_string_visitor.result == "6");
|
||||
|
||||
value = 2.5;
|
||||
std::visit(to_string_visitor, value);
|
||||
REQUIRE(to_string_visitor.result == "2.5");
|
||||
|
||||
value = true;
|
||||
std::visit(to_string_visitor, value);
|
||||
REQUIRE(to_string_visitor.result == "true");
|
||||
|
||||
value = "hello";
|
||||
std::visit(to_string_visitor, value);
|
||||
REQUIRE(to_string_visitor.result == "hello");
|
||||
|
||||
value = std::string{"world"};
|
||||
std::visit(to_string_visitor, value);
|
||||
REQUIRE(to_string_visitor.result == "world");
|
||||
}
|
||||
|
||||
TEST_CASE("Convert any type to integral", "[any type visitor]") {
|
||||
any_type_to_visitor<long> to_long_visitor;
|
||||
|
||||
any_type value = 6;
|
||||
std::visit(to_long_visitor, value);
|
||||
REQUIRE(to_long_visitor.result == 6);
|
||||
|
||||
value = 2.5;
|
||||
std::visit(to_long_visitor, value);
|
||||
REQUIRE(to_long_visitor.result == 2);
|
||||
|
||||
value = true;
|
||||
std::visit(to_long_visitor, value);
|
||||
REQUIRE(to_long_visitor.result == 1);
|
||||
|
||||
value = "hello";
|
||||
std::visit(to_long_visitor, value);
|
||||
REQUIRE(to_long_visitor.result == 0);
|
||||
|
||||
value = std::string{"world"};
|
||||
std::visit(to_long_visitor, value);
|
||||
REQUIRE(to_long_visitor.result == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Convert any type to floating point", "[any type visitor]") {
|
||||
any_type_to_visitor<double> to_double_visitor;
|
||||
|
||||
any_type value = 6;
|
||||
std::visit(to_double_visitor, value);
|
||||
REQUIRE(to_double_visitor.result == 6);
|
||||
|
||||
value = 2.5;
|
||||
std::visit(to_double_visitor, value);
|
||||
REQUIRE(to_double_visitor.result == 2.5);
|
||||
|
||||
value = true;
|
||||
std::visit(to_double_visitor, value);
|
||||
REQUIRE(to_double_visitor.result == 1);
|
||||
|
||||
value = "hello";
|
||||
std::visit(to_double_visitor, value);
|
||||
REQUIRE(to_double_visitor.result == 0);
|
||||
|
||||
value = std::string{"world"};
|
||||
std::visit(to_double_visitor, value);
|
||||
REQUIRE(to_double_visitor.result == 0);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/sql/connection_info.hpp"
|
||||
#include "matador/sql/backend_provider.hpp"
|
||||
|
||||
#include "matador/utils/os.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
|
||||
TEST_CASE("Load backend", "[backend provider]") {
|
||||
auto path = matador::utils::os::getenv("MATADOR_BACKENDS_PATH");
|
||||
REQUIRE(!path.empty());
|
||||
|
||||
if (path.back() != '\\') {
|
||||
path.push_back('\\');
|
||||
}
|
||||
|
||||
REQUIRE(!path.empty());
|
||||
|
||||
connection_info ci{};
|
||||
const auto &d = backend_provider::instance().connection_dialect("noop");
|
||||
auto *connection = backend_provider::instance().create_connection("noop", ci);
|
||||
REQUIRE(connection != nullptr);
|
||||
backend_provider::instance().destroy_connection("noop", connection);
|
||||
}
|
||||
+3
-55
@@ -1,56 +1,4 @@
|
||||
Include(FetchContent)
|
||||
enable_testing()
|
||||
|
||||
FetchContent_Declare(
|
||||
Catch2
|
||||
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
||||
GIT_TAG v3.5.4 # or a later release
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(Catch2)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
|
||||
include(CTest)
|
||||
include(Catch)
|
||||
|
||||
add_executable(tests
|
||||
QueryBuilderTest.cpp
|
||||
TableDefinitionTest.cpp
|
||||
ConnectionPoolTest.cpp
|
||||
BackendProviderTest.cpp
|
||||
models/product.hpp
|
||||
models/order.hpp
|
||||
models/order_details.hpp
|
||||
ColumnDefinitionGeneratorTest.cpp
|
||||
ColumnGeneratorTest.cpp
|
||||
ValueGeneratorTest.cpp
|
||||
models/category.hpp
|
||||
models/supplier.hpp
|
||||
models/airplane.hpp
|
||||
models/flight.hpp
|
||||
models/person.hpp
|
||||
AnyTypeToVisitorTest.cpp
|
||||
ColumnTest.cpp
|
||||
models/coordinate.hpp
|
||||
models/location.hpp
|
||||
models/optional.hpp
|
||||
ConvertTest.cpp
|
||||
EntityQueryBuilderTest.cpp
|
||||
models/author.hpp
|
||||
models/book.hpp
|
||||
FieldTest.cpp
|
||||
models/recipe.hpp
|
||||
ValueTest.cpp
|
||||
ResultTest.cpp
|
||||
utils/auto_reset_event.hpp
|
||||
utils/auto_reset_event.cpp
|
||||
models/student.hpp)
|
||||
|
||||
target_link_libraries(tests PRIVATE
|
||||
Catch2::Catch2WithMain
|
||||
matador
|
||||
${CMAKE_DL_LIBS}
|
||||
${SQLite3_LIBRARIES}
|
||||
${PostgreSQL_LIBRARY})
|
||||
target_include_directories(tests PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>/include)
|
||||
|
||||
catch_discover_tests(tests)
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(orm)
|
||||
@@ -1,56 +0,0 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/sql/column_definition_generator.hpp"
|
||||
#include "matador/sql/schema.hpp"
|
||||
|
||||
#include "models/product.hpp"
|
||||
#include "models/optional.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
using namespace matador::utils;
|
||||
|
||||
TEST_CASE("Generate column definitions from object", "[column][definition][generator]") {
|
||||
schema repo("main");
|
||||
|
||||
auto columns = column_definition_generator::generate<matador::test::product>(repo);
|
||||
|
||||
const std::vector<column_definition> expected_columns = {
|
||||
column_definition{"product_name", data_type_t::type_varchar, constraints::PRIMARY_KEY, null_option::NOT_NULL },
|
||||
column_definition{"supplier_id", data_type_t::type_unsigned_long, constraints::FOREIGN_KEY, null_option::NOT_NULL },
|
||||
column_definition{"category_id", data_type_t::type_unsigned_long, constraints::FOREIGN_KEY, null_option::NOT_NULL },
|
||||
column_definition{"quantity_per_unit", data_type_t::type_varchar, null_attributes, null_option::NOT_NULL },
|
||||
column_definition{"unit_price", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL },
|
||||
column_definition{"units_in_stock", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL },
|
||||
column_definition{"units_in_order", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL },
|
||||
column_definition{"reorder_level", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL },
|
||||
column_definition{"discontinued", data_type_t::type_bool, null_attributes, null_option::NOT_NULL }
|
||||
};
|
||||
REQUIRE(!columns.empty());
|
||||
REQUIRE(columns.size() == expected_columns.size());
|
||||
|
||||
for (size_t i = 0; i != expected_columns.size(); ++i) {
|
||||
REQUIRE(expected_columns[i].name() == columns[i].name());
|
||||
REQUIRE(expected_columns[i].attributes().options() == columns[i].attributes().options() );
|
||||
REQUIRE(expected_columns[i].type() == columns[i].type() );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Generate columns from object with nullable columns", "[column generator]") {
|
||||
schema repo("main");
|
||||
|
||||
auto columns = column_definition_generator::generate<matador::test::optional>(repo);
|
||||
|
||||
const std::vector<column_definition> expected_columns = {
|
||||
column_definition{"id", data_type_t::type_unsigned_long, constraints::PRIMARY_KEY, null_option::NOT_NULL },
|
||||
column_definition{"name", data_type_t::type_varchar, null_attributes, null_option::NOT_NULL },
|
||||
column_definition{"age", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL }
|
||||
};
|
||||
REQUIRE(!columns.empty());
|
||||
REQUIRE(columns.size() == expected_columns.size());
|
||||
|
||||
for (size_t i = 0; i != expected_columns.size(); ++i) {
|
||||
REQUIRE(expected_columns[i].name() == columns[i].name());
|
||||
REQUIRE(expected_columns[i].attributes().options() == columns[i].attributes().options() );
|
||||
REQUIRE(expected_columns[i].type() == columns[i].type() );
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/sql/column_generator.hpp"
|
||||
#include "matador/sql/schema.hpp"
|
||||
|
||||
#include "models/order.hpp"
|
||||
#include "models/product.hpp"
|
||||
#include "models/book.hpp"
|
||||
#include "models/author.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
|
||||
TEST_CASE("Generate columns from object", "[column][generator]") {
|
||||
using namespace matador::test;
|
||||
schema s("main");
|
||||
s.attach<product>("product");
|
||||
|
||||
auto columns = column_generator::generate<product>(s);
|
||||
|
||||
const std::vector<std::string> expected_columns = {
|
||||
"product_name",
|
||||
"supplier_id",
|
||||
"category_id",
|
||||
"quantity_per_unit",
|
||||
"unit_price",
|
||||
"units_in_stock",
|
||||
"units_in_order",
|
||||
"reorder_level",
|
||||
"discontinued"
|
||||
};
|
||||
REQUIRE(!columns.empty());
|
||||
REQUIRE(columns.size() == expected_columns.size());
|
||||
|
||||
for (size_t i = 0; i != expected_columns.size(); ++i) {
|
||||
REQUIRE(expected_columns[i] == columns[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Generate columns for object with has many relation", "[column][generator][relation]") {
|
||||
using namespace matador::test;
|
||||
schema s("main");
|
||||
s.attach<product>("product");
|
||||
s.attach<order_details>("order_details");
|
||||
s.attach<order>("order");
|
||||
|
||||
auto columns = column_generator::generate<order>(s);
|
||||
|
||||
const std::vector<column> expected_columns = {
|
||||
{ "order", "order_id", "c01" },
|
||||
{ "order", "order_date", "c02" },
|
||||
{ "order", "required_date", "c03" },
|
||||
{ "order", "shipped_date", "c04" },
|
||||
{ "order", "ship_via", "c05" },
|
||||
{ "order", "freight", "c06" },
|
||||
{ "order", "ship_name", "c07" },
|
||||
{ "order", "ship_address", "c08" },
|
||||
{ "order", "ship_city", "c09" },
|
||||
{ "order", "ship_region", "c10" },
|
||||
{ "order", "ship_postal_code", "c11" },
|
||||
{ "order", "ship_country", "c12" },
|
||||
{ "order_details", "order_details_id", "c13" },
|
||||
{ "order_details", "order_id", "c14" },
|
||||
{ "order_details", "product_id", "c15" }
|
||||
};
|
||||
REQUIRE(!columns.empty());
|
||||
REQUIRE(columns.size() == expected_columns.size());
|
||||
|
||||
for (size_t i = 0; i != expected_columns.size(); ++i) {
|
||||
REQUIRE(expected_columns[i].equals(columns[i]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("Generate columns for object with eager foreign key relation", "[column][generator][eager]") {
|
||||
using namespace matador::test;
|
||||
schema s("main");
|
||||
s.attach<book>("books");
|
||||
s.attach<author>("authors");
|
||||
|
||||
const std::vector<column> expected_columns {
|
||||
{ "books", "id", "c01" },
|
||||
{ "books", "title", "c02" },
|
||||
{ "authors", "id", "c03" },
|
||||
{ "authors", "first_name", "c04" },
|
||||
{ "authors", "last_name", "c05" },
|
||||
{ "authors", "date_of_birth", "c06" },
|
||||
{ "authors", "year_of_birth", "c07" },
|
||||
{ "authors", "distinguished", "c08" },
|
||||
{ "books", "published_in", "c09" }
|
||||
};
|
||||
auto columns = column_generator::generate<book>(s);
|
||||
|
||||
REQUIRE(!columns.empty());
|
||||
REQUIRE(columns.size() == expected_columns.size());
|
||||
for (size_t i = 0; i != expected_columns.size(); ++i) {
|
||||
REQUIRE(expected_columns[i].equals(columns[i]));
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/sql/connection_pool.hpp"
|
||||
#include "matador/sql/noop_connection.hpp"
|
||||
|
||||
#include "utils/auto_reset_event.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
using namespace matador::test::utils;
|
||||
|
||||
TEST_CASE("Create connection pool", "[connection pool]") {
|
||||
using pool_t = connection_pool<noop_connection>;
|
||||
|
||||
pool_t pool("noop://noop.db", 4);
|
||||
|
||||
REQUIRE(pool.size() == 4);
|
||||
REQUIRE(pool.idle() == 4);
|
||||
REQUIRE(pool.inuse() == 0);
|
||||
|
||||
auto ptr = pool.acquire();
|
||||
REQUIRE(ptr.valid());
|
||||
REQUIRE(ptr.id().value() > 0);
|
||||
REQUIRE(ptr->is_open());
|
||||
|
||||
REQUIRE(pool.idle() == 3);
|
||||
REQUIRE(pool.inuse() == 1);
|
||||
|
||||
pool.release(ptr);
|
||||
REQUIRE(!ptr.valid());
|
||||
REQUIRE(pool.idle() == 4);
|
||||
REQUIRE(pool.inuse() == 0);
|
||||
|
||||
ptr = pool.acquire(3);
|
||||
REQUIRE(ptr.valid());
|
||||
REQUIRE(ptr.id() == 3);
|
||||
REQUIRE(ptr->is_open());
|
||||
{
|
||||
auto ptr2 = pool.acquire();
|
||||
REQUIRE(ptr2.valid());
|
||||
REQUIRE(ptr2->is_open());
|
||||
|
||||
REQUIRE(pool.idle() == 2);
|
||||
REQUIRE(pool.inuse() == 2);
|
||||
}
|
||||
|
||||
REQUIRE(pool.idle() == 3);
|
||||
REQUIRE(pool.inuse() == 1);
|
||||
|
||||
pool.release(ptr);
|
||||
REQUIRE(!ptr.valid());
|
||||
REQUIRE(pool.idle() == 4);
|
||||
REQUIRE(pool.inuse() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Acquire connection by id", "[connection pool]") {
|
||||
using pool_t = connection_pool<noop_connection>;
|
||||
|
||||
pool_t pool("noop://noop.db", 4);
|
||||
|
||||
REQUIRE(pool.size() == 4);
|
||||
REQUIRE(pool.idle() == 4);
|
||||
REQUIRE(pool.inuse() == 0);
|
||||
|
||||
auto ptr = pool.acquire();
|
||||
REQUIRE(ptr.valid());
|
||||
REQUIRE(ptr.id());
|
||||
REQUIRE(ptr.id().value() > 0);
|
||||
REQUIRE(ptr->is_open());
|
||||
|
||||
auto same_ptr = pool.acquire(ptr.id().value());
|
||||
|
||||
REQUIRE(!same_ptr.valid());
|
||||
|
||||
const auto connection_id = ptr.id().value();
|
||||
|
||||
pool.release(ptr);
|
||||
REQUIRE(!ptr.valid());
|
||||
|
||||
same_ptr = pool.acquire(connection_id);
|
||||
|
||||
REQUIRE(same_ptr.valid());
|
||||
REQUIRE(same_ptr.id() == connection_id);
|
||||
}
|
||||
|
||||
TEST_CASE("Try acquire connection", "[connection pool][try acquire]") {
|
||||
using pool_t = connection_pool<noop_connection>;
|
||||
|
||||
pool_t pool("noop://noop.db", 1);
|
||||
|
||||
REQUIRE(pool.size() == 1);
|
||||
REQUIRE(pool.idle() == 1);
|
||||
REQUIRE(pool.inuse() == 0);
|
||||
|
||||
auto ptr = pool.try_acquire();
|
||||
REQUIRE(ptr.valid());
|
||||
REQUIRE(ptr.id());
|
||||
REQUIRE(ptr.id().value() > 0);
|
||||
REQUIRE(ptr->is_open());
|
||||
REQUIRE(pool.size() == 1);
|
||||
REQUIRE(pool.idle() == 0);
|
||||
REQUIRE(pool.inuse() == 1);
|
||||
|
||||
auto ptr2 = pool.try_acquire();
|
||||
REQUIRE(!ptr2.valid());
|
||||
|
||||
pool.release(ptr);
|
||||
REQUIRE(!ptr.valid());
|
||||
REQUIRE(pool.size() == 1);
|
||||
REQUIRE(pool.idle() == 1);
|
||||
REQUIRE(pool.inuse() == 0);
|
||||
|
||||
ptr2 = pool.try_acquire();
|
||||
REQUIRE(ptr2.valid());
|
||||
REQUIRE(ptr2.id());
|
||||
REQUIRE(ptr2.id().value() > 0);
|
||||
REQUIRE(ptr2->is_open());
|
||||
REQUIRE(pool.size() == 1);
|
||||
REQUIRE(pool.idle() == 0);
|
||||
REQUIRE(pool.inuse() == 1);
|
||||
|
||||
pool.release(ptr2);
|
||||
|
||||
auto_reset_event reset_main_event;
|
||||
auto_reset_event reset_thread_event;
|
||||
|
||||
std::thread t([&reset_main_event, &reset_thread_event, &pool]() {
|
||||
auto c1 = pool.acquire();
|
||||
REQUIRE(c1.valid());
|
||||
REQUIRE(c1.id());
|
||||
REQUIRE(c1.id().value() > 0);
|
||||
|
||||
reset_main_event.set();
|
||||
|
||||
reset_thread_event.wait_one();
|
||||
|
||||
pool.release(c1);
|
||||
REQUIRE(!c1.valid());
|
||||
reset_main_event.set();
|
||||
});
|
||||
reset_main_event.wait_one();
|
||||
|
||||
ptr2 = pool.try_acquire();
|
||||
REQUIRE(!ptr2.valid());
|
||||
|
||||
reset_thread_event.set();
|
||||
|
||||
reset_main_event.wait_one();
|
||||
ptr2 = pool.try_acquire();
|
||||
REQUIRE(ptr2.valid());
|
||||
REQUIRE(ptr2.id());
|
||||
REQUIRE(ptr2.id().value() > 0);
|
||||
|
||||
t.join();
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/sql/convert.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
|
||||
TEST_CASE("Test convert function", "[convert]") {
|
||||
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <matador/sql/connection.hpp>
|
||||
#include <matador/sql/entity_query_builder.hpp>
|
||||
|
||||
#include "models/airplane.hpp"
|
||||
#include "models/author.hpp"
|
||||
#include "models/book.hpp"
|
||||
#include "models/flight.hpp"
|
||||
#include "models/recipe.hpp"
|
||||
#include "models/order.hpp"
|
||||
#include "models/student.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
|
||||
TEST_CASE("Create sql query data for entity with eager has one", "[query][entity][builder]") {
|
||||
using namespace matador::test;
|
||||
connection db("noop://noop.db");
|
||||
schema scm("noop");
|
||||
scm.attach<airplane>("airplanes");
|
||||
scm.attach<flight>("flights");
|
||||
|
||||
entity_query_builder eqb(scm);
|
||||
|
||||
auto data = eqb.build<flight>(17);
|
||||
|
||||
REQUIRE(data.is_ok());
|
||||
REQUIRE(data->root_table_name == "flights");
|
||||
REQUIRE(data->joins.size() == 1);
|
||||
const std::vector<column> expected_columns {
|
||||
{ "flights", "id", "c01" },
|
||||
{ "airplanes", "id", "c02" },
|
||||
{ "airplanes", "brand", "c03" },
|
||||
{ "airplanes", "model", "c04" },
|
||||
{ "flights", "pilot_name", "c05" },
|
||||
};
|
||||
REQUIRE(data->columns.size() == expected_columns.size());
|
||||
for (size_t i = 0; i != expected_columns.size(); ++i) {
|
||||
REQUIRE(expected_columns[i].equals(data->columns[i]));
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> expected_join_data {
|
||||
{ "airplanes", R"("flights"."airplane_id" = "airplanes"."id")"}
|
||||
};
|
||||
|
||||
query_context qc;
|
||||
size_t index{0};
|
||||
for (const auto &jd : data->joins) {
|
||||
REQUIRE(jd.join_table.name == expected_join_data[index].first);
|
||||
REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second);
|
||||
++index;
|
||||
}
|
||||
|
||||
REQUIRE(data->where_clause);
|
||||
auto cond = data->where_clause->evaluate(db.dialect(), qc);
|
||||
REQUIRE(cond == R"("flights"."id" = 17)");
|
||||
}
|
||||
|
||||
TEST_CASE("Create sql query data for entity with eager belongs to", "[query][entity][builder]") {
|
||||
using namespace matador::test;
|
||||
connection db("noop://noop.db");
|
||||
schema scm("noop");
|
||||
scm.attach<author>("authors");
|
||||
scm.attach<book>("books");
|
||||
|
||||
entity_query_builder eqb(scm);
|
||||
|
||||
auto data = eqb.build<book>(17);
|
||||
|
||||
REQUIRE(data.is_ok());
|
||||
REQUIRE(data->root_table_name == "books");
|
||||
REQUIRE(data->joins.size() == 1);
|
||||
const std::vector<column> expected_columns {
|
||||
{ "books", "id", "c01" },
|
||||
{ "books", "title", "c02" },
|
||||
{ "authors", "id", "c03" },
|
||||
{ "authors", "first_name", "c04" },
|
||||
{ "authors", "last_name", "c05" },
|
||||
{ "authors", "date_of_birth", "c06" },
|
||||
{ "authors", "year_of_birth", "c07" },
|
||||
{ "authors", "distinguished", "c08" },
|
||||
{ "books", "published_in", "c09" }
|
||||
};
|
||||
REQUIRE(data->columns.size() == expected_columns.size());
|
||||
for (size_t i = 0; i != expected_columns.size(); ++i) {
|
||||
REQUIRE(expected_columns[i].equals(data->columns[i]));
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> expected_join_data {
|
||||
{ "authors", R"("books"."author_id" = "authors"."id")"}
|
||||
};
|
||||
|
||||
query_context qc;
|
||||
size_t index{0};
|
||||
for (const auto &jd : data->joins) {
|
||||
REQUIRE(jd.join_table.name == expected_join_data[index].first);
|
||||
REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second);
|
||||
++index;
|
||||
}
|
||||
|
||||
REQUIRE(data->where_clause);
|
||||
auto cond = data->where_clause->evaluate(db.dialect(), qc);
|
||||
REQUIRE(cond == R"("books"."id" = 17)");
|
||||
|
||||
auto q = db.query(scm)
|
||||
.select(data->columns)
|
||||
.from(data->root_table_name);
|
||||
|
||||
for (auto &jd : data->joins) {
|
||||
q.join_left(jd.join_table)
|
||||
.on(std::move(jd.condition));
|
||||
}
|
||||
auto context = q
|
||||
.where(std::move(data->where_clause))
|
||||
.build();
|
||||
}
|
||||
|
||||
TEST_CASE("Create sql query data for entity with eager has many belongs to", "[query][entity][builder]") {
|
||||
using namespace matador::test;
|
||||
connection db("noop://noop.db");
|
||||
schema scm("noop");
|
||||
scm.attach<product>("products");
|
||||
scm.attach<order_details>("order_details");
|
||||
scm.attach<order>("orders");
|
||||
|
||||
entity_query_builder eqb(scm);
|
||||
|
||||
auto data = eqb.build<order>(17);
|
||||
|
||||
REQUIRE(data.is_ok());
|
||||
REQUIRE(data->root_table_name == "orders");
|
||||
REQUIRE(data->joins.size() == 1);
|
||||
const std::vector<column> expected_columns = {
|
||||
{ "orders", "order_id", "c01" },
|
||||
{ "orders", "order_date", "c02" },
|
||||
{ "orders", "required_date", "c03" },
|
||||
{ "orders", "shipped_date", "c04" },
|
||||
{ "orders", "ship_via", "c05" },
|
||||
{ "orders", "freight", "c06" },
|
||||
{ "orders", "ship_name", "c07" },
|
||||
{ "orders", "ship_address", "c08" },
|
||||
{ "orders", "ship_city", "c09" },
|
||||
{ "orders", "ship_region", "c10" },
|
||||
{ "orders", "ship_postal_code", "c11" },
|
||||
{ "orders", "ship_country", "c12" },
|
||||
{ "order_details", "order_details_id", "c13" },
|
||||
{ "order_details", "order_id", "c14" },
|
||||
{ "order_details", "product_id", "c15" }
|
||||
};
|
||||
REQUIRE(data->columns.size() == expected_columns.size());
|
||||
for (size_t i = 0; i != expected_columns.size(); ++i) {
|
||||
REQUIRE(expected_columns[i].equals(data->columns[i]));
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> expected_join_data {
|
||||
{ "order_details", R"("orders"."order_id" = "order_details"."order_id")"}
|
||||
};
|
||||
|
||||
query_context qc;
|
||||
size_t index{0};
|
||||
for (const auto &jd : data->joins) {
|
||||
REQUIRE(jd.join_table.name == expected_join_data[index].first);
|
||||
REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second);
|
||||
++index;
|
||||
}
|
||||
|
||||
REQUIRE(data->where_clause);
|
||||
auto cond = data->where_clause->evaluate(db.dialect(), qc);
|
||||
REQUIRE(cond == R"("orders"."order_id" = 17)");
|
||||
}
|
||||
|
||||
TEST_CASE("Create sql query data for entity with eager many to many", "[query][entity][builder]") {
|
||||
using namespace matador::test;
|
||||
connection db("noop://noop.db");
|
||||
schema scm("noop");
|
||||
scm.attach<recipe>("recipes");
|
||||
scm.attach<ingredient>("ingredients");
|
||||
scm.attach<recipe_ingredient>("recipe_ingredients");
|
||||
|
||||
entity_query_builder eqb(scm);
|
||||
|
||||
auto data = eqb.build<ingredient>(17);
|
||||
|
||||
REQUIRE(data.is_ok());
|
||||
REQUIRE(data->root_table_name == "ingredients");
|
||||
REQUIRE(data->joins.size() == 2);
|
||||
const std::vector<column> expected_columns {
|
||||
{ "ingredients", "id", "c01" },
|
||||
{ "ingredients", "name", "c02" },
|
||||
{ "recipes", "id", "c03" },
|
||||
{ "recipes", "name", "c04" }
|
||||
};
|
||||
REQUIRE(data->columns.size() == expected_columns.size());
|
||||
for (size_t i = 0; i != expected_columns.size(); ++i) {
|
||||
REQUIRE(expected_columns[i].equals(data->columns[i]));
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> expected_join_data {
|
||||
{ "recipe_ingredients", R"("ingredients"."id" = "recipe_ingredients"."ingredient_id")"},
|
||||
{ "recipes", R"("recipe_ingredients"."recipe_id" = "recipes"."id")"}
|
||||
};
|
||||
|
||||
query_context qc;
|
||||
size_t index{0};
|
||||
for (const auto &jd : data->joins) {
|
||||
REQUIRE(jd.join_table.name == expected_join_data[index].first);
|
||||
REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second);
|
||||
++index;
|
||||
}
|
||||
|
||||
REQUIRE(data->where_clause);
|
||||
auto cond = data->where_clause->evaluate(db.dialect(), qc);
|
||||
REQUIRE(cond == R"("ingredients"."id" = 17)");
|
||||
}
|
||||
|
||||
TEST_CASE("Create sql query data for entity with eager many to many (inverse part)", "[query][entity][builder]") {
|
||||
using namespace matador::test;
|
||||
connection db("noop://noop.db");
|
||||
schema scm("noop");
|
||||
scm.attach<student>("students");
|
||||
scm.attach<course>("courses");
|
||||
scm.attach<student_course>("student_courses");
|
||||
|
||||
entity_query_builder eqb(scm);
|
||||
|
||||
auto data = eqb.build<course>(17);
|
||||
|
||||
REQUIRE(data.is_ok());
|
||||
REQUIRE(data->root_table_name == "courses");
|
||||
REQUIRE(data->joins.size() == 2);
|
||||
const std::vector<column> expected_columns {
|
||||
{ "courses", "id", "c01" },
|
||||
{ "courses", "title", "c02" },
|
||||
{ "students", "id", "c03" },
|
||||
{ "students", "name", "c04" }
|
||||
};
|
||||
REQUIRE(data->columns.size() == expected_columns.size());
|
||||
for (size_t i = 0; i != expected_columns.size(); ++i) {
|
||||
REQUIRE(expected_columns[i].equals(data->columns[i]));
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> expected_join_data {
|
||||
{ "student_courses", R"("courses"."id" = "student_courses"."course_id")"},
|
||||
{ "students", R"("student_courses"."student_id" = "students"."id")"}
|
||||
};
|
||||
|
||||
query_context qc;
|
||||
size_t index{0};
|
||||
for (const auto &jd : data->joins) {
|
||||
REQUIRE(jd.join_table.name == expected_join_data[index].first);
|
||||
REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second);
|
||||
++index;
|
||||
}
|
||||
|
||||
REQUIRE(data->where_clause);
|
||||
auto cond = data->where_clause->evaluate(db.dialect(), qc);
|
||||
REQUIRE(cond == R"("courses"."id" = 17)");
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <matador/sql/column_definition.hpp>
|
||||
#include <matador/sql/condition.hpp>
|
||||
#include <matador/sql/connection.hpp>
|
||||
#include <matador/sql/dialect_builder.hpp>
|
||||
#include <matador/sql/query.hpp>
|
||||
|
||||
using namespace matador::sql;
|
||||
using namespace matador::utils;
|
||||
|
||||
TEST_CASE("Create table sql statement string", "[query]")
|
||||
{
|
||||
connection noop("noop://noop.db");
|
||||
schema scm("noop");
|
||||
query q(noop, scm);
|
||||
auto result = q.create().table({"person"}, {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age")
|
||||
}).build();
|
||||
|
||||
REQUIRE(result.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "age" INTEGER NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##");
|
||||
REQUIRE(result.table.name == "person");
|
||||
|
||||
result = q.create().table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", {255, constraints::UNIQUE}, null_option::NOT_NULL),
|
||||
make_column<unsigned short>("age"),
|
||||
make_fk_column<unsigned long>("address", "address", "id")
|
||||
}).build();
|
||||
|
||||
REQUIRE(result.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL UNIQUE, "age" INTEGER NOT NULL, "address" BIGINT NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id), CONSTRAINT FK_person_address FOREIGN KEY (address) REFERENCES address(id)))##");
|
||||
REQUIRE(result.table.name == "person");
|
||||
}
|
||||
|
||||
TEST_CASE("Drop table sql statement string", "[query]")
|
||||
{
|
||||
connection noop("noop://noop.db");
|
||||
schema scm("noop");
|
||||
query q(noop, scm);
|
||||
const auto result = q.drop().table("person").build();
|
||||
|
||||
REQUIRE(result.sql == R"(DROP TABLE "person")");
|
||||
REQUIRE(result.table.name == "person");
|
||||
}
|
||||
|
||||
TEST_CASE("Select sql statement string", "[query]")
|
||||
{
|
||||
connection noop("noop://noop.db");
|
||||
schema scm("noop");
|
||||
query q(noop, scm);
|
||||
const auto result = q.select({"id", "name", "age"}).from("person").build();
|
||||
|
||||
REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person")");
|
||||
REQUIRE(result.table.name == "person");
|
||||
}
|
||||
|
||||
TEST_CASE("Insert sql statement string", "[query]")
|
||||
{
|
||||
connection noop("noop://noop.db");
|
||||
schema scm("noop");
|
||||
query q(noop, scm);
|
||||
const auto result = q.insert().into("person", {
|
||||
"id", "name", "age"
|
||||
}).values({7UL, "george", 65U}).build();
|
||||
|
||||
REQUIRE(result.sql == R"(INSERT INTO "person" ("id", "name", "age") VALUES (7, 'george', 65))");
|
||||
REQUIRE(result.table.name == "person");
|
||||
}
|
||||
|
||||
TEST_CASE("Update sql statement string", "[query]")
|
||||
{
|
||||
connection noop("noop://noop.db");
|
||||
schema scm("noop");
|
||||
query q(noop, scm);
|
||||
const auto result = q.update("person").set({
|
||||
{"id", 7UL},
|
||||
{"name", "george"},
|
||||
{"age", 65U}
|
||||
}).build();
|
||||
|
||||
REQUIRE(result.sql == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65)");
|
||||
REQUIRE(result.table.name == "person");
|
||||
}
|
||||
|
||||
TEST_CASE("Update limit sql statement", "[query][update][limit]")
|
||||
{
|
||||
connection noop("noop://noop.db");
|
||||
schema scm("noop");
|
||||
query q(noop, scm);
|
||||
const auto result = q.update("person")
|
||||
.set({{"id", 7UL}, {"name", "george"}, {"age", 65U}})
|
||||
.where("name"_col == "george")
|
||||
.order_by("id"_col).asc()
|
||||
.limit(2)
|
||||
.build();
|
||||
|
||||
REQUIRE(result.sql == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65 WHERE "name" = 'george' ORDER BY "id" ASC LIMIT 2)");
|
||||
REQUIRE(result.table.name == "person");
|
||||
}
|
||||
|
||||
TEST_CASE("Delete sql statement string", "[query]")
|
||||
{
|
||||
connection noop("noop://noop.db");
|
||||
schema scm("noop");
|
||||
query q(noop, scm);
|
||||
const auto result = q.remove().from("person").build();
|
||||
|
||||
REQUIRE(result.sql == R"(DELETE FROM "person")");
|
||||
REQUIRE(result.table.name == "person");
|
||||
}
|
||||
|
||||
TEST_CASE("Delete limit sql statement", "[query][delete][limit]")
|
||||
{
|
||||
connection noop("noop://noop.db");
|
||||
schema scm("noop");
|
||||
query q(noop, scm);
|
||||
const auto result = q.remove()
|
||||
.from("person")
|
||||
.where("name"_col == "george")
|
||||
.order_by("id"_col).asc()
|
||||
.limit(2)
|
||||
.build();
|
||||
|
||||
REQUIRE(result.sql == R"(DELETE FROM "person" WHERE "name" = 'george' ORDER BY "id" ASC LIMIT 2)");
|
||||
REQUIRE(result.table.name == "person");
|
||||
}
|
||||
|
||||
TEST_CASE("Select sql statement string with where clause", "[query]")
|
||||
{
|
||||
connection noop("noop://noop.db");
|
||||
schema scm("noop");
|
||||
query q(noop, scm);
|
||||
auto result = q.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.where("id"_col == 8 && "age"_col > 50)
|
||||
.build();
|
||||
|
||||
REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = 8 AND "age" > 50))");
|
||||
REQUIRE(result.table.name == "person");
|
||||
|
||||
result = q.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.where("id"_col == _ && "age"_col > 50)
|
||||
.build();
|
||||
|
||||
REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = ? AND "age" > 50))");
|
||||
REQUIRE(result.table.name == "person");
|
||||
}
|
||||
|
||||
TEST_CASE("Insert sql statement with placeholder", "[query]")
|
||||
{
|
||||
connection noop("noop://noop.db");
|
||||
schema scm("noop");
|
||||
query q(noop, scm);
|
||||
const auto result = q.insert().into("person", {
|
||||
"id", "name", "age"
|
||||
}).values({_, _, _}).build();
|
||||
|
||||
REQUIRE(result.sql == R"(INSERT INTO "person" ("id", "name", "age") VALUES (?, ?, ?))");
|
||||
REQUIRE(result.table.name == "person");
|
||||
REQUIRE(result.bind_vars.size() == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("Select sql statement string with order by", "[query]")
|
||||
{
|
||||
connection noop("noop://noop.db");
|
||||
schema scm("noop");
|
||||
query q(noop, scm);
|
||||
const auto result = q.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.order_by("name").asc()
|
||||
.build();
|
||||
|
||||
REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "name" ASC)");
|
||||
REQUIRE(result.table.name == "person");
|
||||
}
|
||||
|
||||
TEST_CASE("Select sql statement string with group by", "[query]")
|
||||
{
|
||||
connection noop("noop://noop.db");
|
||||
schema scm("noop");
|
||||
query q(noop, scm);
|
||||
const auto result = q.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.group_by("age")
|
||||
.build();
|
||||
|
||||
REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person" GROUP BY "age")");
|
||||
REQUIRE(result.table.name == "person");
|
||||
}
|
||||
|
||||
TEST_CASE("Select sql statement string with offset and limit", "[query]")
|
||||
{
|
||||
connection noop("noop://noop.db");
|
||||
schema scm("noop");
|
||||
query q(noop, scm);
|
||||
const auto result = q.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.order_by("id"_col).asc()
|
||||
.limit(20)
|
||||
.offset(10)
|
||||
.build();
|
||||
|
||||
REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "id" ASC LIMIT 20 OFFSET 10)");
|
||||
REQUIRE(result.table.name == "person");
|
||||
}
|
||||
|
||||
TEST_CASE("Create, insert and select a blob column", "[query][blob]")
|
||||
{
|
||||
connection noop("noop://noop.db");
|
||||
schema scm("noop");
|
||||
query q(noop, scm);
|
||||
auto result = q.create().table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<blob>("data")
|
||||
}).build();
|
||||
|
||||
REQUIRE(result.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "data" BLOB NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##");
|
||||
REQUIRE(result.table.name == "person");
|
||||
|
||||
result = q.insert().into("person", {
|
||||
"id", "name", "data"
|
||||
}).values({7UL, "george", blob{1, 'A', 3, 4}}).build();
|
||||
|
||||
REQUIRE(result.sql == R"(INSERT INTO "person" ("id", "name", "data") VALUES (7, 'george', X'01410304'))");
|
||||
REQUIRE(result.table.name == "person");
|
||||
|
||||
result = q.select({"id", "name", "data"}).from("person").build();
|
||||
|
||||
REQUIRE(result.sql == R"(SELECT "id", "name", "data" FROM "person")");
|
||||
REQUIRE(result.table.name == "person");
|
||||
}
|
||||
|
||||
TEST_CASE("Select statement with join_left", "[query][join_left]")
|
||||
{
|
||||
connection noop("noop://noop.db");
|
||||
schema scm("noop");
|
||||
query q(noop, scm);
|
||||
auto result = q.select({"f.id", "ap.brand", "f.pilot_name"})
|
||||
.from({"flight", "f"})
|
||||
.join_left({"airplane", "ap"})
|
||||
.on("f.airplane_id"_col == "ap.id"_col)
|
||||
.build();
|
||||
|
||||
REQUIRE(result.sql == R"(SELECT "f"."id", "ap"."brand", "f"."pilot_name" FROM "flight" AS "f" INNER JOIN "airplane" AS "ap" ON "f"."airplane_id" = "ap"."id")");
|
||||
REQUIRE(result.table.name == "flight");
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/utils/result.hpp"
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
enum class math_error : int32_t {
|
||||
DIVISION_BY_ZERO = 1
|
||||
};
|
||||
|
||||
utils::result<float, math_error>divide(int x, int y) {
|
||||
if (y == 0) {
|
||||
return utils::error(math_error::DIVISION_BY_ZERO);
|
||||
}
|
||||
|
||||
return utils::ok(float(x) / y);
|
||||
}
|
||||
|
||||
utils::result<float, math_error>multiply(int x, int y) {
|
||||
return utils::ok(float(x) * y);
|
||||
}
|
||||
|
||||
utils::result<float, math_error>plus(int x, int y) {
|
||||
return utils::ok(float(x) + y);
|
||||
}
|
||||
|
||||
utils::result<float, std::string>error_to_string(math_error err) {
|
||||
switch (err) {
|
||||
case math_error::DIVISION_BY_ZERO:
|
||||
return utils::error(std::string("division by zero error"));
|
||||
default:
|
||||
return utils::error(std::string("unknown error"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using namespace matador;
|
||||
|
||||
TEST_CASE("Result tests", "[result]") {
|
||||
auto res = test::divide(4, 2);
|
||||
REQUIRE(res);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(!res.is_error());
|
||||
|
||||
REQUIRE((res.value() == 2.0));
|
||||
|
||||
REQUIRE_THROWS(res.err());
|
||||
|
||||
res = test::divide(4, 0);
|
||||
REQUIRE(!res);
|
||||
REQUIRE(!res.is_ok());
|
||||
REQUIRE(res.is_error());
|
||||
|
||||
REQUIRE((res.err() == test::math_error::DIVISION_BY_ZERO));
|
||||
|
||||
res = test::divide(4, 2)
|
||||
.and_then([](const auto &val) { return test::multiply(val, 5); })
|
||||
.and_then([](const auto &val) { return test::plus(val, 10); });
|
||||
|
||||
REQUIRE(res);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(!res.is_error());
|
||||
REQUIRE((res.value() == 20.0));
|
||||
|
||||
res = test::divide(4, 0)
|
||||
.and_then([](const auto &val) {
|
||||
return test::multiply(val, 5);
|
||||
});
|
||||
|
||||
REQUIRE(!res);
|
||||
REQUIRE(!res.is_ok());
|
||||
REQUIRE(res.is_error());
|
||||
REQUIRE((res.err() == test::math_error::DIVISION_BY_ZERO));
|
||||
|
||||
auto res2 = test::divide(4, 0)
|
||||
.or_else([](const auto &err) {
|
||||
return test::error_to_string(err);
|
||||
});
|
||||
|
||||
REQUIRE(!res2);
|
||||
REQUIRE(!res2.is_ok());
|
||||
REQUIRE(res2.is_error());
|
||||
REQUIRE((res2.err() == "division by zero error"));
|
||||
|
||||
res = test::divide(4, 2)
|
||||
.and_then([](const auto &val) { return test::multiply(val, 5); })
|
||||
.transform([](const auto &val) { return val + 10; });
|
||||
|
||||
REQUIRE(res);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(!res.is_error());
|
||||
REQUIRE((res.value() == 20.0));
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <matador/sql/table_definition.hpp>
|
||||
|
||||
#include <list>
|
||||
|
||||
using namespace matador::sql;
|
||||
|
||||
TEST_CASE("Create record", "[record]") {
|
||||
table_definition def({
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<std::string>("color", 255)
|
||||
});
|
||||
|
||||
REQUIRE(def.size() == 3);
|
||||
|
||||
std::list<std::string> expected_columns = {"id", "name", "color"};
|
||||
for(const auto &col : expected_columns) {
|
||||
REQUIRE(def.find(col) != def.end());
|
||||
}
|
||||
|
||||
for(const auto& col : def) {
|
||||
expected_columns.remove(col.name());
|
||||
}
|
||||
|
||||
REQUIRE(expected_columns.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("Append to record", "[record]") {
|
||||
table_definition rec;
|
||||
|
||||
rec.append(make_pk_column<unsigned long>("id"));
|
||||
rec.append<std::string>("name", 255);
|
||||
rec.append<std::string>("color", 63);
|
||||
|
||||
REQUIRE(rec.size() == 3);
|
||||
|
||||
std::list<std::string> expected_columns = {"id", "name", "color"};
|
||||
for(const auto &col : expected_columns) {
|
||||
REQUIRE(rec.find(col) != rec.end());
|
||||
}
|
||||
|
||||
for(const auto& col : rec) {
|
||||
expected_columns.remove(col.name());
|
||||
}
|
||||
|
||||
REQUIRE(expected_columns.empty());
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/sql/value_extractor.hpp"
|
||||
|
||||
#include "models/product.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
|
||||
TEST_CASE("Extract values object", "[value extractor]") {
|
||||
matador::test::product p;
|
||||
p.discontinued = false;
|
||||
p.reorder_level = 1;
|
||||
p.units_in_order = 2;
|
||||
p.units_in_stock = 100;
|
||||
p.unit_price = 49;
|
||||
p.quantity_per_unit = "pcs";
|
||||
p.category = make_entity<matador::test::category>();
|
||||
p.category->id = 7;
|
||||
p.supplier = make_entity<matador::test::supplier>();
|
||||
p.supplier->id = 13;
|
||||
p.product_name = "candle";
|
||||
|
||||
const std::vector<any_type> expected_values {
|
||||
std::string{"candle"}, 13UL, 7UL, std::string{"pcs"}, 49U, 100U, 2U, 1U, false
|
||||
};
|
||||
auto values = value_extractor::extract(p);
|
||||
|
||||
REQUIRE(values == expected_values);
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/sql/value.hpp"
|
||||
#include "matador/utils/types.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
|
||||
TEST_CASE("Test value class", "[value]") {
|
||||
value v;
|
||||
|
||||
REQUIRE(v.is_unknown());
|
||||
REQUIRE(v.type() == data_type_t::type_unknown);
|
||||
REQUIRE(v.size() == 0);
|
||||
|
||||
v = 7;
|
||||
|
||||
REQUIRE(v.is_integer());
|
||||
REQUIRE(v.type() == data_type_t::type_int);
|
||||
REQUIRE(v.size() == 0);
|
||||
REQUIRE(v.as<int>() == 7);
|
||||
REQUIRE(v.as<long>() == 7);
|
||||
|
||||
v = "test";
|
||||
|
||||
REQUIRE(v.is_varchar());
|
||||
REQUIRE(v.type() == data_type_t::type_char_pointer);
|
||||
REQUIRE(v.size() == 4);
|
||||
|
||||
v = std::string{"hello"};
|
||||
|
||||
REQUIRE(v.is_varchar());
|
||||
REQUIRE(v.type() == data_type_t::type_varchar);
|
||||
REQUIRE(v.size() == 5);
|
||||
|
||||
v = 4.5;
|
||||
|
||||
REQUIRE(v.is_floating_point());
|
||||
REQUIRE(v.type() == data_type_t::type_double);
|
||||
REQUIRE(v.size() == 0);
|
||||
|
||||
v = 6.7f;
|
||||
|
||||
REQUIRE(v.is_floating_point());
|
||||
REQUIRE(v.type() == data_type_t::type_float);
|
||||
REQUIRE(v.size() == 0);
|
||||
|
||||
v = std::string();
|
||||
|
||||
REQUIRE(v.is_string());
|
||||
REQUIRE(v.type() == data_type_t::type_text);
|
||||
REQUIRE(v.size() == 0);
|
||||
|
||||
v = true;
|
||||
|
||||
REQUIRE(v.is_bool());
|
||||
REQUIRE(v.type() == data_type_t::type_bool);
|
||||
REQUIRE(v.size() == 0);
|
||||
|
||||
v = nullptr;
|
||||
|
||||
REQUIRE(v.is_null());
|
||||
REQUIRE(v.type() == data_type_t::type_null);
|
||||
REQUIRE(v.size() == 0);
|
||||
|
||||
v = matador::utils::blob{ 1, 2, 3, 4 };
|
||||
|
||||
REQUIRE(v.is_blob());
|
||||
REQUIRE(v.type() == data_type_t::type_blob);
|
||||
REQUIRE(v.size() == 4);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "ColorEnumTraits.hpp"
|
||||
|
||||
#include "matador/utils/attribute_writer.hpp"
|
||||
#include "matador/utils/attribute_reader.hpp"
|
||||
|
||||
namespace matador::utils {
|
||||
|
||||
void data_type_traits<test::Color, void>::read_value(attribute_reader &reader, const char *id, size_t index,
|
||||
test::Color &value)
|
||||
{
|
||||
std::string enum_string;
|
||||
reader.read_value(id, index, enum_string, 64);
|
||||
if (const auto enum_opt = color_enum.to_enum(enum_string)) {
|
||||
value = enum_opt.value();
|
||||
}
|
||||
}
|
||||
|
||||
void data_type_traits<test::Color, void>::bind_value(attribute_writer &binder, const size_t index, const test::Color &value)
|
||||
{
|
||||
binder.write_value(index, color_enum.to_string(value));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef MATADOR_COLOR_ENUM_TRAITS_HPP
|
||||
#define MATADOR_COLOR_ENUM_TRAITS_HPP
|
||||
|
||||
#include "matador/utils/default_type_traits.hpp"
|
||||
|
||||
#include "matador/utils/enum_mapper.hpp"
|
||||
|
||||
#include "models/location.hpp"
|
||||
|
||||
static const matador::utils::enum_mapper<matador::test::Color> color_enum({
|
||||
{matador::test::Color::Green, "green"},
|
||||
{matador::test::Color::Red, "red"},
|
||||
{matador::test::Color::Blue, "blue"},
|
||||
{matador::test::Color::Yellow, "yellow"},
|
||||
{matador::test::Color::Black, "black"},
|
||||
{matador::test::Color::White, "white"},
|
||||
{matador::test::Color::Brown, "brown"}
|
||||
});
|
||||
|
||||
template<>
|
||||
struct matador::utils::data_type_traits<matador::test::Color, void>
|
||||
{
|
||||
static basic_type type(const std::size_t size) { return data_type_traits<std::string>::type(size); }
|
||||
static void read_value(attribute_reader &reader, const char *id, size_t index, test::Color &value);
|
||||
static void bind_value(attribute_writer &binder, size_t index, const test::Color &value);
|
||||
};
|
||||
|
||||
#endif //MATADOR_COLOR_ENUM_TRAITS_HPP
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "catch2/catch_test_macros.hpp"
|
||||
|
||||
#include "matador/sql/connection.hpp"
|
||||
|
||||
#include "connection.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
|
||||
TEST_CASE("Create connection test", "[connection]") {
|
||||
const connection c(matador::test::connection::dns);
|
||||
REQUIRE(!c.is_open());
|
||||
|
||||
auto result = c.open();
|
||||
REQUIRE(result.is_ok());
|
||||
REQUIRE(c.is_open());
|
||||
|
||||
result = c.close();
|
||||
REQUIRE(result.is_ok());
|
||||
REQUIRE(!c.is_open());
|
||||
}
|
||||
@@ -0,0 +1,551 @@
|
||||
#include "catch2/catch_test_macros.hpp"
|
||||
#include "catch2/matchers/catch_matchers_string.hpp"
|
||||
|
||||
#include "matador/sql/column_definition.hpp"
|
||||
#include "matador/sql/connection.hpp"
|
||||
#include "matador/query/condition.hpp"
|
||||
#include "matador/query/query.hpp"
|
||||
|
||||
#include "matador/sql/schema.hpp"
|
||||
|
||||
#include "matador/utils/basic_types.hpp"
|
||||
#include "matador/utils/string.hpp"
|
||||
|
||||
#include "models/types.hpp"
|
||||
|
||||
#include "QueryFixture.hpp"
|
||||
|
||||
using namespace matador::test;
|
||||
using namespace matador::sql;
|
||||
using namespace matador::query;
|
||||
|
||||
TEST_CASE_METHOD( QueryFixture, "Insert and select basic datatypes", "[query][datatypes]" ) {
|
||||
schema.attach<types>("types");
|
||||
auto res = query::create()
|
||||
.table<types>("types", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("types");
|
||||
|
||||
float float_value = 2.44557f;
|
||||
double double_value = 11111.23433345;
|
||||
char cval = 'c';
|
||||
short sval = (std::numeric_limits<short>::min)();
|
||||
int ival = (std::numeric_limits<int>::min)();
|
||||
long lval = (std::numeric_limits<long>::min)();
|
||||
long long llval = (std::numeric_limits<long long>::max)();
|
||||
unsigned char ucval = (std::numeric_limits<unsigned char>::max)();
|
||||
unsigned short usval = (std::numeric_limits<unsigned short>::max)();
|
||||
unsigned int uival = (std::numeric_limits<unsigned int>::max)();
|
||||
unsigned long ulval = (std::numeric_limits<unsigned long>::max)();
|
||||
unsigned long long ullval = (std::numeric_limits<unsigned long long>::max)();
|
||||
if (db.type() == "sqlite" || db.type() == "postgres") {
|
||||
ulval = (std::numeric_limits<long>::max)();
|
||||
ullval = (std::numeric_limits<long long>::max)();
|
||||
}
|
||||
bool bval = true;
|
||||
const char *cstr("Armer schwarzer Kater");
|
||||
std::string varcharval("hallo welt");
|
||||
std::string strval = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam "
|
||||
"nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, "
|
||||
"sed diam voluptua. At vero eos et accusam et justo duo dolores et ea "
|
||||
"rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. "
|
||||
"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy "
|
||||
"eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. "
|
||||
"At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd "
|
||||
"gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.";
|
||||
matador::date date_val(15, 3, 2015);
|
||||
auto time_val = matador::time(2015, 3, 15, 13, 56, 23, 123);
|
||||
matador::utils::blob blob_val {1,2,3,4,5,6,7,8};
|
||||
|
||||
types t {
|
||||
1,
|
||||
cval, sval, ival, lval, llval,
|
||||
ucval, usval, uival, ulval, ullval,
|
||||
float_value, double_value,
|
||||
bval,
|
||||
"Armer schwarzer Kater",
|
||||
strval, varcharval,
|
||||
date_val, time_val,
|
||||
blob_val
|
||||
};
|
||||
|
||||
res = query::insert()
|
||||
.into("types", matador::sql::column_generator::generate<types>(schema, true))
|
||||
.values(t)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto result = query::select<types>(schema)
|
||||
.from("types")
|
||||
.fetch_one<types>(db);
|
||||
REQUIRE(result.is_ok());
|
||||
REQUIRE(*result != nullptr);
|
||||
|
||||
REQUIRE((*result)->id_ == 1);
|
||||
REQUIRE((*result)->char_ == cval);
|
||||
REQUIRE((*result)->short_ == sval);
|
||||
REQUIRE((*result)->int_ == ival);
|
||||
REQUIRE((*result)->long_ == lval);
|
||||
REQUIRE((*result)->long64_ == llval);
|
||||
REQUIRE((*result)->unsigned_char_ == ucval);
|
||||
REQUIRE((*result)->unsigned_short_ == usval);
|
||||
REQUIRE((*result)->unsigned_int_ == uival);
|
||||
REQUIRE((*result)->unsigned_long_ == ulval);
|
||||
REQUIRE((*result)->unsigned_long64_ == ullval);
|
||||
REQUIRE((*result)->float_ == float_value);
|
||||
REQUIRE((*result)->double_ == double_value);
|
||||
REQUIRE(strcmp((*result)->cstr_, cstr) == 0);
|
||||
REQUIRE((*result)->bool_ == bval);
|
||||
REQUIRE((*result)->varchar_ == varcharval);
|
||||
REQUIRE((*result)->string_ == strval);
|
||||
REQUIRE((*result)->date_ == date_val);
|
||||
REQUIRE((*result)->time_ == time_val);
|
||||
REQUIRE((*result)->binary_ == blob_val);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD( QueryFixture, "Test quoted identifier", "[query][quotes][identifier]" ) {
|
||||
using namespace matador::sql;
|
||||
|
||||
auto res = query::create()
|
||||
.table("quotes", {
|
||||
make_column<std::string>("from", 255),
|
||||
make_column<std::string>("to", 255)
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("quotes");
|
||||
|
||||
// check table description
|
||||
std::vector<std::string> column_names = { "from", "to"};
|
||||
std::vector<matador::data_type> types = {matador::data_type::type_varchar, matador::data_type::type_varchar};
|
||||
const auto columns = db.describe("quotes");
|
||||
REQUIRE(columns.is_ok());
|
||||
|
||||
for (const auto &col : *columns) {
|
||||
REQUIRE(col.name() == column_names[col.index()]);
|
||||
REQUIRE(col.type() == types[col.index()]);
|
||||
}
|
||||
|
||||
res = query::insert()
|
||||
.into("quotes", {"from", "to"})
|
||||
.values({"Berlin", "London"})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto row = query::select({"from", "to"})
|
||||
.from("quotes")
|
||||
.fetch_one(db);
|
||||
REQUIRE(row.is_ok());
|
||||
|
||||
REQUIRE(row->has_value());
|
||||
REQUIRE(row.value()->at("from").as<std::string>() == "Berlin");
|
||||
REQUIRE(row.value()->at("to").as<std::string>() == "London");
|
||||
|
||||
res = query::update("quotes")
|
||||
.set({{"from", "Hamburg"}, {"to", "New York"}})
|
||||
.where("from"_col == "Berlin")
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
row = query::select({"from", "to"})
|
||||
.from("quotes")
|
||||
.fetch_one(db);
|
||||
REQUIRE(row.is_ok());
|
||||
|
||||
REQUIRE(row->has_value());
|
||||
REQUIRE(row.value()->at("from").as<std::string>() == "Hamburg");
|
||||
REQUIRE(row.value()->at("to").as<std::string>() == "New York");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD( QueryFixture, "Test quoted column names", "[query][quotes][column]" ) {
|
||||
using namespace matador::sql;
|
||||
|
||||
const auto start_quote = db.dialect().token_at(matador::sql::dialect_token::START_QUOTE);
|
||||
const auto end_quote = db.dialect().token_at(matador::sql::dialect_token::END_QUOTE);
|
||||
|
||||
const std::string column_name = "name_with_" + start_quote + "open_close_quotes" + end_quote + "_in_backend_ctx";
|
||||
|
||||
std::vector<std::string> column_names = {
|
||||
"normal_name",
|
||||
column_name,
|
||||
"name_with_'string'_\"literal\"_quotes",
|
||||
"name_with_`identifier_quotes`_in_backend_ctx",
|
||||
"from"
|
||||
};
|
||||
tables_to_drop.emplace("quotes");
|
||||
|
||||
for (const auto &name : column_names) {
|
||||
auto res = query::create()
|
||||
.table("quotes", {
|
||||
make_column<std::string>(name, 255),
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
|
||||
const auto columns = db.describe("quotes");
|
||||
REQUIRE(columns.is_ok());
|
||||
|
||||
for (const auto &col : *columns) {
|
||||
REQUIRE(col.name() == name);
|
||||
REQUIRE(col.type() == matador::data_type::type_varchar);
|
||||
}
|
||||
|
||||
res = query::drop()
|
||||
.table("quotes")
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test quoted literals", "[query][quotes][literals]") {
|
||||
using namespace matador::sql;
|
||||
|
||||
auto res = query::create()
|
||||
.table("escapes", {
|
||||
make_column<std::string>("name", 255),
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("escapes");
|
||||
|
||||
res = query::insert()
|
||||
.into("escapes", {"name"})
|
||||
.values({"text"})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto row = query::select({"name"})
|
||||
.from("escapes")
|
||||
.fetch_one(db);
|
||||
REQUIRE(row.is_ok());
|
||||
|
||||
REQUIRE(row->has_value());
|
||||
REQUIRE(row.value()->at("name").as<std::string>() == "text");
|
||||
|
||||
res = query::update("escapes")
|
||||
.set({{"name", "text'd"}})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
row = query::select({"name"})
|
||||
.from("escapes")
|
||||
.fetch_one(db);
|
||||
REQUIRE(row.is_ok());
|
||||
|
||||
REQUIRE(row->has_value());
|
||||
REQUIRE(row.value()->at("name").as<std::string>() == "text'd");
|
||||
|
||||
res = query::update("escapes")
|
||||
.set({{"name", "text\nhello\tworld"}})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
row = query::select({"name"})
|
||||
.from("escapes")
|
||||
.fetch_one(db);
|
||||
REQUIRE(row.is_ok());
|
||||
|
||||
REQUIRE(row->has_value());
|
||||
REQUIRE(row.value()->at("name").as<std::string>() == "text\nhello\tworld");
|
||||
|
||||
res = query::update("escapes")
|
||||
.set({{"name", "text \"text\""}})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
row = query::select({"name"})
|
||||
.from("escapes")
|
||||
.fetch_one(db);
|
||||
REQUIRE(row.is_ok());
|
||||
|
||||
REQUIRE(row->has_value());
|
||||
REQUIRE(row.value()->at("name").as<std::string>() == "text \"text\"");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test describe table", "[query][describe][table]") {
|
||||
using namespace matador::sql;
|
||||
|
||||
schema.attach<types>("types");
|
||||
const auto res = query::create()
|
||||
.table<types>("types", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("types");
|
||||
|
||||
const auto columns = db.describe("types");
|
||||
REQUIRE(columns.is_ok());
|
||||
|
||||
std::vector<std::string> column_names = { "id",
|
||||
"val_char", "val_float", "val_double", "val_short",
|
||||
"val_int", "val_long", "val_long_long", "val_unsigned_char",
|
||||
"val_unsigned_short", "val_unsigned_int", "val_unsigned_long", "val_unsigned_long_long",
|
||||
"val_bool", "val_cstr", "val_string", "val_varchar", "val_date", "val_time",
|
||||
"val_binary"};
|
||||
const std::vector<std::function<bool (const column_definition&)>> type_check = {
|
||||
[](const column_definition &cf) { return cf.is_integer(); },
|
||||
[](const column_definition &cf) { return cf.is_integer(); },
|
||||
[](const column_definition &cf) { return cf.is_floating_point(); },
|
||||
[](const column_definition &cf) { return cf.is_floating_point(); },
|
||||
[](const column_definition &cf) { return cf.is_integer(); },
|
||||
[](const column_definition &cf) { return cf.is_integer(); },
|
||||
[](const column_definition &cf) { return cf.is_integer(); },
|
||||
[](const column_definition &cf) { return cf.is_integer(); },
|
||||
[](const column_definition &cf) { return cf.is_integer(); },
|
||||
[](const column_definition &cf) { return cf.is_integer(); },
|
||||
[](const column_definition &cf) { return cf.is_integer(); },
|
||||
[](const column_definition &cf) { return cf.is_integer(); },
|
||||
[](const column_definition &cf) { return cf.is_integer(); },
|
||||
[](const column_definition &cf) { return cf.is_bool(); },
|
||||
[](const column_definition &cf) { return cf.is_varchar(); },
|
||||
[](const column_definition &cf) { return cf.is_string(); },
|
||||
[](const column_definition &cf) { return cf.is_varchar(); },
|
||||
[](const column_definition &cf) { return cf.is_date(); },
|
||||
[](const column_definition &cf) { return cf.is_time(); },
|
||||
[](const column_definition &cf) { return cf.is_blob(); }
|
||||
};
|
||||
|
||||
const auto &cols = columns.value();
|
||||
for (const auto &col : cols) {
|
||||
REQUIRE(col.name() == column_names[col.index()]);
|
||||
REQUIRE(type_check[col.index()](col));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test unknown table", "[query][table]") {
|
||||
const auto result = query::select({"name"})
|
||||
.from("person")
|
||||
.fetch_all(db);
|
||||
|
||||
REQUIRE(result.is_error());
|
||||
}
|
||||
|
||||
namespace matador::test::temporary {
|
||||
struct pk {
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
matador::access::primary_key(op, "id", id);
|
||||
matador::access::attribute(op, "name", name, 255);
|
||||
}
|
||||
|
||||
unsigned long id{};
|
||||
std::string name;
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test primary key", "[query][primary key]") {
|
||||
using namespace matador::test::temporary;
|
||||
using namespace matador::sql;
|
||||
|
||||
schema.attach<pk>("pk");
|
||||
auto res = query::create()
|
||||
.table<pk>("pk", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("pk");
|
||||
|
||||
pk pk1{ 7, "george" };
|
||||
|
||||
res = query::insert()
|
||||
.into("pk", column_generator::generate<pk>(schema))
|
||||
.values(pk1)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto row = query::select<pk>(schema)
|
||||
.from("pk")
|
||||
.fetch_one<pk>(db);
|
||||
REQUIRE(row.is_ok());
|
||||
REQUIRE(*row != nullptr);
|
||||
REQUIRE(row.value()->id > 0);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test primary key prepared", "[query][primary key][prepared]") {
|
||||
using namespace matador::test::temporary;
|
||||
using namespace matador::sql;
|
||||
|
||||
schema.attach<pk>("pk");
|
||||
auto res = query::create()
|
||||
.table<pk>("pk", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("pk");
|
||||
|
||||
pk pk1{ 7, "george" };
|
||||
|
||||
auto stmt = query::insert()
|
||||
.into("pk", column_generator::generate<pk>(schema))
|
||||
.values<pk>()
|
||||
.prepare(db);
|
||||
|
||||
res = stmt.bind(pk1)
|
||||
.execute();
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
stmt = query::select<pk>(schema)
|
||||
.from("pk")
|
||||
.prepare(db);
|
||||
|
||||
auto row = stmt.fetch_one<pk>();
|
||||
REQUIRE(row.is_ok());
|
||||
REQUIRE(*row != nullptr);
|
||||
REQUIRE(row.value()->id > 0);
|
||||
REQUIRE(row.value()->name == "george");
|
||||
}
|
||||
|
||||
namespace matador::test::temporary {
|
||||
struct appointment
|
||||
{
|
||||
unsigned long id{};
|
||||
std::string name;
|
||||
matador::time time_point{};
|
||||
matador::date date_point{};
|
||||
|
||||
template < class Operator >
|
||||
void process(Operator &op)
|
||||
{
|
||||
matador::access::primary_key(op, "id", id);
|
||||
matador::access::attribute(op, "name", name, 255);
|
||||
matador::access::attribute(op, "time_point", time_point);
|
||||
matador::access::attribute(op, "date_point", date_point);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
TEST_CASE_METHOD(QueryFixture, "Test select time and date", "[query][select][time]") {
|
||||
using namespace matador::test::temporary;
|
||||
using namespace matador::sql;
|
||||
schema.attach<appointment>("appointment");
|
||||
auto res = query::create()
|
||||
.table<appointment>("appointment", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("appointment");
|
||||
|
||||
auto dinner = appointment{ 1, "dinner" };
|
||||
auto time_str = matador::utils::to_string(dinner.time_point);
|
||||
auto date_str = matador::utils::to_string(dinner.date_point);
|
||||
|
||||
res = query::insert()
|
||||
.into("appointment", column_generator::generate<appointment>(schema))
|
||||
.values(dinner)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto row = query::select<appointment>(schema)
|
||||
.from("appointment")
|
||||
.fetch_one<appointment>(db);
|
||||
REQUIRE(row.is_ok());
|
||||
|
||||
REQUIRE(*row != nullptr);
|
||||
REQUIRE(matador::utils::to_string(row.value()->time_point) == time_str);
|
||||
REQUIRE(matador::utils::to_string(row.value()->date_point) == date_str);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test null column", "[query][select][null]") {
|
||||
using namespace matador::sql;
|
||||
|
||||
auto res = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("first_name", 255, null_option::NULLABLE),
|
||||
make_column<std::string>("last_name", 255, null_option::NULLABLE)
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
res = query::insert()
|
||||
.into("person", {"id", "first_name"})
|
||||
.values({1, "george"})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
res = query::insert()
|
||||
.into("person", {"id", "last_name"})
|
||||
.values({2, "clooney"})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto result = query::select({"id", "first_name", "last_name"})
|
||||
.from("person")
|
||||
.fetch_all(db);
|
||||
REQUIRE(result.is_ok());
|
||||
|
||||
std::vector<std::string> expected_first_names{"george", ""};
|
||||
std::vector<std::string> expected_last_names{"", "clooney"};
|
||||
size_t index{0};
|
||||
for (const auto& row : *result) {
|
||||
auto first_name = row.at<std::string>("first_name");
|
||||
auto last_name = row.at<std::string>("last_name");
|
||||
std::cout << "first name " << first_name.value() << " last name " << last_name.value() << std::endl;
|
||||
REQUIRE(first_name == expected_first_names[index]);
|
||||
REQUIRE(last_name == expected_last_names[index++]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test null column prepared", "[query][select][null][prepared]") {
|
||||
using namespace matador::sql;
|
||||
|
||||
auto res = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("first_name", 255, null_option::NULLABLE),
|
||||
make_column<std::string>("last_name", 255, null_option::NULLABLE)
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
res = query::insert()
|
||||
.into("person", {"id", "first_name"})
|
||||
.values({1, "george"})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
res = query::insert()
|
||||
.into("person", {"id", "last_name"})
|
||||
.values({2, "clooney"})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto result = query::select({"id", "first_name", "last_name"})
|
||||
.from("person")
|
||||
.fetch_all(db);
|
||||
REQUIRE(result.is_ok());
|
||||
|
||||
std::vector<std::string> expected_first_names{"george", ""};
|
||||
std::vector<std::string> expected_last_names{"", "clooney"};
|
||||
size_t index{0};
|
||||
for (const auto& row : *result) {
|
||||
auto first_name = row.at("first_name").as<std::string>();
|
||||
auto last_name = row.at("last_name").as<std::string>();
|
||||
REQUIRE(first_name == expected_first_names[index]);
|
||||
REQUIRE(last_name == expected_last_names[index++]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "QueryFixture.hpp"
|
||||
|
||||
#include "matador/sql/query.hpp"
|
||||
|
||||
#include "catch2/catch_test_macros.hpp"
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
QueryFixture::QueryFixture()
|
||||
: db(connection::dns)
|
||||
, schema(db.dialect().default_schema_name())
|
||||
{
|
||||
db.open();
|
||||
}
|
||||
|
||||
QueryFixture::~QueryFixture() {
|
||||
while (!tables_to_drop.empty()) {
|
||||
drop_table_if_exists(tables_to_drop.top());
|
||||
tables_to_drop.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void QueryFixture::check_table_exists(const std::string &table_name) const
|
||||
{
|
||||
auto result = db.exists(table_name);
|
||||
REQUIRE(result.is_ok());
|
||||
REQUIRE(*result);
|
||||
}
|
||||
|
||||
void QueryFixture::check_table_not_exists(const std::string &table_name) const
|
||||
{
|
||||
auto result = db.exists(table_name);
|
||||
REQUIRE(result.is_ok());
|
||||
REQUIRE(!*result);
|
||||
}
|
||||
|
||||
void QueryFixture::drop_table_if_exists(const std::string &table_name) const {
|
||||
const auto result = db.exists(table_name).and_then([&table_name, this](bool exists) {
|
||||
if (exists) {
|
||||
if (sql::query::drop()
|
||||
.table(table_name)
|
||||
.execute(db).is_ok()) {
|
||||
this->check_table_not_exists(table_name);
|
||||
} else {
|
||||
FAIL("Failed to drop table");
|
||||
}
|
||||
}
|
||||
return utils::ok(true);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef MATADOR_QUERY_FIXTURE_HPP
|
||||
#define MATADOR_QUERY_FIXTURE_HPP
|
||||
|
||||
#include "matador/sql/connection.hpp"
|
||||
#include "matador/sql/schema.hpp"
|
||||
|
||||
#include "connection.hpp"
|
||||
|
||||
#include <stack>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
class QueryFixture {
|
||||
public:
|
||||
QueryFixture();
|
||||
~QueryFixture();
|
||||
|
||||
void check_table_exists(const std::string &table_name) const;
|
||||
void check_table_not_exists(const std::string &table_name) const;
|
||||
|
||||
protected:
|
||||
sql::connection db;
|
||||
sql::schema schema;
|
||||
std::stack <std::string> tables_to_drop;
|
||||
|
||||
private:
|
||||
void drop_table_if_exists(const std::string &table_name) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //MATADOR_QUERY_FIXTURE_HPP
|
||||
@@ -0,0 +1,772 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/sql/column.hpp"
|
||||
#include "matador/sql/condition.hpp"
|
||||
#include "matador/sql/connection.hpp"
|
||||
#include "matador/sql/query.hpp"
|
||||
|
||||
#include "matador/utils/types.hpp"
|
||||
#include "matador/utils/string.hpp"
|
||||
|
||||
#include "QueryFixture.hpp"
|
||||
|
||||
#include <list>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace matador::sql;
|
||||
using namespace matador::test;
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test all data types for record", "[query][record][data types]") {
|
||||
check_table_not_exists("types");
|
||||
|
||||
auto res = query::create()
|
||||
.table("types", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<char>("val_char"),
|
||||
make_column<short>("val_short"),
|
||||
make_column<int>("val_int"),
|
||||
make_column<long>("val_long"),
|
||||
make_column<long long>("val_long_long"),
|
||||
make_column<unsigned char>("val_uchar"),
|
||||
make_column<unsigned short>("val_ushort"),
|
||||
make_column<unsigned int>("val_uint"),
|
||||
make_column<unsigned long>("val_ulong"),
|
||||
make_column<unsigned long long>("val_ulong_long"),
|
||||
make_column<bool>("val_bool"),
|
||||
make_column<float>("val_float"),
|
||||
make_column<double>("val_double"),
|
||||
make_column<std::string>("val_string"),
|
||||
make_column<std::string>("val_varchar", 63),
|
||||
make_column<matador::date>("val_date"),
|
||||
make_column<matador::time>("val_time"),
|
||||
make_column<matador::utils::blob>("val_blob"),
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 0);
|
||||
|
||||
check_table_exists("types");
|
||||
tables_to_drop.emplace("types");
|
||||
|
||||
auto cols = std::vector<std::string>{"id",
|
||||
"val_char", "val_short", "val_int", "val_long", "val_long_long",
|
||||
"val_uchar", "val_ushort", "val_uint", "val_ulong", "val_ulong_long",
|
||||
"val_bool",
|
||||
"val_float", "val_double",
|
||||
"val_string", "val_varchar",
|
||||
"val_date", "val_time", "val_blob"};
|
||||
|
||||
const auto fields = db.describe("types");
|
||||
REQUIRE(fields.is_ok());
|
||||
for (const auto &fld : *fields) {
|
||||
REQUIRE(std::find(cols.begin(), cols.end(), fld.name()) != cols.end());
|
||||
}
|
||||
|
||||
unsigned long id{1};
|
||||
char c{-11};
|
||||
short s{-256};
|
||||
int i{-123456};
|
||||
long l{-9876543};
|
||||
long long ll{-987654321};
|
||||
unsigned char uc{13};
|
||||
unsigned short us{1024};
|
||||
unsigned int ui{654321};
|
||||
unsigned long ul{12345678};
|
||||
unsigned long long ull{1234567890};
|
||||
bool b{true};
|
||||
float f{3.1415f};
|
||||
double d{2.71828};
|
||||
std::string str{"long text"};
|
||||
std::string varchar{"good day"};
|
||||
auto md{matador::date()};
|
||||
auto mt{matador::time::now()};
|
||||
matador::utils::blob bin{0x01,0x02,0x03,0x04};
|
||||
|
||||
res = query::insert()
|
||||
.into("types", cols)
|
||||
.values({id, c, s, i, l, ll, uc, us, ui, ul, ull, b, f, d, str, varchar, md, mt, bin})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 1);
|
||||
|
||||
auto row = query::select(cols)
|
||||
.from("types")
|
||||
.fetch_one(db);
|
||||
|
||||
REQUIRE(row.is_ok());
|
||||
REQUIRE(row.value().has_value());
|
||||
|
||||
REQUIRE(id == (*row)->at<unsigned long>("id"));
|
||||
REQUIRE(c == (*row)->at<char>("val_char"));
|
||||
REQUIRE(s == (*row)->at<short>("val_short"));
|
||||
REQUIRE(i == (*row)->at<int>("val_int"));
|
||||
REQUIRE(l == (*row)->at<long>("val_long"));
|
||||
REQUIRE(ll == (*row)->at<long long>("val_long_long"));
|
||||
REQUIRE(uc == (*row)->at<unsigned char>("val_uchar"));
|
||||
REQUIRE(us == (*row)->at<unsigned short>("val_ushort"));
|
||||
REQUIRE(ui == (*row)->at<unsigned int>("val_uint"));
|
||||
REQUIRE(ul == (*row)->at<unsigned long>("val_ulong"));
|
||||
REQUIRE(ull == (*row)->at<unsigned long long>("val_ulong_long"));
|
||||
REQUIRE((*row)->at<bool>("val_bool"));
|
||||
REQUIRE(f == (*row)->at<float>("val_float"));
|
||||
REQUIRE(d == (*row)->at<double>("val_double"));
|
||||
REQUIRE(str == (*row)->at<std::string>("val_string"));
|
||||
REQUIRE(varchar == (*row)->at<std::string>("val_varchar"));
|
||||
REQUIRE(md == (*row)->at<matador::date>("val_date"));
|
||||
REQUIRE(mt == (*row)->at<matador::time>("val_time"));
|
||||
REQUIRE(bin == (*row)->at<matador::utils::blob>("val_blob"));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Create and drop table statement", "[query][record]")
|
||||
{
|
||||
check_table_not_exists("person");
|
||||
auto res = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age")
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 0);
|
||||
|
||||
check_table_exists("person");
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
res = query::drop()
|
||||
.table("person")
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 0);
|
||||
|
||||
check_table_not_exists("person");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Create and drop table statement with foreign key", "[query][record]")
|
||||
{
|
||||
auto res = query::create()
|
||||
.table("airplane", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("brand", 255),
|
||||
make_column<std::string>("model", 255),
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 0);
|
||||
|
||||
check_table_exists("airplane");
|
||||
tables_to_drop.emplace("airplane");
|
||||
|
||||
res = query::create()
|
||||
.table("flight", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_fk_column<unsigned long>("airplane_id", "airplane", "id"),
|
||||
make_column<std::string>("pilot_name", 255),
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 0);
|
||||
|
||||
check_table_exists("flight");
|
||||
tables_to_drop.emplace("flight");
|
||||
|
||||
res = query::drop()
|
||||
.table("flight")
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 0);
|
||||
|
||||
check_table_not_exists("flight");
|
||||
|
||||
res = query::drop()
|
||||
.table("airplane")
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 0);
|
||||
|
||||
check_table_not_exists("airplane");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Execute insert record statement", "[query][record]")
|
||||
{
|
||||
auto res = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age")
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 0);
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
res = query::insert()
|
||||
.into("person", {"id", "name", "age"})
|
||||
.values({7, "george", 45})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 1);
|
||||
|
||||
auto result = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.fetch_all(db);
|
||||
|
||||
REQUIRE(result.is_ok());
|
||||
for (const auto &i: *result) {
|
||||
REQUIRE(i.size() == 3);
|
||||
REQUIRE(i.at(0).name() == "id");
|
||||
REQUIRE(i.at(0).is_integer());
|
||||
REQUIRE(i.at(0).template as<long long>() == 7);
|
||||
REQUIRE(i.at(1).name() == "name");
|
||||
REQUIRE(i.at(1).is_varchar());
|
||||
REQUIRE(i.at(1).template as<std::string>() == "george");
|
||||
REQUIRE(i.at(2).name() == "age");
|
||||
REQUIRE(i.at(2).is_integer());
|
||||
REQUIRE(i.at(2).template as<int>() == 45);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Execute insert record statement with foreign key", "[query][record]")
|
||||
{
|
||||
auto res = query::create()
|
||||
.table("airplane", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("brand", 255),
|
||||
make_column<std::string>("model", 255),
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 0);
|
||||
tables_to_drop.emplace("airplane");
|
||||
|
||||
res = query::create()
|
||||
.table("flight", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_fk_column<unsigned long>("airplane_id", "airplane", "id"),
|
||||
make_column<std::string>("pilot_name", 255),
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 0);
|
||||
tables_to_drop.emplace("flight");
|
||||
|
||||
std::vector<std::vector<matador::utils::any_type>> values_list{
|
||||
{1, "Airbus", "A380"},
|
||||
{2, "Boeing", "707"},
|
||||
{3, "Boeing", "747"}
|
||||
};
|
||||
|
||||
for(auto &&values : values_list) {
|
||||
res = query::insert()
|
||||
.into("airplane", {"id", "brand", "model"})
|
||||
.values(std::move(values))
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 1);
|
||||
}
|
||||
|
||||
auto count = query::select({count_all()})
|
||||
.from("airplane")
|
||||
.fetch_value<int>(db);
|
||||
REQUIRE(count.is_ok());
|
||||
REQUIRE(count->has_value());
|
||||
REQUIRE(*count == 3);
|
||||
|
||||
res = query::insert()
|
||||
.into("flight", {"id", "airplane_id", "pilot_name"})
|
||||
.values({4, 1, "George"})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Execute update record statement", "[query][record]")
|
||||
{
|
||||
auto res = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age")
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 0);
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
res = query::insert()
|
||||
.into("person", {"id", "name", "age"})
|
||||
.values({7, "george", 45})
|
||||
.execute(db);
|
||||
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 1);
|
||||
|
||||
res = query::update("person")
|
||||
.set({{"id", 7},
|
||||
{"name", "jane"},
|
||||
{"age", 35}})
|
||||
.where("id"_col == 7)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 1);
|
||||
|
||||
auto result = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.fetch_all(db);
|
||||
|
||||
REQUIRE(result.is_ok());
|
||||
for (const auto &i: *result) {
|
||||
REQUIRE(i.size() == 3);
|
||||
REQUIRE(i.at(0).name() == "id");
|
||||
REQUIRE(i.at(0).is_integer());
|
||||
REQUIRE(i.at(0).as<long long>() == 7);
|
||||
REQUIRE(i.at(1).name() == "name");
|
||||
REQUIRE(i.at(1).is_varchar());
|
||||
REQUIRE(i.at(1).as<std::string>() == "jane");
|
||||
REQUIRE(i.at(2).name() == "age");
|
||||
REQUIRE(i.at(2).is_integer());
|
||||
REQUIRE(i.at(2).as<int>() == 35);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Execute select statement", "[query][record]")
|
||||
{
|
||||
auto res = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age")
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 0);
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
std::vector<std::vector<matador::utils::any_type>> values_list{
|
||||
{1, "george", 45},
|
||||
{2, "jane", 32},
|
||||
{3, "michael", 67},
|
||||
{4, "bob", 13}
|
||||
};
|
||||
|
||||
for(auto &&values : values_list) {
|
||||
res = query::insert()
|
||||
.into("person", {"id", "name", "age"})
|
||||
.values(std::move(values))
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 1);
|
||||
}
|
||||
|
||||
auto result = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.fetch_all(db);
|
||||
REQUIRE(result.is_ok());
|
||||
|
||||
std::list<std::string> expected_names{"george", "jane", "michael", "bob"};
|
||||
for (const auto &p: *result) {
|
||||
REQUIRE(p.at(1).str() == expected_names.front());
|
||||
expected_names.pop_front();
|
||||
}
|
||||
REQUIRE(expected_names.empty());
|
||||
|
||||
auto rec = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.fetch_one(db);
|
||||
REQUIRE(rec.is_ok());
|
||||
REQUIRE(rec->has_value());
|
||||
REQUIRE((*rec)->at(1).str() == "george");
|
||||
|
||||
auto name = query::select({"name"})
|
||||
.from("person")
|
||||
.fetch_value<std::string>(db);
|
||||
|
||||
REQUIRE(name.is_ok());
|
||||
REQUIRE(*name == "george");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Execute select statement with order by", "[query][record]")
|
||||
{
|
||||
auto res = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age")
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 0);
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
std::vector<std::vector<matador::utils::any_type>> values_list{
|
||||
{1, "george", 45},
|
||||
{2, "jane", 32},
|
||||
{3, "michael", 67},
|
||||
{4, "bob", 13}
|
||||
};
|
||||
|
||||
for(auto &&values : values_list) {
|
||||
res = query::insert()
|
||||
.into("person", {"id", "name", "age"})
|
||||
.values(std::move(values))
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 1);
|
||||
}
|
||||
|
||||
auto result = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.order_by("name").asc()
|
||||
.fetch_all(db);
|
||||
REQUIRE(result.is_ok());
|
||||
|
||||
std::list<std::string> expected_names{"bob", "george", "jane", "michael"};
|
||||
for (const auto &p: *result) {
|
||||
REQUIRE(p.at(1).str() == expected_names.front());
|
||||
expected_names.pop_front();
|
||||
}
|
||||
REQUIRE(expected_names.empty());
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Execute select statement with group by and order by", "[query][record]")
|
||||
{
|
||||
auto res = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age")
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
std::vector<std::vector<matador::utils::any_type>> values_list{
|
||||
{1, "george", 45},
|
||||
{2, "jane", 45},
|
||||
{3, "joe", 45},
|
||||
{4, "michael", 13},
|
||||
{5, "bob", 13},
|
||||
{6, "charlie", 67}
|
||||
};
|
||||
|
||||
for(auto &&values : values_list) {
|
||||
res = query::insert()
|
||||
.into("person", {"id", "name", "age"})
|
||||
.values(std::move(values))
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(res.value() == 1);
|
||||
}
|
||||
|
||||
auto result = query::select({count("age").as("age_count"), "age"})
|
||||
.from("person")
|
||||
.group_by("age")
|
||||
.order_by("age_count").desc()
|
||||
.fetch_all(db);
|
||||
REQUIRE(result.is_ok());
|
||||
|
||||
std::list<std::pair<int, int>> expected_values{{3, 45},
|
||||
{2, 13},
|
||||
{1, 67}};
|
||||
for (const auto &r: *result) {
|
||||
const auto age_count_val = r.at<int>(0);
|
||||
const auto age_val = r.at<int>(1);
|
||||
REQUIRE(age_count_val == expected_values.front().first);
|
||||
REQUIRE(age_val == expected_values.front().second);
|
||||
expected_values.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Execute delete statement", "[query][record]")
|
||||
{
|
||||
auto res = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age")
|
||||
}).execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
res = query::insert()
|
||||
.into("person", {"id", "name", "age"})
|
||||
.values({1, "george", 45})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
res = query::insert()
|
||||
.into("person", {"id", "name", "age"})
|
||||
.values({2, "jane", 45})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto count = query::select({count_all()})
|
||||
.from("person")
|
||||
.fetch_value<int>(db);
|
||||
REQUIRE(count.is_ok());
|
||||
|
||||
REQUIRE(*count == 2);
|
||||
|
||||
res = query::remove()
|
||||
.from("person")
|
||||
.where("id"_col == 1)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
count = query::select({count_all()})
|
||||
.from("person")
|
||||
.fetch_value<int>(db);
|
||||
REQUIRE(count.is_ok());
|
||||
REQUIRE(*count == 1);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test quoted identifier record", "[query][record]") {
|
||||
auto res = query::create()
|
||||
.table("quotes", {
|
||||
make_column<std::string>("from", 255),
|
||||
make_column<std::string>("to", 255)
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("quotes");
|
||||
|
||||
// check table description
|
||||
std::vector<std::string> columns = { "from", "to"};
|
||||
std::vector<matador::data_type> types = {
|
||||
matador::data_type::type_varchar,
|
||||
matador::data_type::type_varchar
|
||||
};
|
||||
auto fields = db.describe("quotes");
|
||||
REQUIRE(fields.is_ok());
|
||||
|
||||
for (const auto &field : *fields) {
|
||||
REQUIRE(field.name() == columns[field.index()]);
|
||||
REQUIRE(field.type() == types[field.index()]);
|
||||
}
|
||||
|
||||
res = query::insert()
|
||||
.into("quotes", {"from", "to"})
|
||||
.values({"Berlin", "London"})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto result = query::select({"from", "to"})
|
||||
.from("quotes")
|
||||
.fetch_one(db);
|
||||
REQUIRE(result.is_ok());
|
||||
|
||||
REQUIRE(result->has_value());
|
||||
REQUIRE("Berlin" == (*result)->at("from").str());
|
||||
REQUIRE("London" == (*result)->at("to").str());
|
||||
|
||||
res = query::update("quotes")
|
||||
.set({{"from", "Hamburg"}, {"to", "New York"}})
|
||||
.where("from"_col == "Berlin")
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
result = query::select({"from", "to"})
|
||||
.from("quotes")
|
||||
.fetch_one(db);
|
||||
REQUIRE(result.is_ok());
|
||||
|
||||
REQUIRE("Hamburg" == (*result)->at("from").str());
|
||||
REQUIRE("New York" == (*result)->at("to").str());
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test create record", "[query][record][create]") {
|
||||
check_table_not_exists("person");
|
||||
auto res = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age")
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
check_table_exists("person");
|
||||
const std::vector<std::string> cols = {"id", "name", "age"};
|
||||
const auto fields = db.describe("person");
|
||||
REQUIRE(fields.is_ok());
|
||||
|
||||
for (const auto &fld : *fields) {
|
||||
REQUIRE(std::find(cols.begin(), cols.end(), fld.name()) != cols.end());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test insert record", "[query][record][insert]") {
|
||||
check_table_not_exists("person");
|
||||
auto res = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age")
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
check_table_exists("person");
|
||||
|
||||
res = query::insert()
|
||||
.into("person", {"id", "name", "age"})
|
||||
.values({1, "hans", 45})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto row = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.fetch_one(db);
|
||||
REQUIRE(row.is_ok());
|
||||
|
||||
REQUIRE(row->has_value());
|
||||
REQUIRE((*row)->at("id").as<unsigned long>() == 1);
|
||||
REQUIRE((*row)->at("name").as<std::string>() == "hans");
|
||||
REQUIRE((*row)->at("age").as<unsigned short>() == 45);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test update record", "[query][record][update]") {
|
||||
check_table_not_exists("person");
|
||||
auto res = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age")
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
check_table_exists("person");
|
||||
|
||||
res = query::insert()
|
||||
.into("person", {"id", "name", "age"})
|
||||
.values({1, "hans", 45})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto row = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.fetch_one(db);
|
||||
REQUIRE(row.is_ok());
|
||||
|
||||
REQUIRE(row->has_value());
|
||||
REQUIRE((*row)->at("id").as<unsigned long>() == 1);
|
||||
REQUIRE((*row)->at("name").as<std::string>() == "hans");
|
||||
REQUIRE((*row)->at("age").as<unsigned short>() == 45);
|
||||
|
||||
res = query::update("person")
|
||||
.set({{"name", "jane"}, {"age", 47}})
|
||||
.where("name"_col == "hans")
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
row = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.fetch_one(db);
|
||||
REQUIRE(row.is_ok());
|
||||
|
||||
REQUIRE(row->has_value());
|
||||
REQUIRE((*row)->at("id").as<unsigned long>() == 1);
|
||||
REQUIRE((*row)->at("name").as<std::string>() == "jane");
|
||||
REQUIRE((*row)->at("age").as<unsigned short>() == 47);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test prepared record statement", "[query][record][prepared]") {
|
||||
check_table_not_exists("person");
|
||||
auto stmt = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age")
|
||||
})
|
||||
.prepare(db);
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
auto res = stmt.execute();
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
check_table_exists("person");
|
||||
|
||||
const std::vector<std::string> cols = {"id", "name", "age"};
|
||||
const auto fields = db.describe("person");
|
||||
REQUIRE(fields.is_ok());
|
||||
|
||||
for (const auto &fld : *fields) {
|
||||
REQUIRE(std::find(cols.begin(), cols.end(), fld.name()) != cols.end());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test scalar result", "[query][record][scalar][result]") {
|
||||
check_table_not_exists("person");
|
||||
auto res = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
|
||||
check_table_exists("person");
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
std::vector<unsigned long> ids({ 1,2,3,4 });
|
||||
|
||||
for(auto id : ids) {
|
||||
res = query::insert()
|
||||
.into("person", {"id"})
|
||||
.values({id})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
}
|
||||
|
||||
auto stmt = query::select({"id"})
|
||||
.from("person")
|
||||
.order_by("id"_col).asc()
|
||||
.prepare(db);
|
||||
|
||||
auto rows = stmt.fetch();
|
||||
REQUIRE(rows.is_ok());
|
||||
|
||||
size_t index{0};
|
||||
for (const auto &row : *rows) {
|
||||
REQUIRE(row.at("id").as<unsigned long>() == ids[index]);
|
||||
++index;
|
||||
}
|
||||
REQUIRE(index == 4);
|
||||
|
||||
stmt.reset();
|
||||
|
||||
rows = stmt.fetch();
|
||||
REQUIRE(rows.is_ok());
|
||||
|
||||
index = 0;
|
||||
for (const auto &row : *rows) {
|
||||
REQUIRE(row.at("id").as<unsigned long>() == ids[index]);
|
||||
++index;
|
||||
}
|
||||
REQUIRE(index == 4);
|
||||
|
||||
stmt.reset();
|
||||
|
||||
auto row = stmt.fetch_one();
|
||||
REQUIRE(row.is_ok());
|
||||
REQUIRE(row->has_value());
|
||||
REQUIRE(row.value()->at("id").as<unsigned long>() == ids[0]);
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/sql/column.hpp"
|
||||
#include "matador/sql/condition.hpp"
|
||||
#include "matador/sql/connection.hpp"
|
||||
#include "matador/sql/query.hpp"
|
||||
|
||||
#include "models/person.hpp"
|
||||
|
||||
#include "QueryFixture.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
using namespace matador::sql;
|
||||
using namespace matador::test;
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test create statement", "[query][statement][create]") {
|
||||
schema.attach<matador::test::person>("person");
|
||||
auto stmt = query::create()
|
||||
.table<matador::test::person>("person", schema)
|
||||
.prepare(db);
|
||||
|
||||
auto res = stmt.execute();
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
check_table_exists("person");
|
||||
const std::vector<std::string> cols = {"id", "name", "age", "image"};
|
||||
const auto fields = db.describe("person");
|
||||
REQUIRE(fields.is_ok());
|
||||
|
||||
for (const auto &fld : *fields) {
|
||||
REQUIRE(std::find(cols.begin(), cols.end(), fld.name()) != cols.end());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test insert statement", "[query][statement][insert]") {
|
||||
using namespace matador::test;
|
||||
|
||||
schema.attach<person>("person");
|
||||
auto stmt = query::create()
|
||||
.table<person>("person", schema)
|
||||
.prepare(db);
|
||||
|
||||
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}};
|
||||
|
||||
stmt = query::insert()
|
||||
.into<person>("person", schema)
|
||||
.values<person>()
|
||||
.prepare(db);
|
||||
|
||||
res = stmt.bind(george)
|
||||
.execute();
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto row = query::select<person>(schema)
|
||||
.from("person")
|
||||
.fetch_one<person>(db);
|
||||
REQUIRE(row.is_ok());
|
||||
|
||||
REQUIRE(*row != nullptr);
|
||||
REQUIRE((*row)->id == 1);
|
||||
REQUIRE((*row)->name == "george");
|
||||
REQUIRE((*row)->age == 45);
|
||||
REQUIRE((*row)->image == matador::utils::blob{1,2,3,4});
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test update statement", "[query][statement][update]") {
|
||||
using namespace matador::test;
|
||||
using namespace matador::utils;
|
||||
|
||||
schema.attach<matador::test::person>("person");
|
||||
auto stmt = query::create()
|
||||
.table<matador::test::person>("person", schema)
|
||||
.prepare(db);
|
||||
|
||||
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}};
|
||||
|
||||
stmt = query::insert()
|
||||
.into<person>("person", schema)
|
||||
.values<person>()
|
||||
.prepare(db);
|
||||
|
||||
res = stmt.bind(george)
|
||||
.execute();
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto row = query::select<person>(schema)
|
||||
.from("person")
|
||||
.fetch_one<person>(db);
|
||||
REQUIRE(row.is_ok());
|
||||
|
||||
REQUIRE(*row != nullptr);
|
||||
REQUIRE((*row)->id == 1);
|
||||
REQUIRE((*row)->name == "george");
|
||||
REQUIRE((*row)->age == 45);
|
||||
REQUIRE((*row)->image == blob{1,2,3,4});
|
||||
|
||||
george.age = 36;
|
||||
george.image = {5,6,7,8};
|
||||
stmt = query::update("person")
|
||||
.set<person>()
|
||||
.where("id"_col == _)
|
||||
.prepare(db);
|
||||
|
||||
res = stmt.bind(george)
|
||||
.bind(4, george.id)
|
||||
.execute();
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
row = query::select<person>(schema)
|
||||
.from("person")
|
||||
.fetch_one<person>(db);
|
||||
REQUIRE(row.is_ok());
|
||||
|
||||
REQUIRE(*row != nullptr);
|
||||
REQUIRE((*row)->id == 1);
|
||||
REQUIRE((*row)->name == "george");
|
||||
REQUIRE((*row)->age == 36);
|
||||
REQUIRE((*row)->image == blob{5,6,7,8});
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test delete statement", "[query][statement][delete]") {
|
||||
using namespace matador::test;
|
||||
|
||||
schema.attach<matador::test::person>("person");
|
||||
auto stmt = query::create()
|
||||
.table<matador::test::person>("person", schema)
|
||||
.prepare(db);
|
||||
|
||||
auto res = stmt.execute();
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
check_table_exists("person");
|
||||
|
||||
stmt = query::insert()
|
||||
.into<person>("person", schema)
|
||||
.values<person>()
|
||||
.prepare(db);
|
||||
|
||||
std::vector<person> peoples {
|
||||
{1,"george", 45, {1,2,3,4}},
|
||||
{2,"jane", 36, {1,2,3,4}},
|
||||
{3,"lukas", 68, {1,2,3,4}},
|
||||
{4,"merlin", 99, {1,2,3,4}}
|
||||
};
|
||||
|
||||
for (const auto &p : peoples) {
|
||||
res = stmt.bind(p)
|
||||
.execute();
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
stmt.reset();
|
||||
}
|
||||
|
||||
auto select_stmt = query::select<person>(schema)
|
||||
.from("person")
|
||||
.where("name"_col == matador::utils::_)
|
||||
.prepare(db);
|
||||
|
||||
auto rows = select_stmt
|
||||
.bind(0, "jane")
|
||||
.fetch<person>();
|
||||
REQUIRE(rows.is_ok());
|
||||
|
||||
for (const auto &r : *rows) {
|
||||
constexpr size_t index = 1;
|
||||
REQUIRE(r.id == peoples[index].id);
|
||||
REQUIRE(r.name == peoples[index].name);
|
||||
REQUIRE(r.age == peoples[index].age);
|
||||
REQUIRE(r.image == peoples[index].image);
|
||||
}
|
||||
|
||||
stmt = query::remove()
|
||||
.from("person")
|
||||
.where("name"_col == matador::utils::_)
|
||||
.prepare(db);
|
||||
|
||||
res = stmt.bind(0, "jane")
|
||||
.execute();
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
select_stmt.reset();
|
||||
auto row = select_stmt
|
||||
.bind(0, "jane")
|
||||
.fetch_one<person>();
|
||||
REQUIRE(row.is_ok());
|
||||
|
||||
REQUIRE(*row == nullptr);
|
||||
|
||||
stmt.reset();
|
||||
res = stmt
|
||||
.bind(0, "merlin")
|
||||
.execute();
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
select_stmt.reset();
|
||||
row = select_stmt
|
||||
.bind(0, "merlin")
|
||||
.fetch_one<person>();
|
||||
REQUIRE(row.is_ok());
|
||||
|
||||
REQUIRE(*row == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test reuse prepared statement", "[query][statement][reuse]") {
|
||||
using namespace matador::test;
|
||||
|
||||
schema.attach<matador::test::person>("person");
|
||||
auto stmt = query::create()
|
||||
.table<matador::test::person>("person", schema)
|
||||
.prepare(db);
|
||||
|
||||
auto res = stmt.execute();
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
check_table_exists("person");
|
||||
|
||||
stmt = query::insert()
|
||||
.into<person>("person", schema)
|
||||
.values<person>()
|
||||
.prepare(db);
|
||||
|
||||
std::vector<person> peoples {
|
||||
{1,"george", 45, {1,2,3,4}},
|
||||
{2,"jane", 36, {1,2,3,4}},
|
||||
{3,"lukas", 68, {1,2,3,4}},
|
||||
{4,"merlin", 99, {1,2,3,4}}
|
||||
};
|
||||
|
||||
for (const auto &p : peoples) {
|
||||
res = stmt.bind(p)
|
||||
.execute();
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
stmt.reset();
|
||||
}
|
||||
|
||||
stmt = query::select<person>(schema)
|
||||
.from("person")
|
||||
.prepare(db);
|
||||
|
||||
auto rows = stmt.fetch<person>();
|
||||
REQUIRE(rows.is_ok());
|
||||
|
||||
size_t index = 0;
|
||||
for (const auto &r : *rows) {
|
||||
REQUIRE(r.id == peoples[index].id);
|
||||
REQUIRE(r.name == peoples[index].name);
|
||||
REQUIRE(r.age == peoples[index].age);
|
||||
REQUIRE(r.image == peoples[index].image);
|
||||
++index;
|
||||
}
|
||||
|
||||
stmt.reset();
|
||||
|
||||
rows = stmt.fetch<person>();
|
||||
REQUIRE(rows.is_ok());
|
||||
|
||||
index = 0;
|
||||
for (const auto &r : *rows) {
|
||||
REQUIRE(r.id == peoples[index].id);
|
||||
REQUIRE(r.name == peoples[index].name);
|
||||
REQUIRE(r.age == peoples[index].age);
|
||||
REQUIRE(r.image == peoples[index].image);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,532 @@
|
||||
#include "catch2/catch_test_macros.hpp"
|
||||
|
||||
#include "matador/sql/column_definition.hpp"
|
||||
#include "matador/sql/condition.hpp"
|
||||
#include "matador/sql/query.hpp"
|
||||
|
||||
#include "QueryFixture.hpp"
|
||||
|
||||
#include "models/airplane.hpp"
|
||||
#include "models/flight.hpp"
|
||||
#include "models/person.hpp"
|
||||
#include "models/recipe.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
using namespace matador::test;
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Create table with foreign key relation", "[query][foreign][relation]")
|
||||
{
|
||||
schema.attach<airplane>("airplane");
|
||||
schema.attach<flight>("flight");
|
||||
auto res = query::create()
|
||||
.table<airplane>("airplane", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
|
||||
check_table_exists("airplane");
|
||||
tables_to_drop.emplace("airplane");
|
||||
|
||||
res = query::create()
|
||||
.table<flight>("flight", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
|
||||
check_table_exists("flight");
|
||||
tables_to_drop.emplace("flight");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Execute select statement with where clause", "[query][where]")
|
||||
{
|
||||
schema.attach<person>("person");
|
||||
auto res = query::create()
|
||||
.table<person>("person", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
|
||||
check_table_exists("person");
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
person george{7, "george", 45};
|
||||
george.image.emplace_back(37);
|
||||
|
||||
res = query::insert()
|
||||
.into("person", column_generator::generate<person>(schema, true))
|
||||
.values(george)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
// fetch person as record
|
||||
auto result_record = query::select(column_generator::generate<person>(schema, true))
|
||||
.from("person")
|
||||
.where("id"_col == 7)
|
||||
.fetch_all(db);
|
||||
REQUIRE(result_record.is_ok());
|
||||
|
||||
for (const auto &i: *result_record) {
|
||||
REQUIRE(i.size() == 4);
|
||||
REQUIRE(i.at(0).name() == "id");
|
||||
REQUIRE(i.at(0).is_integer());
|
||||
REQUIRE(i.at(0).as<long long>() == george.id);
|
||||
REQUIRE(i.at(1).name() == "name");
|
||||
REQUIRE(i.at(1).is_varchar());
|
||||
REQUIRE(i.at(1).as<std::string>() == george.name);
|
||||
REQUIRE(i.at(2).name() == "age");
|
||||
REQUIRE(i.at(2).is_integer());
|
||||
REQUIRE(i.at(2).as<long long>() == george.age);
|
||||
}
|
||||
|
||||
// fetch person as person
|
||||
auto result_person = query::select(column_generator::generate<person>(schema, true))
|
||||
.from("person")
|
||||
.where("id"_col == 7)
|
||||
.fetch_all<person>(db);
|
||||
REQUIRE(result_person.is_ok());
|
||||
|
||||
for (const auto &i: *result_person) {
|
||||
REQUIRE(i.id == 7);
|
||||
REQUIRE(i.name == "george");
|
||||
REQUIRE(i.age == 45);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Execute insert statement", "[query][insert]")
|
||||
{
|
||||
auto res = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<std::string>("color", 63)
|
||||
})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
|
||||
REQUIRE(db.exists("person"));
|
||||
tables_to_drop.emplace("person");
|
||||
|
||||
res = query::insert()
|
||||
.into("person", {{"", "id", ""}, {"", "name", ""}, {"", "color", ""}})
|
||||
.values({7, "george", "green"})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
// fetch person as record
|
||||
auto result_record = query::select({"id", "name", "color"})
|
||||
.from("person")
|
||||
.where("id"_col == 7)
|
||||
.fetch_all(db);
|
||||
REQUIRE(result_record.is_ok());
|
||||
|
||||
for (const auto &i: *result_record) {
|
||||
REQUIRE(i.size() == 3);
|
||||
REQUIRE(i.at(0).name() == "id");
|
||||
REQUIRE(i.at(0).is_integer());
|
||||
REQUIRE(i.at(0).as<unsigned long>() == 7);
|
||||
REQUIRE(i.at(1).name() == "name");
|
||||
REQUIRE(i.at(1).is_varchar());
|
||||
REQUIRE(i.at(1).as<std::string>() == "george");
|
||||
REQUIRE(i.at(2).name() == "color");
|
||||
REQUIRE(i.at(2).is_varchar());
|
||||
REQUIRE(i.at(2).as<std::string>() == "green");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key", "[query][foreign]")
|
||||
{
|
||||
schema.attach<airplane>("airplane");
|
||||
schema.attach<flight>("flight");
|
||||
auto res = query::create()
|
||||
.table<airplane>("airplane", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
|
||||
REQUIRE(db.exists("airplane"));
|
||||
tables_to_drop.emplace("airplane");
|
||||
|
||||
res = query::create()
|
||||
.table<flight>("flight", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
|
||||
REQUIRE(db.exists("flight"));
|
||||
tables_to_drop.emplace("flight");
|
||||
|
||||
std::vector<matador::object_ptr<airplane>> planes{
|
||||
matador::object_ptr<airplane>(new airplane{1, "Airbus", "A380"}),
|
||||
matador::object_ptr<airplane>(new airplane{2, "Boeing", "707"}),
|
||||
matador::object_ptr<airplane>(new airplane{3, "Boeing", "747"})
|
||||
};
|
||||
|
||||
for (const auto &plane: planes) {
|
||||
res = query::insert()
|
||||
.into("airplane", column_generator::generate<airplane>(schema, true))
|
||||
.values(*plane)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
}
|
||||
|
||||
auto count = query::select({count_all()})
|
||||
.from("airplane")
|
||||
.fetch_value<int>(db);
|
||||
REQUIRE(count.is_ok());
|
||||
REQUIRE(*count == 3);
|
||||
|
||||
flight f4711{4, planes.at(1), "hans"};
|
||||
|
||||
res = query::insert()
|
||||
.into("flight", column_generator::generate<flight>(schema, true))
|
||||
.values(f4711)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto f = query::select(column_generator::generate<flight>(schema, true))
|
||||
.from("flight")
|
||||
.fetch_one(db);
|
||||
REQUIRE(f.is_ok());
|
||||
|
||||
REQUIRE(f.value()->at(0).as<unsigned long>() == 4);
|
||||
REQUIRE(f.value()->at(1).as<unsigned long>() == 2);
|
||||
REQUIRE(f.value()->at(2).as<std::string>() == "hans");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left", "[query][foreign][join_left]")
|
||||
{
|
||||
schema.attach<airplane>("airplane");
|
||||
schema.attach<flight>("flight");
|
||||
auto res = query::create()
|
||||
.table<airplane>("airplane", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
|
||||
REQUIRE(db.exists("airplane"));
|
||||
tables_to_drop.emplace("airplane");
|
||||
|
||||
res = query::create()
|
||||
.table<flight>("flight", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
|
||||
REQUIRE(db.exists("flight"));
|
||||
tables_to_drop.emplace("flight");
|
||||
|
||||
std::vector<matador::object_ptr<airplane>> planes{
|
||||
matador::object_ptr<airplane>(new airplane{1, "Airbus", "A380"}),
|
||||
matador::object_ptr<airplane>(new airplane{2, "Boeing", "707"}),
|
||||
matador::object_ptr<airplane>(new airplane{3, "Boeing", "747"})
|
||||
};
|
||||
|
||||
for (const auto &plane: planes) {
|
||||
res = query::insert()
|
||||
.into("airplane", column_generator::generate<airplane>(schema, true))
|
||||
.values(*plane)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
}
|
||||
|
||||
auto count = query::select({count_all()})
|
||||
.from("airplane")
|
||||
.fetch_value<int>(db).value();
|
||||
REQUIRE(count == 3);
|
||||
|
||||
std::vector<matador::object_ptr<flight>> flights{
|
||||
matador::object_ptr<flight>(new flight{4, planes.at(0), "hans"}),
|
||||
matador::object_ptr<flight>(new flight{5, planes.at(0), "otto"}),
|
||||
matador::object_ptr<flight>(new flight{6, planes.at(1), "george"}),
|
||||
matador::object_ptr<flight>(new flight{7, planes.at(2), "paul"})
|
||||
};
|
||||
|
||||
for (const auto &f: flights) {
|
||||
res = query::insert()
|
||||
.into("flight", {"id", "airplane_id", "pilot_name"})
|
||||
.values(*f)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
}
|
||||
|
||||
auto f = query::select(column_generator::generate<flight>(schema, true))
|
||||
.from("flight")
|
||||
.fetch_one(db);
|
||||
REQUIRE(f.is_ok());
|
||||
REQUIRE(f->has_value());
|
||||
REQUIRE(f.value()->at(0).as<unsigned long>() == 4);
|
||||
REQUIRE(f.value()->at(1).as<unsigned long>() == 1);
|
||||
REQUIRE(f.value()->at(2).as<std::string>() == "hans");
|
||||
|
||||
auto result = query::select({"f.id", "ap.brand", "ap.model", "f.pilot_name"})
|
||||
.from({"flight", "f"})
|
||||
.join_left({"airplane", "ap"})
|
||||
.on("f.airplane_id"_col == "ap.id"_col)
|
||||
.order_by("f.id").asc()
|
||||
.fetch_all(db);
|
||||
REQUIRE(result.is_ok());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single entity", "[query][join_left][find]") {
|
||||
schema.attach<airplane>("airplane");
|
||||
schema.attach<flight>("flight");
|
||||
auto res = query::create()
|
||||
.table<airplane>("airplane", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
|
||||
REQUIRE(db.exists("airplane"));
|
||||
tables_to_drop.emplace("airplane");
|
||||
|
||||
res = query::create()
|
||||
.table<flight>("flight", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
|
||||
REQUIRE(db.exists("flight"));
|
||||
tables_to_drop.emplace("flight");
|
||||
|
||||
std::vector<matador::object_ptr<airplane>> planes{
|
||||
matador::object_ptr<airplane>(new airplane{1, "Airbus", "A380"}),
|
||||
matador::object_ptr<airplane>(new airplane{2, "Boeing", "707"}),
|
||||
matador::object_ptr<airplane>(new airplane{3, "Boeing", "747"})
|
||||
};
|
||||
|
||||
for (const auto &plane: planes) {
|
||||
res = query::insert()
|
||||
.into("airplane", column_generator::generate<airplane>(schema, true))
|
||||
.values(*plane)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
}
|
||||
|
||||
auto count = query::select({count_all()})
|
||||
.from("airplane")
|
||||
.fetch_value<int>(db);
|
||||
REQUIRE(count.is_ok());
|
||||
REQUIRE(count->has_value());
|
||||
REQUIRE(count->value() == 3);
|
||||
|
||||
std::vector<matador::object_ptr<flight>> flights{
|
||||
matador::object_ptr<flight>(new flight{4, planes.at(0), "hans"}),
|
||||
matador::object_ptr<flight>(new flight{5, planes.at(0), "otto"}),
|
||||
matador::object_ptr<flight>(new flight{6, planes.at(1), "george"}),
|
||||
matador::object_ptr<flight>(new flight{7, planes.at(2), "paul"})
|
||||
};
|
||||
|
||||
for (const auto &f: flights) {
|
||||
res = query::insert()
|
||||
.into("flight", column_generator::generate<flight>(schema, true))
|
||||
.values(*f)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
}
|
||||
|
||||
auto f = query::select(column_generator::generate<flight>(schema, true))
|
||||
.from("flight")
|
||||
.fetch_one(db);
|
||||
REQUIRE(f.is_ok());
|
||||
REQUIRE(f->has_value());
|
||||
REQUIRE(f.value()->at(0).as<unsigned long>() == 4);
|
||||
REQUIRE(f.value()->at(1).as<unsigned long>() == 1);
|
||||
REQUIRE(f.value()->at(2).as<std::string>() == "hans");
|
||||
|
||||
auto result = query::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>(db);
|
||||
|
||||
auto expected_flight = flights[0];
|
||||
|
||||
REQUIRE(result.is_ok());
|
||||
REQUIRE(*result);
|
||||
REQUIRE(result.value()->id == expected_flight->id);
|
||||
REQUIRE(result.value()->pilot_name == expected_flight->pilot_name);
|
||||
REQUIRE(result.value()->airplane.get());
|
||||
REQUIRE(result.value()->airplane->id == 1);
|
||||
REQUIRE(result.value()->airplane->model == "A380");
|
||||
REQUIRE(result.value()->airplane->brand == "Airbus");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Select statement with many to many relationship", "[query][join][many_to_many]") {
|
||||
schema.attach<recipe>("recipes");
|
||||
schema.attach<ingredient>("ingredients");
|
||||
schema.attach<recipe_ingredient>("recipe_ingredients");
|
||||
auto res = query::create()
|
||||
.table<recipe>("recipes", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
|
||||
REQUIRE(db.exists("recipes"));
|
||||
tables_to_drop.emplace("recipes");
|
||||
|
||||
res = query::create()
|
||||
.table<ingredient>("ingredients", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
|
||||
REQUIRE(db.exists("ingredients"));
|
||||
tables_to_drop.emplace("ingredients");
|
||||
|
||||
res = query::create()
|
||||
.table<recipe_ingredient>("recipe_ingredients", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 0);
|
||||
|
||||
REQUIRE(db.exists("recipe_ingredients"));
|
||||
tables_to_drop.emplace("recipe_ingredients");
|
||||
|
||||
std::vector<ingredient> ingredients {
|
||||
{1, "Apple"},
|
||||
{2, "Strawberry"},
|
||||
{3, "Pineapple"},
|
||||
{4, "Sugar"},
|
||||
{5, "Flour"},
|
||||
{6, "Butter"},
|
||||
{7, "Beans"}
|
||||
};
|
||||
|
||||
for (const auto &i: ingredients) {
|
||||
res = query::insert()
|
||||
.into("ingredients", column_generator::generate<ingredient>(schema, true))
|
||||
.values(i)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
}
|
||||
|
||||
std::vector<recipe> recipes{
|
||||
{7, "Apple Crumble"},
|
||||
{8, "Beans Chili"},
|
||||
{9, "Fruit Salad"}
|
||||
};
|
||||
|
||||
for (const auto &r: recipes) {
|
||||
res = query::insert()
|
||||
.into("recipes", column_generator::generate<recipe>(schema, true))
|
||||
.values(r)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, int>> recipe_ingredients {
|
||||
{ 7, 1 },
|
||||
{ 7, 4 },
|
||||
{ 7, 5 },
|
||||
{ 8, 6 },
|
||||
{ 8, 7 },
|
||||
{ 9, 1 },
|
||||
{ 9, 2 },
|
||||
{ 9, 3 }
|
||||
};
|
||||
|
||||
for (const auto &ri: recipe_ingredients) {
|
||||
res = query::insert()
|
||||
.into("recipe_ingredients", column_generator::generate<recipe_ingredient>(schema, true))
|
||||
.values({ri.first, ri.second})
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
}
|
||||
|
||||
auto result = query::select({"r.id", "r.name", "ri.ingredient_id"})
|
||||
.from({"recipes", "r"})
|
||||
.join_left({"recipe_ingredients", "ri"})
|
||||
.on("r.id"_col == "ri.recipe_id"_col)
|
||||
.fetch_all(db);
|
||||
REQUIRE(result.is_ok());
|
||||
|
||||
std::vector<std::tuple<unsigned long, std::string, unsigned long>> expected_result_one_join {
|
||||
{7, "Apple Crumble", 1},
|
||||
{7, "Apple Crumble", 4},
|
||||
{7, "Apple Crumble", 5},
|
||||
{8, "Beans Chili", 6},
|
||||
{8, "Beans Chili", 7},
|
||||
{9, "Fruit Salad", 1},
|
||||
{9, "Fruit Salad", 2},
|
||||
{9, "Fruit Salad", 3}
|
||||
};
|
||||
size_t index{0};
|
||||
for (const auto &r: *result) {
|
||||
REQUIRE(r.size() == 3);
|
||||
REQUIRE(r.at(0).as<unsigned long>().value() == std::get<0>(expected_result_one_join[index]));
|
||||
REQUIRE(r.at(1).as<std::string>().value() == std::get<1>(expected_result_one_join[index]));
|
||||
REQUIRE(r.at(2).as<unsigned long>().value() == std::get<2>(expected_result_one_join[index]));
|
||||
++index;
|
||||
}
|
||||
|
||||
result = query::select({"r.id", "r.name", "ri.ingredient_id", "i.name"})
|
||||
.from({"recipes", "r"})
|
||||
.join_left({"recipe_ingredients", "ri"}).on("r.id"_col == "ri.recipe_id"_col)
|
||||
.join_left({"ingredients", "i"}).on("ri.ingredient_id"_col == "i.id"_col)
|
||||
.fetch_all(db);
|
||||
REQUIRE(result.is_ok());
|
||||
|
||||
std::vector<std::tuple<unsigned long, std::string, unsigned long, std::string>> expected_result_two_joins {
|
||||
{7, "Apple Crumble", 1, "Apple"},
|
||||
{7, "Apple Crumble", 4, "Sugar"},
|
||||
{7, "Apple Crumble", 5, "Flour"},
|
||||
{8, "Beans Chili", 6, "Butter"},
|
||||
{8, "Beans Chili", 7, "Beans"},
|
||||
{9, "Fruit Salad", 1, "Apple"},
|
||||
{9, "Fruit Salad", 2, "Strawberry"},
|
||||
{9, "Fruit Salad", 3, "Pineapple"}
|
||||
};
|
||||
index = 0;
|
||||
for (const auto &r: *result) {
|
||||
REQUIRE(r.size() == 4);
|
||||
REQUIRE(r.at(0).as<unsigned long>().value() == std::get<0>(expected_result_two_joins[index]));
|
||||
REQUIRE(r.at(1).as<std::string>().value() == std::get<1>(expected_result_two_joins[index]));
|
||||
REQUIRE(r.at(2).as<unsigned long>().value() == std::get<2>(expected_result_two_joins[index]));
|
||||
REQUIRE(r.at(3).as<std::string>().value() == std::get<3>(expected_result_two_joins[index]));
|
||||
++index;
|
||||
}
|
||||
|
||||
result = query::select({"r.id", "r.name", "ri.ingredient_id", "i.name"})
|
||||
.from({"recipes", "r"})
|
||||
.join_left({"recipe_ingredients", "ri"}).on("r.id"_col == "ri.recipe_id"_col)
|
||||
.join_left({"ingredients", "i"}).on("ri.ingredient_id"_col == "i.id"_col)
|
||||
.where("r.id"_col == 8)
|
||||
.fetch_all(db);
|
||||
REQUIRE(result.is_ok());
|
||||
|
||||
index = 3;
|
||||
for (const auto &r: *result) {
|
||||
REQUIRE(r.size() == 4);
|
||||
REQUIRE(r.at(0).as<unsigned long>().value() == std::get<0>(expected_result_two_joins[index]));
|
||||
REQUIRE(r.at(1).as<std::string>().value() == std::get<1>(expected_result_two_joins[index]));
|
||||
REQUIRE(r.at(2).as<unsigned long>().value() == std::get<2>(expected_result_two_joins[index]));
|
||||
REQUIRE(r.at(3).as<std::string>().value() == std::get<3>(expected_result_two_joins[index]));
|
||||
++index;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "SessionFixture.hpp"
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
SessionFixture::SessionFixture()
|
||||
: pool(connection::dns, 4), ses(pool) {}
|
||||
|
||||
SessionFixture::~SessionFixture() {
|
||||
while (!tables_to_drop.empty()) {
|
||||
drop_table_if_exists(tables_to_drop.top());
|
||||
tables_to_drop.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void SessionFixture::drop_table_if_exists(const std::string &table_name) const {
|
||||
if (ses.table_exists(table_name)) {
|
||||
ses.drop_table(table_name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef MATADOR_SESSION_FIXTURE_HPP
|
||||
#define MATADOR_SESSION_FIXTURE_HPP
|
||||
|
||||
#include "matador/sql/session.hpp"
|
||||
|
||||
#include "connection.hpp"
|
||||
|
||||
#include <stack>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
class SessionFixture {
|
||||
public:
|
||||
SessionFixture();
|
||||
~SessionFixture();
|
||||
|
||||
protected:
|
||||
sql::connection_pool<sql::connection> pool;
|
||||
sql::session ses;
|
||||
std::stack <std::string> tables_to_drop;
|
||||
|
||||
private:
|
||||
void drop_table_if_exists(const std::string &table_name) const;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //MATADOR_SESSION_FIXTURE_HPP
|
||||
@@ -0,0 +1,136 @@
|
||||
#include "catch2/catch_test_macros.hpp"
|
||||
|
||||
#include "SessionFixture.hpp"
|
||||
|
||||
#include "models/airplane.hpp"
|
||||
#include "models/author.hpp"
|
||||
#include "models/book.hpp"
|
||||
#include "models/flight.hpp"
|
||||
|
||||
using namespace matador;
|
||||
using namespace matador::test;
|
||||
|
||||
TEST_CASE_METHOD(SessionFixture, "Session relation test", "[session][relation]") {
|
||||
ses.attach<airplane>("airplanes");
|
||||
ses.attach<flight>("flights");
|
||||
ses.create_schema();
|
||||
|
||||
tables_to_drop.emplace("airplanes");
|
||||
tables_to_drop.emplace("flights");
|
||||
|
||||
auto plane = ses.insert<airplane>(1, "Boeing", "A380");
|
||||
auto f = ses.insert<flight>(2, plane, "sully");
|
||||
|
||||
const auto result = ses.find<flight>(2);
|
||||
REQUIRE(result.is_ok());
|
||||
REQUIRE(result->get()->id == f->id);
|
||||
REQUIRE(result->get()->pilot_name == f->pilot_name);
|
||||
REQUIRE(result->get()->airplane);
|
||||
REQUIRE(result->get()->airplane->id == plane->id);
|
||||
REQUIRE(result->get()->airplane->brand == plane->brand);
|
||||
REQUIRE(result->get()->airplane->model == plane->model);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(SessionFixture, "Use session to find object with id", "[session][find]") {
|
||||
ses.attach<airplane>("airplanes");
|
||||
ses.create_schema();
|
||||
|
||||
tables_to_drop.emplace("airplanes");
|
||||
|
||||
auto a380 = ses.insert<airplane>(1, "Boeing", "A380");
|
||||
|
||||
auto result = ses.find<airplane>(2);
|
||||
REQUIRE(!result.is_ok());
|
||||
REQUIRE((result.err().ec() == sql::session_error_code::FailedToFindObject));
|
||||
|
||||
result = ses.find<airplane>(1);
|
||||
|
||||
REQUIRE(result);
|
||||
auto read_a380 = result.value();
|
||||
REQUIRE(a380->id == read_a380->id);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(SessionFixture, "Use session to find all objects", "[session][find]") {
|
||||
ses.attach<airplane>("airplanes");
|
||||
ses.create_schema();
|
||||
|
||||
tables_to_drop.emplace("airplanes");
|
||||
|
||||
std::vector<std::unique_ptr<airplane>> planes;
|
||||
planes.emplace_back(new airplane(1, "Airbus", "A380"));
|
||||
planes.emplace_back(new airplane(2, "Boeing", "707"));
|
||||
planes.emplace_back(new airplane(3, "Boeing", "747"));
|
||||
|
||||
for (auto &&plane: planes) {
|
||||
ses.insert(plane.release());
|
||||
}
|
||||
|
||||
auto result = ses.find<airplane>();
|
||||
|
||||
std::vector<std::tuple<unsigned long, std::string, std::string>> expected_result {
|
||||
{1, "Airbus", "A380"},
|
||||
{2, "Boeing", "707"},
|
||||
{3, "Boeing", "747"}
|
||||
};
|
||||
REQUIRE(result);
|
||||
auto all_planes = result.release();
|
||||
size_t index {0};
|
||||
for (const auto &i: all_planes) {
|
||||
REQUIRE(i.id == std::get<0>(expected_result[index]));
|
||||
REQUIRE(i.brand == std::get<1>(expected_result[index]));
|
||||
REQUIRE(i.model == std::get<2>(expected_result[index]));
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with one-to-many relation", "[session][find][one-to-many]") {
|
||||
ses.attach<author>("authors");
|
||||
ses.attach<book>("books");
|
||||
|
||||
tables_to_drop.emplace("authors");
|
||||
tables_to_drop.emplace("books");
|
||||
|
||||
ses.create_schema();
|
||||
|
||||
std::vector<std::unique_ptr<author>> authors;
|
||||
authors.emplace_back(new author{1, "Michael", "Crichton", "23.10.1942", 1975, true, {}});
|
||||
authors.emplace_back(new author{ 2, "Steven", "King", "21.9.1947", 1956, false, {}});
|
||||
|
||||
for (auto &&a: authors) {
|
||||
ses.insert(a.release());
|
||||
}
|
||||
|
||||
auto result = ses.find<author>();
|
||||
REQUIRE(result.is_ok());
|
||||
auto all_authors = result.release();
|
||||
std::vector<object_ptr<author>> author_repo;
|
||||
for (auto it = all_authors.begin(); it != all_authors.end(); ++it) {
|
||||
std::cout << "author: " << it->first_name << " (books: " << it->books.size() << ")\n";
|
||||
author_repo.emplace_back(it.release());
|
||||
}
|
||||
REQUIRE(author_repo.size() == 2);
|
||||
|
||||
std::vector<std::unique_ptr<book>> books;
|
||||
books.emplace_back( new book{3, "Jurassic Park", author_repo[0], 1990} );
|
||||
books.emplace_back( new book{4, "Timeline", author_repo[0], 1999} );
|
||||
books.emplace_back( new book{5, "The Andromeda Strain", author_repo[0], 1969} );
|
||||
books.emplace_back( new book{6, "Congo", author_repo[0], 1980} );
|
||||
books.emplace_back( new book{7, "Prey", author_repo[0], 2002} );
|
||||
books.emplace_back( new book{8, "Carrie", author_repo[1], 1974} );
|
||||
books.emplace_back( new book{9, "The Shining", author_repo[1], 1977} );
|
||||
books.emplace_back( new book{10, "It", author_repo[1], 1986} );
|
||||
books.emplace_back( new book{11, "Misery", author_repo[1], 1987} );
|
||||
books.emplace_back( new book{12, "The Dark Tower: The Gunslinger", author_repo[1], 1982} );
|
||||
|
||||
for (auto &&b: books) {
|
||||
ses.insert(b.release());
|
||||
}
|
||||
|
||||
result = ses.find<author>();
|
||||
REQUIRE(result);
|
||||
|
||||
all_authors = result.release();
|
||||
for (auto it = all_authors.begin(); it != all_authors.end(); ++it) {
|
||||
std::cout << "author: " << it->first_name << " (books: " << it->books.size() << ")\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/sql/connection_info.hpp"
|
||||
#include "matador/sql/connection_pool.hpp"
|
||||
#include "matador/sql/session.hpp"
|
||||
#include "matador/sql/statement_cache.hpp"
|
||||
|
||||
#include "connection.hpp"
|
||||
#include "matador/sql/dialect_builder.hpp"
|
||||
|
||||
using namespace matador;
|
||||
|
||||
class StatementCacheFixture
|
||||
{
|
||||
public:
|
||||
StatementCacheFixture()
|
||||
: pool(matador::test::connection::dns, 4), ses(pool)
|
||||
{}
|
||||
~StatementCacheFixture() = default;
|
||||
|
||||
protected:
|
||||
matador::sql::connection_pool<matador::sql::connection> pool;
|
||||
matador::sql::session ses;
|
||||
};
|
||||
|
||||
TEST_CASE_METHOD(StatementCacheFixture, "Acquire prepared statement", "[statement cache]") {
|
||||
// const auto d = sql::dialect_builder::builder().create().build();
|
||||
// sql::statement_cache cache(pool, d);
|
||||
|
||||
// auto conn = pool.acquire();
|
||||
|
||||
std::string sql = R"(SELECT * FROM person WHERE name = 'george')";
|
||||
// auto &stmt = cache.acquire(sql, *conn);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/sql/column_definition.hpp"
|
||||
#include "matador/sql/condition.hpp"
|
||||
#include "matador/sql/connection.hpp"
|
||||
#include "matador/sql/query.hpp"
|
||||
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
|
||||
#include "QueryFixture.hpp"
|
||||
|
||||
#include "models/airplane.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
using namespace matador::test;
|
||||
|
||||
namespace matador::test::detail {
|
||||
template<class Type, typename... Args>
|
||||
[[maybe_unused]] object_ptr<Type> make_object_ptr(Args&&... args)
|
||||
{
|
||||
return object_ptr(new Type(std::forward<Args>(args)...));
|
||||
}
|
||||
}
|
||||
|
||||
class StatementTestFixture : public QueryFixture
|
||||
{
|
||||
public:
|
||||
StatementTestFixture()
|
||||
{
|
||||
const auto res = query::create()
|
||||
.table<airplane>("airplane", schema)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
tables_to_drop.emplace("airplane");
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<matador::object_ptr<airplane>> planes{
|
||||
matador::test::detail::make_object_ptr<airplane>(1, "Airbus", "A380"),
|
||||
matador::test::detail::make_object_ptr<airplane>(2, "Boeing", "707"),
|
||||
matador::test::detail::make_object_ptr<airplane>(3, "Boeing", "747")
|
||||
};
|
||||
};
|
||||
|
||||
TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement]")
|
||||
{
|
||||
using namespace matador::utils;
|
||||
schema.attach<airplane>("airplane");
|
||||
table ap{"airplane"};
|
||||
SECTION("Insert with prepared statement and placeholder") {
|
||||
auto stmt = query::insert()
|
||||
.into("airplane", column_generator::generate<airplane>(schema, true))
|
||||
.values<airplane>()
|
||||
.prepare(db);
|
||||
|
||||
for (const auto &plane: planes) {
|
||||
auto res = stmt.bind(*plane).execute();
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
stmt.reset();
|
||||
}
|
||||
|
||||
auto result = query::select(column_generator::generate<airplane>(schema, true))
|
||||
.from(ap)
|
||||
.fetch_all<airplane>(db);
|
||||
|
||||
REQUIRE(result.is_ok());
|
||||
size_t index{0};
|
||||
for (const auto &i: *result) {
|
||||
REQUIRE(i.id == planes[index]->id);
|
||||
REQUIRE(i.brand == planes[index]->brand);
|
||||
REQUIRE(i.model == planes[index++]->model);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Select with prepared statement") {
|
||||
for (const auto &plane: planes) {
|
||||
auto res = query::insert()
|
||||
.into("airplane", column_generator::generate<airplane>(schema, true))
|
||||
.values(*plane)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
}
|
||||
|
||||
auto stmt = query::select(column_generator::generate<airplane>(schema, true))
|
||||
.from(ap)
|
||||
.where("brand"_col == _)
|
||||
.prepare(db);
|
||||
|
||||
auto result = stmt.bind(0, "Airbus")
|
||||
.fetch<airplane>();
|
||||
|
||||
REQUIRE(result.is_ok());
|
||||
for (const auto &i: *result) {
|
||||
REQUIRE(i.id == planes[0]->id);
|
||||
REQUIRE(i.brand == planes[0]->brand);
|
||||
REQUIRE(i.model == planes[0]->model);
|
||||
}
|
||||
|
||||
stmt.reset();
|
||||
|
||||
result = stmt.bind(0, "Boeing")
|
||||
.fetch<airplane>();
|
||||
|
||||
size_t index{1};
|
||||
REQUIRE(result.is_ok());
|
||||
for (const auto &i: *result) {
|
||||
REQUIRE(i.id == planes[index]->id);
|
||||
REQUIRE(i.brand == planes[index]->brand);
|
||||
REQUIRE(i.model == planes[index++]->model);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "ColorEnumTraits.hpp"
|
||||
|
||||
#include "matador/sql/connection.hpp"
|
||||
#include "matador/sql/column_generator.hpp"
|
||||
#include "matador/sql/query.hpp"
|
||||
|
||||
#include "QueryFixture.hpp"
|
||||
|
||||
#include "models/location.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
using namespace matador::test;
|
||||
|
||||
class TypeTraitsTestFixture : public QueryFixture
|
||||
{
|
||||
public:
|
||||
TypeTraitsTestFixture()
|
||||
{
|
||||
db.open();
|
||||
const auto res = query::create()
|
||||
.table<location>("location", schema)
|
||||
.execute(db);
|
||||
tables_to_drop.emplace("location");
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with type traits", "[typetraits]")
|
||||
{
|
||||
schema.attach<location>("location");
|
||||
SECTION("Insert and select with direct execution") {
|
||||
location loc{1, "center", {1, 2, 3}, Color::Black};
|
||||
|
||||
auto res = query::insert()
|
||||
.into("location", column_generator::generate<location>(schema, true))
|
||||
.values(loc)
|
||||
.execute(db);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto result = query::select(column_generator::generate<location>(schema, true))
|
||||
.from("location")
|
||||
.fetch_one<location>(db);
|
||||
|
||||
REQUIRE(result.is_ok());
|
||||
REQUIRE((*result)->name == "center");
|
||||
REQUIRE((*result)->color == Color::Black);
|
||||
REQUIRE((*result)->coord.x == 1);
|
||||
REQUIRE((*result)->coord.y == 2);
|
||||
REQUIRE((*result)->coord.z == 3);
|
||||
}
|
||||
|
||||
SECTION("Insert and select with prepared statement") {
|
||||
location loc{1, "center", {1, 2, 3}, Color::Black};
|
||||
|
||||
auto stmt = query::insert()
|
||||
.into("location", column_generator::generate<location>(schema, true))
|
||||
.values<location>()
|
||||
.prepare(db);
|
||||
|
||||
auto res = stmt
|
||||
.bind(loc)
|
||||
.execute();
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == 1);
|
||||
|
||||
auto result = query::select(column_generator::generate<location>(schema, true))
|
||||
.from("location")
|
||||
.fetch_one<location>(db);
|
||||
|
||||
REQUIRE(result.is_ok());
|
||||
REQUIRE((*result)->name == "center");
|
||||
REQUIRE((*result)->color == Color::Black);
|
||||
REQUIRE((*result)->coord.x == 1);
|
||||
REQUIRE((*result)->coord.y == 2);
|
||||
REQUIRE((*result)->coord.z == 3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
CPMAddPackage("gh:catchorg/Catch2@3.7.1")
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
|
||||
|
||||
add_executable(CoreTests
|
||||
utils/BasicTypeToVisitorTest.cpp
|
||||
utils/ConvertTest.cpp
|
||||
utils/DefaultTypeTraitsTest.cpp
|
||||
utils/DependencyInjectionTest.cpp
|
||||
utils/IdentifierTest.cpp
|
||||
utils/ResultTest.cpp
|
||||
utils/FieldAttributeTest.cpp
|
||||
utils/VersionTest.cpp
|
||||
utils/StringTest.cpp
|
||||
object/PrototypeTreeTest.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(CoreTests matador-core Catch2::Catch2WithMain)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
target_compile_options(CoreTests PRIVATE -coverage)
|
||||
target_link_options(CoreTests PRIVATE -coverage)
|
||||
endif ()
|
||||
|
||||
add_test(NAME CoreTests COMMAND CoreTests)
|
||||
@@ -0,0 +1,35 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/object/schema.hpp"
|
||||
|
||||
struct node {};
|
||||
|
||||
using namespace matador;
|
||||
|
||||
struct person {
|
||||
virtual ~person() = default;
|
||||
};
|
||||
|
||||
struct student final : person {};
|
||||
struct teacher final : person {};
|
||||
|
||||
TEST_CASE("Test empty prototype tree", "[prototype_tree][empty]") {
|
||||
const object::schema tree;
|
||||
|
||||
REQUIRE( tree.empty() );
|
||||
}
|
||||
|
||||
TEST_CASE("Test add type to prototype tree", "[prototype_tree][add]") {
|
||||
object::schema tree;
|
||||
|
||||
REQUIRE( tree.empty() );
|
||||
|
||||
auto res = tree.attach<person>("person");
|
||||
REQUIRE( res.is_ok() );
|
||||
res = tree.attach<student, person>("student");
|
||||
REQUIRE( res.is_ok() );
|
||||
res = tree.attach<teacher, person>("teacher");
|
||||
REQUIRE( res.is_ok() );
|
||||
|
||||
REQUIRE( tree.size() == 3 );
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/utils/types.hpp"
|
||||
#include "matador/utils/basic_type_converter.hpp"
|
||||
|
||||
using namespace matador::utils;
|
||||
|
||||
TEST_CASE("Test convert any type to string", "[any type visitor]") {
|
||||
basic_type_converter<std::string> to_string_visitor;
|
||||
|
||||
database_type value = 6;
|
||||
auto res = basic_type_converter<std::string>::convert_value(value);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == "6");
|
||||
|
||||
value = 2.5;
|
||||
res = basic_type_converter<std::string>::convert_value(value);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == "2.5");
|
||||
|
||||
value = true;
|
||||
res = basic_type_converter<std::string>::convert_value(value);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == "true");
|
||||
|
||||
value = "hello";
|
||||
res = basic_type_converter<std::string>::convert_value(value);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == "hello");
|
||||
|
||||
value = std::string{"world"};
|
||||
res = basic_type_converter<std::string>::convert_value(value);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == "world");}
|
||||
|
||||
TEST_CASE("Test convert any type to integral", "[any type visitor]") {
|
||||
std::vector<std::pair<database_type, int>> values = {
|
||||
{6, 6},
|
||||
{2.5, 2},
|
||||
{true, 1},
|
||||
{"hello", 0},
|
||||
{std::string{"world"}, 0}
|
||||
};
|
||||
|
||||
for (const auto &value : values) {
|
||||
const auto res = basic_type_converter<long>::convert_value(value.first);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == value.second);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Test convert any type to floating point", "[any type visitor]") {
|
||||
std::vector<std::pair<database_type, double>> values = {
|
||||
{6, 6},
|
||||
{2.5, 2.5},
|
||||
{true, 1},
|
||||
{"hello", 0},
|
||||
{std::string{"world"}, 0}
|
||||
};
|
||||
|
||||
for (const auto &value : values) {
|
||||
const auto res = basic_type_converter<double>::convert_value(value.first);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == value.second);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/utils/convert.hpp"
|
||||
//#include "matador/utils/date.hpp"
|
||||
//#include "matador/utils/time.hpp"
|
||||
#include "matador/utils/types.hpp"
|
||||
|
||||
using namespace matador::utils;
|
||||
|
||||
template<typename From, typename To>
|
||||
void validate_conversion(From from)
|
||||
{
|
||||
auto res = to<To>(from);
|
||||
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(static_cast<To>(from) == *res);
|
||||
}
|
||||
|
||||
template<typename From, typename To>
|
||||
void validate_conversion(const From &from, To expected_to)
|
||||
{
|
||||
auto res = to<To>(from);
|
||||
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == expected_to);
|
||||
}
|
||||
|
||||
template<typename From>
|
||||
void validate_conversion(const From &from, const std::string &expected_to)
|
||||
{
|
||||
auto res = to<std::string>(from);
|
||||
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(*res == expected_to);
|
||||
}
|
||||
|
||||
template<typename From>
|
||||
void validate_conversion(const From &from, const char *expected_to)
|
||||
{
|
||||
validate_conversion<From>(from, std::string{expected_to});
|
||||
}
|
||||
|
||||
template<typename From>
|
||||
void validate_integral_conversion(From from) {
|
||||
validate_conversion<From, int8_t>(from);
|
||||
validate_conversion<From, int16_t>(from);
|
||||
validate_conversion<From, int32_t>(from);
|
||||
validate_conversion<From, int64_t>(from);
|
||||
}
|
||||
|
||||
TEST_CASE("Test integral conversion", "[convert][integral]") {
|
||||
validate_integral_conversion<int8_t>(-56);
|
||||
validate_integral_conversion<int16_t>(-127);
|
||||
validate_integral_conversion<int32_t>(-9876543);
|
||||
validate_integral_conversion<int64_t>(-123456790);
|
||||
|
||||
validate_integral_conversion<uint8_t>(56);
|
||||
validate_integral_conversion<uint16_t>(127);
|
||||
validate_integral_conversion<uint32_t>(9876543);
|
||||
validate_integral_conversion<uint64_t>(123456790);
|
||||
|
||||
validate_conversion<int16_t, int8_t>(513, 1);
|
||||
validate_conversion<int32_t, int8_t>(515, 3);
|
||||
validate_conversion<int64_t, int8_t>(516, 4);
|
||||
}
|
||||
TEST_CASE("Test floating point conversion", "[convert][floating_point]") {
|
||||
validate_conversion<float, float>(-0.1f);
|
||||
validate_conversion<float, double>(-0.1f);
|
||||
validate_conversion<double, double>(-0.44444);
|
||||
validate_conversion<double, float>(-0.44444);
|
||||
}
|
||||
|
||||
TEST_CASE("Test integral to string conversion", "[convert][integral][string]") {
|
||||
validate_conversion<int8_t>(-56, "-56");
|
||||
validate_conversion<int16_t>(-127, "-127");
|
||||
validate_conversion<int32_t>(-9876543, "-9876543");
|
||||
validate_conversion<int64_t>(-123456790, "-123456790");
|
||||
|
||||
validate_conversion<uint8_t>(56, "56");
|
||||
validate_conversion<uint16_t>(127, "127");
|
||||
validate_conversion<uint32_t>(9876543, "9876543");
|
||||
validate_conversion<uint64_t>(123456790, "123456790");
|
||||
}
|
||||
|
||||
TEST_CASE("Test floating point to string conversion", "[convert][floating_point][string]") {
|
||||
validate_conversion<float>(-56.1234f, "-56.1234");
|
||||
validate_conversion<double>(-127.444449, "-127.444449");
|
||||
}
|
||||
|
||||
TEST_CASE("Test string to integral conversion", "[convert][string][integral]") {
|
||||
validate_conversion<std::string, int8_t>("-56", -56);
|
||||
validate_conversion<std::string, int16_t>("-127", -127);
|
||||
validate_conversion<std::string, int32_t>("-9876543", -9876543);
|
||||
validate_conversion<std::string, int64_t>("-123456790", -123456790);
|
||||
|
||||
validate_conversion<std::string, uint8_t>("56", 56);
|
||||
validate_conversion<std::string, uint16_t>("127", 127);
|
||||
validate_conversion<std::string, uint32_t>("9876543", 9876543);
|
||||
validate_conversion<std::string, uint64_t>("123456790", 123456790);
|
||||
|
||||
validate_conversion<const char*, int8_t>("-56", -56);
|
||||
validate_conversion<const char*, int16_t>("-127", -127);
|
||||
validate_conversion<const char*, int32_t>("-9876543", -9876543);
|
||||
validate_conversion<const char*, int64_t>("-123456790", -123456790);
|
||||
|
||||
validate_conversion<const char*, uint8_t>("56", 56);
|
||||
validate_conversion<const char*, uint16_t>("127", 127);
|
||||
validate_conversion<const char*, uint32_t>("9876543", 9876543);
|
||||
validate_conversion<const char*, uint64_t>("123456790", 123456790);
|
||||
}
|
||||
|
||||
TEST_CASE("Test string to floating point conversion", "[convert][string][floating_point]") {
|
||||
validate_conversion<std::string, float>("-56.1234", -56.1234f);
|
||||
validate_conversion<std::string, double>("-127.444449", -127.444449);
|
||||
|
||||
validate_conversion<const char*, float>("-56.1234", -56.1234f);
|
||||
validate_conversion<const char*, double>("-127.444449", -127.444449);
|
||||
}
|
||||
|
||||
TEST_CASE("Test blob to blob conversion", "[convert][blob]") {
|
||||
blob from{1, 2, 3, 4};
|
||||
const auto res = to<blob>(from);
|
||||
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(from == *res);
|
||||
}
|
||||
|
||||
// TEST_CASE("Validate date to string conversion", "[convert][date][string]") {
|
||||
// matador::date today;
|
||||
// const auto expected_string = matador::utils::to_string(today);
|
||||
// std::string to;
|
||||
//
|
||||
// convert(to, today);
|
||||
//
|
||||
// REQUIRE(expected_string == to);
|
||||
// }
|
||||
//
|
||||
// TEST_CASE("Validate date conversion leads to an exception", "[convert][date][exception]") {
|
||||
// matador::date today;
|
||||
// int to{};
|
||||
// convert(to, today);
|
||||
// REQUIRE(to == today.julian_date());
|
||||
// }
|
||||
//
|
||||
// TEST_CASE("Validate time to string conversion", "[convert][time][string]") {
|
||||
// matador::time now;
|
||||
// const auto expected_string = matador::utils::to_string(now);
|
||||
// std::string to;
|
||||
//
|
||||
// convert(to, now);
|
||||
//
|
||||
// REQUIRE(expected_string == to);
|
||||
// }
|
||||
//
|
||||
// TEST_CASE("Validate time conversion leads to an exception", "[convert][time][exception]") {
|
||||
// matador::time now;
|
||||
// int to;
|
||||
// convert(to, now);
|
||||
// REQUIRE(to == now.get_time_info().seconds_since_epoch);
|
||||
// }
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/utils/default_type_traits.hpp"
|
||||
|
||||
using namespace matador::utils;
|
||||
|
||||
TEST_CASE("Test default data types", "[data_types][type]") {
|
||||
REQUIRE(data_type_traits<int8_t>::type() == basic_type::type_int8);
|
||||
REQUIRE(data_type_traits<int16_t>::type() == basic_type::type_int16);
|
||||
REQUIRE(data_type_traits<int32_t>::type() == basic_type::type_int32);
|
||||
REQUIRE(data_type_traits<int64_t>::type() == basic_type::type_int64);
|
||||
REQUIRE(data_type_traits<uint8_t>::type() == basic_type::type_uint8);
|
||||
REQUIRE(data_type_traits<uint16_t>::type() == basic_type::type_uint16);
|
||||
REQUIRE(data_type_traits<uint32_t>::type() == basic_type::type_uint32);
|
||||
REQUIRE(data_type_traits<uint64_t>::type() == basic_type::type_uint64);
|
||||
REQUIRE(data_type_traits<bool>::type() == basic_type::type_bool);
|
||||
REQUIRE(data_type_traits<float>::type() == basic_type::type_float);
|
||||
REQUIRE(data_type_traits<double>::type() == basic_type::type_double);
|
||||
REQUIRE(data_type_traits<const char*>::type(32) == basic_type::type_varchar);
|
||||
REQUIRE(data_type_traits<const char*>::type(0) == basic_type::type_text);
|
||||
REQUIRE(data_type_traits<char*>::type(32) == basic_type::type_varchar);
|
||||
REQUIRE(data_type_traits<char*>::type(0) == basic_type::type_text);
|
||||
REQUIRE(data_type_traits<char[]>::type(32) == basic_type::type_varchar);
|
||||
REQUIRE(data_type_traits<char[]>::type(0) == basic_type::type_text);
|
||||
REQUIRE(data_type_traits<std::string>::type(32) == basic_type::type_varchar);
|
||||
REQUIRE(data_type_traits<std::string>::type(0) == basic_type::type_text);
|
||||
REQUIRE(data_type_traits<blob>::type(0) == basic_type::type_blob);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/utils/di.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace detail {
|
||||
|
||||
class greeter {
|
||||
public:
|
||||
virtual ~greeter() = default;
|
||||
|
||||
[[nodiscard]] virtual std::string greet() const = 0;
|
||||
};
|
||||
|
||||
class smart_greeter final : public greeter {
|
||||
public:
|
||||
[[nodiscard]] std::string greet() const override { return "hey dude"; }
|
||||
};
|
||||
|
||||
class hello_greeter final : public greeter {
|
||||
public:
|
||||
[[nodiscard]] std::string greet() const override { return "hello"; }
|
||||
};
|
||||
|
||||
class vehicle
|
||||
{
|
||||
public:
|
||||
virtual ~vehicle() = default;
|
||||
|
||||
[[nodiscard]] virtual long id() const = 0;
|
||||
};
|
||||
|
||||
class truck final : public vehicle
|
||||
{
|
||||
public:
|
||||
truck() : id_(++id_counter_) {}
|
||||
|
||||
[[nodiscard]] long id() const override { return id_; }
|
||||
|
||||
private:
|
||||
static long id_counter_;
|
||||
long id_{};
|
||||
};
|
||||
|
||||
long truck::id_counter_ = 0;
|
||||
|
||||
class unknown {};
|
||||
|
||||
class per_thread {
|
||||
public:
|
||||
virtual ~per_thread() = default;
|
||||
virtual void dump() = 0;
|
||||
};
|
||||
|
||||
class per_thread_dumper final : public per_thread
|
||||
{
|
||||
public:
|
||||
explicit per_thread_dumper(std::string name ) : name_(std::move(name)) {}
|
||||
void dump() override {
|
||||
std::cout << name_ << ": thread id " << std::this_thread::get_id() << "\n";
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using namespace matador::utils;
|
||||
|
||||
TEST_CASE("Test dependency injection", "[dependency-injection]") {
|
||||
di::install_module([](di::module &module) {
|
||||
module.bind<detail::greeter>()->to_singleton<detail::smart_greeter>();
|
||||
});
|
||||
|
||||
di::inject<detail::greeter> g1;
|
||||
|
||||
REQUIRE(g1);
|
||||
|
||||
auto g2 = g1;
|
||||
|
||||
REQUIRE(g2);
|
||||
REQUIRE(g1 == g2);
|
||||
|
||||
g2 = std::move(g1);
|
||||
|
||||
REQUIRE(!g1);
|
||||
REQUIRE(g2);
|
||||
REQUIRE(g1 != g2);
|
||||
|
||||
auto g3(std::move(g2));
|
||||
|
||||
REQUIRE(!g2);
|
||||
REQUIRE(g3);
|
||||
REQUIRE(g3 != g2);
|
||||
|
||||
di::module m;
|
||||
m.bind<detail::greeter>()->to_singleton<detail::hello_greeter>();
|
||||
m.bind<detail::greeter>("smart")->to_singleton<detail::smart_greeter>();
|
||||
|
||||
di::inject<detail::greeter> g4(m, "smart");
|
||||
REQUIRE(g4);
|
||||
REQUIRE(g4->greet() == "hey dude");
|
||||
|
||||
di::inject<detail::greeter> g5(m);
|
||||
REQUIRE(g5);
|
||||
REQUIRE(g5->greet() == "hello");
|
||||
|
||||
REQUIRE(g4 != g5);
|
||||
|
||||
// UNIT_ASSERT_EXCEPTION(matador::di::inject<detail::unknown> u, std::logic_error, "unknown type");
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
|
||||
using namespace matador::utils;
|
||||
|
||||
TEST_CASE("Test field attribute", "[field-attribute]") {
|
||||
field_attributes attr;
|
||||
|
||||
REQUIRE(attr.size() == 0);
|
||||
REQUIRE(attr.options() == constraints::NONE);
|
||||
|
||||
attr = 255;
|
||||
REQUIRE(attr.size() == 255);
|
||||
REQUIRE(attr.options() == constraints::NONE);
|
||||
|
||||
attr = constraints::INDEX;
|
||||
REQUIRE(attr.size() == 0);
|
||||
REQUIRE(attr.options() == constraints::INDEX);
|
||||
|
||||
attr = { 255, constraints::DEFAULT };
|
||||
REQUIRE(attr.size() == 255);
|
||||
REQUIRE(attr.options() == constraints::DEFAULT);
|
||||
|
||||
field_attributes attr2{255};
|
||||
REQUIRE(attr2.size() == 255);
|
||||
REQUIRE(attr2.options() == constraints::NONE);
|
||||
|
||||
field_attributes attr3{constraints::UNIQUE};
|
||||
REQUIRE(attr3.size() == 0);
|
||||
REQUIRE(attr3.options() == constraints::UNIQUE);
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/utils/identifier.hpp"
|
||||
#include "matador/utils/default_type_traits.hpp"
|
||||
|
||||
using namespace matador::utils;
|
||||
|
||||
TEST_CASE("Test create identifier", "[identifier][create]") {
|
||||
const identifier id;
|
||||
|
||||
REQUIRE(id.is_null());
|
||||
REQUIRE(!id.is_integer());
|
||||
REQUIRE(!id.is_floating_point());
|
||||
REQUIRE(!id.is_bool());
|
||||
REQUIRE(!id.is_varchar());
|
||||
REQUIRE(!id.is_date());
|
||||
REQUIRE(!id.is_time());
|
||||
REQUIRE(!id.is_blob());
|
||||
REQUIRE(!id.is_valid());
|
||||
REQUIRE(id.str() == "null");
|
||||
}
|
||||
|
||||
TEST_CASE("Test assign value to identifier", "[identifier][assign]") {
|
||||
identifier id;
|
||||
|
||||
REQUIRE(id.is_null());
|
||||
REQUIRE(!id.is_valid());
|
||||
REQUIRE(id.str() == "null");
|
||||
|
||||
id = 7;
|
||||
|
||||
REQUIRE(!id.is_null());
|
||||
REQUIRE(id.is_valid());
|
||||
REQUIRE(id.is_integer());
|
||||
REQUIRE(id.str() == "7");
|
||||
REQUIRE(id.type() == basic_type::type_int32);
|
||||
REQUIRE(id.type_index() == std::type_index(typeid(int)));
|
||||
|
||||
id = std::string{"UniqueId"};
|
||||
|
||||
REQUIRE(!id.is_null());
|
||||
REQUIRE(id.is_valid());
|
||||
REQUIRE(id.is_varchar());
|
||||
REQUIRE(id.str() == "UniqueId");
|
||||
|
||||
id = "UniqueId";
|
||||
|
||||
REQUIRE(!id.is_null());
|
||||
REQUIRE(id.is_valid());
|
||||
REQUIRE(id.is_varchar());
|
||||
|
||||
// REQUIRE(id == identifier{"UniqueId"});
|
||||
}
|
||||
|
||||
TEST_CASE("Test compare identifier", "[identifier][compare]") {
|
||||
identifier id1{6}, id2{7};
|
||||
|
||||
REQUIRE(id1 != id2);
|
||||
REQUIRE(id1 < id2);
|
||||
REQUIRE(id1 <= id2);
|
||||
REQUIRE(id2 > id1);
|
||||
REQUIRE(id2 >= id1);
|
||||
REQUIRE(id1.hash() != id2.hash());
|
||||
|
||||
id2 = 6;
|
||||
|
||||
REQUIRE(id1 == id2);
|
||||
REQUIRE(id1.hash() == id2.hash());
|
||||
}
|
||||
|
||||
identifier create(const int id) {
|
||||
return identifier{id};
|
||||
}
|
||||
|
||||
TEST_CASE("Test copy identifier" "[identifier][copy]") {
|
||||
identifier id1{"Unique"};
|
||||
REQUIRE(id1.is_valid());
|
||||
REQUIRE(id1.str() == "Unique");
|
||||
|
||||
const identifier id2(id1);
|
||||
|
||||
REQUIRE(id1 == id2);
|
||||
REQUIRE(id1.hash() == id2.hash());
|
||||
|
||||
id1.clear();
|
||||
|
||||
REQUIRE(id1.is_null());
|
||||
identifier id3 = id1;
|
||||
|
||||
REQUIRE(id1 == id3);
|
||||
REQUIRE(id1 < id3);
|
||||
REQUIRE(id3.is_null());
|
||||
REQUIRE(id1.hash() == id3.hash());
|
||||
|
||||
id3 = create(9);
|
||||
id3 = id1;
|
||||
}
|
||||
|
||||
TEST_CASE("Test move identifier", "[identifier][move]") {
|
||||
identifier id1{6};
|
||||
|
||||
REQUIRE(id1.is_integer());
|
||||
REQUIRE(!id1.is_null());
|
||||
|
||||
const auto id2 = std::move(id1);
|
||||
REQUIRE(id1.is_null());
|
||||
REQUIRE(id2.is_integer());
|
||||
}
|
||||
|
||||
TEST_CASE("Test share identifier", "[identifier][share]") {
|
||||
const identifier id1{6};
|
||||
|
||||
REQUIRE(id1.use_count() == 1);
|
||||
|
||||
auto id2 = id1.share();
|
||||
REQUIRE(id1 == id2);
|
||||
REQUIRE(id1.use_count() == 2);
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "matador/utils/result.hpp"
|
||||
|
||||
namespace matador::test {
|
||||
enum class math_error : int32_t {
|
||||
OK = 0,
|
||||
DIVISION_BY_ZERO = 1,
|
||||
FAILURE = 2
|
||||
};
|
||||
|
||||
utils::result<float, math_error>divide(const float x, const float y) {
|
||||
if (y == 0) {
|
||||
return utils::failure(math_error::DIVISION_BY_ZERO);
|
||||
}
|
||||
|
||||
return utils::ok(x / y);
|
||||
}
|
||||
|
||||
utils::result<float, math_error>multiply(const float x, const float y) {
|
||||
return utils::ok(x * y);
|
||||
}
|
||||
|
||||
utils::result<float, math_error>plus(const float x, const float y) {
|
||||
return utils::ok(x + y);
|
||||
}
|
||||
|
||||
utils::result<void, math_error>action_on_greater_42(const float i) {
|
||||
if (i > 42) {
|
||||
return utils::ok<void>();
|
||||
}
|
||||
return utils::failure(math_error::FAILURE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using namespace matador;
|
||||
|
||||
TEST_CASE("Test result", "[result]") {
|
||||
auto res = test::divide(4, 2);
|
||||
REQUIRE(res);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(!res.is_error());
|
||||
|
||||
REQUIRE((res.value() == 2.0));
|
||||
|
||||
REQUIRE_THROWS(res.err());
|
||||
|
||||
res = test::divide(4, 0);
|
||||
REQUIRE(!res);
|
||||
REQUIRE(!res.is_ok());
|
||||
REQUIRE(res.is_error());
|
||||
|
||||
REQUIRE((res.err() == test::math_error::DIVISION_BY_ZERO));
|
||||
|
||||
res = test::divide(4, 2)
|
||||
.and_then([](const auto &val) { return test::multiply(val, 5); })
|
||||
.and_then([](const auto &val) { return test::plus(val, 10); });
|
||||
|
||||
REQUIRE(res);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(!res.is_error());
|
||||
REQUIRE((res.value() == 20.0));
|
||||
|
||||
res = test::divide(4, 0)
|
||||
.and_then([](const auto &val) {
|
||||
return test::multiply(val, 5);
|
||||
});
|
||||
|
||||
REQUIRE(!res);
|
||||
REQUIRE(!res.is_ok());
|
||||
REQUIRE(res.is_error());
|
||||
REQUIRE((res.err() == test::math_error::DIVISION_BY_ZERO));
|
||||
|
||||
auto res2 = test::divide(4, 0)
|
||||
.or_else([](const auto &err) {
|
||||
switch (err) {
|
||||
case test::math_error::DIVISION_BY_ZERO:
|
||||
return utils::failure(std::string("division by zero error"));
|
||||
default:
|
||||
return utils::failure(std::string("unknown error"));
|
||||
}
|
||||
});
|
||||
|
||||
REQUIRE(!res2);
|
||||
REQUIRE(!res2.is_ok());
|
||||
REQUIRE(res2.is_error());
|
||||
const auto e = res2.err();
|
||||
// REQUIRE(res2.err() == "division by zero error");
|
||||
|
||||
res = test::divide(4, 2)
|
||||
.and_then([](const auto &val) { return test::multiply(val, 5); })
|
||||
.map([](const auto &val) { return val + 10; });
|
||||
|
||||
REQUIRE(res);
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(!res.is_error());
|
||||
REQUIRE((res.value() == 20.0));
|
||||
|
||||
auto res_void = test::action_on_greater_42(43);
|
||||
REQUIRE(res_void);
|
||||
REQUIRE(res_void.is_ok());
|
||||
REQUIRE(!res_void.is_error());
|
||||
|
||||
res_void = test::action_on_greater_42(41);
|
||||
REQUIRE(!res_void);
|
||||
REQUIRE(!res_void.is_ok());
|
||||
REQUIRE(res_void.is_error());
|
||||
|
||||
auto res_float = test::divide(4, 2)
|
||||
.and_then([](const auto &val) { return test::action_on_greater_42(val); });
|
||||
REQUIRE(!res_float);
|
||||
REQUIRE(!res_float.is_ok());
|
||||
REQUIRE(res_float.is_error());
|
||||
|
||||
res_void = test::divide(120, 2)
|
||||
.and_then([](const auto &val) { return test::action_on_greater_42(val); });
|
||||
REQUIRE(res_void);
|
||||
REQUIRE(res_void.is_ok());
|
||||
REQUIRE(!res_void.is_error());
|
||||
}
|
||||
|
||||
namespace matador::test {
|
||||
class CustomError {
|
||||
public:
|
||||
CustomError( const math_error err, std::string msg)
|
||||
: error_(err), message_(std::move(msg)) {}
|
||||
|
||||
[[nodiscard]] math_error error() const { return error_; }
|
||||
[[nodiscard]] std::string message() const { return message_; }
|
||||
|
||||
private:
|
||||
math_error error_{};
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
utils::result<float, CustomError> custom_divide(const float x, const float y) {
|
||||
utils::result<float, math_error> res = divide(x, y);
|
||||
|
||||
if (res.is_ok()) {
|
||||
return utils::ok(res.value());
|
||||
}
|
||||
|
||||
return utils::failure<CustomError>({res.err(), "ERROR"});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Test result with custom error", "[result][custom]") {
|
||||
auto res = test::custom_divide(4, 2);
|
||||
|
||||
REQUIRE(res.is_ok());
|
||||
REQUIRE(!res.is_error());
|
||||
|
||||
res = test::custom_divide(4, 0);
|
||||
|
||||
REQUIRE(!res.is_ok());
|
||||
REQUIRE(res.is_error());
|
||||
|
||||
const auto err = res.err();
|
||||
|
||||
REQUIRE(err.error() == test::math_error::DIVISION_BY_ZERO);
|
||||
REQUIRE(err.message() == "ERROR");
|
||||
}
|
||||
|
||||
TEST_CASE("Test result with void type", "[result][void]") {
|
||||
auto res = test::action_on_greater_42(43);
|
||||
|
||||
REQUIRE(res.is_ok());
|
||||
|
||||
res = test::action_on_greater_42(41);
|
||||
|
||||
REQUIRE(res.is_error());
|
||||
REQUIRE(res.err() == test::math_error::FAILURE);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/utils/string.hpp"
|
||||
|
||||
using namespace matador::utils;
|
||||
|
||||
TEST_CASE("Test splitting of string", "[string][split]") {
|
||||
const std::string str("1,2,3,5,6");
|
||||
|
||||
std::vector<std::string> string_vec;
|
||||
|
||||
const size_t count = split(str, ',', string_vec);
|
||||
|
||||
REQUIRE(count == 5);
|
||||
REQUIRE(string_vec.size() == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("Test trimming of string", "[string][trim]") {
|
||||
std::string str(" middle ");
|
||||
|
||||
std::string result = trim(str);
|
||||
|
||||
REQUIRE(result == "middle");
|
||||
|
||||
result = trim(str, "-");
|
||||
|
||||
REQUIRE(result == str);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/utils/version.hpp"
|
||||
|
||||
using namespace matador::utils;
|
||||
|
||||
TEST_CASE("Test version interface", "[version][interface]") {
|
||||
version v0;
|
||||
|
||||
REQUIRE(v0.major() == 0);
|
||||
REQUIRE(v0.minor() == 0);
|
||||
REQUIRE(v0.patch() == 0);
|
||||
REQUIRE(v0.str() == "0.0.0");
|
||||
|
||||
v0.major(1);
|
||||
v0.minor(2);
|
||||
v0.patch(3);
|
||||
|
||||
REQUIRE(v0.major() == 1);
|
||||
REQUIRE(v0.minor() == 2);
|
||||
REQUIRE(v0.patch() == 3);
|
||||
REQUIRE(v0.str() == "1.2.3");
|
||||
|
||||
version v02;
|
||||
|
||||
REQUIRE(v02 < v0);
|
||||
REQUIRE(v02 <= v0);
|
||||
REQUIRE(v0 > v02);
|
||||
REQUIRE(v0 >= v02);
|
||||
REQUIRE(v02 != v0);
|
||||
|
||||
version v1 = v0;
|
||||
|
||||
REQUIRE(v1 == v0);
|
||||
|
||||
version v2(v1);
|
||||
|
||||
REQUIRE(v2 == v1);
|
||||
}
|
||||
|
||||
TEST_CASE("Test version parsing", "[version][parse]") {
|
||||
const auto version_str{"13.67.34"};
|
||||
const auto v1 = version::from_string(version_str);
|
||||
REQUIRE(v1.is_ok());
|
||||
|
||||
REQUIRE(v1->major() == 13);
|
||||
REQUIRE(v1->minor() == 67);
|
||||
REQUIRE(v1->patch() == 34);
|
||||
REQUIRE(v1->str() == version_str);
|
||||
|
||||
const auto v2 = version::from_string("01.02.03");
|
||||
REQUIRE(v2.is_ok());
|
||||
|
||||
REQUIRE(v2->major() == 1);
|
||||
REQUIRE(v2->minor() == 2);
|
||||
REQUIRE(v2->patch() == 3);
|
||||
REQUIRE(v2->str() == "1.2.3");
|
||||
|
||||
const auto v3 = version::from_string("a.b.c");
|
||||
REQUIRE(v3.is_error());
|
||||
REQUIRE(v3.err().message() == "Invalid version string");
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
#ifndef QUERY_AIRPLANE_HPP
|
||||
#define QUERY_AIRPLANE_HPP
|
||||
|
||||
#include "category.hpp"
|
||||
#include "supplier.hpp"
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/cascade_type.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
|
||||
#include "matador/sql/entity.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
struct airplane
|
||||
{
|
||||
airplane() = default;
|
||||
airplane(unsigned long id, std::string b, std::string m)
|
||||
: id(id)
|
||||
, brand(std::move(b))
|
||||
, model(std::move(m)) {}
|
||||
unsigned long id{};
|
||||
std::string brand;
|
||||
std::string model;
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::utils::access;
|
||||
using namespace matador::utils;
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "brand", brand, 255);
|
||||
field::attribute(op, "model", model, 255);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_AIRPLANE_HPP
|
||||
@@ -1,40 +0,0 @@
|
||||
#ifndef QUERY_AUTHOR_HPP
|
||||
#define QUERY_AUTHOR_HPP
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
|
||||
#include "matador/sql/entity.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
struct book;
|
||||
|
||||
struct author
|
||||
{
|
||||
unsigned long id{};
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
std::string date_of_birth;
|
||||
unsigned short year_of_birth{};
|
||||
bool distinguished{false};
|
||||
std::vector<matador::sql::entity<book>> books;
|
||||
|
||||
template<typename Operator>
|
||||
void process(Operator &op)
|
||||
{
|
||||
namespace field = matador::utils::access;
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "first_name", first_name, 63);
|
||||
field::attribute(op, "last_name", last_name, 63);
|
||||
field::attribute(op, "date_of_birth", date_of_birth, 31);
|
||||
field::attribute(op, "year_of_birth", year_of_birth);
|
||||
field::attribute(op, "distinguished", distinguished);
|
||||
field::has_many(op, books, "author_id", utils::fetch_type::LAZY);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_AUTHOR_HPP
|
||||
@@ -1,34 +0,0 @@
|
||||
#ifndef QUERY_BOOK_HPP
|
||||
#define QUERY_BOOK_HPP
|
||||
|
||||
#include "matador/sql/entity.hpp"
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
struct author;
|
||||
|
||||
struct book
|
||||
{
|
||||
unsigned long id{};
|
||||
matador::sql::entity<author> book_author;
|
||||
std::string title;
|
||||
unsigned short published_in{};
|
||||
|
||||
template<typename Operator>
|
||||
void process(Operator &op)
|
||||
{
|
||||
namespace field = matador::utils::access;
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "title", title, 511);
|
||||
field::belongs_to(op, "author_id", book_author, utils::fetch_type::EAGER);
|
||||
field::attribute(op, "published_in", published_in);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_BOOK_HPP
|
||||
@@ -1,25 +0,0 @@
|
||||
#ifndef QUERY_CATEGORY_HPP
|
||||
#define QUERY_CATEGORY_HPP
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
struct category
|
||||
{
|
||||
unsigned long id{};
|
||||
std::string name;
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::utils::access;
|
||||
using namespace matador::utils;
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "name", name, 255);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_CATEGORY_HPP
|
||||
@@ -2,6 +2,7 @@
|
||||
#define QUERY_COORDINATE_HPP
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
|
||||
namespace matador::test {
|
||||
struct coordinate
|
||||
@@ -13,10 +14,10 @@ struct coordinate
|
||||
|
||||
}
|
||||
|
||||
namespace matador::utils::access {
|
||||
namespace matador::access {
|
||||
|
||||
template<class Operator>
|
||||
void attribute(Operator &op, const char *id, test::coordinate &value, const field_attributes &attr = null_attributes) {
|
||||
void attribute(Operator &op, const char *id, test::coordinate &value, const utils::field_attributes &attr = utils::null_attributes) {
|
||||
attribute(op, (std::string(id) + "_x").c_str(), value.x, attr);
|
||||
attribute(op, (std::string(id) + "_y").c_str(), value.y, attr);
|
||||
attribute(op, (std::string(id) + "_z").c_str(), value.z, attr);
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#ifndef QUERY_FLIGHT_HPP
|
||||
#define QUERY_FLIGHT_HPP
|
||||
|
||||
#include "airplane.hpp"
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/cascade_type.hpp"
|
||||
#include "matador/utils/fetch_type.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
|
||||
#include "matador/sql/entity.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
struct flight
|
||||
{
|
||||
flight() = default;
|
||||
flight(unsigned long id, const sql::entity<airplane> &plane, std::string name)
|
||||
: id(id), airplane(plane), pilot_name(std::move(name)) {}
|
||||
|
||||
unsigned long id{};
|
||||
sql::entity<test::airplane> airplane;
|
||||
std::string pilot_name;
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::utils::access;
|
||||
using namespace matador::utils;
|
||||
field::primary_key(op, "id", id);
|
||||
field::has_one(op, "airplane_id", airplane, {utils::cascade_type::ALL, utils::fetch_type::EAGER});
|
||||
field::attribute(op, "pilot_name", pilot_name, 255);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_FLIGHT_HPP
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include "coordinate.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
enum class Color : uint8_t {
|
||||
@@ -21,7 +23,7 @@ struct location
|
||||
template < class Operator >
|
||||
void process(Operator &op)
|
||||
{
|
||||
namespace field = matador::utils::access;
|
||||
namespace field = matador::access;
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "name", name, 255);
|
||||
field::attribute(op, "coordinate", coord);
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#ifndef QUERY_OPTIONAL_HPP
|
||||
#define QUERY_OPTIONAL_HPP
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
struct optional
|
||||
{
|
||||
unsigned long id{};
|
||||
std::optional<std::string> name;
|
||||
std::optional<unsigned int> age{};
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::utils::access;
|
||||
using namespace matador::utils;
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "name", name, 255);
|
||||
field::attribute(op, "age", age);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_OPTIONAL_HPP
|
||||
@@ -1,50 +0,0 @@
|
||||
#ifndef QUERY_ORDER_HPP
|
||||
#define QUERY_ORDER_HPP
|
||||
|
||||
#include "order_details.hpp"
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
|
||||
#include "matador/sql/entity.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
struct order
|
||||
{
|
||||
unsigned long order_id{};
|
||||
std::string order_date;
|
||||
std::string required_date;
|
||||
std::string shipped_date;
|
||||
unsigned int ship_via{};
|
||||
unsigned int freight{};
|
||||
std::string ship_name;
|
||||
std::string ship_address;
|
||||
std::string ship_city;
|
||||
std::string ship_region;
|
||||
std::string ship_postal_code;
|
||||
std::string ship_country;
|
||||
std::vector<sql::entity<order_details>> order_details_;
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::utils::access;
|
||||
field::primary_key(op, "order_id", order_id);
|
||||
field::attribute(op, "order_date", order_date, 255);
|
||||
field::attribute(op, "required_date", required_date, 255);
|
||||
field::attribute(op, "shipped_date", shipped_date, 255);
|
||||
field::attribute(op, "ship_via", ship_via);
|
||||
field::attribute(op, "freight", freight);
|
||||
field::attribute(op, "ship_name", ship_name, 255);
|
||||
field::attribute(op, "ship_address", ship_address, 255);
|
||||
field::attribute(op, "ship_city", ship_city, 255);
|
||||
field::attribute(op, "ship_region", ship_region, 255);
|
||||
field::attribute(op, "ship_postal_code", ship_postal_code, 255);
|
||||
field::attribute(op, "ship_country", ship_country, 255);
|
||||
field::has_many(op, order_details_, "order_id", utils::fetch_type::EAGER);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_ORDER_HPP
|
||||
@@ -1,28 +0,0 @@
|
||||
#ifndef QUERY_ORDER_DETAILS_HPP
|
||||
#define QUERY_ORDER_DETAILS_HPP
|
||||
|
||||
#include "product.hpp"
|
||||
|
||||
#include "matador/sql/entity.hpp"
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
struct order;
|
||||
|
||||
struct order_details
|
||||
{
|
||||
unsigned long order_details_id;
|
||||
sql::entity<order> order_;
|
||||
sql::entity<product> product_;
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::utils::access;
|
||||
field::primary_key(op, "order_details_id", order_details_id);
|
||||
field::belongs_to(op, "order_id", order_, utils::default_foreign_attributes);
|
||||
field::has_one(op, "product_id", product_, utils::default_foreign_attributes);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_ORDER_DETAILS_HPP
|
||||
@@ -1,31 +0,0 @@
|
||||
#ifndef QUERY_PERSON_HPP
|
||||
#define QUERY_PERSON_HPP
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
#include "matador/utils/types.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
struct person
|
||||
{
|
||||
unsigned long id{};
|
||||
std::string name;
|
||||
unsigned int age{};
|
||||
utils::blob image;
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::utils::access;
|
||||
using namespace matador::utils;
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "name", name, 255);
|
||||
field::attribute(op, "age", age);
|
||||
field::attribute(op, "image", image);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_PERSON_HPP
|
||||
@@ -1,46 +0,0 @@
|
||||
#ifndef QUERY_PRODUCT_HPP
|
||||
#define QUERY_PRODUCT_HPP
|
||||
|
||||
#include "category.hpp"
|
||||
#include "supplier.hpp"
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/cascade_type.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
|
||||
#include "matador/sql/entity.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
struct product
|
||||
{
|
||||
std::string product_name;
|
||||
sql::entity<test::supplier> supplier;
|
||||
sql::entity<test::category> category;
|
||||
std::string quantity_per_unit;
|
||||
unsigned int unit_price;
|
||||
unsigned int units_in_stock;
|
||||
unsigned int units_in_order;
|
||||
unsigned int reorder_level;
|
||||
bool discontinued;
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::utils::access;
|
||||
using namespace matador::utils;
|
||||
field::primary_key(op, "product_name", product_name, 255);
|
||||
field::has_one(op, "supplier_id", supplier, utils::cascade_type::ALL);
|
||||
field::has_one(op, "category_id", category, utils::cascade_type::ALL);
|
||||
field::attribute(op, "quantity_per_unit", quantity_per_unit, 255);
|
||||
field::attribute(op, "unit_price", unit_price);
|
||||
field::attribute(op, "units_in_stock", units_in_stock);
|
||||
field::attribute(op, "units_in_order", units_in_order);
|
||||
field::attribute(op, "reorder_level", reorder_level);
|
||||
field::attribute(op, "discontinued", discontinued);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //QUERY_PRODUCT_HPP
|
||||
@@ -1,55 +0,0 @@
|
||||
#ifndef QUERY_RECIPE_HPP
|
||||
#define QUERY_RECIPE_HPP
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
#include "matador/sql/entity.hpp"
|
||||
#include "matador/sql/has_many_to_many_relation.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
struct recipe;
|
||||
struct ingredient
|
||||
{
|
||||
unsigned long id{};
|
||||
std::string name;
|
||||
std::vector<matador::sql::entity<recipe>> recipes;
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::utils::access;
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "name", name, 255);
|
||||
field::has_many_to_many(op, "recipe_ingredients", recipes, "ingredient_id", "recipe_id", utils::fetch_type::EAGER);
|
||||
}
|
||||
};
|
||||
|
||||
struct recipe
|
||||
{
|
||||
unsigned long id{};
|
||||
std::string name;
|
||||
std::vector<matador::sql::entity<ingredient>> ingredients;
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::utils::access;
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "name", name, 255);
|
||||
field::has_many_to_many(op, "recipe_ingredients", ingredients, utils::fetch_type::LAZY);
|
||||
}
|
||||
};
|
||||
|
||||
class recipe_ingredient : public sql::has_many_to_many_relation<recipe, ingredient>
|
||||
{
|
||||
public:
|
||||
recipe_ingredient()
|
||||
: has_many_to_many_relation("recipe_id", "ingredient_id") {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_RECIPE_HPP
|
||||
@@ -1,68 +0,0 @@
|
||||
#ifndef QUERY_STUDENT_HPP
|
||||
#define QUERY_STUDENT_HPP
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
#include "matador/sql/entity.hpp"
|
||||
#include "matador/sql/has_many_to_many_relation.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
class course;
|
||||
|
||||
struct student
|
||||
{
|
||||
unsigned long id{};
|
||||
std::string name;
|
||||
std::vector<matador::sql::entity<course>> courses;
|
||||
|
||||
student() = default;
|
||||
explicit student(unsigned long id, std::string name)
|
||||
: id(id)
|
||||
, name(std::move(name)) {}
|
||||
|
||||
template < class Operator >
|
||||
void process(Operator &op)
|
||||
{
|
||||
namespace field = matador::utils::access;
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "name", name, 255);
|
||||
field::has_many_to_many(op, "student_courses", courses, "student_id", "course_id", utils::fetch_type::LAZY);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct course
|
||||
{
|
||||
unsigned long id{};
|
||||
std::string title;
|
||||
std::vector<matador::sql::entity<student>> students;
|
||||
|
||||
course() = default;
|
||||
explicit course(unsigned long id, std::string title)
|
||||
: id(id)
|
||||
, title(std::move(title)) {}
|
||||
|
||||
template < class Operator >
|
||||
void process(Operator &op)
|
||||
{
|
||||
namespace field = matador::utils::access;
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "title", title, 255);
|
||||
field::has_many_to_many(op, "student_courses", students, utils::fetch_type::EAGER);
|
||||
}
|
||||
};
|
||||
|
||||
class student_course : public sql::has_many_to_many_relation<student, course>
|
||||
{
|
||||
public:
|
||||
student_course()
|
||||
: has_many_to_many_relation("student_id", "course_id") {}
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_STUDENT_HPP
|
||||
@@ -1,25 +0,0 @@
|
||||
#ifndef QUERY_SUPPLIER_HPP
|
||||
#define QUERY_SUPPLIER_HPP
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
struct supplier
|
||||
{
|
||||
unsigned long id{};
|
||||
std::string name;
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::utils::access;
|
||||
using namespace matador::utils;
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "name", name, 255);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_SUPPLIER_HPP
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef QUERY_TYPES_HPP
|
||||
#define QUERY_TYPES_HPP
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/types.hpp"
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
struct types
|
||||
{
|
||||
enum { CSTR_LEN=255 };
|
||||
|
||||
unsigned long id_ = 0;
|
||||
char char_ = 'c';
|
||||
short short_ = -127;
|
||||
int int_ = -65000;
|
||||
long long_ = -128000;
|
||||
long long long64_ = -1234567890;
|
||||
unsigned char unsigned_char_ = 'H';
|
||||
unsigned short unsigned_short_ = 128;
|
||||
unsigned int unsigned_int_ = 65000;
|
||||
unsigned long unsigned_long_ = 128000;
|
||||
unsigned long long unsigned_long64_ = 1234567890;
|
||||
float float_ = 3.1415f;
|
||||
double double_ = 1.1414;
|
||||
bool bool_ = true;
|
||||
char cstr_[CSTR_LEN]{};
|
||||
std::string string_ = "Welt";
|
||||
std::string varchar_ = "Erde";
|
||||
matador::date date_;
|
||||
matador::time time_;
|
||||
utils::blob binary_{ 1, 2, 3, 4 };
|
||||
|
||||
template < class Operator >
|
||||
void process(Operator &op)
|
||||
{
|
||||
namespace field = matador::access;
|
||||
using namespace matador::utils;
|
||||
field::primary_key(op, "id", id_);
|
||||
field::attribute(op, "val_char", char_);
|
||||
field::attribute(op, "val_float", float_);
|
||||
field::attribute(op, "val_double", double_);
|
||||
field::attribute(op, "val_short", short_);
|
||||
field::attribute(op, "val_int", int_);
|
||||
field::attribute(op, "val_long", long_);
|
||||
field::attribute(op, "val_long_long", long64_);
|
||||
field::attribute(op, "val_unsigned_char", unsigned_char_);
|
||||
field::attribute(op, "val_unsigned_short", unsigned_short_);
|
||||
field::attribute(op, "val_unsigned_int", unsigned_int_);
|
||||
field::attribute(op, "val_unsigned_long", unsigned_long_);
|
||||
field::attribute(op, "val_unsigned_long_long", unsigned_long64_);
|
||||
field::attribute(op, "val_bool", bool_);
|
||||
field::attribute(op, "val_cstr", cstr_, CSTR_LEN);
|
||||
field::attribute(op, "val_string", string_);
|
||||
field::attribute(op, "val_varchar", varchar_, 63);
|
||||
field::attribute(op, "val_date", date_);
|
||||
field::attribute(op, "val_time", time_);
|
||||
field::attribute(op, "val_binary", binary_);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // QUERY_TYPES_HPP
|
||||
@@ -0,0 +1,30 @@
|
||||
CPMAddPackage("gh:catchorg/Catch2@3.7.1")
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
|
||||
|
||||
add_executable(OrmTests
|
||||
backend/test_backend_service.cpp
|
||||
backend/test_backend_service.hpp
|
||||
backend/test_connection.cpp
|
||||
backend/test_connection.hpp
|
||||
query/ConditionTests.cpp
|
||||
query/QueryBuilderTest.cpp
|
||||
query/QueryFixture.cpp
|
||||
query/QueryFixture.hpp
|
||||
sql/ColumnTest.cpp
|
||||
sql/FieldTest.cpp
|
||||
backend/test_result_reader.cpp
|
||||
backend/test_result_reader.hpp
|
||||
backend/test_statement.cpp
|
||||
backend/test_statement.hpp
|
||||
backend/test_parameter_binder.cpp
|
||||
backend/test_parameter_binder.hpp
|
||||
query/QueryTest.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(OrmTests matador-orm matador-core Catch2::Catch2WithMain)
|
||||
|
||||
target_compile_options(OrmTests PRIVATE -coverage)
|
||||
target_link_options(OrmTests PRIVATE -coverage)
|
||||
|
||||
add_test(NAME OrmTests COMMAND OrmTests)
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "test_backend_service.hpp"
|
||||
#include "test_connection.hpp"
|
||||
|
||||
#include "matador/sql/dialect_builder.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace matador::test::orm {
|
||||
|
||||
sql::connection_impl *test_backend_service::create(const sql::connection_info &info)
|
||||
{
|
||||
return noop_connections_.insert(std::make_unique<test_connection>(info)).first->get();
|
||||
}
|
||||
|
||||
void test_backend_service::destroy(sql::connection_impl *impl)
|
||||
{
|
||||
auto it = std::find_if(noop_connections_.begin(), noop_connections_.end(), [impl](const auto &item) {
|
||||
return impl == item.get();
|
||||
});
|
||||
if (it != noop_connections_.end()) {
|
||||
noop_connections_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
const sql::dialect *test_backend_service::dialect() const
|
||||
{
|
||||
static sql::dialect dialect_ = sql::dialect_builder::builder().create().build();
|
||||
return &dialect_;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef NOOP_BACKEND_SERVICE_HPP
|
||||
#define NOOP_BACKEND_SERVICE_HPP
|
||||
|
||||
#include "matador/sql/backend_provider.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace matador::test::orm {
|
||||
|
||||
class test_backend_service final : public sql::backend_provider::basic_backend_service {
|
||||
public:
|
||||
sql::connection_impl *create(const sql::connection_info &info) override;
|
||||
void destroy(sql::connection_impl *impl) override;
|
||||
[[nodiscard]] const sql::dialect *dialect() const override;
|
||||
|
||||
private:
|
||||
std::unordered_set<std::unique_ptr<sql::connection_impl>> noop_connections_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //NOOP_BACKEND_SERVICE_HPP
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "test_connection.hpp"
|
||||
#include "test_result_reader.hpp"
|
||||
|
||||
#include "matador/sql/query_context.hpp"
|
||||
#include "matador/sql/record.hpp"
|
||||
|
||||
#include "matador/sql/internal/query_result_impl.hpp"
|
||||
|
||||
#include "matador/sql/interface/statement_impl.hpp"
|
||||
|
||||
#include "matador/utils/string.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace matador::test::orm {
|
||||
test_connection::test_connection(const sql::connection_info &info)
|
||||
: connection_impl(info) {}
|
||||
|
||||
utils::result<void, utils::error> test_connection::open()
|
||||
{
|
||||
is_open_ = true;
|
||||
return utils::ok<void>();
|
||||
}
|
||||
|
||||
utils::result<void, utils::error> test_connection::close()
|
||||
{
|
||||
is_open_ = false;
|
||||
return utils::ok<void>();
|
||||
}
|
||||
|
||||
utils::result<bool, utils::error> test_connection::is_open() const
|
||||
{
|
||||
return utils::ok(is_open_);
|
||||
}
|
||||
|
||||
utils::result<bool, utils::error> test_connection::is_valid() const
|
||||
{
|
||||
return is_open();
|
||||
}
|
||||
|
||||
utils::result<utils::version, utils::error> test_connection::client_version() const {
|
||||
return utils::ok(utils::version{1, 2, 3});
|
||||
}
|
||||
|
||||
utils::result<utils::version, utils::error> test_connection::server_version() const {
|
||||
return utils::ok(utils::version{3, 2, 1});
|
||||
}
|
||||
|
||||
utils::result<size_t, utils::error> test_connection::execute(const std::string &/*stmt*/)
|
||||
{
|
||||
return utils::ok(static_cast<size_t>(4));
|
||||
}
|
||||
|
||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> test_connection::fetch(const sql::query_context &context)
|
||||
{
|
||||
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<test_result_reader>(), context.prototype, context.prototype.size()));
|
||||
}
|
||||
|
||||
utils::result<std::unique_ptr<sql::statement_impl>, utils::error> test_connection::prepare(const sql::query_context &/*context*/)
|
||||
{
|
||||
return utils::ok(std::unique_ptr<sql::statement_impl>{});
|
||||
}
|
||||
|
||||
utils::result<std::vector<sql::column_definition>, utils::error> test_connection::describe(const std::string &/*table*/)
|
||||
{
|
||||
return utils::ok(std::vector<sql::column_definition>{});
|
||||
}
|
||||
|
||||
utils::result<bool, utils::error> test_connection::exists(const std::string &/*schema_name*/, const std::string &/*table_name*/)
|
||||
{
|
||||
return utils::ok(false);
|
||||
}
|
||||
|
||||
std::string test_connection::to_escaped_string(const utils::blob& value) const
|
||||
{
|
||||
return utils::to_string(value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef QUERY_NOOP_CONNECTION_HPP
|
||||
#define QUERY_NOOP_CONNECTION_HPP
|
||||
|
||||
#include "matador/sql/interface/connection_impl.hpp"
|
||||
|
||||
namespace matador::test::orm {
|
||||
|
||||
class test_connection final : public sql::connection_impl
|
||||
{
|
||||
public:
|
||||
explicit test_connection(const sql::connection_info &info);
|
||||
|
||||
utils::result<void, utils::error> open() override;
|
||||
utils::result<void, utils::error> close() override;
|
||||
[[nodiscard]] utils::result<bool, utils::error> is_open() const override;
|
||||
[[nodiscard]] utils::result<bool, utils::error> is_valid() const override;
|
||||
[[nodiscard]] utils::result<utils::version, utils::error> client_version() const override;
|
||||
[[nodiscard]] utils::result<utils::version, utils::error> server_version() const override;
|
||||
utils::result<size_t, utils::error> execute(const std::string &stmt) override;
|
||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::query_context &context) override;
|
||||
utils::result<std::unique_ptr<sql::statement_impl>, utils::error> prepare(const sql::query_context &context) override;
|
||||
utils::result<std::vector<sql::column_definition>, utils::error> describe(const std::string &table) override;
|
||||
utils::result<bool, utils::error> exists(const std::string &schema_name, const std::string &table_name) override;
|
||||
|
||||
[[nodiscard]] std::string to_escaped_string( const utils::blob& value ) const override;
|
||||
|
||||
private:
|
||||
bool is_open_{false};
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_NOOP_CONNECTION_HPP
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "test_parameter_binder.hpp"
|
||||
|
||||
namespace matador::test::orm {
|
||||
void test_parameter_binder::write_value(size_t pos, const int8_t &x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const int16_t &x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const int32_t &x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const int64_t &x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const uint8_t &x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const uint16_t &x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const uint32_t &x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const uint64_t &x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const bool &x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const float &x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const double &x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const time &x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const date &x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const char *x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const char *x, size_t size) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const std::string &x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const std::string &x, size_t size) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const utils::blob &x) {}
|
||||
void test_parameter_binder::write_value(size_t pos, const utils::value &x, size_t size) {}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef TEST_PARAMETER_BINDER_HPP
|
||||
#define TEST_PARAMETER_BINDER_HPP
|
||||
|
||||
#include "matador/utils/attribute_writer.hpp"
|
||||
|
||||
namespace matador::test::orm {
|
||||
|
||||
class test_parameter_binder final : public utils::attribute_writer {
|
||||
public:
|
||||
void write_value(size_t pos, const int8_t &x) override;
|
||||
void write_value(size_t pos, const int16_t &x) override;
|
||||
void write_value(size_t pos, const int32_t &x) override;
|
||||
void write_value(size_t pos, const int64_t &x) override;
|
||||
void write_value(size_t pos, const uint8_t &x) override;
|
||||
void write_value(size_t pos, const uint16_t &x) override;
|
||||
void write_value(size_t pos, const uint32_t &x) override;
|
||||
void write_value(size_t pos, const uint64_t &x) override;
|
||||
void write_value(size_t pos, const bool &x) override;
|
||||
void write_value(size_t pos, const float &x) override;
|
||||
void write_value(size_t pos, const double &x) override;
|
||||
void write_value(size_t pos, const time &x) override;
|
||||
void write_value(size_t pos, const date &x) override;
|
||||
void write_value(size_t pos, const char *x) override;
|
||||
void write_value(size_t pos, const char *x, size_t size) override;
|
||||
void write_value(size_t pos, const std::string &x) override;
|
||||
void write_value(size_t pos, const std::string &x, size_t size) override;
|
||||
void write_value(size_t pos, const utils::blob &x) override;
|
||||
void write_value(size_t pos, const utils::value &x, size_t size) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //TEST_PARAMETER_BINDER_HPP
|
||||
@@ -0,0 +1,92 @@
|
||||
#include "test_result_reader.hpp"
|
||||
|
||||
#include "matador/utils/value.hpp"
|
||||
|
||||
namespace matador::test::orm {
|
||||
|
||||
size_t test_result_reader::column_count() const {
|
||||
return 10;
|
||||
}
|
||||
|
||||
const char *test_result_reader::column(size_t index) const {
|
||||
return "";
|
||||
}
|
||||
|
||||
utils::result<bool, utils::error> test_result_reader::fetch() { return utils::ok(rows_-- > 0); }
|
||||
|
||||
size_t test_result_reader::start_column_index() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, int8_t &value) {
|
||||
value = -8;
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, int16_t &value) {
|
||||
value = -16;
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, int32_t &value) {
|
||||
value = -32;
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, int64_t &value) {
|
||||
value = -64;
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, uint8_t &value) {
|
||||
value = 8;
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, uint16_t &value) {
|
||||
value = 16;
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, uint32_t &value) {
|
||||
value = 32;
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, uint64_t &value) {
|
||||
value = 64;
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, bool &value) {
|
||||
value = true;
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, float &value) {
|
||||
value = 3.141572f;
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, double &value) {
|
||||
value = 2.14159265358979323846;
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, matador::time &value) {
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, matador::date &value) {
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, char *value, const size_t size) {
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, std::string &value) {
|
||||
value = "Lorem ipsum";
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, std::string &value, const size_t size) {
|
||||
value = "Hello world";
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, utils::blob &value) {
|
||||
value = {'b', 'l', 'o', 'b'};
|
||||
}
|
||||
|
||||
void test_result_reader::read_value(const char *id, const size_t index, utils::value &val, const size_t size) {
|
||||
val = "value";
|
||||
}
|
||||
utils::attribute_reader &test_result_reader::result_binder() {
|
||||
return query_result_reader::result_binder();
|
||||
}
|
||||
} // namespace matador::test::orm
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef TEST_RESULT_READER_HPP
|
||||
#define TEST_RESULT_READER_HPP
|
||||
|
||||
#include "matador/sql/interface/query_result_reader.hpp"
|
||||
|
||||
namespace matador::test::orm {
|
||||
|
||||
class test_result_reader final : public sql::query_result_reader {
|
||||
public:
|
||||
[[nodiscard]] size_t column_count() const override;
|
||||
[[nodiscard]] const char *column(size_t index) const override;
|
||||
[[nodiscard]] utils::result<bool, utils::error> fetch() override;
|
||||
[[nodiscard]] size_t start_column_index() const override;
|
||||
|
||||
void read_value(const char *id, size_t index, int8_t &value) override;
|
||||
void read_value(const char *id, size_t index, int16_t &value) override;
|
||||
void read_value(const char *id, size_t index, int32_t &value) override;
|
||||
void read_value(const char *id, size_t index, int64_t &value) override;
|
||||
void read_value(const char *id, size_t index, uint8_t &value) override;
|
||||
void read_value(const char *id, size_t index, uint16_t &value) override;
|
||||
void read_value(const char *id, size_t index, uint32_t &value) override;
|
||||
void read_value(const char *id, size_t index, uint64_t &value) override;
|
||||
void read_value(const char *id, size_t index, bool &value) override;
|
||||
void read_value(const char *id, size_t index, float &value) override;
|
||||
void read_value(const char *id, size_t index, double &value) override;
|
||||
void read_value(const char *id, size_t index, matador::time &value) override;
|
||||
void read_value(const char *id, size_t index, matador::date &value) override;
|
||||
void read_value(const char *id, size_t index, char *value, size_t size) override;
|
||||
void read_value(const char *id, size_t index, std::string &value) override;
|
||||
void read_value(const char *id, size_t index, std::string &value, size_t size) override;
|
||||
void read_value(const char *id, size_t index, utils::blob &value) override;
|
||||
void read_value(const char *id, size_t index, utils::value &val, size_t size) override;
|
||||
|
||||
protected:
|
||||
attribute_reader &result_binder() override;
|
||||
|
||||
private:
|
||||
uint8_t rows_{5};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //TEST_RESULT_READER_HPP
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "test_statement.hpp"
|
||||
#include "test_result_reader.hpp"
|
||||
|
||||
namespace matador::test::orm {
|
||||
test_statement::test_statement(const sql::query_context &query)
|
||||
: statement_impl(query) {}
|
||||
|
||||
utils::result<size_t, utils::error> test_statement::execute() {
|
||||
return utils::ok(static_cast<size_t>(8));
|
||||
}
|
||||
|
||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> test_statement::fetch() {
|
||||
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<test_result_reader>(), query_.prototype, query_.prototype.size()));
|
||||
}
|
||||
|
||||
void test_statement::reset() {}
|
||||
|
||||
utils::attribute_writer &test_statement::binder() {
|
||||
return binder_;
|
||||
}
|
||||
|
||||
} // namespace matador::test::orm
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef TEST_STATEMENT_HPP
|
||||
#define TEST_STATEMENT_HPP
|
||||
|
||||
#include "test_parameter_binder.hpp"
|
||||
|
||||
#include "matador/sql/interface/statement_impl.hpp"
|
||||
|
||||
namespace matador::test::orm {
|
||||
|
||||
class test_statement final : public sql::statement_impl {
|
||||
public:
|
||||
explicit test_statement(const sql::query_context &query);
|
||||
utils::result<size_t, utils::error> execute() override;
|
||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch() override;
|
||||
void reset() override;
|
||||
|
||||
protected:
|
||||
utils::attribute_writer &binder() override;
|
||||
|
||||
private:
|
||||
test_parameter_binder binder_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //TEST_STATEMENT_HPP
|
||||
@@ -0,0 +1,112 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/query/condition.hpp"
|
||||
|
||||
#include "matador/sql/dialect_builder.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
using namespace matador::query;
|
||||
|
||||
class ConditionFixture {
|
||||
protected:
|
||||
dialect dlc = dialect_builder::builder()
|
||||
.create()
|
||||
.build();
|
||||
query_context ctx;
|
||||
};
|
||||
|
||||
TEST_CASE_METHOD(ConditionFixture, "Test column user defined literal", "[column][literal]") {
|
||||
const auto col = "name"_col;
|
||||
|
||||
REQUIRE(col.name == "name");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(ConditionFixture, "Test logical condition", "[condition][logical]") {
|
||||
const auto name_col = "name"_col;
|
||||
|
||||
REQUIRE(name_col.name == "name");
|
||||
|
||||
auto cond1 = name_col != "george";
|
||||
|
||||
auto clause = cond1.evaluate(dlc, ctx);
|
||||
REQUIRE(clause == "\"name\" <> 'george'");
|
||||
|
||||
auto cond2 = "age"_col != 9;
|
||||
clause = cond2.evaluate(dlc, ctx);
|
||||
REQUIRE(clause == "\"age\" <> 9");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(ConditionFixture, "Test and condition", "[condition][bin][and]") {
|
||||
const auto name_col = "name"_col;
|
||||
|
||||
REQUIRE(name_col.name == "name");
|
||||
|
||||
const auto cond = name_col == "Hans" && name_col == "Dieter";
|
||||
|
||||
auto clause = cond.evaluate(dlc, ctx);
|
||||
REQUIRE(clause == "(\"name\" = 'Hans' AND \"name\" = 'Dieter')");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(ConditionFixture, "Test or condition", "[condition][bin][or]") {
|
||||
const auto name_col = "name"_col;
|
||||
|
||||
REQUIRE(name_col.name == "name");
|
||||
|
||||
const auto cond = name_col == "Hans" || name_col == "Dieter";
|
||||
|
||||
auto clause = cond.evaluate(dlc, ctx);
|
||||
REQUIRE(clause == "\"name\" = 'Hans' OR \"name\" = 'Dieter'");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(ConditionFixture, "Test not condition", "[condition][not]") {
|
||||
const auto name_col = "name"_col;
|
||||
|
||||
REQUIRE(name_col.name == "name");
|
||||
|
||||
const auto cond = !(name_col != "Hans");
|
||||
|
||||
auto clause = cond.evaluate(dlc, ctx);
|
||||
REQUIRE(clause == "NOT (\"name\" <> 'Hans')");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(ConditionFixture, "Test in condition", "[condition][in]") {
|
||||
const auto age_col = "age"_col;
|
||||
|
||||
REQUIRE(age_col.name == "age");
|
||||
auto cond = age_col != 7 && in(age_col, {7,5,5,8});
|
||||
|
||||
auto clause = cond.evaluate(dlc, ctx);
|
||||
REQUIRE(clause == "(\"age\" <> 7 AND \"age\" IN (7, 5, 5, 8))");
|
||||
|
||||
cond = age_col != 7 && in(age_col, {7});
|
||||
clause = cond.evaluate(dlc, ctx);
|
||||
REQUIRE(clause == "(\"age\" <> 7 AND \"age\" IN (7))");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(ConditionFixture, "Test in query condition", "[condition][in query]") {
|
||||
const auto age_col = "age"_col;
|
||||
const auto name_col = "name"_col;
|
||||
|
||||
query_context sub_ctx;
|
||||
sub_ctx.sql = R"(SELECT "name" FROM "test")";
|
||||
|
||||
auto cond = age_col != 7 && in(name_col, std::move(sub_ctx));
|
||||
auto clause = cond.evaluate(dlc, ctx);
|
||||
REQUIRE(clause == "(\"age\" <> 7 AND \"name\" IN (SELECT \"name\" FROM \"test\"))");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(ConditionFixture, "Test between condition", "[condition][between]") {
|
||||
const auto age_col = "age"_col;
|
||||
|
||||
auto cond = age_col != 7 && between(age_col, 21, 30);
|
||||
auto clause = cond.evaluate(dlc, ctx);
|
||||
REQUIRE(clause == "(\"age\" <> 7 AND \"age\" BETWEEN 21 AND 30)");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(ConditionFixture, "Test like condition", "[condition][like]") {
|
||||
const auto name_col = "name"_col;
|
||||
|
||||
auto cond = like(name_col, "%er%");
|
||||
auto clause = cond.evaluate(dlc, ctx);
|
||||
REQUIRE(clause == "\"name\" LIKE '%er%'");
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "QueryFixture.hpp"
|
||||
|
||||
#include <matador/query/condition.hpp>
|
||||
#include <matador/query/query.hpp>
|
||||
|
||||
#include <matador/sql/column_definition.hpp>
|
||||
#include <matador/sql/connection.hpp>
|
||||
#include <matador/sql/table.hpp>
|
||||
|
||||
#include <matador/utils/placeholder.hpp>
|
||||
|
||||
// #include "models/author.hpp"
|
||||
// #include "models/book.hpp"
|
||||
|
||||
using namespace matador::test;
|
||||
using namespace matador::sql;
|
||||
using namespace matador::query;
|
||||
using namespace matador::utils;
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test create table sql statement string", "[query]") {
|
||||
auto result = query::create()
|
||||
.table({"person"}, {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age")
|
||||
}).str(*db);
|
||||
|
||||
REQUIRE(result == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "age" INTEGER NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##");
|
||||
|
||||
result = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", {255, constraints::UNIQUE}, null_option::NOT_NULL),
|
||||
make_column<unsigned short>("age"),
|
||||
make_fk_column<unsigned long>("address", "address", "id")
|
||||
}).str(*db);
|
||||
|
||||
REQUIRE(result == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL UNIQUE, "age" INTEGER NOT NULL, "address" BIGINT NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id), CONSTRAINT FK_person_address FOREIGN KEY (address) REFERENCES address(id)))##");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test drop table sql statement string", "[query]") {
|
||||
const auto result = query::drop()
|
||||
.table("person")
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(DROP TABLE "person")");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test select sql statement string", "[query]") {
|
||||
const auto result = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person")");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test insert sql statement string", "[query]") {
|
||||
const auto result = query::insert()
|
||||
.into("person", {
|
||||
"id", "name", "age"
|
||||
})
|
||||
.values({7UL, "george", 65U})
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "age") VALUES (7, 'george', 65))");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test update sql statement string", "[query]") {
|
||||
auto result = query::update("person")
|
||||
.set({
|
||||
{"id", 7UL},
|
||||
{"name", "george"},
|
||||
{"age", 65U}
|
||||
})
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65)");
|
||||
|
||||
result = query::update("person")
|
||||
.set({
|
||||
{"id", 7UL},
|
||||
{"name", "george"},
|
||||
{"age", 65U}
|
||||
})
|
||||
.where("id"_col > 9)
|
||||
.order_by("id").asc()
|
||||
.limit(3)
|
||||
.offset(2)
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65 WHERE "id" > 9 ORDER BY "id" ASC LIMIT 3 OFFSET 2)");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test update limit sql statement", "[query][update][limit]") {
|
||||
const auto result = query::update("person")
|
||||
.set({{"id", 7UL}, {"name", "george"}, {"age", 65U}})
|
||||
.where("name"_col == "george")
|
||||
.order_by("id"_col).asc()
|
||||
.limit(2)
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65 WHERE "name" = 'george' ORDER BY "id" ASC LIMIT 2)");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test delete sql statement string", "[query]") {
|
||||
const auto result = query::remove().from("person").str(*db);
|
||||
|
||||
REQUIRE(result == R"(DELETE FROM "person")");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test delete limit sql statement", "[query][delete][limit]") {
|
||||
const auto result = query::remove()
|
||||
.from("person")
|
||||
.where("name"_col == "george")
|
||||
.order_by("id"_col).asc()
|
||||
.limit(2)
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(DELETE FROM "person" WHERE "name" = 'george' ORDER BY "id" ASC LIMIT 2)");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test select sql statement string with where clause", "[query]") {
|
||||
auto result = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.where("id"_col == 8 && "age"_col > 50)
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = 8 AND "age" > 50))");
|
||||
|
||||
result = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.where("id"_col == _ && "age"_col > 50)
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = ? AND "age" > 50))");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test insert sql statement with placeholder", "[query]") {
|
||||
auto result = query::insert()
|
||||
.into("person", {"id", "name", "age"})
|
||||
.values({_, _, _})
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "age") VALUES (?, ?, ?))");
|
||||
|
||||
result = query::insert()
|
||||
.into("person", {"id", "name", "age"})
|
||||
.values({9, "george", _})
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "age") VALUES (9, 'george', ?))");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test select sql statement string with order by", "[query]") {
|
||||
const auto result = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.order_by("name"_col).asc()
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "name" ASC)");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test select sql statement string with group by", "[query]") {
|
||||
const auto result = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.group_by("age"_col)
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" GROUP BY "age")");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test select sql statement string with offset and limit", "[query]") {
|
||||
const auto result = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.order_by("id"_col).asc()
|
||||
.limit(20)
|
||||
.offset(10)
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "id" ASC LIMIT 20 OFFSET 10)");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test create, insert and select a blob column", "[query][blob]") {
|
||||
auto result = query::create()
|
||||
.table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<blob>("data")
|
||||
})
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "data" BLOB NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##");
|
||||
|
||||
result = query::insert()
|
||||
.into("person", {"id", "name", "data"})
|
||||
.values({7UL, "george", blob{1, 'A', 3, 4}})
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "data") VALUES (7, 'george', X'01410304'))");
|
||||
|
||||
result = query::select({"id", "name", "data"})
|
||||
.from("person")
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(SELECT "id", "name", "data" FROM "person")");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test select statement with join_left", "[query][statement][join_left]") {
|
||||
const auto result = query::select({"f.id", "ap.brand", "f.pilot_name"})
|
||||
.from({"flight", "f"})
|
||||
.join_left({"airplane", "ap"})
|
||||
.on("f.airplane_id"_col == "ap.id"_col)
|
||||
.str(*db);
|
||||
|
||||
REQUIRE(result == R"(SELECT "f"."id", "ap"."brand", "f"."pilot_name" FROM "flight" "f" LEFT JOIN "airplane" "ap" ON "f"."airplane_id" = "ap"."id")");
|
||||
}
|
||||
|
||||
// TEST_CASE_METHOD(QueryFixture, "Select statement with aliased columns", "[query][select][alias]") {
|
||||
// using namespace matador::test;
|
||||
// connection noop("noop://noop.db");
|
||||
// schema scm("noop");
|
||||
// scm.attach<author>("authors");
|
||||
// scm.attach<book>("books");
|
||||
//
|
||||
//
|
||||
// const auto result = query::select<author>(scm)
|
||||
// .from("authors"_tab.as("T01"))
|
||||
// .str(*db);
|
||||
//
|
||||
// const auto expected_sql = R"(SELECT "T01"."id", "T01"."first_name", "T01"."last_name", "T01"."date_of_birth", "T01"."year_of_birth", "T01"."distinguished" FROM "authors" "T01")";
|
||||
//
|
||||
// REQUIRE(result == expected_sql);
|
||||
// }
|
||||
@@ -0,0 +1,15 @@
|
||||
#include "QueryFixture.hpp"
|
||||
|
||||
#include "../backend/test_backend_service.hpp"
|
||||
|
||||
#include "matador/sql/interface/connection_impl.hpp"
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
QueryFixture::QueryFixture() {
|
||||
sql::backend_provider::instance().register_backend("noop", std::make_unique<orm::test_backend_service>());
|
||||
|
||||
db = std::make_unique<sql::connection>("noop://noop.db");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef QUERY_FIXTURE_HPP
|
||||
#define QUERY_FIXTURE_HPP
|
||||
|
||||
#include "matador/sql/connection.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace matador::test {
|
||||
|
||||
class QueryFixture {
|
||||
public:
|
||||
QueryFixture();
|
||||
~QueryFixture() = default;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<sql::connection> db;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_FIXTURE_HPP
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <matador/query/query.hpp>
|
||||
|
||||
#include "QueryFixture.hpp"
|
||||
|
||||
using namespace matador::test;
|
||||
using namespace matador::query;
|
||||
|
||||
TEST_CASE_METHOD(QueryFixture, "Test simple query", "[query][fetch]") {
|
||||
auto result = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.fetch_all(*db);
|
||||
|
||||
REQUIRE(result.is_ok());
|
||||
|
||||
for (const auto& row : *result) {
|
||||
REQUIRE(row.size() == 3);
|
||||
}
|
||||
|
||||
auto single = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.fetch_one(*db);
|
||||
|
||||
REQUIRE(single.is_ok());
|
||||
REQUIRE(single.value().has_value());
|
||||
REQUIRE(single.value().value().size() == 3);
|
||||
|
||||
auto val = query::select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.fetch_value<int>(*db);
|
||||
|
||||
REQUIRE(val.is_ok());
|
||||
REQUIRE(val.value().has_value());
|
||||
// REQUIRE(val.value().value() == -1);
|
||||
}
|
||||
@@ -3,35 +3,36 @@
|
||||
#include "matador/sql/column_definition.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
using namespace matador::utils;
|
||||
|
||||
TEST_CASE("Create empty column", "[column]") {
|
||||
TEST_CASE("Test create empty column", "[column]") {
|
||||
column_definition c("name");
|
||||
|
||||
REQUIRE(c.name() == "name");
|
||||
REQUIRE(c.index() == -1);
|
||||
REQUIRE(c.type() == data_type_t::type_unknown);
|
||||
REQUIRE(c.type() == basic_type::type_null);
|
||||
REQUIRE(c.ref_table().empty());
|
||||
REQUIRE(c.ref_column().empty());
|
||||
|
||||
c.set(std::string{"george"}, 255);
|
||||
REQUIRE(c.type() == data_type_t::type_varchar);
|
||||
REQUIRE(c.type() == basic_type::type_varchar);
|
||||
REQUIRE(c.as<std::string>() == "george");
|
||||
|
||||
c.set(7);
|
||||
REQUIRE(c.type() == data_type_t::type_int);
|
||||
REQUIRE(c.type() == basic_type::type_int32);
|
||||
REQUIRE(c.as<std::string>() == "7");
|
||||
REQUIRE(c.as<long>() == 7);
|
||||
REQUIRE(c.str() == "7");
|
||||
}
|
||||
|
||||
TEST_CASE("Copy and move column", "[column]") {
|
||||
TEST_CASE("Test copy and move column", "[column]") {
|
||||
column_definition c("name");
|
||||
c.set(std::string{"george"}, 255);
|
||||
REQUIRE(c.name() == "name");
|
||||
REQUIRE(c.index() == -1);
|
||||
REQUIRE(c.ref_table().empty());
|
||||
REQUIRE(c.ref_column().empty());
|
||||
REQUIRE(c.type() == data_type_t::type_varchar);
|
||||
REQUIRE(c.type() == basic_type::type_varchar);
|
||||
REQUIRE(c.as<std::string>() == "george");
|
||||
REQUIRE(c.attributes().size() == 255);
|
||||
|
||||
@@ -40,7 +41,7 @@ TEST_CASE("Copy and move column", "[column]") {
|
||||
REQUIRE(c2.index() == -1);
|
||||
REQUIRE(c2.ref_table().empty());
|
||||
REQUIRE(c2.ref_column().empty());
|
||||
REQUIRE(c2.type() == data_type_t::type_varchar);
|
||||
REQUIRE(c2.type() == basic_type::type_varchar);
|
||||
REQUIRE(c2.as<std::string>() == "george");
|
||||
REQUIRE(c2.attributes().size() == 255);
|
||||
|
||||
@@ -49,7 +50,7 @@ TEST_CASE("Copy and move column", "[column]") {
|
||||
REQUIRE(c3.index() == -1);
|
||||
REQUIRE(c3.ref_table().empty());
|
||||
REQUIRE(c3.ref_column().empty());
|
||||
REQUIRE(c3.type() == data_type_t::type_varchar);
|
||||
REQUIRE(c3.type() == basic_type::type_varchar);
|
||||
REQUIRE(c3.as<std::string>() == "george");
|
||||
REQUIRE(c3.attributes().size() == 255);
|
||||
|
||||
@@ -57,7 +58,7 @@ TEST_CASE("Copy and move column", "[column]") {
|
||||
REQUIRE(c2.index() == -1);
|
||||
REQUIRE(c2.ref_table().empty());
|
||||
REQUIRE(c2.ref_column().empty());
|
||||
REQUIRE(c2.type() == data_type_t::type_varchar);
|
||||
REQUIRE(c2.as<std::string>().empty());
|
||||
REQUIRE(c2.type() == basic_type::type_null);
|
||||
// REQUIRE(!c2.as<std::string>().has_value());
|
||||
REQUIRE(c2.attributes().size() == 255);
|
||||
}
|
||||
@@ -4,12 +4,11 @@
|
||||
|
||||
using namespace matador;
|
||||
|
||||
TEST_CASE("Field test", "[field]") {
|
||||
TEST_CASE("Test field", "[field]") {
|
||||
sql::field f("name");
|
||||
|
||||
REQUIRE(f.name() == "name");
|
||||
REQUIRE(f.index() == -1);
|
||||
REQUIRE(!f.is_unknown());
|
||||
REQUIRE(f.is_null());
|
||||
REQUIRE(!f.is_integer());
|
||||
REQUIRE(!f.is_floating_point());
|
||||
@@ -51,5 +50,5 @@ TEST_CASE("Field test", "[field]") {
|
||||
REQUIRE(blob_val.has_value());
|
||||
REQUIRE(blob_val.value() == utils::blob{ 7,8,6,5,4,3 });
|
||||
|
||||
REQUIRE_THROWS_AS(f.as<std::string>(), std::logic_error);
|
||||
REQUIRE(!f.as<std::string>().has_value());
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#include "auto_reset_event.hpp"
|
||||
|
||||
namespace matador::test::utils {
|
||||
|
||||
auto_reset_event::auto_reset_event() : state(false) {}
|
||||
|
||||
void auto_reset_event::wait_one()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(sync);
|
||||
underlying.wait(lock, [this](){return state.load();});
|
||||
state = false;
|
||||
}
|
||||
|
||||
void auto_reset_event::set()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(sync);
|
||||
state = true;
|
||||
underlying.notify_one();
|
||||
}
|
||||
|
||||
void auto_reset_event::reset()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(sync);
|
||||
state = false;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#ifndef QUERY_AUTO_RESET_EVENT_HPP
|
||||
#define QUERY_AUTO_RESET_EVENT_HPP
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
namespace matador::test::utils {
|
||||
|
||||
class auto_reset_event
|
||||
{
|
||||
public:
|
||||
auto_reset_event();
|
||||
auto_reset_event(const auto_reset_event& other) = delete;
|
||||
|
||||
void wait_one();
|
||||
void set();
|
||||
void reset();
|
||||
|
||||
private:
|
||||
std::condition_variable underlying;
|
||||
std::mutex sync;
|
||||
std::atomic<bool> state;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_AUTO_RESET_EVENT_HPP
|
||||
Reference in New Issue
Block a user