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

View File

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

View File

@ -1,13 +1,18 @@
#include "matador/sql/column.hpp" #include "matador/sql/column.hpp"
#include "matador/sql/condition.hpp"
#include "matador/sql/schema.hpp"
#include "matador/sql/connection.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/access.hpp"
#include "matador/utils/default_type_traits.hpp"
#include "matador/utils/enum_mapper.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 <iostream>
#include <string> #include <string>
@ -22,7 +27,7 @@ struct author {
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::utils::access; namespace field = matador::access;
field::primary_key( op, "id", id ); field::primary_key( op, "id", id );
field::attribute( op, "first_name", first_name, 63 ); field::attribute( op, "first_name", first_name, 63 );
field::attribute( op, "last_name", last_name, 63 ); field::attribute( op, "last_name", last_name, 63 );
@ -34,13 +39,13 @@ struct author {
struct book { struct book {
unsigned long id{}; unsigned long id{};
matador::sql::entity<author> book_author; matador::object::object_ptr<author> book_author;
std::string title; std::string title;
unsigned short published_in{}; unsigned short published_in{};
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::utils::access; namespace field = matador::access;
field::primary_key( op, "id", id ); field::primary_key( op, "id", id );
field::attribute( op, "title", title, 511 ); field::attribute( op, "title", title, 511 );
field::has_one( op, "author_id", book_author, matador::utils::default_foreign_attributes ); field::has_one( op, "author_id", book_author, matador::utils::default_foreign_attributes );
@ -53,7 +58,7 @@ struct payload {
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::utils::access; namespace field = matador::access;
field::primary_key( op, "id", id ); field::primary_key( op, "id", id );
} }
}; };
@ -73,7 +78,7 @@ struct job {
}; };
unsigned long id{}; unsigned long id{};
matador::sql::entity<payload> payload; matador::object::object_ptr<payload> payload;
std::string type; std::string type;
std::string description; std::string description;
job_state state; job_state state;
@ -81,7 +86,7 @@ struct job {
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::utils::access; namespace field = matador::access;
field::primary_key( op, "id", id ); field::primary_key( op, "id", id );
field::belongs_to( op, "payload", payload, matador::utils::default_foreign_attributes ); field::belongs_to( op, "payload", payload, matador::utils::default_foreign_attributes );
field::attribute( op, "type", type, 511 ); field::attribute( op, "type", type, 511 );
@ -105,13 +110,10 @@ static const matador::utils::enum_mapper<job::job_mode> job_mode_enum({
}); });
template<> template<>
struct matador::sql::data_type_traits<job::job_state, void> 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); }
inline static data_type_t builtin_type(std::size_t size)
{ return data_type_traits<std::string>::builtin_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; std::string enum_string;
reader.read_value(id, index, enum_string, 64); reader.read_value(id, index, enum_string, 64);
if (const auto enum_opt = job_state_enum.to_enum(enum_string)) { 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) 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));
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));
} }
}; };
template<> template<>
struct matador::sql::data_type_traits<job::job_mode, void> 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); }
inline static data_type_t builtin_type(std::size_t size)
{ return data_type_traits<std::string>::builtin_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; std::string enum_string;
reader.read_value(id, index, enum_string, 64); reader.read_value(id, index, enum_string, 64);
if (const auto enum_opt = job_mode_enum.to_enum(enum_string)) { 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) 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));
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));
} }
}; };
@ -168,7 +155,7 @@ QUERY_HELPER( temporary_table, id );
int main() { int main() {
using namespace matador::sql; using namespace matador::sql;
using namespace matador; using namespace matador::object;
const std::string env_var{"MATADOR_BACKENDS_PATH"}; const std::string env_var{"MATADOR_BACKENDS_PATH"};
@ -178,118 +165,118 @@ int main() {
s.attach<book>( "books" ); s.attach<book>( "books" );
connection c( dns ); connection c( dns );
c.open(); auto result = c.open();
s.create( c ); // s.create( c );
//
auto create_authors_sql = c.query( s ) // auto create_authors_sql = c.query( s )
.create() // .create()
.table<author>( qh::authors ) // .table<author>( qh::authors )
.execute(); // .execute();
//
c.query( s ) // c.query( s )
.create() // .create()
.table<book>( qh::books ) // .table<book>( qh::books )
.execute(); // .execute();
//
std::cout << "SQL: " << create_authors_sql << "\n"; // std::cout << "SQL: " << create_authors_sql << "\n";
//
author mc; // author mc;
mc.id = 1; // mc.id = 1;
mc.first_name = "Michael"; // mc.first_name = "Michael";
mc.last_name = "Crichton"; // mc.last_name = "Crichton";
mc.date_of_birth = "19.8.1954"; // mc.date_of_birth = "19.8.1954";
mc.year_of_birth = 1954; // mc.year_of_birth = 1954;
mc.distinguished = true; // mc.distinguished = true;
auto insert_authors_sql = c.query( s ) // auto insert_authors_sql = c.query( s )
.insert() // .insert()
.into( qh::authors ) // .into( qh::authors )
.values( mc ) // .values( mc )
.execute(); // .execute();
//
std::cout << "SQL: " << insert_authors_sql << "\n"; // std::cout << "SQL: " << insert_authors_sql << "\n";
//
auto result = c.query( s ) // auto result = c.query( s )
.select( qh::authors.columns ) // .select( qh::authors.columns )
.from( qh::authors ) // .from( qh::authors )
.fetch_all(); // .fetch_all();
//
for (const auto& row: result) { std::cout << "Author " << row.at( qh::authors.first_name ) << "\n"; } // for (const auto& row: result) { std::cout << "Author " << row.at( qh::authors.first_name ) << "\n"; }
//
auto update_authors_sql = c.query( s ) // auto update_authors_sql = c.query( s )
.update( qh::authors ) // .update( qh::authors )
.set( {{qh::authors.first_name, "Stephen"}, // .set( {{qh::authors.first_name, "Stephen"},
{qh::authors.last_name, "King"}} ) // {qh::authors.last_name, "King"}} )
.where( qh::authors.last_name == "Crichton" ) // .where( qh::authors.last_name == "Crichton" )
.execute(); // .execute();
//
std::cout << "SQL: " << update_authors_sql << "\n"; // std::cout << "SQL: " << update_authors_sql << "\n";
//
auto authors = c.query( s ) // auto authors = c.query( s )
.select( qh::authors.columns ) // .select( qh::authors.columns )
.from( qh::authors ) // .from( qh::authors )
.fetch_all<author>(); // .fetch_all<author>();
//
for (const auto& a: authors) { std::cout << "Author " << a.first_name << "\n"; } // for (const auto& a: authors) { std::cout << "Author " << a.first_name << "\n"; }
//
c.query( s ) // c.query( s )
.insert() // .insert()
.into( qh::books ) // .into( qh::books )
.values( {2, "It", mc.id, 1980} ) // .values( {2, "It", mc.id, 1980} )
.execute(); // .execute();
//
c.query( s ) // c.query( s )
.insert() // .insert()
.into( qh::books ) // .into( qh::books )
.values( {3, "Misery", mc.id, 1984} ) // .values( {3, "Misery", mc.id, 1984} )
.execute(); // .execute();
//
auto select_books_sql = c.query( s ) // auto select_books_sql = c.query( s )
.select( qh::books.columns, {qh::authors.last_name} ) // .select( qh::books.columns, {qh::authors.last_name} )
.from( qh::books ) // .from( qh::books )
.join_left( qh::authors ) // .join_left( qh::authors )
.on( qh::books.author_id == qh::authors.id ) // .on( qh::books.author_id == qh::authors.id )
.where( qh::books.published_in < 2008 && qh::authors.last_name == "King" ) // .where( qh::books.published_in < 2008 && qh::authors.last_name == "King" )
.group_by( qh::books.published_in ) // .group_by( qh::books.published_in )
.order_by( qh::books.title ).asc() // .order_by( qh::books.title ).asc()
.limit( 5 ) // .limit( 5 )
.offset( 2 ) // .offset( 2 )
.fetch_all(); // .fetch_all();
//
for (const auto& r: select_books_sql) { std::cout << "R: " << r.at( qh::books.title ) << ", " << r.at( qh::authors.last_name ) << "\n"; } // 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 // // SELECT book.title, book.id, book.author_id, book.published_in, author.name
// FROM book // // FROM book
// INNER JOIN author ON book.author_id = author.id // // INNER JOIN author ON book.author_id = author.id
// WHERE book.published_in < 2008 AND author.name = "Michael Crichton" // // WHERE book.published_in < 2008 AND author.name = "Michael Crichton"
// ORDER BY "book.title" ASC // // ORDER BY "book.title" ASC
// OFFSET 2 LIMIT 5 // // OFFSET 2 LIMIT 5
//
c.query( s ).drop().table( qh::books ).execute(); // c.query( s ).drop().table( qh::books ).execute();
//
auto drop_authors_sql = c.query( s ) // auto drop_authors_sql = c.query( s )
.drop() // .drop()
.table( qh::authors ) // .table( qh::authors )
.execute(); // .execute();
//
std::cout << "SQL: " << drop_authors_sql << "\n"; // std::cout << "SQL: " << drop_authors_sql << "\n";
//
auto res = c.query( s ) // auto res = c.query( s )
.select( {qh::payload.id} ) // .select( {qh::payload.id} )
.from( qh::payload ) // .from( qh::payload )
.join_left( qh::job ) // .join_left( qh::job )
.on( qh::job.payload == qh::payload.id ) // .on( qh::job.payload == qh::payload.id )
.where( // .where(
in( qh::payload.id, c.query( s ) // in( qh::payload.id, c.query( s )
.select( {qh::job.state} ) // .select( {qh::job.state} )
.from( qh::job ) // .from( qh::job )
.where( qh::job.state == job::job_state::Running ) // .where( qh::job.state == job::job_state::Running )
) && // ) &&
in( qh::payload.id, c.query( s ) // in( qh::payload.id, c.query( s )
.select( {qh::temporary_table.id} ) // .select( {qh::temporary_table.id} )
.from( qh::temporary_table ) ) // .from( qh::temporary_table ) )
) // )
.build(); // .build();
// .fetch_value<unsigned long>(); // // .fetch_value<unsigned long>();
std::cout << "SQL: " << res.sql << "\n"; // std::cout << "SQL: " << res.sql << "\n";
return 0; 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: public:
typedef const_schema_node_iterator const_iterator; /**< Shortcut for the list const iterator. */ 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> template <typename Type>
utils::result<void, utils::error> attach(const std::string name, const std::string &parent = "") { 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) { utils::result<void, utils::error> attach(const std::string name) {
auto node = schema_node::make_node<Type>(*this, 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>(); return utils::ok<void>();
} }
@ -53,26 +61,46 @@ public:
*/ */
[[nodiscard]] const_iterator end() const; [[nodiscard]] const_iterator end() const;
/**
* Returns true if the schema contains
* no schema nodes.
*
* @return True if schema is empty
*/
[[nodiscard]] bool empty() const; [[nodiscard]] bool empty() const;
/**
* Returns the current number of schema node.
*
* @return Number of schema nodes
*/
[[nodiscard]] size_t size() const; [[nodiscard]] size_t size() const;
/**
* Returns the name of the schema.
*
* @return The name of the schema
*/
[[nodiscard]] std::string name() const;
private: 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 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, [[nodiscard]] utils::result<std::shared_ptr<schema_node>, utils::error> attach_node(const std::shared_ptr<schema_node> &node,
const std::string &parent); const std::string &parent);
utils::result<std::shared_ptr<schema_node>, utils::error> find_parent(const std::string &name) const; [[nodiscard]] utils::result<std::shared_ptr<schema_node>, utils::error> attach_node(const std::shared_ptr<schema_node> &node,
utils::result<std::shared_ptr<schema_node>, utils::error> find_node(const std::string &name) const; 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: private:
std::string name_;
std::shared_ptr<schema_node> root_; std::shared_ptr<schema_node> root_;
t_node_map node_map_; t_node_map node_map_;

View File

@ -12,6 +12,8 @@ class schema;
class schema_node final { class schema_node final {
public: public:
using node_ptr = std::shared_ptr<schema_node>;
template < typename Type > template < typename Type >
static std::shared_ptr<schema_node> make_node(schema& tree, const std::string& name) { 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))); 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); void insert(const std::shared_ptr<schema_node> &child);
[[nodiscard]] node_ptr next() const;
[[nodiscard]] node_ptr prev() const;
private: private:
explicit schema_node(schema& tree); explicit schema_node(schema& tree);
template < typename Type > template < typename Type >

View File

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

View File

@ -10,7 +10,6 @@
#include <string> #include <string>
namespace matador::sql { namespace matador::sql {
class schema;
class connection_impl; 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/foreign_attributes.hpp
../../include/matador/utils/identifier.hpp ../../include/matador/utils/identifier.hpp
../../include/matador/utils/library.hpp ../../include/matador/utils/library.hpp
../../include/matador/utils/macro_map.hpp
../../include/matador/utils/os.hpp ../../include/matador/utils/os.hpp
../../include/matador/utils/placeholder.hpp ../../include/matador/utils/placeholder.hpp
../../include/matador/utils/result.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); return utils::error(ec, msg);
} }
schema::schema() schema::schema( const std::string& name)
: root_(std::shared_ptr<schema_node>(new schema_node(*this))) { : 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_->first_child_ = std::shared_ptr<schema_node>(new schema_node(*this));
root_->last_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_; 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())); 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, utils::result<std::shared_ptr<schema_node>, utils::error> schema::attach_node(const std::shared_ptr<schema_node> &node,
const std::string &parent) { 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.")); return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + node->name() + "' already exists."));
} }
// set node to root node // set node to root node
auto parent_node = root_; 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) { if (!result.is_ok() && result.err().ec() != error_code::NodeNotFound) {
return result; 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 // Todo: check return value
node_map_.insert(std::make_pair(node->name(), node))/*.first*/; node_map_.insert({node->name(), node})/*.first*/;
type_index_node_map_[node->type_index()].insert(std::make_pair(node->name(), node)); type_index_node_map_.insert({node->type_index(), node});
// return nptr.release(); return utils::ok(node);
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);
} }
utils::result<std::shared_ptr<schema_node>, utils::error> schema::find_node(const std::string &name) const { utils::result<std::shared_ptr<schema_node>, utils::error> schema::find_node(const std::string &name) const {
// first search in the prototype map // first search in the prototype map
const auto i = node_map_.find(name); const auto i = node_map_.find(name);
if (i == node_map_.end()) { if (i == node_map_.end()) {
// if not found search in the typeid to prototype map return utils::failure(make_error(error_code::NodeNotFound, "Couldn't find node by name '" + name + "'"));
// const auto j = type_index_node_map_.find(name); }
// if (j == type_index_node_map_.end()) { return utils::ok(i->second);
// return utils::failure( }
// make_error(error_code::NodeNotFound, std::string("Could not find node with name '") + name + "'"));
// } utils::result<std::shared_ptr<schema_node>, utils::error> schema::find_node( const std::type_index& type_index ) const {
// const t_node_map &val = j->second; const auto i = type_index_node_map_.find(type_index);
/* if (i == type_index_node_map_.end()) {
* if size is greater one (1) the name return utils::failure(make_error(error_code::NodeNotFound, "Couldn't find node by type '" + std::string(type_index.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::ok(i->second); 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->parent_ = parent;
child->previous_sibling_ = parent->last_child_->previous_sibling_; child->previous_sibling_ = parent->last_child_->previous_sibling_;
child->next_sibling_ = parent->last_child_; 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_->next_sibling_ = child;
parent->last_child_->previous_sibling_ = child; parent->last_child_->previous_sibling_ = child;
// set depth // set depth

View File

@ -57,4 +57,38 @@ void schema_node::insert(const std::shared_ptr<schema_node> &child) {
// // 3. last // // 3. last
// child->op_last = op_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; 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_; const std::shared_ptr<value_type> tmp = node_;
increment(); increment();
return const_schema_node_iterator(tmp); return const_schema_node_iterator(tmp);
@ -65,42 +64,15 @@ void const_schema_node_iterator::increment()
return; return;
} }
// if we have a child, child is the next iterator to return node_ = node_->next();
// (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_;
}
} }
void const_schema_node_iterator::decrement() void const_schema_node_iterator::decrement()
{ {
if (!node_) { if (!node_) {
return; return;
} }
// if node has a previous sibling, we set it
// as our next iterator. then we check if there node_ = node_->prev();
// 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_;
}
} }
} }

View File

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

View File

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

View File

@ -1,35 +1,52 @@
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include "matador/object/schema.hpp" #include "matador/object/schema.hpp"
struct node {}; struct node {};
using namespace matador; using namespace matador;
struct person { struct person {
virtual ~person() = default; virtual ~person() = default;
}; };
struct student final : person {}; struct student final : person {};
struct teacher 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; const object::schema tree;
REQUIRE( tree.empty() ); 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; object::schema tree;
REQUIRE( tree.empty() ); REQUIRE( tree.empty() );
auto res = tree.attach<person>("person"); auto res = tree.attach<person>("person");
REQUIRE( res.is_ok() ); REQUIRE( res.is_ok() );
res = tree.attach<student, person>("student"); res = tree.attach<student, person>("student");
REQUIRE( res.is_ok() ); REQUIRE( res.is_ok() );
res = tree.attach<teacher, person>("teacher"); res = tree.attach<teacher, person>("teacher");
REQUIRE( res.is_ok() ); REQUIRE( res.is_ok() );
REQUIRE( tree.size() == 3 ); 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() );
}