schema progress

This commit is contained in:
Sascha Kühl 2025-02-04 15:15:16 +01:00
parent 6a5974337d
commit f01e9ff87f
17 changed files with 416 additions and 276 deletions

View File

@ -29,10 +29,11 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_CXX_FLAGS_DEBUG "${GCC_CLANG_COMMON_FLAGS_DEBUG}")
set(CMAKE_CXX_FLAGS_RELEASE "${GCC_CLANG_COMMON_FLAGS_RELEASE}")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
MESSAGE(STATUS "MSVC detected - Adding compiler flags")
SET(CMAKE_CXX_FLAGS "/W3 /EHsc /bigobj")
SET(CMAKE_CXX_FLAGS_DEBUG "/MDd /Od /Zi /D_DEBUG /DDEBUG")
SET(CMAKE_CXX_FLAGS_RELEASE "/O1 /DNDEBUG")
message(STATUS "MSVC detected - Adding compiler flags")
set(CMAKE_CXX_FLAGS "/W3 /EHsc /bigobj")
set(CMAKE_CXX_FLAGS_DEBUG "/MDd /Od /Zi /D_DEBUG /DDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "/O1 /DNDEBUG")
add_compile_options(/Zc:preprocessor)
endif ()
#if(ENABLE_COVERAGE)
# # set compiler flags
@ -53,5 +54,6 @@ endif ()
add_subdirectory(source)
add_subdirectory(demo)
add_subdirectory(backends)
add_subdirectory(test)

View File

@ -2,7 +2,8 @@
add_executable(demo main.cpp)
target_link_libraries(demo PRIVATE
matador
matador-core
matador-orm
${CMAKE_DL_LIBS}
${SQLite3_LIBRARIES}
)

View File

@ -1,13 +1,18 @@
#include "matador/sql/column.hpp"
#include "matador/sql/condition.hpp"
#include "matador/sql/schema.hpp"
#include "matador/sql/connection.hpp"
#include "matador/sql/entity.hpp"
#include "matador/sql/query_macro.hpp"
#include "matador/query/condition.hpp"
#include "matador/object/object_ptr.hpp"
#include "matador/object/schema.hpp"
#include "matador/utils/access.hpp"
#include "matador/utils/default_type_traits.hpp"
#include "matador/utils/enum_mapper.hpp"
#include "matador/utils/types.hpp"
#include "matador/sql/query_helper.hpp"
#include "matador/sql/query_macro.hpp"
#include <iostream>
#include <string>
@ -22,7 +27,7 @@ struct author {
template<typename Operator>
void process( Operator& op ) {
namespace field = matador::utils::access;
namespace field = matador::access;
field::primary_key( op, "id", id );
field::attribute( op, "first_name", first_name, 63 );
field::attribute( op, "last_name", last_name, 63 );
@ -34,13 +39,13 @@ struct author {
struct book {
unsigned long id{};
matador::sql::entity<author> book_author;
matador::object::object_ptr<author> book_author;
std::string title;
unsigned short published_in{};
template<typename Operator>
void process( Operator& op ) {
namespace field = matador::utils::access;
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::default_foreign_attributes );
@ -53,7 +58,7 @@ struct payload {
template<typename Operator>
void process( Operator& op ) {
namespace field = matador::utils::access;
namespace field = matador::access;
field::primary_key( op, "id", id );
}
};
@ -73,7 +78,7 @@ struct job {
};
unsigned long id{};
matador::sql::entity<payload> payload;
matador::object::object_ptr<payload> payload;
std::string type;
std::string description;
job_state state;
@ -81,7 +86,7 @@ struct job {
template<typename Operator>
void process( Operator& op ) {
namespace field = matador::utils::access;
namespace field = matador::access;
field::primary_key( op, "id", id );
field::belongs_to( op, "payload", payload, matador::utils::default_foreign_attributes );
field::attribute( op, "type", type, 511 );
@ -105,13 +110,10 @@ static const matador::utils::enum_mapper<job::job_mode> job_mode_enum({
});
template<>
struct matador::sql::data_type_traits<job::job_state, void>
{
inline static data_type_t builtin_type(std::size_t size)
{ return data_type_traits<std::string>::builtin_type(size); }
struct matador::utils::data_type_traits<job::job_state, void> {
static basic_type type(const std::size_t size) { return data_type_traits<std::string>::type(size); }
static void read_value(query_result_reader &reader, const char *id, size_t index, job::job_state &value)
{
static void read_value(attribute_reader &reader, const char *id, const size_t index, job::job_state &value) {
std::string enum_string;
reader.read_value(id, index, enum_string, 64);
if (const auto enum_opt = job_state_enum.to_enum(enum_string)) {
@ -119,25 +121,16 @@ struct matador::sql::data_type_traits<job::job_state, void>
}
}
static any_type create_value(const job::job_state &value)
{
return job_state_enum.to_string(value);
}
static void bind_value(parameter_binder &binder, size_t index, job::job_state &value)
{
binder.bind(index, job_state_enum.to_string(value));
static void write_value(attribute_writer &binder, const size_t index, const job::job_state &value) {
binder.write_value(index, job_state_enum.to_string(value));
}
};
template<>
struct matador::sql::data_type_traits<job::job_mode, void>
{
inline static data_type_t builtin_type(std::size_t size)
{ return data_type_traits<std::string>::builtin_type(size); }
struct matador::utils::data_type_traits<job::job_mode, void> {
static basic_type type( const std::size_t size) { return data_type_traits<std::string>::type(size); }
static void read_value(query_result_reader &reader, const char *id, size_t index, job::job_mode &value)
{
static void read_value(attribute_reader &reader, const char *id, const size_t index, job::job_mode &value) {
std::string enum_string;
reader.read_value(id, index, enum_string, 64);
if (const auto enum_opt = job_mode_enum.to_enum(enum_string)) {
@ -145,14 +138,8 @@ struct matador::sql::data_type_traits<job::job_mode, void>
}
}
static any_type create_value(const job::job_mode &value)
{
return job_mode_enum.to_string(value);
}
static void bind_value(parameter_binder &binder, size_t index, job::job_mode &value)
{
binder.bind(index, job_mode_enum.to_string(value));
static void bind_value(attribute_writer &binder, const size_t index, const job::job_mode &value) {
binder.write_value(index, job_mode_enum.to_string(value));
}
};
@ -168,7 +155,7 @@ QUERY_HELPER( temporary_table, id );
int main() {
using namespace matador::sql;
using namespace matador;
using namespace matador::object;
const std::string env_var{"MATADOR_BACKENDS_PATH"};
@ -178,118 +165,118 @@ int main() {
s.attach<book>( "books" );
connection c( dns );
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"; }
auto update_authors_sql = c.query( s )
.update( qh::authors )
.set( {{qh::authors.first_name, "Stephen"},
{qh::authors.last_name, "King"}} )
.where( qh::authors.last_name == "Crichton" )
.execute();
std::cout << "SQL: " << update_authors_sql << "\n";
auto authors = c.query( s )
.select( qh::authors.columns )
.from( qh::authors )
.fetch_all<author>();
for (const auto& a: authors) { std::cout << "Author " << a.first_name << "\n"; }
c.query( s )
.insert()
.into( qh::books )
.values( {2, "It", mc.id, 1980} )
.execute();
c.query( s )
.insert()
.into( qh::books )
.values( {3, "Misery", mc.id, 1984} )
.execute();
auto select_books_sql = c.query( s )
.select( qh::books.columns, {qh::authors.last_name} )
.from( qh::books )
.join_left( qh::authors )
.on( qh::books.author_id == qh::authors.id )
.where( qh::books.published_in < 2008 && qh::authors.last_name == "King" )
.group_by( qh::books.published_in )
.order_by( qh::books.title ).asc()
.limit( 5 )
.offset( 2 )
.fetch_all();
for (const auto& r: select_books_sql) { std::cout << "R: " << r.at( qh::books.title ) << ", " << r.at( qh::authors.last_name ) << "\n"; }
// SELECT book.title, book.id, book.author_id, book.published_in, author.name
// FROM book
// INNER JOIN author ON book.author_id = author.id
// WHERE book.published_in < 2008 AND author.name = "Michael Crichton"
// ORDER BY "book.title" ASC
// OFFSET 2 LIMIT 5
c.query( s ).drop().table( qh::books ).execute();
auto drop_authors_sql = c.query( s )
.drop()
.table( qh::authors )
.execute();
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();
// .fetch_value<unsigned long>();
std::cout << "SQL: " << res.sql << "\n";
auto 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"; }
//
// auto update_authors_sql = c.query( s )
// .update( qh::authors )
// .set( {{qh::authors.first_name, "Stephen"},
// {qh::authors.last_name, "King"}} )
// .where( qh::authors.last_name == "Crichton" )
// .execute();
//
// std::cout << "SQL: " << update_authors_sql << "\n";
//
// auto authors = c.query( s )
// .select( qh::authors.columns )
// .from( qh::authors )
// .fetch_all<author>();
//
// for (const auto& a: authors) { std::cout << "Author " << a.first_name << "\n"; }
//
// c.query( s )
// .insert()
// .into( qh::books )
// .values( {2, "It", mc.id, 1980} )
// .execute();
//
// c.query( s )
// .insert()
// .into( qh::books )
// .values( {3, "Misery", mc.id, 1984} )
// .execute();
//
// auto select_books_sql = c.query( s )
// .select( qh::books.columns, {qh::authors.last_name} )
// .from( qh::books )
// .join_left( qh::authors )
// .on( qh::books.author_id == qh::authors.id )
// .where( qh::books.published_in < 2008 && qh::authors.last_name == "King" )
// .group_by( qh::books.published_in )
// .order_by( qh::books.title ).asc()
// .limit( 5 )
// .offset( 2 )
// .fetch_all();
//
// for (const auto& r: select_books_sql) { std::cout << "R: " << r.at( qh::books.title ) << ", " << r.at( qh::authors.last_name ) << "\n"; }
// // SELECT book.title, book.id, book.author_id, book.published_in, author.name
// // FROM book
// // INNER JOIN author ON book.author_id = author.id
// // WHERE book.published_in < 2008 AND author.name = "Michael Crichton"
// // ORDER BY "book.title" ASC
// // OFFSET 2 LIMIT 5
//
// c.query( s ).drop().table( qh::books ).execute();
//
// auto drop_authors_sql = c.query( s )
// .drop()
// .table( qh::authors )
// .execute();
//
// 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();
// // .fetch_value<unsigned long>();
// std::cout << "SQL: " << res.sql << "\n";
return 0;
}

View File

@ -0,0 +1,17 @@
#ifndef OBJECT_PTR_HPP
#define OBJECT_PTR_HPP
namespace matador::object {
template <typename Type>
class object_ptr {
public:
Type* operator->() { return ptr; };
Type& operator*() { return *ptr; };
private:
Type* ptr{nullptr};
};
}
#endif //OBJECT_PTR_HPP

View File

@ -18,7 +18,10 @@ class schema {
public:
typedef const_schema_node_iterator const_iterator; /**< Shortcut for the list const iterator. */
schema();
/**
* Creates an empty schema
*/
explicit schema( const std::string& name = "");
template <typename Type>
utils::result<void, utils::error> attach(const std::string name, const std::string &parent = "") {
@ -36,6 +39,11 @@ public:
utils::result<void, utils::error> attach(const std::string name) {
auto node = schema_node::make_node<Type>(*this, name);
auto result = attach_node(node, std::type_index(typeid(ParentType)));
if (!result) {
return utils::failure(result.err());
}
return utils::ok<void>();
}
@ -53,26 +61,46 @@ public:
*/
[[nodiscard]] const_iterator end() const;
/**
* Returns true if the schema contains
* no schema nodes.
*
* @return True if schema is empty
*/
[[nodiscard]] bool empty() const;
/**
* Returns the current number of schema node.
*
* @return Number of schema nodes
*/
[[nodiscard]] size_t size() const;
/**
* Returns the name of the schema.
*
* @return The name of the schema
*/
[[nodiscard]] std::string name() const;
private:
using t_node_map = std::unordered_map<std::string, std::shared_ptr<schema_node>>;
// type_index -> [name -> prototype]
using t_type_index_node_map = std::unordered_map<std::type_index, t_node_map>;
using node_ptr = std::shared_ptr<schema_node>;
using t_node_map = std::unordered_map<std::string, node_ptr>;
using t_type_index_node_map = std::unordered_map<std::type_index, node_ptr>;
utils::result<std::shared_ptr<schema_node>, utils::error> attach_node(const std::shared_ptr<schema_node> &node,
const std::string &parent);
utils::result<std::shared_ptr<schema_node>, utils::error> find_parent(const std::string &name) const;
utils::result<std::shared_ptr<schema_node>, utils::error> find_node(const std::string &name) const;
[[nodiscard]] utils::result<std::shared_ptr<schema_node>, utils::error> attach_node(const std::shared_ptr<schema_node> &node,
const std::string &parent);
[[nodiscard]] utils::result<std::shared_ptr<schema_node>, utils::error> attach_node(const std::shared_ptr<schema_node> &node,
const std::type_index &type_index);
[[nodiscard]] utils::result<std::shared_ptr<schema_node>, utils::error> find_node(const std::string &name) const;
[[nodiscard]] utils::result<std::shared_ptr<schema_node>, utils::error> find_node(const std::type_index &type_index) const;
void push_back_child(const node_ptr &parent, const node_ptr &child);
[[nodiscard]] bool has_node(const std::type_index& index, const std::string &name) const;
bool has_node(const std::type_index& index, const std::string &name) const;
static void push_back_child(const node_ptr &parent, const node_ptr &child);
private:
std::string name_;
std::shared_ptr<schema_node> root_;
t_node_map node_map_;

View File

@ -12,6 +12,8 @@ class schema;
class schema_node final {
public:
using node_ptr = std::shared_ptr<schema_node>;
template < typename Type >
static std::shared_ptr<schema_node> make_node(schema& tree, const std::string& name) {
return std::shared_ptr<schema_node>(new schema_node(tree, name, static_cast<Type*>(nullptr)));
@ -41,6 +43,9 @@ public:
*/
void insert(const std::shared_ptr<schema_node> &child);
[[nodiscard]] node_ptr next() const;
[[nodiscard]] node_ptr prev() const;
private:
explicit schema_node(schema& tree);
template < typename Type >

View File

@ -81,7 +81,7 @@ public:
*
* @return Returns iterator before incrementing.
*/
const_schema_node_iterator operator++(int);
const_schema_node_iterator operator++( int );
/**
* Pre increments the iterator

View File

@ -10,7 +10,6 @@
#include <string>
namespace matador::sql {
class schema;
class connection_impl;
/**

View File

@ -0,0 +1,24 @@
#ifndef QUERY_QUERY_HELPER_HPP
#define QUERY_QUERY_HELPER_HPP
#include "matador/utils/macro_map.hpp"
#include "matador/sql/table.hpp"
#include "matador/sql/column.hpp"
#include <string>
#include <ostream>
#define FIELD(x) const sql::column x{*this, #x, ""};
#define QUERY_HELPER(C, ...) \
namespace matador::qh { \
namespace internal { \
struct C##_query : sql::table { \
C##_query() : table(#C) {} \
MAP(FIELD, __VA_ARGS__) \
}; } \
static const internal:: C##_query C; \
}
#endif //QUERY_QUERY_HELPER_HPP

View File

@ -0,0 +1,42 @@
#ifndef QUERY_MACRO_MAP_HPP
#define QUERY_MACRO_MAP_HPP
#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define MAP_END(...)
#define MAP_OUT
#define MAP_COMMA ,
#define MAP_GET_END2() 0, MAP_END
#define MAP_GET_END1(...) MAP_GET_END2
#define MAP_GET_END(...) MAP_GET_END1
#define MAP_NEXT0(test, next, ...) next MAP_OUT
#define MAP_NEXT1(test, next) MAP_NEXT0(test, next, 0)
#define MAP_NEXT(test, next) MAP_NEXT1(MAP_GET_END test, next)
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__)
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__)
#define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0)
#define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next)
#define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__)
#define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__)
/**
* Applies the function macro `f` to each of the remaining parameters.
*/
#define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
/**
* Applies the function macro `f` to each of the remaining parameters and
* inserts commas between the results.
*/
#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
#endif //QUERY_MACRO_MAP_HPP

View File

@ -20,6 +20,7 @@ add_library(matador-core STATIC
../../include/matador/utils/foreign_attributes.hpp
../../include/matador/utils/identifier.hpp
../../include/matador/utils/library.hpp
../../include/matador/utils/macro_map.hpp
../../include/matador/utils/os.hpp
../../include/matador/utils/placeholder.hpp
../../include/matador/utils/result.hpp

View File

@ -6,8 +6,9 @@ utils::error make_error(const error_code ec, const std::string& msg) {
return utils::error(ec, msg);
}
schema::schema()
: root_(std::shared_ptr<schema_node>(new schema_node(*this))) {
schema::schema( const std::string& name)
: name_(name)
, root_(std::shared_ptr<schema_node>(new schema_node(*this))) {
root_->first_child_ = std::shared_ptr<schema_node>(new schema_node(*this));
root_->last_child_ = std::shared_ptr<schema_node>(new schema_node(*this));
root_->first_child_->next_sibling_ = root_->last_child_;
@ -30,64 +31,67 @@ size_t schema::size() const {
return static_cast<size_t>(std::distance(begin(), end()));
}
std::string schema::name() const {
return name_;
}
utils::result<std::shared_ptr<schema_node>, utils::error> schema::attach_node(const std::shared_ptr<schema_node> &node,
const std::string &parent) {
if (!has_node(node->type_index(), node->name())) {
if (has_node(node->type_index(), node->name())) {
return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + node->name() + "' already exists."));
}
// set node to root node
auto parent_node = root_;
auto result = find_parent(parent);
if (!parent.empty()) {
auto result = find_node(parent);
if (!result.is_ok() && result.err().ec() != error_code::NodeNotFound) {
return result;
}
parent_node = *result;
}
push_back_child(parent_node, node);
// Todo: check return value
node_map_.insert({node->name(), node})/*.first*/;
type_index_node_map_.insert({node->type_index(), node});
return utils::ok(node);
}
utils::result<std::shared_ptr<schema_node>, utils::error> schema::attach_node( const std::shared_ptr<schema_node>& node,
const std::type_index& type_index ) {
if (has_node(node->type_index(), node->name())) {
return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + node->name() + "' already exists."));
}
auto result = find_node(type_index);
if (!result.is_ok() && result.err().ec() != error_code::NodeNotFound) {
return result;
}
parent_node = *result;
push_back_child(root_, node);
push_back_child(*result, node);
// if (!pk.is_null()) {
// node->primary_key_ = pk;
// }
// store prototype in map
// Todo: check return value
node_map_.insert(std::make_pair(node->name(), node))/*.first*/;
type_index_node_map_[node->type_index()].insert(std::make_pair(node->name(), node));
node_map_.insert({node->name(), node})/*.first*/;
type_index_node_map_.insert({node->type_index(), node});
// return nptr.release();
return {};
}
utils::result<std::shared_ptr<schema_node>, utils::error> schema::find_parent(const std::string &name) const {
if (name.empty()) {
return utils::failure(make_error(error_code::Failure, "Name of parent cannot be empty"));
}
return find_node(name);
return utils::ok(node);
}
utils::result<std::shared_ptr<schema_node>, utils::error> schema::find_node(const std::string &name) const {
// first search in the prototype map
const auto i = node_map_.find(name);
if (i == node_map_.end()) {
// if not found search in the typeid to prototype map
// const auto j = type_index_node_map_.find(name);
// if (j == type_index_node_map_.end()) {
// return utils::failure(
// make_error(error_code::NodeNotFound, std::string("Could not find node with name '") + name + "'"));
// }
// const t_node_map &val = j->second;
/*
* if size is greater one (1) the name
* is a typeid and has more than one prototype
* node and therefor it is not unique and an
* exception is thrown
*/
// if (val.size() > 1) {
// return utils::failure(make_error(error_code::NodeNotFound, std::string("Type id '") + name + "' is not unique"));
// }
// // return the only prototype
// return utils::ok(val.begin()->second);
return utils::failure(make_error(error_code::NodeNotFound, "Couldn't find node by name '" + name + "'"));
}
return utils::ok(i->second);
}
utils::result<std::shared_ptr<schema_node>, utils::error> schema::find_node( const std::type_index& type_index ) const {
const auto i = type_index_node_map_.find(type_index);
if (i == type_index_node_map_.end()) {
return utils::failure(make_error(error_code::NodeNotFound, "Couldn't find node by type '" + std::string(type_index.name()) + "'"));
}
return utils::ok(i->second);
}
@ -96,6 +100,12 @@ void schema::push_back_child(const node_ptr &parent, const node_ptr &child) {
child->parent_ = parent;
child->previous_sibling_ = parent->last_child_->previous_sibling_;
child->next_sibling_ = parent->last_child_;
/*
* +-----------------------------<- (first) parent (last) -> ----------------------------+
* | |
* first (next) -> <- (prev) child_1 (next) -> <- (prev) new_child (next) -> <- (prev) last
* ^^^^^^^ inserted ^^^^^^
*/
parent->last_child_->previous_sibling_->next_sibling_ = child;
parent->last_child_->previous_sibling_ = child;
// set depth

View File

@ -57,4 +57,38 @@ void schema_node::insert(const std::shared_ptr<schema_node> &child) {
// // 3. last
// child->op_last = op_last;
}
schema_node::node_ptr schema_node::next() const {
// if we have a child, child is the next iterator to return
// (if we don't do iterate over the siblings)
if (first_child_ && first_child_->next_sibling_ != last_child_) {
return first_child_->next_sibling_;
}
// if there is no child, we check for sibling
// if there is a sibling, this is our next iterator to return
// if not, we go back to the parent
auto *node = this;
while (node->parent_ && node->next_sibling_ == node->parent_->last_child_) {
node = node->parent_.get();
}
return node->parent_ ? node->next_sibling_ : node->last_child_;
}
schema_node::node_ptr schema_node::prev() const {
// if node has a previous sibling, we set it
// as our next iterator. then we check if there
// are last children. if so, we set the last-last
// child as our iterator
if (previous_sibling_ && previous_sibling_->previous_sibling_) {
auto node = previous_sibling_;
while (node->last_child_ && node->first_child_->next_sibling_ != node->last_child_) {
node = node->last_child_->previous_sibling_;
}
return node;
}
// if there is no previous sibling, our next iterator
// is the parent of the node
return parent_->parent_ ? parent_ : parent_->first_child_->next_sibling_;
}
}

View File

@ -24,8 +24,7 @@ const_schema_node_iterator& const_schema_node_iterator::operator++()
return *this;
}
const_schema_node_iterator const_schema_node_iterator::operator++(int)
{
const_schema_node_iterator const_schema_node_iterator::operator++( int ) {
const std::shared_ptr<value_type> tmp = node_;
increment();
return const_schema_node_iterator(tmp);
@ -65,42 +64,15 @@ void const_schema_node_iterator::increment()
return;
}
// if we have a child, child is the next iterator to return
// (if we don't do iterate over the siblings)
if (node_->first_child_ && node_->first_child_->next_sibling_ != node_->last_child_) {
node_ = node_->first_child_->next_sibling_;
} else {
// if there is no child, we check for sibling
// if there is a sibling, this is our next iterator to return
// if not, we go back to the parent
std::shared_ptr<value_type> node = node_;
while (node_->parent_ && node_->next_sibling_ == node_->parent_->last_child_) {
node = node->parent_;
}
node_ = node_->next_sibling_;
}
node_ = node_->next();
}
void const_schema_node_iterator::decrement()
{
if (!node_) {
return;
}
// if node has a previous sibling, we set it
// as our next iterator. then we check if there
// are last children. if so, we set the last-last
// child as our iterator
if (node_->previous_sibling_ && node_->previous_sibling_->previous_sibling_) {
std::shared_ptr<value_type> node = node_->previous_sibling_;
while (node_->last_child_ && node_->first_child_->next_sibling_ != node_->last_child_) {
node = node->last_child_->previous_sibling_;
}
node_ = node;
// if there is no previous sibling, our next iterator
// is the parent of the node
} else {
node_ = node_->parent_;
}
node_ = node_->prev();
}
}

View File

@ -59,6 +59,7 @@ add_library(matador-orm STATIC
../../include/matador/sql/internal/object_result_binder.hpp
../../include/matador/sql/internal/query_result_impl.hpp
../../include/matador/sql/query_context.hpp
../../include/matador/sql/query_macro.hpp
../../include/matador/sql/query_result.hpp
../../include/matador/sql/record.hpp
../../include/matador/sql/statement.hpp

View File

@ -12,7 +12,7 @@ add_executable(CoreTests
utils/FieldAttributeTest.cpp
utils/VersionTest.cpp
utils/StringTest.cpp
object/PrototypeTreeTest.cpp
object/SchemaTest.cpp
)
target_link_libraries(CoreTests matador-core Catch2::Catch2WithMain)

View File

@ -13,13 +13,13 @@ struct person {
struct student final : person {};
struct teacher final : person {};
TEST_CASE("Test empty prototype tree", "[prototype_tree][empty]") {
TEST_CASE("Test empty prototype tree", "[schema_node][empty]") {
const object::schema tree;
REQUIRE( tree.empty() );
}
TEST_CASE("Test add type to prototype tree", "[prototype_tree][add]") {
TEST_CASE("Test add type to prototype tree", "[schema_node][add]") {
object::schema tree;
REQUIRE( tree.empty() );
@ -31,5 +31,22 @@ TEST_CASE("Test add type to prototype tree", "[prototype_tree][add]") {
res = tree.attach<teacher, person>("teacher");
REQUIRE( res.is_ok() );
REQUIRE( !tree.empty() );
REQUIRE( tree.size() == 3 );
}
TEST_CASE("Test next and previous of schema node", "[schema_node][next][previous]") {
object::schema tree;
REQUIRE( tree.empty() );
auto res = tree.attach<person>("person");
REQUIRE( res.is_ok() );
REQUIRE( tree.size() == 1 );
auto it = tree.begin();
REQUIRE( it->name() == "person" );
REQUIRE( (--it)->name() == "person" );
REQUIRE( ++it == tree.end() );
}