introduced table repository
This commit is contained in:
parent
7c1e940814
commit
c801d53e8c
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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_{};
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -4,13 +4,14 @@
|
|||
namespace matador::utils {
|
||||
|
||||
enum class constraints : unsigned char {
|
||||
NONE = 0,
|
||||
NOT_NULL = 1 << 0,
|
||||
INDEX = 1 << 1,
|
||||
UNIQUE = 1 << 2,
|
||||
PRIMARY_KEY = 1 << 3,
|
||||
FOREIGN_KEY = 1 << 4,
|
||||
DEFAULT = 1 << 5,
|
||||
NONE = 0,
|
||||
NOT_NULL = 1 << 0,
|
||||
INDEX = 1 << 1,
|
||||
UNIQUE = 1 << 2,
|
||||
PRIMARY_KEY = 1 << 3,
|
||||
FOREIGN_KEY = 1 << 4,
|
||||
DEFAULT = 1 << 5,
|
||||
AUTO_INCREMENT = 1 << 6,
|
||||
UNIQUE_NOT_NULL = UNIQUE | NOT_NULL
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
271
test/session.cpp
271
test/session.cpp
|
|
@ -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,149 +79,216 @@ 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"})
|
||||
.from("person")
|
||||
.where("id"_col == 8)
|
||||
.fetch_all();
|
||||
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 == 7)
|
||||
.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>() == 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);
|
||||
}
|
||||
|
||||
// Todo: prepare test data
|
||||
REQUIRE(true);
|
||||
// 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"})
|
||||
.from("person")
|
||||
.where("id"_col == 8)
|
||||
.order_by("name").desc()
|
||||
.fetch_all();
|
||||
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"})
|
||||
.from("person")
|
||||
.where("id"_col == 8)
|
||||
.group_by("color")
|
||||
.order_by("name").asc()
|
||||
.fetch_all();
|
||||
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")
|
||||
.order_by("name").asc()
|
||||
.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()
|
||||
.into("person", {"id", "name", "color"})
|
||||
.values({7, "george", "green"})
|
||||
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")
|
||||
.set({
|
||||
{"name", "george"},
|
||||
{"color", "green"}
|
||||
})
|
||||
.where("id"_col == 9)
|
||||
.execute();
|
||||
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.second == R"(UPDATE "person" SET "name"='george', "color"='green' WHERE "id" = 9)");
|
||||
res = s.insert()
|
||||
.into("person", {"id", "name", "color"})
|
||||
.values({7, "george", "green"})
|
||||
.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";
|
||||
REQUIRE(res.first == 1);
|
||||
REQUIRE(res.second == R"(INSERT INTO "person" ("id", "name", "color") VALUES (7, 'george', 'green'))");
|
||||
|
||||
res = s.update("product")
|
||||
.set(p)
|
||||
res = s.update("person")
|
||||
.set({
|
||||
{"name", "george"},
|
||||
{"color", "green"}
|
||||
})
|
||||
.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)");
|
||||
REQUIRE(res.second == R"(UPDATE "person" SET "name"='george', "color"='green' WHERE "id" = 9)");
|
||||
|
||||
s.drop().table("person").execute();
|
||||
}
|
||||
|
||||
TEST_CASE("Execute delete statement", "[session]") {
|
||||
|
|
|
|||
Loading…
Reference in New Issue