introduced table repository

This commit is contained in:
Sascha Kuehl 2023-11-16 20:15:52 +01:00
parent 7c1e940814
commit c801d53e8c
25 changed files with 589 additions and 150 deletions

View File

@ -183,36 +183,53 @@ bool sqlite_query_result::fetch()
return ++row_index_ < result_.size();
}
template < typename Type >
void convert(const char *valstr, sql::any_type &value)
{
Type val{};
read(val, valstr);
value = val;
}
void sqlite_query_result::read_value(const char *id, size_t index, sql::any_type &value, sql::data_type_t type, size_t size)
{
switch (type) {
case sql::data_type_t::type_char:
convert<char>(result_[row_index_][index], value);
break;
case sql::data_type_t::type_short:
convert<short>(result_[row_index_][index], value);
break;
case sql::data_type_t::type_int:
convert<int>(result_[row_index_][index], value);
break;
case sql::data_type_t::type_long:
case sql::data_type_t::type_long_long: {
long long val{};
read(val, result_[row_index_][index]);
value = val;
convert<long>(result_[row_index_][index], value);
break;
case sql::data_type_t::type_long_long:
convert<long long>(result_[row_index_][index], value);
break;
}
case sql::data_type_t::type_unsigned_char:
convert<unsigned char>(result_[row_index_][index], value);
break;
case sql::data_type_t::type_unsigned_short:
convert<unsigned short>(result_[row_index_][index], value);
break;
case sql::data_type_t::type_unsigned_int:
convert<unsigned int>(result_[row_index_][index], value);
break;
case sql::data_type_t::type_unsigned_long:
case sql::data_type_t::type_unsigned_long_long: {
unsigned long long val{};
read(val, result_[row_index_][index]);
value = val;
convert<unsigned long>(result_[row_index_][index], value);
break;
case sql::data_type_t::type_unsigned_long_long:
convert<unsigned long long>(result_[row_index_][index], value);
break;
}
case sql::data_type_t::type_float:
case sql::data_type_t::type_double: {
double val{};
read(val, result_[row_index_][index]);
value = val;
convert<float>(result_[row_index_][index], value);
break;
case sql::data_type_t::type_double:
convert<double>(result_[row_index_][index], value);
break;
}
case sql::data_type_t::type_bool: {
int val{};
read(val, result_[row_index_][index]);

View File

@ -9,8 +9,8 @@ namespace matador::sql {
using any_type = std::variant<
char, short, int, long, long long,
unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long,
bool,
float, double,
bool,
const char*,
std::string,
nullptr_t>;

View File

@ -6,7 +6,8 @@
#include "matador/utils/field_attributes.hpp"
#include <any>
#include <optional>
#include <vector>
namespace matador::sql {
@ -37,6 +38,17 @@ public:
[[nodiscard]] const std::string& ref_table() const;
[[nodiscard]] const std::string& ref_column() const;
template< typename Type >
[[nodiscard]] bool is_type_of() const {
return std::holds_alternative<Type>(value_);
}
template< typename Type >
std::optional<Type> value() const {
const Type* ptr= std::get_if<Type>(&value_);
return ptr ? std::make_optional<Type>(*ptr) : std::nullopt;
}
private:
template<class Operator>
void process(Operator &op)
@ -44,9 +56,13 @@ private:
op.on_attribute(name_.c_str(), value_, type_, attributes_);
}
using data_type_index = std::vector<data_type_t>;
private:
friend class record;
static const data_type_index data_type_index_;
std::string name_;
utils::field_attributes attributes_;
data_type_t type_{};

View File

@ -2,6 +2,7 @@
#define QUERY_COLUMN_GENERATOR_HPP
#include "matador/sql/column.hpp"
#include "matador/sql/table_repository.hpp"
#include "matador/sql/types.hpp"
#include "matador/utils/access.hpp"
@ -18,10 +19,10 @@ public:
fk_column_generator() = default;
template<class Type>
column generate(const char *id, Type &x)
column generate(const char *id, Type &x, const std::string &ref_table, const std::string &ref_column)
{
utils::access::process(*this, x);
return column{id, type_, { utils::constraints::FOREIGN_KEY }};
return column{id, type_, ref_table, ref_column, { utils::constraints::FOREIGN_KEY }};
}
template<typename ValueType>
@ -50,16 +51,16 @@ private:
class column_generator
{
private:
explicit column_generator(std::vector<column> &columns);
column_generator(std::vector<column> &columns, const table_repository &repo);
public:
~column_generator() = default;
template < class Type >
static std::vector<column> generate()
static std::vector<column> generate(const table_repository &repo)
{
std::vector<column> columns;
column_generator gen(columns);
column_generator gen(columns, repo);
Type obj;
matador::utils::access::process(gen, obj);
return std::move(columns);
@ -76,21 +77,27 @@ public:
template<class Pointer>
void on_belongs_to(const char *id, Pointer &x, utils::cascade_type)
{
columns_.push_back(fk_column_generator_.generate(id, *x));
const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type)));
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column));
}
template<class Pointer>
void on_has_one(const char *id, Pointer &x, utils::cascade_type)
{
columns_.push_back(fk_column_generator_.generate(id, *x));
const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type)));
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column));
}
template<class ContainerType>
void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {}
template<class ContainerType>
void on_has_many(const char *, ContainerType &, utils::cascade_type) {}
private:
std::pair<std::string, std::string> determine_foreign_ref(const std::type_index &ti);
private:
size_t index_ = 0;
std::vector<column> &columns_;
const table_repository &repo_;
fk_column_generator fk_column_generator_;
};

View File

@ -9,18 +9,22 @@ template < class Type >
class foreign
{
public:
using value_type = Type;
using pointer = value_type*;
using reference = value_type&;
foreign() = default;
explicit foreign(Type *obj)
: obj_(obj) {}
Type* operator->() { return obj_.get(); }
const Type* operator->() const { return obj_.get(); }
pointer operator->() { return obj_.get(); }
const value_type* operator->() const { return obj_.get(); }
Type& operator*() { return *obj_; }
const Type& operator*() const { return *obj_; }
reference operator*() { return *obj_; }
const value_type& operator*() const { return *obj_; }
private:
std::unique_ptr<Type> obj_;
std::unique_ptr<value_type> obj_;
};
template<class Type, typename... Args>

View File

@ -11,7 +11,6 @@
#include <unordered_set>
#include <vector>
namespace matador::sql {
class dialect;
@ -97,6 +96,7 @@ public:
query_builder& create();
query_builder& drop();
query_builder& select(std::initializer_list<std::string> column_names);
query_builder& select(const std::vector<std::string> &column_names);
query_builder& insert();
query_builder& update(const std::string &table);
query_builder& remove();

View File

@ -9,6 +9,7 @@
#include "matador/sql/query_builder.hpp"
#include "matador/sql/query_result.hpp"
#include "matador/sql/record.hpp"
#include "matador/sql/table_repository.hpp"
#include "matador/sql/value_extractor.hpp"
#include <string>
@ -142,14 +143,18 @@ public:
class query_create_intermediate : query_intermediate
{
public:
using query_intermediate::query_intermediate;
query_create_intermediate(session &db, query_builder &query, table_repository &repo);
query_execute_finish table(const std::string &table, std::initializer_list<column> columns);
template<class Type>
query_execute_finish table(const std::string &table)
query_execute_finish table(const std::string &table_name)
{
return {db(), query().table(table, column_generator::generate<Type>())};
const auto &info = repository_.attach<Type>(table_name, record{column_generator::generate<Type>(repository_)});
return {db(), query().table(table_name, info.prototype.columns())};
}
private:
table_repository &repository_;
};
class query_drop_intermediate : query_intermediate

View File

@ -23,6 +23,7 @@ public:
record() = default;
record(std::initializer_list<column> columns);
explicit record(const std::vector<column> &columns);
record(const record&) = default;
record& operator=(const record&) = default;
record(record&&) noexcept = default;
@ -44,8 +45,13 @@ public:
void append(column col);
bool has_primary_key() const;
const column& primary_key() const;
[[nodiscard]] const std::vector<column>& columns() const;
[[nodiscard]] const column& at(const std::string &name) const;
const column& at(size_t index) const;
[[nodiscard]] const column& at(size_t index) const;
iterator find(const std::string &column_name);
[[nodiscard]] const_iterator find(const std::string &column_name) const;
@ -62,9 +68,15 @@ public:
[[nodiscard]] bool empty() const;
void clear();
private:
void init();
void add_to_map(column &col, size_t index);
private:
column_by_index columns_;
column_by_name_map columns_by_name_;
int pk_index_{-1};
};
}

View File

@ -1,10 +1,14 @@
#ifndef QUERY_SESSION_HPP
#define QUERY_SESSION_HPP
#include "matador/sql/column_name_generator.hpp"
#include "matador/sql/connection.hpp"
#include "matador/sql/connection_pool.hpp"
#include "matador/sql/query_builder.hpp"
#include "matador/sql/query_intermediates.hpp"
#include "matador/sql/table_repository.hpp"
#include <unordered_map>
namespace matador::sql {
@ -15,6 +19,11 @@ public:
query_create_intermediate create();
query_drop_intermediate drop();
template < class Type >
query_select_intermediate select()
{
return query_select_intermediate{*this, query_.select(column_name_generator::generate<Type>())};
}
query_select_intermediate select(std::initializer_list<std::string> column_names);
query_insert_intermediate insert();
query_update_intermediate update(const std::string &table);
@ -23,9 +32,19 @@ public:
query_result<record> fetch(const std::string &sql);
std::pair<size_t, std::string> execute(const std::string &sql);
template<typename Type>
void attach(const std::string &table_name)
{
table_repository_.attach<Type>(table_name);
}
[[nodiscard]] const table_repository& tables() const;
private:
connection_pool<connection> &pool_;
query_builder query_;
table_repository table_repository_;
};
}

View File

@ -0,0 +1,55 @@
#ifndef QUERY_TABLE_REPOSITORY_HPP
#define QUERY_TABLE_REPOSITORY_HPP
#include "matador/sql/record.hpp"
#include <optional>
#include <string>
#include <typeindex>
#include <unordered_map>
namespace matador::sql {
struct table_info
{
std::string name;
record prototype;
};
class table_repository
{
public:
template<typename Type>
const table_info& attach(const std::string &table_name, const record &proto)
{
return attach(std::type_index(typeid(Type)), table_name, proto);
}
const table_info& attach(std::type_index ti, const std::string &table_name, const record &proto);
const table_info& attach(std::type_index ti, const table_info& table);
template<typename Type>
std::optional<table_info> info()
{
return info(std::type_index(typeid(Type)));
}
std::optional<table_info> info(std::type_index ti);
template<typename Type>
[[nodiscard]] std::pair<std::string, std::string> reference() const
{
return reference(std::type_index(typeid(Type)));
}
[[nodiscard]] std::pair<std::string, std::string> reference(const std::type_index &ti) const;
private:
using repository = std::unordered_map<std::type_index, table_info>;
repository repository_;
};
;
}
#endif //QUERY_TABLE_REPOSITORY_HPP

View File

@ -11,6 +11,7 @@ enum class constraints : unsigned char {
PRIMARY_KEY = 1 << 3,
FOREIGN_KEY = 1 << 4,
DEFAULT = 1 << 5,
AUTO_INCREMENT = 1 << 6,
UNIQUE_NOT_NULL = UNIQUE | NOT_NULL
};

View File

@ -15,7 +15,8 @@ set(SQL_SOURCES
sql/column_generator.cpp
sql/column_name_generator.cpp
sql/key_value_generator.cpp
sql/fk_value_extractor.cpp)
sql/fk_value_extractor.cpp
sql/table_repository.cpp)
set(SQL_HEADER
../include/matador/sql/dialect.hpp
@ -42,7 +43,8 @@ set(SQL_HEADER
../include/matador/sql/any_type.hpp
../include/matador/sql/key_value_generator.hpp
../include/matador/sql/foreign.hpp
../include/matador/sql/fk_value_extractor.hpp)
../include/matador/sql/fk_value_extractor.hpp
"../include/matador/sql/table_repository.hpp")
set(UTILS_HEADER
../include/matador/utils/field_attributes.hpp

View File

@ -3,6 +3,27 @@
#include "matador/sql/column.hpp"
namespace matador::sql {
const column::data_type_index column::data_type_index_ {
data_type_t::type_char,
data_type_t::type_short,
data_type_t::type_int,
data_type_t::type_long,
data_type_t::type_long_long,
data_type_t::type_unsigned_char,
data_type_t::type_unsigned_short,
data_type_t::type_unsigned_int,
data_type_t::type_unsigned_long,
data_type_t::type_unsigned_long_long,
data_type_t::type_float,
data_type_t::type_double,
data_type_t::type_bool,
data_type_t::type_char_pointer,
data_type_t::type_varchar,
data_type_t::type_text,
data_type_t::type_null,
};
column::column(std::string name, data_type_t type, utils::field_attributes attr)
: name_(std::move(name))
, type_(type)

View File

@ -2,8 +2,10 @@
namespace matador::sql {
column_generator::column_generator(std::vector<column> &columns)
: columns_(columns) {}
column_generator::column_generator(std::vector<column> &columns, const table_repository &repo)
: columns_(columns)
, repo_(repo)
{}
void column_generator::on_primary_key(const char *id, std::string &pk, size_t size)
{
@ -15,6 +17,11 @@ void column_generator::on_revision(const char *id, unsigned long long int &x)
on_attribute(id, x);
}
std::pair<std::string, std::string> column_generator::determine_foreign_ref(const std::type_index &ti)
{
return repo_.reference(ti);
}
void fk_column_generator::on_primary_key(const char *, std::string &, size_t size)
{
type_ = data_type_traits<std::string>::builtin_type(size);

View File

@ -98,6 +98,11 @@ query_builder& query_builder::drop() {
}
query_builder& query_builder::select(std::initializer_list<std::string> column_names)
{
return select(std::vector<std::string>{column_names});
}
query_builder& query_builder::select(const std::vector<std::string> &column_names)
{
initialize(command_t::SELECT, state_t::QUERY_SELECT);

View File

@ -100,6 +100,10 @@ query_execute_finish query_into_intermediate::values(std::initializer_list<any_t
return {db(), query().values(values)};
}
query_create_intermediate::query_create_intermediate(session &db, query_builder &query, table_repository &repo)
: query_intermediate(db, query)
, repository_(repo) {}
query_execute_finish query_create_intermediate::table(const std::string &table, std::initializer_list<column> columns)
{
return {db(), query().table(table, columns)};

View File

@ -1,13 +1,38 @@
#include "matador/sql/record.hpp"
#include <stdexcept>
namespace matador::sql {
record::record(std::initializer_list<column> columns)
: columns_(columns) {
size_t index{0};
for(auto &col : columns_) {
columns_by_name_.emplace(col.name(), column_index_pair {std::ref(col), index++});
: columns_(columns)
{
init();
}
record::record(const std::vector<column> &columns)
: columns_(columns)
{
init();
}
bool record::has_primary_key() const
{
return pk_index_ > -1;
}
const column &record::primary_key() const
{
if (!has_primary_key()) {
throw std::logic_error("record has no primary key");
}
return columns_[pk_index_];
}
const std::vector<column> &record::columns() const
{
return columns_;
}
const column &record::at(const std::string &name) const
@ -34,7 +59,7 @@ record::const_iterator record::find(const std::string &column_name) const {
void record::append(column col)
{
auto &ref = columns_.emplace_back(std::move(col));
columns_by_name_.emplace(ref.name(), column_index_pair{std::ref(ref), columns_.size()-1});
add_to_map(ref, columns_.size()-1);
}
record::iterator record::begin()
@ -83,4 +108,21 @@ void record::clear()
columns_by_name_.clear();
}
void record::init()
{
size_t index{0};
for(auto &col : columns_) {
add_to_map(col, index++);
// columns_by_name_.emplace(col.name(), column_index_pair {std::ref(col), index++});
}
}
void record::add_to_map(column &col, size_t index)
{
columns_by_name_.emplace(col.name(), column_index_pair {std::ref(col), index});
if (utils::is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) {
pk_index_ = static_cast<int>(index);
}
}
}

View File

@ -12,7 +12,7 @@ session::session(connection_pool<connection> &pool)
query_create_intermediate session::create()
{
return query_create_intermediate{*this, query_.create()};
return query_create_intermediate{*this, query_.create(), table_repository_};
}
query_drop_intermediate session::drop()
@ -55,4 +55,9 @@ std::pair<size_t, std::string> session::execute(const std::string &sql) {
}
return c->execute(sql);
}
const table_repository& session::tables() const
{
return table_repository_;
}
}

View File

@ -0,0 +1,39 @@
#include "matador/sql/table_repository.hpp"
#include <stdexcept>
namespace matador::sql {
const table_info &table_repository::attach(std::type_index ti, const std::string &table_name, const record &proto)
{
return attach(ti, table_info{table_name, proto});
}
const table_info& table_repository::attach(const std::type_index ti, const table_info& table)
{
return repository_.try_emplace(ti, table).first->second;
}
std::optional<table_info> table_repository::info(std::type_index ti)
{
const auto it = repository_.find(ti);
if (it == repository_.end()) {
return std::nullopt;
}
return it->second;
}
std::pair<std::string, std::string> table_repository::reference(const std::type_index &ti) const
{
const auto it = repository_.find(ti);
if (it != repository_.end()) {
if (!it->second.prototype.has_primary_key()) {
throw std::logic_error("table doesn't has primary key");
}
return { it->second.name, it->second.prototype.primary_key().name() };
}
return {};
}
}

View File

@ -20,7 +20,10 @@ add_executable(tests builder.cpp
column_name_generator.cpp
value_generator.cpp
models/category.hpp
models/supplier.hpp)
models/supplier.hpp
models/airplane.hpp
models/flight.hpp
models/person.hpp)
target_link_libraries(tests PRIVATE
Catch2::Catch2WithMain
matador

View File

@ -1,14 +1,16 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/sql/column_generator.hpp"
#include "matador/sql/table_repository.hpp"
#include "models/product.hpp"
using namespace matador::sql;
TEST_CASE("Generate columns from object", "[column generator]") {
table_repository repo;
auto columns = column_generator::generate<matador::test::product>();
auto columns = column_generator::generate<matador::test::product>(repo);
const std::vector<std::string> expected_columns = {
"product_name",

35
test/models/airplane.hpp Normal file
View File

@ -0,0 +1,35 @@
#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/foreign.hpp"
#include <string>
namespace matador::test {
struct airplane
{
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

33
test/models/flight.hpp Normal file
View File

@ -0,0 +1,33 @@
#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/field_attributes.hpp"
#include "matador/sql/foreign.hpp"
#include <string>
namespace matador::test {
struct flight {
unsigned long id;
sql::foreign<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);
field::attribute(op, "pilot_name", pilot_name, 255);
}
};
}
#endif //QUERY_FLIGHT_HPP

28
test/models/person.hpp Normal file
View File

@ -0,0 +1,28 @@
#ifndef QUERY_PERSON_HPP
#define QUERY_PERSON_HPP
#include "matador/utils/access.hpp"
#include "matador/utils/field_attributes.hpp"
#include <string>
namespace matador::test {
struct person
{
unsigned long id{};
std::string name;
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_PERSON_HPP

View File

@ -5,11 +5,14 @@
#include <matador/sql/session.hpp>
#include "models/product.hpp"
#include "models/airplane.hpp"
#include "models/flight.hpp"
#include "models/person.hpp"
using namespace matador::sql;
using namespace matador::test;
TEST_CASE("Execute create and drop table statement", "[session]") {
TEST_CASE("Create and drop table statement", "[session]") {
connection_pool<connection> pool("sqlite://sqlite.db", 4);
session s(pool);
@ -27,13 +30,20 @@ TEST_CASE("Execute create and drop table statement", "[session]") {
REQUIRE(res.second == R"(DROP TABLE "person")");
}
TEST_CASE("Execute create table statement with foreign keys", "[session]") {
// res = s.create().table<product>("product").execute();
TEST_CASE("Create table with foreign key relation", "[session]") {
connection_pool<connection> pool("sqlite://sqlite.db", 4);
session s(pool);
// REQUIRE(res.second == R"(CREATE TABLE "product" ("product_name" VARCHAR(255) PRIMARY KEY, "supplier_id" BIGINT NOT NULL FOREIGN KEY, "category_id" BIGINT NOT NULL FOREIGN KEY, "quantity_per_unit" VARCHAR(255), "unit_price" BIGINT, "units_in_stock" BIGINT, "units_in_order" BIGINT, "reorder_level" BIGINT, "discontinued" BOOLEAN))");
auto res = s.create().table<airplane>("airplane").execute();
REQUIRE(res.first == 0);
REQUIRE(res.second == R"(CREATE TABLE "airplane" ("id" BIGINT, "brand" VARCHAR(255), "model" VARCHAR(255), CONSTRAINT PK_airplane PRIMARY KEY (id)))");
// res = s.drop().table("person").execute();
// REQUIRE(res.first == 1);
res = s.create().table<flight>("flight").execute();
REQUIRE(res.first == 0);
REQUIRE(res.second == R"(CREATE TABLE "flight" ("id" BIGINT, "airplane_id" BIGINT, "pilot_name" VARCHAR(255), CONSTRAINT PK_flight PRIMARY KEY (id), CONSTRAINT FK_flight_airplane_id FOREIGN KEY (airplane_id) REFERENCES airplane(id)))");
s.drop().table("flight").execute();
s.drop().table("airplane").execute();
}
TEST_CASE("Execute insert record statement", "[session]") {
@ -69,70 +79,151 @@ TEST_CASE("Execute insert record statement", "[session]") {
REQUIRE(i.size() == 3);
REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).type() == data_type_t::type_long_long);
REQUIRE(i.at(0).value<long long>() == 7);
REQUIRE(i.at(1).name() == "name");
REQUIRE(i.at(1).type() == data_type_t::type_varchar);
REQUIRE(i.at(1).value<std::string>() == "george");
REQUIRE(i.at(2).name() == "age");
REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_int);
REQUIRE(i.at(2).value<int>() == 45);
}
s.drop().table("person").execute();
// using namespace 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_foreign<matador::test::category>();
// p.category->id = 7;
// p.supplier = make_foreign<matador::test::supplier>();;
// p.supplier->id = 13;
// p.product_name = "candle";
//
// res = s.insert().into<product>("product").values(p).execute();
//
// REQUIRE(res.second == R"(INSERT INTO "product" ("product_name", "supplier_id", "category_id", "quantity_per_unit", "unit_price", "units_in_stock", "units_in_order", "reorder_level", "discontinued") VALUES ('candle', 13, 7, 'pcs', 49, 100, 2, 1, 0))");
//
// res = s.insert().into("product", p).execute();
//
// REQUIRE(res.second == R"(INSERT INTO "product" ("product_name", "supplier_id", "category_id", "quantity_per_unit", "unit_price", "units_in_stock", "units_in_order", "reorder_level", "discontinued") VALUES ('candle', 13, 7, 'pcs', 49, 100, 2, 1, 0))");
s.drop().table("person").execute();
}
TEST_CASE("Execute update record statement", "[session]") {
connection_pool<connection> pool("sqlite://sqlite.db", 4);
session s(pool);
auto res = s
.create()
.table("person", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255),
make_column<unsigned short>("age")
})
.execute();
REQUIRE(res.first == 0);
res = s
.insert()
.into("person", {"id", "name", "age"})
.values({7, "george", 45})
.execute();
REQUIRE(res.first == 1);
REQUIRE(res.second == R"(INSERT INTO "person" ("id", "name", "age") VALUES (7, 'george', 45))");
res = s.update("person")
.set({{"id", 7}, {"name", "jane"}, {"age", 35}})
.where("id"_col == 7)
.execute();
REQUIRE(res.first == 1);
REQUIRE(res.second == R"(UPDATE "person" SET "id"=7, "name"='jane', "age"=35 WHERE "id" = 7)");
auto result = s
.select({"id", "name", "age"})
.from("person")
.fetch_all();
for (const auto& i : result) {
REQUIRE(i.size() == 3);
REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).type() == data_type_t::type_long_long);
REQUIRE(i.at(0).value<long long>() == 7);
REQUIRE(i.at(1).name() == "name");
REQUIRE(i.at(1).type() == data_type_t::type_varchar);
REQUIRE(i.at(1).value<std::string>() == "jane");
REQUIRE(i.at(2).name() == "age");
REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_int);
REQUIRE(i.at(2).value<int>() == 35);
}
s.drop().table("person").execute();
}
TEST_CASE("Execute select statement with where clause", "[session]") {
connection_pool<connection> pool("sqlite://sqlite.db", 4);
session s(pool);
auto res = s.select({"id", "name", "color"})
auto res = s.create()
.table<person>("person")
.execute();
REQUIRE(res.first == 0);
person george{7, "george", 45};
res = s.insert()
.into("person", george)
.execute();
REQUIRE(res.first == 1);
auto result = s.select<person>()
.from("person")
.where("id"_col == 8)
.where("id"_col == 7)
.fetch_all();
// Todo: prepare test data
REQUIRE(true);
for (const auto& i : result) {
REQUIRE(i.size() == 3);
REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).type() == data_type_t::type_long_long);
REQUIRE(i.at(0).value<long long>() == george.id);
REQUIRE(i.at(1).name() == "name");
REQUIRE(i.at(1).type() == data_type_t::type_varchar);
REQUIRE(i.at(1).value<std::string>() == george.name);
REQUIRE(i.at(2).name() == "age");
REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_long_long);
REQUIRE(i.at(2).value<long long>() == george.age);
}
// REQUIRE(res.str() == R"(SELECT "id", "name", "color" FROM "person" WHERE "id" = 8)");
s.drop().table("person").execute();
}
TEST_CASE("Execute select statement with order by", "[session]") {
connection_pool<connection> pool("sqlite://sqlite.db", 4);
session s(pool);
auto res = s.select({"id", "name", "color"})
auto res = s
.create()
.table("person", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255),
make_column<std::string>("color", 63)
})
.execute();
REQUIRE(res.first == 0);
auto result = s.select({"id", "name", "color"})
.from("person")
.where("id"_col == 8)
.order_by("name").desc()
.fetch_all();
// Todo: prepare test data
REQUIRE(true);
// REQUIRE(res.str() == R"(SELECT "id", "name", "color" FROM "person" WHERE "id" = 8 ORDER BY "name" DESC)");
s.drop().table("person").execute();
}
TEST_CASE("Execute select statement with group by and order by", "[session]") {
connection_pool<connection> pool("sqlite://sqlite.db", 4);
session s(pool);
auto res = s.select({"id", "name", "color"})
auto res = s.create()
.table("person", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255),
make_column<std::string>("color", 63)
})
.execute();
auto result = s.select({"id", "name", "color"})
.from("person")
.where("id"_col == 8)
.group_by("color")
@ -140,49 +231,54 @@ TEST_CASE("Execute select statement with group by and order by", "[session]") {
.fetch_all();
// Todo: prepare test data
REQUIRE(true);
// REQUIRE(res.str() == R"(SELECT "id", "name", "color" FROM "person" WHERE "id" = 8 GROUP BY "color" ORDER BY "name" ASC)");
s.drop().table("person").execute();
}
TEST_CASE("Execute insert statement", "[session]") {
connection_pool<connection> pool("sqlite://sqlite.db", 4);
session s(pool);
auto res = s.insert()
auto res = s.create()
.table("person", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255),
make_column<std::string>("color", 63)
})
.execute();
res = s.insert()
.into("person", {"id", "name", "color"})
.values({7, "george", "green"})
.execute();
REQUIRE(res.first == 1);
REQUIRE(res.second == R"(INSERT INTO "person" ("id", "name", "color") VALUES (7, 'george', 'green'))");
using namespace 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_foreign<matador::test::category>();
p.category->id = 7;
p.supplier = make_foreign<matador::test::supplier>();
p.supplier->id = 13;
p.product_name = "candle";
res = s.insert().into<product>("product").values(p).execute();
REQUIRE(res.second == R"(INSERT INTO "product" ("product_name", "supplier_id", "category_id", "quantity_per_unit", "unit_price", "units_in_stock", "units_in_order", "reorder_level", "discontinued") VALUES ('candle', 13, 7, 'pcs', 49, 100, 2, 1, 0))");
res = s.insert().into("product", p).execute();
REQUIRE(res.second == R"(INSERT INTO "product" ("product_name", "supplier_id", "category_id", "quantity_per_unit", "unit_price", "units_in_stock", "units_in_order", "reorder_level", "discontinued") VALUES ('candle', 13, 7, 'pcs', 49, 100, 2, 1, 0))");
s.drop().table("person").execute();
}
TEST_CASE("Execute update statement", "[session]") {
connection_pool<connection> pool("sqlite://sqlite.db", 4);
session s(pool);
auto res = s.update("person")
auto res = s.create()
.table("person", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255),
make_column<std::string>("color", 63)
})
.execute();
res = s.insert()
.into("person", {"id", "name", "color"})
.values({7, "george", "green"})
.execute();
REQUIRE(res.first == 1);
REQUIRE(res.second == R"(INSERT INTO "person" ("id", "name", "color") VALUES (7, 'george', 'green'))");
res = s.update("person")
.set({
{"name", "george"},
{"color", "green"}
@ -192,26 +288,7 @@ TEST_CASE("Execute update statement", "[session]") {
REQUIRE(res.second == R"(UPDATE "person" SET "name"='george', "color"='green' WHERE "id" = 9)");
using namespace 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_foreign<matador::test::category>();
p.category->id = 7;
p.supplier = make_foreign<matador::test::supplier>();
p.supplier->id = 13;
p.product_name = "candle";
res = s.update("product")
.set(p)
.where("id"_col == 9)
.execute();
REQUIRE(res.second == R"(UPDATE "product" SET "product_name"='candle', "supplier_id"=13, "category_id"=7, "quantity_per_unit"='pcs', "unit_price"=49, "units_in_stock"=100, "units_in_order"=2, "reorder_level"=1, "discontinued"=0 WHERE "id" = 9)");
s.drop().table("person").execute();
}
TEST_CASE("Execute delete statement", "[session]") {