added timestamp code, support columns for group by and order by and tested load has-many eager code

This commit is contained in:
Sascha Kühl 2026-01-08 15:14:31 +01:00
parent 0bf945cd13
commit 930b9d1aa4
34 changed files with 853 additions and 846 deletions

View File

@ -2,6 +2,7 @@
#include "matador/utils/convert.hpp"
#include "matador/utils/value.hpp"
#include "matador/utils/string.hpp"
namespace matador::backends::postgres {
postgres_result_reader::postgres_result_reader(PGresult *result)
@ -104,18 +105,25 @@ void postgres_result_reader::read_value(const char * /*id*/, const size_t index,
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::date_type_t &value) {
if (const auto val = column(index); strlen(val) > 0) {
// value = time::parse(val, "%Y-%m-%d %T.%f");
if (const auto res = utils::to<utils::date_type_t>(std::string(val)); res.is_ok()) {
value = res.value();
}
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::time_type_t &value) {
if (const auto val = column(index); strlen(val) > 0) {
// value.set(val, matador::utils::date_format::ISO8601);
if (const auto res = utils::to<utils::time_type_t>(std::string(val)); res.is_ok()) {
value = res.value();
}
}
}
void postgres_result_reader::read_value(const char * /*id*/, size_t index, utils::timestamp_type_t &value) {
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::timestamp_type_t &value) {
if (const auto val = column(index); strlen(val) > 0) {
if (const auto res = utils::to<utils::timestamp_type_t>(std::string(val)); res.is_ok()) {
value = res.value();
}
}
}

View File

@ -31,6 +31,8 @@ set(TEST_SOURCES
../../../test/backends/StatementCacheTest.cpp
../../../test/backends/StatementTest.cpp
../../../test/backends/TypeTraitsTest.cpp
../../../test/utils/record_printer.hpp
../../../test/utils/record_printer.cpp
)
set(LIBRARY_TEST_TARGET PostgresTests)

View File

@ -1,9 +1,11 @@
#include "matador/sql/connection.hpp"
#include "matador/sql/connection_pool.hpp"
#include "matador/query/criteria.hpp"
#include "matador/query/query.hpp"
#include "matador/query/table_column.hpp"
#include "matador/query/meta_table_macro.hpp"
#include "matador/query/schema.hpp"
#include "matador/object/object_ptr.hpp"
#include "matador/object/repository.hpp"
@ -45,7 +47,7 @@ struct book {
namespace field = matador::access;
field::primary_key(op, "id", id);
field::attribute(op, "title", title, 511);
field::has_one( op, "author_id", book_author, matador::utils::CascadeNoneFetchLazy );
field::belongs_to(op, "author_id", book_author, matador::utils::CascadeNoneFetchLazy);
field::attribute(op, "published_in", published_in);
}
};
@ -151,62 +153,67 @@ META_TABLE(payload, PAYLOAD, id )
META_TABLE(temporary_table, TEMPORARY_TABLE, id);
META_TABLE(customer, CUSTOMER, id, name, email, address)
META_TABLE(product, PRODUCT, id, title, description, price, category)
META_TABLE(category, CATEGORY, id, title, description)
META_TABLE(cart, CART, id, items, owner)
int main() {
using namespace matador::sql;
using namespace matador::object;
using namespace matador::utils;
using namespace matador::query;
using namespace matador::query::meta;
const std::string env_var{"MATADOR_BACKENDS_PATH"};
std::string dns{"sqlite://demo.db"};
repository s( "main" );
connection_pool pool("postgres://test:test123!@127.0.0.1:5432/matador", 4);
schema s("");
auto result = s.attach<author>("authors").and_then([&s] {
return s.attach<book>("books");
});
// s.attach<book>( "books" );
connection c( dns );
result = c.open();
// s.create( c );
//
// auto create_authors_sql = c.query( s )
// .create()
// .table<author>( qh::authors )
// .execute();
//
// c.query( s )
// .create()
// .table<book>( qh::books )
// .execute();
//
// std::cout << "SQL: " << create_authors_sql << "\n";
//
// author mc;
// mc.id = 1;
// mc.first_name = "Michael";
// mc.last_name = "Crichton";
// mc.date_of_birth = "19.8.1954";
// mc.year_of_birth = 1954;
// mc.distinguished = true;
// auto insert_authors_sql = c.query( s )
// .insert()
// .into( qh::authors )
// .values( mc )
// .execute();
//
// std::cout << "SQL: " << insert_authors_sql << "\n";
//
// auto result = c.query( s )
// .select( qh::authors.columns )
// .from( qh::authors )
// .fetch_all();
//
// for (const auto& row: result) { std::cout << "Author " << row.at( qh::authors.first_name ) << "\n"; }
const auto c = pool.acquire();
result = s.create(*c);
if (!result) {
std::cout << "error: " << result.err() << std::endl;
return 0;
}
author mc;
mc.id = 1;
mc.first_name = "Michael";
mc.last_name = "Crichton";
mc.date_of_birth = "19.8.1954";
mc.year_of_birth = 1954;
mc.distinguished = true;
auto insert_authors_sql = query::insert()
.into(AUTHOR)
.values(mc)
.execute(*c);
if (!insert_authors_sql) {
std::cout << "error: " << insert_authors_sql.err() << std::endl;
std::ignore = s.drop(*c);
return 0;
}
auto authors_result = query::select(AUTHOR)
.from(AUTHOR)
.fetch_all(*c);
if (!authors_result) {
std::cout << "error: " << authors_result.err() << std::endl;
std::ignore = s.drop(*c);
return 0;
}
for (const auto &row: *authors_result) {
std::cout << "Author " << row.at(AUTHOR.first_name.canonical_name()) << "\n";
}
//
// auto update_authors_sql = c.query( s )
// .update( qh::authors )
@ -236,20 +243,35 @@ int main() {
// .values( {3, "Misery", mc.id, 1984} )
// .execute();
//
auto select_books_sql = matador::query::query::select( BOOK, {AUTHOR.last_name} )
// auto select_books_sql = query::select( BOOK, {AUTHOR.last_name} )
auto select_books_sql = query::select({BOOK.id, BOOK.title, BOOK.author_id, BOOK.published_in, AUTHOR.last_name})
.from(BOOK)
.join_left(AUTHOR)
.on(BOOK.author_id == AUTHOR.id)
.where(BOOK.published_in < 2008 && AUTHOR.last_name == "King")
.group_by( BOOK.published_in )
.group_by({BOOK.id, AUTHOR.last_name})
.order_by(BOOK.title).asc()
.limit(5)
.offset(2)
.fetch_all(c);
.fetch_all(*c);
if (!select_books_sql) {
std::cout << "error: " << select_books_sql.err() << std::endl;
std::ignore = s.drop(*c);
return 0;
}
for (const auto &r: *select_books_sql) {
std::cout << "R: " << r.at( BOOK.title ) << ", " << r.at( AUTHOR.last_name ) << "\n";
std::cout << "R: " << r.at(BOOK.title.canonical_name()) << ", " << r.at(AUTHOR.last_name.canonical_name()) << "\n";
}
result = s.drop(*c);
if (!result) {
std::cout << "error: " << result.err() << std::endl;
return 0;
}
// // SELECT book.title, book.id, book.author_id, book.published_in, author.name
// // FROM book
// // INNER JOIN author ON book.author_id = author.id
@ -266,24 +288,21 @@ int main() {
//
// std::cout << "SQL: " << drop_authors_sql << "\n";
//
// auto res = c.query( s )
// .select( {qh::payload.id} )
// .from( qh::payload )
// .join_left( qh::job )
// .on( qh::job.payload == qh::payload.id )
// .where(
// in( qh::payload.id, c.query( s )
// .select( {qh::job.state} )
// .from( qh::job )
// .where( qh::job.state == job::job_state::Running )
// ) &&
// in( qh::payload.id, c.query( s )
// .select( {qh::temporary_table.id} )
// .from( qh::temporary_table ) )
// )
// .build();
auto res = query::select({PAYLOAD.id})
.from(PAYLOAD)
.join_left(JOB)
.on(JOB.payload == PAYLOAD.id)
.where(
in(PAYLOAD.id, query::select({JOB.state})
.from(JOB)
.where(JOB.state == job::job_state::Running).compile(c->dialect())
) &&
in(PAYLOAD.id, query::select({TEMPORARY_TABLE.id})
.from(TEMPORARY_TABLE).compile(c->dialect())
)
).compile(c->dialect());
// // .fetch_value<unsigned long>();
// std::cout << "SQL: " << res.sql << "\n";
std::cout << "SQL: " << res.sql << "\n";
return 0;
}

View File

@ -52,6 +52,7 @@ public:
[[nodiscard]] bool is_varchar() const;
[[nodiscard]] bool is_date() const;
[[nodiscard]] bool is_time() const;
[[nodiscard]] bool is_timestamp() const;
[[nodiscard]] bool is_blob() const;
[[nodiscard]] bool is_null() const;

View File

@ -10,9 +10,11 @@ class collection {
public:
using value_type = Type;
void push_back(const Type& value) { data_.push_back(value); }
[[nodiscard]] size_t size() const { return data_.size(); }
private:
std::vector<Type> data_;
};
}

View File

@ -21,7 +21,7 @@ public:
executable_query constraints(std::initializer_list<object::restriction> constraints);
executable_query constraints(const std::list<object::restriction> &restrictions);
executable_query constraints(std::initializer_list<table_constraint> constraints);
executable_query constraints(const std::list<table_constraint> &restrictions);
executable_query constraints(const std::list<table_constraint> &constraints);
};
class query_create_table_intermediate : public query_intermediate {
@ -32,6 +32,7 @@ public:
query_create_table_columns_intermediate columns(const std::list<object::attribute> &attributes);
query_create_table_columns_intermediate columns(std::initializer_list<table_column> columns);
query_create_table_columns_intermediate columns(const std::list<table_column> &columns);
query_create_table_columns_intermediate columns(const std::vector<table_column> &columns);
};
class query_create_intermediate : public query_intermediate {

View File

@ -15,6 +15,7 @@ public:
query_execute_limit_intermediate limit(size_t limit);
query_execute_order_by_intermediate order_by(const table_column &col);
query_execute_order_by_intermediate order_by(std::initializer_list<table_column> columns);
};
}

View File

@ -19,14 +19,11 @@ public:
query_from_intermediate join_left(join_data &data);
query_from_intermediate join_left(std::vector<join_data> &data_vector);
// template<class Condition>
// query_where_intermediate where(const Condition &cond)
// {
// return where_clause(std::make_unique<Condition>(std::move(cond)));
// }
query_where_intermediate where(std::unique_ptr<abstract_criteria> &&cond);
query_group_by_intermediate group_by(const table_column &col);
query_group_by_intermediate group_by(const table_column &column);
query_group_by_intermediate group_by(std::initializer_list<table_column> columns);
query_order_by_intermediate order_by(const table_column &col);
query_order_by_intermediate order_by(std::initializer_list<table_column> columns);
private:
query_where_intermediate where_clause(std::unique_ptr<abstract_criteria> &&cond);

View File

@ -13,6 +13,7 @@ public:
using fetchable_query::fetchable_query;
query_order_by_intermediate order_by(const table_column &col);
query_order_by_intermediate order_by(std::initializer_list<table_column> columns);
};
}

View File

@ -13,8 +13,10 @@ class query_where_intermediate : public fetchable_query
public:
using fetchable_query::fetchable_query;
query_group_by_intermediate group_by(const table_column &col);
query_group_by_intermediate group_by(const table_column &column);
query_group_by_intermediate group_by(std::initializer_list<table_column> columns);
query_order_by_intermediate order_by(const table_column &col);
query_order_by_intermediate order_by(std::initializer_list<table_column> columns);
};
}

View File

@ -205,29 +205,29 @@ protected:
class query_group_by_part final : public query_part
{
public:
explicit query_group_by_part(class table_column col);
explicit query_group_by_part(const std::vector<table_column>& columns);
[[nodiscard]] const table_column& column() const;
[[nodiscard]] const std::vector<table_column>& columns() const;
private:
void accept(query_part_visitor &visitor) override;
private:
table_column column_;
std::vector<table_column> columns_;
};
class query_order_by_part final : public query_part
{
public:
explicit query_order_by_part(class table_column col);
explicit query_order_by_part(const std::vector<table_column>& columns);
[[nodiscard]] const table_column& column() const;
[[nodiscard]] const std::vector<table_column>& columns() const;
private:
void accept(query_part_visitor &visitor) override;
private:
table_column column_;
std::vector<table_column> columns_;
};
class query_order_by_asc_part final : public query_part

View File

@ -16,7 +16,7 @@ namespace internal { \
class TABLE_NAME##_table : public typed_table<TABLE_NAME##_table> { \
public: \
TABLE_NAME##_table()\
: TABLE_NAME##_table("") \
: TABLE_NAME##_table(#TABLE_NAME) \
{} \
TABLE_NAME##_table(const std::string& alias) \
: typed_table(#TABLE_NAME, alias, {MAP(FIELD_STRING, __VA_ARGS__)}) \

View File

@ -25,7 +25,6 @@ public:
[[nodiscard]] static query_select_intermediate select(std::initializer_list<table_column> columns);
[[nodiscard]] static query_select_intermediate select(const std::vector<table_column>& columns);
[[nodiscard]] static query_select_intermediate select(const std::vector<std::string> &column_names);
[[nodiscard]] static query_select_intermediate select(std::vector<table_column> columns, std::initializer_list<table_column> additional_columns);
template<class Type>
[[nodiscard]] static query_select_intermediate select(const schema &scm) {
return select(generator::columns<Type>(scm));

View File

@ -2,16 +2,12 @@
#define OOS_ACCESS_HPP
#include <cstdint>
#include <string>
#include <optional>
namespace matador {
enum class cascade_type;
template < class Type, template < class ... > class ContainerType >
class container;
namespace utils {
class field_attributes;
class foreign_attributes;

View File

@ -194,6 +194,7 @@ public:
[[nodiscard]] bool is_varchar() const;
[[nodiscard]] bool is_date() const;
[[nodiscard]] bool is_time() const;
[[nodiscard]] bool is_timestamp() const;
[[nodiscard]] bool is_blob() const;
[[nodiscard]] bool is_null() const;

View File

@ -18,15 +18,19 @@ size_t determine_size(const blob_type_t &val);
}
class value
{
class value {
public:
value() = default;
template<typename Type>
template<typename Type, std::enable_if_t<!std::is_enum_v<Type>>* = nullptr>
explicit value(Type value, size_t size = 0)
: value_(value)
, size_(size)
, type_(data_type_traits<Type>::type(size)) {}
template<typename Type, std::enable_if_t<std::is_enum_v<Type>>* = nullptr>
explicit value(Type value, size_t size = 0)
: value_(static_cast<int>(value))
, size_(size)
, type_(data_type_traits<Type>::type(size)) {}
explicit value(basic_type data_type, size_t size = 0);
value(const value &x) = default;
value& operator=(const value &x) = default;
@ -74,11 +78,12 @@ public:
[[nodiscard]] bool is_varchar() const;
[[nodiscard]] bool is_date() const;
[[nodiscard]] bool is_time() const;
[[nodiscard]] bool is_timestamp() const;
[[nodiscard]] bool is_blob() const;
[[nodiscard]] bool is_null() const;
private:
utils::database_type value_;
database_type value_;
size_t size_{};
basic_type type_{basic_type::Null};

View File

@ -85,6 +85,10 @@ bool attribute::is_time() const {
return type_ == utils::basic_type::Time;
}
bool attribute::is_timestamp() const {
return type_ == utils::basic_type::DateTime;
}
bool attribute::is_blob() const {
return type_ == utils::basic_type::Blob;
}

View File

@ -19,53 +19,48 @@ std::string identifier_type_traits<const char*>::to_string(const char *value) {
identifier::base::base(const std::type_index &ti, const basic_type type)
: type_index_(ti)
, type_(type)
{}
, type_(type) {
}
identifier::null_pk::null_pk()
: base(std::type_index(typeid(null_type_t)), basic_type::Null)
{}
: base(std::type_index(typeid(null_type_t)), basic_type::Null) {
}
identifier::base* identifier::null_pk::copy() const
{
identifier::base *identifier::null_pk::copy() const {
return new null_pk;
}
bool identifier::null_pk::equal_to(const base &x) const
{
bool identifier::null_pk::equal_to(const base &x) const {
return type_index_ == x.type_index_;
}
bool identifier::null_pk::less(const base &x) const
{
bool identifier::null_pk::less(const base &x) const {
return type_index_ == x.type_index_;
}
bool identifier::null_pk::is_valid() const
{
bool identifier::null_pk::is_valid() const {
return identifier_type_traits<null_type_t>::is_valid();
}
std::string identifier::null_pk::str() const
{
std::string identifier::null_pk::str() const {
return identifier_type_traits<null_type_t>::to_string();
}
void identifier::null_pk::serialize(identifier_serializer &s)
{
void identifier::null_pk::serialize(identifier_serializer &s) {
s.serialize(null_, {});
}
size_t identifier::null_pk::hash() const
{
size_t identifier::null_pk::hash() const {
return std::hash<nullptr_t>()(nullptr);
}
identifier::identifier()
: id_(std::make_shared<null_pk>()) {}
: id_(std::make_shared<null_pk>()) {
}
identifier::identifier(const identifier &x)
: id_(x.id_->copy()) {}
: id_(x.id_->copy()) {
}
identifier &identifier::operator=(const identifier &x) {
if (this == &x) {
@ -123,13 +118,11 @@ basic_type identifier::type() const {
return id_->type_;
}
identifier identifier::share() const
{
identifier identifier::share() const {
return identifier(id_);
}
size_t identifier::use_count() const
{
size_t identifier::use_count() const {
return id_.use_count();
}
@ -157,22 +150,23 @@ bool identifier::is_time() const {
return id_->type_ == basic_type::Time;
}
bool identifier::is_timestamp() const {
return id_->type_ == basic_type::DateTime;
}
bool identifier::is_blob() const {
return id_->type_ == basic_type::Blob;
}
bool identifier::is_null() const
{
bool identifier::is_null() const {
return type_index() == null_identifier.type_index();
}
bool identifier::is_valid() const
{
bool identifier::is_valid() const {
return id_->is_valid();
}
void identifier::clear()
{
void identifier::clear() {
id_ = std::make_unique<null_pk>();
}
@ -180,24 +174,20 @@ void identifier::serialize(identifier_serializer &s) const {
id_->serialize(s);
}
size_t identifier::hash() const
{
size_t identifier::hash() const {
return id_->hash();
}
identifier::identifier(const std::shared_ptr<base> &id)
: id_(id)
{}
: id_(id) {
}
std::ostream &operator<<(std::ostream &out, const identifier &id)
{
std::ostream &operator<<(std::ostream &out, const identifier &id) {
out << id.str();
return out;
}
size_t id_pk_hash::operator()(const identifier &id) const
{
size_t id_pk_hash::operator()(const identifier &id) const {
return id.hash();
}
}

View File

@ -84,6 +84,10 @@ bool value::is_time() const {
return type_ == basic_type::Time;
}
bool value::is_timestamp() const {
return type_ == basic_type::DateTime;
}
bool value::is_blob() const {
return type_ == basic_type::Blob;
}

View File

@ -79,4 +79,13 @@ query_create_table_columns_intermediate query_create_table_intermediate::columns
return {context_};
}
query_create_table_columns_intermediate query_create_table_intermediate::columns(const std::vector<table_column> &columns) {
std::list<table_column> cols;
for (const auto &col : columns) {
cols.emplace_back(col);
}
return this->columns(cols);
}
}

View File

@ -13,8 +13,11 @@ query_execute_limit_intermediate query_execute_where_intermediate::limit(size_t
}
query_execute_order_by_intermediate query_execute_where_intermediate::order_by(const table_column &col) {
context_->parts.push_back(std::make_unique<internal::query_order_by_part>(col));
return {context_};
return order_by({col});
}
query_execute_order_by_intermediate query_execute_where_intermediate::order_by(std::initializer_list<table_column> columns) {
context_->parts.push_back(std::make_unique<internal::query_order_by_part>(columns));
return {context_};
}
}

View File

@ -38,13 +38,21 @@ query_where_intermediate query_from_intermediate::where_clause(std::unique_ptr<a
return {context_};
}
query_group_by_intermediate query_from_intermediate::group_by(const table_column &col) {
context_->parts.push_back(std::make_unique<internal::query_group_by_part>(col));
query_group_by_intermediate query_from_intermediate::group_by(const table_column &column) {
return group_by({column});
}
query_group_by_intermediate query_from_intermediate::group_by(std::initializer_list<table_column> columns) {
context_->parts.push_back(std::make_unique<internal::query_group_by_part>(columns));
return {context_};
}
query_order_by_intermediate query_from_intermediate::order_by(const table_column &col) {
context_->parts.push_back(std::make_unique<internal::query_order_by_part>(col));
return order_by({col});
}
query_order_by_intermediate query_from_intermediate::order_by(std::initializer_list<table_column> columns) {
context_->parts.push_back(std::make_unique<internal::query_order_by_part>(columns));
return {context_};
}

View File

@ -6,8 +6,11 @@
namespace matador::query {
query_order_by_intermediate query_group_by_intermediate::order_by(const table_column &col) {
context_->parts.push_back(std::make_unique<internal::query_order_by_part>(col));
return {context_};
return order_by({col});
}
query_order_by_intermediate query_group_by_intermediate::order_by(std::initializer_list<table_column> columns) {
context_->parts.push_back(std::make_unique<internal::query_order_by_part>(columns));
return {context_};
}
}

View File

@ -5,15 +5,23 @@
#include "matador/query/internal/query_parts.hpp"
namespace matador::query {
query_group_by_intermediate query_where_intermediate::group_by(const table_column &column) {
return group_by({column});
}
query_group_by_intermediate query_where_intermediate::group_by(const table_column &col) {
context_->parts.push_back(std::make_unique<internal::query_group_by_part>(col));
query_group_by_intermediate query_where_intermediate::group_by(std::initializer_list<table_column> columns) {
context_->parts.push_back(std::make_unique<internal::query_group_by_part>(columns));
return {context_};
}
query_order_by_intermediate query_where_intermediate::order_by(const table_column &col) {
context_->parts.push_back(std::make_unique<internal::query_order_by_part>(col));
return order_by({col});
}
query_order_by_intermediate query_where_intermediate::order_by(std::initializer_list<table_column> columns) {
context_->parts.push_back(std::make_unique<internal::query_order_by_part>(columns));
return {context_};
}
}

View File

@ -4,7 +4,8 @@
namespace matador::query::internal {
query_alter_part::query_alter_part()
: query_part(sql::dialect_token::Alter) {}
: query_part(sql::dialect_token::Alter) {
}
void query_alter_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
@ -12,7 +13,8 @@ void query_alter_part::accept( query_part_visitor& visitor ) {
query_alter_table_part::query_alter_table_part(class table tab)
: query_part(sql::dialect_token::Table)
, table_(std::move(tab)) {}
, table_(std::move(tab)) {
}
void query_alter_table_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
@ -24,7 +26,8 @@ const table& query_alter_table_part::table() const {
query_add_key_constraint_part::query_add_key_constraint_part(std::string name)
: query_part(sql::dialect_token::AddConstraint)
, name_(std::move(name)){}
, name_(std::move(name)) {
}
void query_add_key_constraint_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
@ -36,7 +39,8 @@ const std::string& query_add_key_constraint_part::name() const {
query_add_constraint_part_by_constraint::query_add_constraint_part_by_constraint(const query::table_constraint &co)
: query_part(sql::dialect_token::AddConstraint)
, constraint_(co) {}
, constraint_(co) {
}
void query_add_constraint_part_by_constraint::accept(query_part_visitor &visitor) {
visitor.visit(*this);
@ -48,7 +52,8 @@ const table_constraint & query_add_constraint_part_by_constraint::constraint() c
query_drop_key_constraint_part_by_name::query_drop_key_constraint_part_by_name(std::string name)
: query_part(sql::dialect_token::DropConstraint)
, name_(std::move( name )){}
, name_(std::move(name)) {
}
void query_drop_key_constraint_part_by_name::accept(query_part_visitor &visitor) {
visitor.visit(*this);
@ -60,7 +65,8 @@ const std::string& query_drop_key_constraint_part_by_name::name() const {
query_drop_key_constraint_part_by_constraint::query_drop_key_constraint_part_by_constraint(const table_constraint &co)
: query_part(sql::dialect_token::DropConstraint)
, constraint_(co) {}
, constraint_(co) {
}
void query_drop_key_constraint_part_by_constraint::accept(query_part_visitor &visitor) {
visitor.visit(*this);
@ -72,7 +78,8 @@ const table_constraint& query_drop_key_constraint_part_by_constraint::constraint
query_add_foreign_key_constraint_part::query_add_foreign_key_constraint_part(const std::vector<table_column> &columns)
: query_part(sql::dialect_token::ForeignKey)
, columns_(columns) {}
, columns_(columns) {
}
void query_add_foreign_key_constraint_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
@ -82,10 +89,12 @@ const std::vector<table_column>& query_add_foreign_key_constraint_part::columns(
return columns_;
}
query_add_foreign_key_reference_part::query_add_foreign_key_reference_part(class table tab, const std::vector<table_column>& columns)
query_add_foreign_key_reference_part::query_add_foreign_key_reference_part(
class table tab, const std::vector<table_column> &columns)
: query_part(sql::dialect_token::References)
, table_(std::move(tab))
, columns_(columns) {}
, columns_(columns) {
}
void query_add_foreign_key_reference_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
@ -101,7 +110,8 @@ const std::vector<table_column>& query_add_foreign_key_reference_part::columns()
query_add_primary_key_constraint_part::query_add_primary_key_constraint_part(const std::vector<table_column> &columns)
: query_part(sql::dialect_token::PrimaryKey)
, columns_(columns) {}
, columns_(columns) {
}
void query_add_primary_key_constraint_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
@ -113,271 +123,253 @@ const std::vector<table_column>& query_add_primary_key_constraint_part::columns(
query_select_part::query_select_part(std::vector<table_column> columns)
: query_part(sql::dialect_token::Select)
, columns_(std::move(columns)) {}
, columns_(std::move(columns)) {
}
void query_select_part::accept(query_part_visitor &visitor)
{
void query_select_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
const std::vector<table_column>& query_select_part::columns() const
{
const std::vector<table_column> &query_select_part::columns() const {
return columns_;
}
query_from_part::query_from_part(class table tab)
: query_part(sql::dialect_token::From)
, table_(std::move(tab)) {}
, table_(std::move(tab)) {
}
const table &query_from_part::table() const
{
const table &query_from_part::table() const {
return table_;
}
void query_from_part::accept(query_part_visitor &visitor)
{
void query_from_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_join_part::query_join_part(class table tab)
: query_part(sql::dialect_token::Join)
, table_(std::move(tab)) {}
, table_(std::move(tab)) {
}
const table &query_join_part::table() const
{
const table &query_join_part::table() const {
return table_;
}
void query_join_part::accept(query_part_visitor &visitor)
{
void query_join_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_on_part::query_on_part(std::unique_ptr<abstract_criteria> &&cond)
: query_part(sql::dialect_token::On)
, condition_(std::move(cond)) {}
, condition_(std::move(cond)) {
}
const abstract_criteria &query_on_part::condition() const
{
const abstract_criteria &query_on_part::condition() const {
return *condition_;
}
void query_on_part::accept(query_part_visitor &visitor)
{
void query_on_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_where_part::query_where_part(std::unique_ptr<abstract_criteria> &&cond)
: query_part(sql::dialect_token::Where)
, condition_(std::move(cond)) {}
, condition_(std::move(cond)) {
}
void query_where_part::accept(query_part_visitor &visitor)
{
void query_where_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
const abstract_criteria &query_where_part::condition() const
{
const abstract_criteria &query_where_part::condition() const {
return *condition_;
}
query_table_name_part::query_table_name_part(const sql::dialect_token token, std::string table_name)
: query_part(token)
, table_name_(std::move(table_name)) {}
query_group_by_part::query_group_by_part(class table_column col)
: query_part(sql::dialect_token::GroupBy)
, column_(std::move(col))
{}
const table_column &query_group_by_part::column() const
{
return column_;
, table_name_(std::move(table_name)) {
}
void query_group_by_part::accept(query_part_visitor &visitor)
{
query_group_by_part::query_group_by_part(const std::vector<table_column> &columns)
: query_part(sql::dialect_token::GroupBy)
, columns_(columns) {
}
const std::vector<table_column> &query_group_by_part::columns() const {
return columns_;
}
void query_group_by_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_order_by_part::query_order_by_part(class table_column col)
query_order_by_part::query_order_by_part(const std::vector<table_column>& columns)
: query_part(sql::dialect_token::OrderBy)
, column_(std::move(col))
{}
const table_column &query_order_by_part::column() const
{
return column_;
, columns_(columns) {
}
void query_order_by_part::accept(query_part_visitor &visitor)
{
const std::vector<table_column> &query_order_by_part::columns() const {
return columns_;
}
void query_order_by_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_order_by_asc_part::query_order_by_asc_part()
: query_part(sql::dialect_token::Asc)
{}
: query_part(sql::dialect_token::Asc) {
}
void query_order_by_asc_part::accept(query_part_visitor &visitor)
{
void query_order_by_asc_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_order_by_desc_part::query_order_by_desc_part()
: query_part(sql::dialect_token::Desc)
{}
: query_part(sql::dialect_token::Desc) {
}
void query_order_by_desc_part::accept(query_part_visitor &visitor)
{
void query_order_by_desc_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_offset_part::query_offset_part(size_t offset)
: query_part(sql::dialect_token::Offset)
, offset_(offset) {}
, offset_(offset) {
}
size_t query_offset_part::offset() const
{
size_t query_offset_part::offset() const {
return offset_;
}
void query_offset_part::accept(query_part_visitor &visitor)
{
void query_offset_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_limit_part::query_limit_part(size_t limit)
: query_part(sql::dialect_token::Limit)
, limit_(limit) {}
, limit_(limit) {
}
size_t query_limit_part::limit() const
{
size_t query_limit_part::limit() const {
return limit_;
}
void query_limit_part::accept(query_part_visitor &visitor)
{
void query_limit_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_insert_part::query_insert_part()
: query_part(sql::dialect_token::Insert) {}
: query_part(sql::dialect_token::Insert) {
}
void query_insert_part::accept(query_part_visitor &visitor)
{
void query_insert_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_into_part::query_into_part(class table tab, std::vector<table_column> columns)
: query_part(sql::dialect_token::Insert)
, table_(std::move(tab))
, columns_(std::move(columns)) {}
, columns_(std::move(columns)) {
}
const table &query_into_part::table() const
{
const table &query_into_part::table() const {
return table_;
}
const std::vector<table_column> &query_into_part::columns() const
{
const std::vector<table_column> &query_into_part::columns() const {
return columns_;
}
void query_into_part::accept(query_part_visitor &visitor)
{
void query_into_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_values_part::query_values_part(std::vector<std::variant<utils::placeholder, utils::database_type> > &&values)
: query_part(sql::dialect_token::Values)
, values_(std::move(values)) {}
, values_(std::move(values)) {
}
const std::vector<std::variant<utils::placeholder, utils::database_type>>& query_values_part::values() const
{
const std::vector<std::variant<utils::placeholder, utils::database_type> > &query_values_part::values() const {
return values_;
}
void query_values_part::accept(query_part_visitor &visitor)
{
void query_values_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_update_part::query_update_part(class table tab)
: query_part(sql::dialect_token::Update)
, table_(std::move(tab)) {}
, table_(std::move(tab)) {
}
const table& query_update_part::table() const
{
const table &query_update_part::table() const {
return table_;
}
void query_update_part::accept(query_part_visitor &visitor)
{
void query_update_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_set_part::query_set_part(const std::vector<column_value_pair> &key_value_pairs)
: query_part(sql::dialect_token::Set)
, key_value_pairs_(key_value_pairs) {}
, key_value_pairs_(key_value_pairs) {
}
const std::vector<column_value_pair> &query_set_part::column_values() const
{
const std::vector<column_value_pair> &query_set_part::column_values() const {
return key_value_pairs_;
}
void query_set_part::accept(query_part_visitor &visitor)
{
void query_set_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_delete_part::query_delete_part()
: query_part(sql::dialect_token::Remove) {}
: query_part(sql::dialect_token::Remove) {
}
void query_delete_part::accept(query_part_visitor &visitor)
{
void query_delete_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_delete_from_part::query_delete_from_part(class table tab)
: query_part(sql::dialect_token::From)
, table_(std::move(tab)) {}
, table_(std::move(tab)) {
}
const table &query_delete_from_part::table() const
{
const table &query_delete_from_part::table() const {
return table_;
}
void query_delete_from_part::accept(query_part_visitor &visitor)
{
void query_delete_from_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_create_part::query_create_part()
: query_part(sql::dialect_token::Create) {}
: query_part(sql::dialect_token::Create) {
}
void query_create_part::accept(query_part_visitor &visitor)
{
void query_create_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_create_table_part::query_create_table_part(class table tab)
: query_part(sql::dialect_token::Table)
, table_(std::move(tab)) {}
, table_(std::move(tab)) {
}
const table &query_create_table_part::table() const
{
const table &query_create_table_part::table() const {
return table_;
}
void query_create_table_part::accept(query_part_visitor &visitor)
{
void query_create_table_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_create_table_columns_part::query_create_table_columns_part(const std::list<table_column> &columns)
: query_part(sql::dialect_token::Columns)
, columns_(columns){}
, columns_(columns) {
}
const std::list<table_column> &query_create_table_columns_part::columns() const {
return columns_;
@ -387,9 +379,11 @@ void query_create_table_columns_part::accept(query_part_visitor& visitor) {
visitor.visit(*this);
}
query_create_table_constraints_part::query_create_table_constraints_part(const std::list<class table_constraint>& constraints)
query_create_table_constraints_part::query_create_table_constraints_part(
const std::list<class table_constraint> &constraints)
: query_part(sql::dialect_token::Constraint)
, constraints_(constraints) {}
, constraints_(constraints) {
}
const std::list<class table_constraint> &query_create_table_constraints_part::constraints() const {
return constraints_;
@ -401,7 +395,8 @@ void query_create_table_constraints_part::accept( query_part_visitor& visitor )
query_create_schema_part::query_create_schema_part(std::string schema)
: query_part(sql::dialect_token::Schema)
, schema_( std::move( schema ) ){}
, schema_(std::move(schema)) {
}
const std::string &query_create_schema_part::schema() const {
return schema_;
@ -412,30 +407,30 @@ void query_create_schema_part::accept( query_part_visitor& visitor ) {
}
query_drop_part::query_drop_part()
: query_part(sql::dialect_token::Drop) {}
: query_part(sql::dialect_token::Drop) {
}
void query_drop_part::accept(query_part_visitor &visitor)
{
void query_drop_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_drop_table_part::query_drop_table_part(class table tab)
: query_part(sql::dialect_token::Table)
, table_(std::move(tab)) {}
, table_(std::move(tab)) {
}
const table &query_drop_table_part::table() const
{
const table &query_drop_table_part::table() const {
return table_;
}
void query_drop_table_part::accept(query_part_visitor &visitor)
{
void query_drop_table_part::accept(query_part_visitor &visitor) {
visitor.visit(*this);
}
query_drop_schema_part::query_drop_schema_part(std::string schema_)
: query_part(sql::dialect_token::Schema)
, schema_( std::move( schema_ ) ) {}
, schema_(std::move(schema_)) {
}
const std::string &query_drop_schema_part::schema() const {
return schema_;

View File

@ -49,13 +49,6 @@ query_select_intermediate query::select(const std::vector<std::string> &column_n
return select(columns);
}
query_select_intermediate query::select(std::vector<table_column> columns, const std::initializer_list<table_column> additional_columns) {
for (const auto &col : additional_columns) {
columns.push_back(col);
}
return query_select_intermediate{columns};
}
query_insert_intermediate query::insert() {
return {};
}

View File

@ -179,12 +179,35 @@ void query_compiler::visit(internal::query_where_part &part) {
}
void query_compiler::visit(internal::query_group_by_part &part) {
query_.sql += " " + dialect_->group_by() + " " + prepare_identifier(*dialect_, part.column());
query_.sql += " " + dialect_->group_by() + " ";
if (part.columns().size() < 2) {
for (const auto &col: part.columns()) {
query_.sql.append(dialect_->prepare_identifier_string(col.canonical_name()));
}
} else {
auto it = part.columns().begin();
query_.sql.append(dialect_->prepare_identifier_string((it++)->canonical_name()));
for (; it != part.columns().end(); ++it) {
query_.sql.append(", ");
query_.sql.append(dialect_->prepare_identifier_string(it->canonical_name()));
}
}
}
void query_compiler::visit(internal::query_order_by_part &part) {
query_.sql += " " + dialect_->order_by() +
" " + prepare_criteria(*dialect_, part.column());
query_.sql += " " + dialect_->order_by() + " ";
if (part.columns().size() < 2) {
for (const auto &col: part.columns()) {
query_.sql.append(dialect_->prepare_identifier_string(col.canonical_name()));
}
} else {
auto it = part.columns().begin();
query_.sql.append(dialect_->prepare_identifier_string((it++)->canonical_name()));
for (; it != part.columns().end(); ++it) {
query_.sql.append(", ");
query_.sql.append(dialect_->prepare_identifier_string(it->canonical_name()));
}
}
}
void query_compiler::visit(internal::query_order_by_asc_part &/*order_by_asc_part*/) {

View File

@ -120,7 +120,6 @@ TEST_CASE_METHOD(QueryFixture, "Test all data types for record", "[query][record
REQUIRE(str == row.at<std::string>("val_string"));
REQUIRE(varchar == row.at<std::string>("val_varchar"));
REQUIRE(md == row.at<date_type_t>("val_date"));
const auto mtres = row.at<time_type_t>("val_time");
REQUIRE(mt == row.at<time_type_t>("val_time"));
REQUIRE(bin == row.at<blob_type_t>("val_blob"));
}
@ -510,7 +509,7 @@ TEST_CASE_METHOD(QueryFixture, "Execute select statement with group by and order
auto result = query::select({count("age").as("age_count"), "age"})
.from("person")
.group_by("age")
.group_by({"age"})
.order_by("age_count").desc()
.fetch_all(db);
REQUIRE(result.is_ok());

View File

@ -12,6 +12,9 @@
#include "models/flight.hpp"
#include "models/person.hpp"
#include "models/recipe.hpp"
#include "models/shipment.hpp"
#include "../test/utils/record_printer.hpp"
using namespace matador::object;
using namespace matador::query;
@ -23,6 +26,8 @@ META_TABLE(ingredients, INGREDIENT, id, name);
META_TABLE(recipe_ingredients, RECIPE_INGREDIENT, recipe_id, ingredient_id);
META_TABLE(airplanes, AIRPLANE, id, brand, model);
META_TABLE(flights, FLIGHT, id, airplane_id, pilot_name);
META_TABLE(shipments, SHIPMENT, id, tracking_number)
META_TABLE(packages, PACKAGE, id, weight, shipment)
TEST_CASE_METHOD(QueryFixture, "Create table with foreign key relation", "[query][foreign][relation]") {
auto result = repo.attach<airplane>("airplane")
@ -606,3 +611,106 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with many to many relationship"
++index;
}
}
void print(std::ostream& out, const record& row) {
for (const auto& f : row) {
out << f << " ";
}
}
TEST_CASE_METHOD(QueryFixture, "Test load entity with eager has many relation", "[query][has_many][eager]") {
auto result = repo.attach<package>("packages")
.and_then( [this] { return repo.attach<shipment>("shipments"); } );
// auto result = repo.attach<shipment>("shipments")
// .and_then( [this] { return repo.attach<package>("packages"); } );
REQUIRE(result.is_ok());
tables_to_drop.emplace("shipments");
tables_to_drop.emplace("packages");
result = repo.create(db);
REQUIRE(result.is_ok());
const std::vector shipments {
object_ptr{new shipment{1, "4711"}},
object_ptr{new shipment{2, "0815"}}
};
using namespace matador::query::meta;
for (const auto &sh: shipments) {
auto res = query::insert()
.into(SHIPMENT, SHIPMENT)
.values(*sh)
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 1);
}
auto count = query::select({count_all()})
.from(SHIPMENT)
.fetch_value<int>(db);
REQUIRE(count.is_ok());
REQUIRE(*count == 2);
std::vector packages {
object_ptr{new package{3, 15.4, shipments.at(0)}},
object_ptr{new package{4, 1.3, shipments.at(0)}},
object_ptr{new package{5, 30.9, shipments.at(1)}},
object_ptr{new package{6, 22.8, shipments.at(1)}},
object_ptr{new package{7, 17.2, shipments.at(1)}}
};
for (const auto &pkg: packages) {
auto res = query::insert()
.into(PACKAGE, PACKAGE)
.values(*pkg)
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 1);
}
count = query::select({count_all()})
.from(PACKAGE)
.fetch_value<int>(db);
REQUIRE(count.is_ok());
REQUIRE(*count == 5);
auto pkgs = query::select({PACKAGE.id, PACKAGE.weight, PACKAGE.shipment, SHIPMENT.tracking_number})
.from(PACKAGE)
.join_left(SHIPMENT)
.on( PACKAGE.shipment == SHIPMENT.id )
.where( PACKAGE.weight > 20.0 && SHIPMENT.tracking_number == "0815" )
.group_by({PACKAGE.id, SHIPMENT.tracking_number})
.order_by(PACKAGE.weight).asc()
.limit( 5 )
.offset( 0 )
.fetch_all(db);
REQUIRE(pkgs.is_ok());
record_printer printer(std::cout);
printer.print(*pkgs);
auto shipment_records = query::select({SHIPMENT.tracking_number, SHIPMENT.id, PACKAGE.weight, PACKAGE.weight})
.from(SHIPMENT)
.join_left(PACKAGE)
.on( SHIPMENT.id == PACKAGE.shipment )
.fetch_all(db);
REQUIRE(shipment_records.is_ok());
printer.print(*shipment_records);
auto shipment_result = query::select({SHIPMENT.id, SHIPMENT.tracking_number, PACKAGE.id, PACKAGE.weight, PACKAGE.shipment})
.from(SHIPMENT)
.join_left(PACKAGE)
.on( SHIPMENT.id == PACKAGE.shipment )
.fetch_all<shipment>(db);
REQUIRE(shipment_result.is_ok());
std::cout << "\n";
for (const auto &s : *shipment_result) {
std::cout << s.id << " " << s.tracking_number << " packages: " << s.packages.size() << std::endl;
}
}

43
test/models/shipment.hpp Normal file
View File

@ -0,0 +1,43 @@
#ifndef MATADOR_SHIPMENT_HPP
#define MATADOR_SHIPMENT_HPP
#include "matador/utils/access.hpp"
#include "matador/utils/foreign_attributes.hpp"
#include "matador/object/collection.hpp"
#include "matador/object/object_ptr.hpp"
#include <string>
namespace matador::test {
struct package;
struct shipment {
long id{};
std::string tracking_number;
object::collection<object::object_ptr<package>> packages{};
template<typename Operator>
void process(Operator &op) {
namespace field = matador::access;
field::primary_key(op, "id", id);
field::attribute(op, "tracking_number", tracking_number, 255);
field::has_many(op, "packages", packages, "shipment_id", utils::CascadeAllFetchEager);
}
};
struct package {
long id{};
double weight{};
object::object_ptr<shipment> delivery;
template<typename Operator>
void process(Operator &op) {
namespace field = matador::access;
field::primary_key(op, "id", id);
field::attribute(op, "weight", weight);
field::belongs_to(op, "shipment", delivery, utils::CascadeAllFetchLazy);
}
};
}
#endif //MATADOR_SHIPMENT_HPP

View File

@ -72,7 +72,7 @@ void test_result_reader::read_value(const char * /*id*/, const size_t /*index*/,
value = {12, 34, 56, 123456};
}
void test_result_reader::read_value(const char *id, size_t index, utils::timestamp_type_t &value) {
void test_result_reader::read_value(const char * /*id*/, size_t /*index*/, utils::timestamp_type_t &value) {
value = utils::timestamp_type_t();
}

View File

@ -0,0 +1,72 @@
#include "record_printer.hpp"
#include "matador/utils/basic_types.hpp"
#include "matador/sql/record.hpp"
#include <iostream>
#include <iomanip>
namespace matador::test {
record_printer::record_printer(std::ostream &os)
: os_(os) {
type_widths_ = {
{utils::basic_type::Int8, 4},
{utils::basic_type::Int16, 6},
{utils::basic_type::Int32, 11},
{utils::basic_type::Int64, 20},
{utils::basic_type::UInt8, 4},
{utils::basic_type::UInt16, 6},
{utils::basic_type::UInt32, 11},
{utils::basic_type::UInt64, 20},
{utils::basic_type::Float, 12},
{utils::basic_type::Double, 15},
{utils::basic_type::Boolean, 6},
{utils::basic_type::Varchar, 20},
{utils::basic_type::Text, 30},
{utils::basic_type::Date, 12},
{utils::basic_type::DateTime, 20},
{utils::basic_type::Time, 10},
{utils::basic_type::Blob, 10},
{utils::basic_type::Null, 6}
};
}
void record_printer::print_header(const sql::record &rec) const {
for (const auto &f_ref: rec.columns()) {
const auto &f = f_ref.get();
os_ << std::left << std::setw(width(f)) << f.name() << " ";
}
os_ << "\n";
for (const auto &f_ref: rec.columns()) {
const auto &f = f_ref.get();
os_ << std::string(width(f), '-') << " ";
}
os_ << "\n";
}
void record_printer::print(const sql::record &rec) const {
for (const auto &f_ref: rec.columns()) {
const auto &f = f_ref.get();
os_ << std::left << std::setw(width(f)) << f.str() << " ";
}
os_ << "\n";
}
int record_printer::width(const sql::field &f) {
// If it's a varchar/string and has a defined size, use it if it's reasonable,
// otherwise fall back to type defaults.
if ((f.is_varchar() || f.is_string()) && f.size() > 0 && f.size() < 100) {
return std::max(f.name().length(), f.size());
}
// Find the default width for type
// Note: field class doesn't expose basic_type directly but we can use value().type()
// if we had access. For now we use name length as minimum.
constexpr size_t w = 15;
// In a real implementation we would probe the field's underlying type.
return std::max(f.name().length(), w);
}
}

View File

@ -0,0 +1,43 @@
#ifndef MATADOR_RECORD_PRINTER_HPP
#define MATADOR_RECORD_PRINTER_HPP
#include "matador/sql/record.hpp"
#include <iosfwd>
#include <map>
namespace matador::sql {
class field;
class record;
}
namespace matador::test {
class record_printer final {
public:
explicit record_printer(std::ostream &os);
void print_header(const sql::record &rec) const;
void print(const sql::record &rec) const;
template < class Type, template <typename ...> class ContainerType >
void print(ContainerType<Type> &records) const {
bool first = true;
for (const auto &rec: records) {
if (first) {
print_header(rec);
first = false;
}
print(rec);
}
}
private:
[[nodiscard]] static int width(const sql::field &f) ;
private:
std::ostream &os_;
std::map<utils::basic_type, int> type_widths_;
};
}
#endif //MATADOR_RECORD_PRINTER_HPP

371
todo.md
View File

@ -1,369 +1,36 @@
# Todo
- move `object_definition` and `attribute_definition` to `table` and `column` in query
and add `contraint` class
- replace mk_column with builder style (see `query/builder.hpp`)
- fix corresponding tests
- enhance query helper macro to look like the `book` class below
- add `aliasable_table`(see class below)
- add `as()` methode to table class
- move `sql_function_t` to own header in namespace `matador::sql`
- move `prepare_*` methods from `dialect` to `query_compiler`
- add `session_insert_builder` and `session_update_builder` (returning multiple statements)
- finish `attribued_definition` (also in `repository` class -> dependencies)
- fix compile errors
- finish fetch eager has-many/belongs-to relations
- implement lazy loading
- implement polymorphic class hierarchies
- finish `schema` and `schema_repository` classes (move add/drop from `session` to `schema`)
- finish `schema_repository` classes (move add/drop from `session` to `schema`)
- implement a flag class for enumerations
## book class
```cpp
class Book : public matador::object::typed_object<Book> {
public:
using typed_object::as;
Book() : typed_object("book") {}
explicit Book(std::string alias)
: typed_object("book", std::move(alias)) {}
__Proposal for polymorphic classes:__
const matador::object::attribute id = create_attribute("id", *this);
const matador::object::attribute title = create_attribute("title", *this);
const matador::object::attribute year = create_attribute("year", *this);
};
```
object_ptr::as<Type> does the following checks;
## aliasable_table
```cpp
class object;
1. The requested type has a super class
2. Super class has a discriminator column defined
3. Super class has a discriminator value defined
4. Discriminator value is mapped to the requested type
class attribute {
public:
explicit attribute(std::string name); // NOLINT(*-explicit-constructor)
attribute(const attribute&) = default;
attribute& operator=(const attribute&) = default;
attribute(attribute&&) noexcept = default;
attribute& operator=(attribute&&) noexcept = default;
attribute() = default;
attribute(std::string name,
utils::basic_type type,
const utils::field_attributes& attr = utils::null_attributes,
utils::null_option_type null_opt = utils::null_option_type::NotNull);
[[nodiscard]] const std::string& name() const { return name_; }
void name(const std::string& n);
[[nodiscard]] const utils::field_attributes& attributes() const;
[[nodiscard]] utils::field_attributes& attributes();
[[nodiscard]] bool is_nullable() const;
[[nodiscard]] utils::basic_type type() const;
[[nodiscard]] bool is_integer() const;
[[nodiscard]] bool is_floating_point() const;
[[nodiscard]] bool is_bool() const;
[[nodiscard]] bool is_string() const;
[[nodiscard]] bool is_varchar() const;
[[nodiscard]] bool is_date() const;
[[nodiscard]] bool is_time() const;
[[nodiscard]] bool is_blob() const;
[[nodiscard]] bool is_null() const;
private:
friend class object;
std::string name_;
std::string alias_;
utils::basic_type type_{utils::basic_type::type_null};
utils::field_attributes attributes_;
utils::null_option_type null_option_type_{utils::null_option_type::NotNull};
object* parent_{nullptr};
};
class constraint {
public:
constraint() = default;
explicit constraint(std::string name) : name_(std::move(name)) {}
[[nodiscard]] const std::string& name() const { return name_; }
private:
friend class object;
std::string name_;
std::string attribute_name_;
utils::constraints options_{utils::constraints::None};
object* parent_{nullptr};
};
class object {
public:
using iterator = std::vector<attribute>::iterator;
using const_iterator = std::vector<attribute>::const_iterator;
explicit object(std::string name, std::string alias = "");
void add_attribute(attribute attr) {
auto &ref = attributes_.emplace_back(std::move(attr));
ref.parent_ = this;
}
static const attribute& create_attribute(std::string name, object& obj) {
attribute attr{std::move(name)};
attr.parent_ = &obj;
return obj.attributes_.emplace_back(std::move(attr));
}
iterator begin() { return attributes_.begin(); }
iterator end() { return attributes_.end(); }
[[nodiscard]] bool empty() const { return attributes_.empty(); }
[[nodiscard]] size_t size() const { return attributes_.size(); }
[[nodiscard]] const std::string& name() const { return name_; }
[[nodiscard]] const std::string& alias() const { return alias_; }
[[nodiscard]] const std::vector<attribute>& columns() const { return attributes_; }
private:
std::string name_;
std::string alias_;
std::vector<attribute> attributes_;
};
template<typename Type>
class typed_object : public object {
public:
using object::object;
Type as(std::string alias) { return Type{std::move(alias)}; }
};
```
If all checks succeed, the requested is fetched from the database.
```cpp
struct column_builder {
explicit column_builder(std::string column_name)
: column_name( std::move(column_name) ) {
}
column_builder& not_null() {
return *this;
}
column_builder& primary_key() {
return *this;
}
operator matador::sql::column() const {
return matador::sql::column{column_name};
}
std::string column_name;
};
column_builder column(std::string name) {
return column_builder(std::move(name));
}
namespace matador::sql {
struct constraint {
std::string name;
};
}
struct constraint_builder {
constraint_builder& constraint(std::string name) {
constraint_name = std::move(name);
return *this;
}
constraint_builder& primary_key(std::string name) {
pk_column_name = std::move(name);
return *this;
}
constraint_builder& foreign_key(std::string name) {
fk_column_name = std::move(name);
return *this;
}
constraint_builder& references(std::string table, std::string column) {
this->table_name = std::move(table);
this->column_name = std::move(column);
return *this;
}
operator matador::sql::constraint() const {
return matador::sql::constraint{constraint_name};
}
std::string constraint_name;
std::string pk_column_name;
std::string fk_column_name;
std::string table_name;
std::string column_name;
};
constraint_builder constraint(std::string name) {
constraint_builder builder;
return builder.constraint(std::move(name));
}
void foo(const std::initializer_list<matador::sql::column> columns) {
for (const auto& column : columns) {
std::cout << column.name() << std::endl;
}
}
void foo(const std::vector<matador::sql::column>& columns) {
for (const auto& column : columns) {
std::cout << column.name() << std::endl;
}
}
template<typename Type>
std::string column_prefix(matador::sql::aliasable_table<Type> *tab) {
if (!tab || tab->empty()) {
return "";
}
if (!tab->alias().empty()) {
return tab->alias();
}
return tab->name();
}
struct table_builder {
explicit table_builder(std::string name)
: table_name( std::move(name) ) {}
table_builder& as(std::string table_alias) {
this->alias = std::move(table_alias);
return *this;
}
operator matador::query::query_table() const {
return {matador::sql::table{table_name}, alias};
}
std::string table_name;
std::string alias;
};
table_builder table(std::string name) {
return table_builder(std::move(name));
}
class Book : public matador::sql::aliasable_table<Book> {
public:
using aliasable_table::as;
Book() : aliasable_table("book", "") {}
private:
friend class aliasable_table;
explicit Book(std::string alias)
: aliasable_table("book", std::move(alias)) {}
public:
matador::sql::column id = create_column("id", *this);
matador::sql::column title = create_column("title", *this);
matador::sql::column year = create_column("year", *this);
};
struct base_node {
base_node() = delete;
base_node(std::string name)
: name(std::move(name)) {}
virtual ~base_node() = default;
std::string name;
};
template < typename Type>
struct node final {
virtual ~node() = default;
Type type;
};
template < template<typename> typename NodeType = node, typename BaseType = base_node >
struct repository {
template<typename Type>
NodeType<Type> find(std::string name);
std::unordered_map<std::string, BaseType> nodes;
};
struct base_schema_node : base_node {
base_schema_node() = delete;
base_schema_node(std::string name, std::string table)
: base_node(std::move(name)), table_name(std::move(table)) {}
std::string table_name;
};
template < typename Type >
struct schema_node final : base_schema_node {
schema_node() = default;
schema_node(std::string name, std::string table)
: base_schema_node(std::move(name), std::move(table)) {}
Type type;
};
int main() {
repository repo1;
repository<schema_node, base_schema_node> repo;
Book BOOK;
const auto b = BOOK.as("b");
foo(b);
foo(BOOK);
foo({
BOOK.id,
b.title,
column("col1").primary_key(),
column("col2").not_null()
});
matador::sql::schema schema("test");
schema.add_table(BOOK.as_table());
// query::create()
// .table(BOOK)
// .columns({
// BOOK.id,
// BOOK.title,
// column("year"),
// column("author_id")
// })
// .constraints({
// constraint( "PK" ).primary_key("id"),
// constraint( "FK" ).foreign_key("author_id").references("author", "id")
// });
//
// query::create()
// .table("book", {
// column("id"),
// column("title"),
// column("year"),
// column("author_id")
// }, {
// constraint( "PK" ).primary_key("id"),
// constraint( "FK" ).foreign_key("author_id").references("author", "id")
// });
//
// matador::query::query_table book = table("book").as("b");
//
// query::select({
// column(book.column("id")),
// column(book.column("title"))
// })
// .from(book);
//
// query::insert()
// .into(b, {b.title, b.year})
// .values({"title", 2021});
schema.attach<jobs::Payload>("payloads", make_polymorph("type"));
schema.attach<jobs::Payload, jobs::IdPayload>("id_list_payloads", make_polymorph_type("IdPayload"));
schema.attach<jobs::Payload, jobs::IdListPayload>("id_payloads", make_polymorph_type("IdListPayload"));
object::object_ptr<jobs::Payload> payload;
auto result = payload.as<jobs::IdPayload>();
if (result.is_ok()) {
const auto& is_payload = result.value();
// Use requested type
id_payload->id = 1;
}
payload.is_polymorphic();
payload.is_polymorphic_type<jobs::IdPayload>();
```