initial matador ng commit
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user