diff --git a/belongs_to.md b/belongs_to.md new file mode 100644 index 000000000..f8d766c4a --- /dev/null +++ b/belongs_to.md @@ -0,0 +1,78 @@ +has_many <-> belongs_to +=================================================== + +``` +struct employee +{ + oos::belongs_to dep; + + template < class S > + void serialize(S &s) + { + s.serialize("department", dep); + } +}; + +struct department +{ + oos::has_many emps; + + template < class S > + void serialize(S &s) + { + s.serialize("employee", emps); + } +}; + +auto george = ostore.insert(new employee("george")); +auto insurance = ostore.insert(new department("insurance")); + +insurance->emps.push_back(george); + +``` + +Analyze phase +============= + +__Case 1: Department attached first__ + + * attach department + * prototype "employee" not found; prepare new + * mark as has many relation for "department" member "employees" + * attach employee + * prototype "employee found" + * if employee contains belongs_to for "department" tie prototypes + * else throw error + +__Case 2: Employee attached first__ + + * attach employee + * prototype "department" not found: prepare new + * mark as belongs relation for "employee" member "department" + * attach department + * prototype "department" found + * if department contains has_many for "employee" tie prototypes + * else throw error + +Business phase +============== + +__Case 1: emplyoee sets a department__ + + * employee gets object type "department" with member name "employees" from prototype + * employee is added to departments employees container + +__Case 2: department adds an employee__ + + * department gets object type "employee" with member name "department" from prototype + * department is set for employee + + +TODO +==== + +1. Add ```object_store::attach(prototype_node, parent, [observer])``` +2. Add static + * ```prototype_node::make_node()``` + * ```prototype_node::make_relation_node()``` +3. Adjust ```node_anaylzer``` diff --git a/include/oos/db/mysql/mysql_statement.hpp b/include/oos/db/mysql/mysql_statement.hpp index b8df29b5c..3c5062268 100644 --- a/include/oos/db/mysql/mysql_statement.hpp +++ b/include/oos/db/mysql/mysql_statement.hpp @@ -76,8 +76,10 @@ class mysql_statement : public oos::detail::statement_impl private: template < class T > - void bind_value(MYSQL_BIND &bind, enum_field_types type, T value) + void bind_value(std::size_t index, enum_field_types type, T value) { +// std::cout << "binding [" << value << "] (index: " << index << ")\n"; + MYSQL_BIND &bind = host_array[index]; if (bind.buffer == nullptr) { // allocating memory bind.buffer = new char[sizeof(T)]; @@ -85,19 +87,22 @@ class mysql_statement : public oos::detail::statement_impl *static_cast(bind.buffer) = value; bind.buffer_type = type; bind.buffer_length = sizeof(T); - bind.is_null = 0; + is_null_vector[index] = false; + bind.is_null = &is_null_vector[index]; bind.is_unsigned = std::is_unsigned::value; } - void bind_value(MYSQL_BIND &bind, enum_field_types type, char x); - void bind_value(MYSQL_BIND &bind, enum_field_types type, unsigned char x); - void bind_value(MYSQL_BIND &bind, enum_field_types type, const oos::date &x); - void bind_value(MYSQL_BIND &bind, enum_field_types type, const oos::time &x); - void bind_value(MYSQL_BIND &bind, enum_field_types type, const char *value, size_t size); + void bind_value(std::size_t index, enum_field_types type, char x); + void bind_value(std::size_t index, enum_field_types type, unsigned char x); + void bind_value(std::size_t index, enum_field_types type, const oos::date &x); + void bind_value(std::size_t index, enum_field_types type, const oos::time &x); + void bind_value(std::size_t index, enum_field_types type, const char *value, size_t size); + + void bind_null(std::size_t index); private: size_t result_size; size_t host_size; - std::vector length_vector; + std::vector is_null_vector; MYSQL_STMT *stmt_ = nullptr; MYSQL_BIND *host_array = nullptr; }; diff --git a/include/oos/object/attribute_serializer.hpp b/include/oos/object/attribute_serializer.hpp index 3960e685a..c386d5bdf 100644 --- a/include/oos/object/attribute_serializer.hpp +++ b/include/oos/object/attribute_serializer.hpp @@ -58,7 +58,7 @@ class basic_attribute_serializer * with given name must be found and the value must * be convertible into the objects attribute. */ -template < class T, class Enable = void > +template < class T > class attribute_reader : public basic_attribute_serializer { public: @@ -67,10 +67,8 @@ class attribute_reader : public basic_attribute_serializer , from_(from) {} - ~attribute_reader() {} - template < class V > - void serialize(const char *id, V &to, typename std::enable_if< std::is_arithmetic::value && std::is_arithmetic::value >::type* = 0) + void serialize(const char *id, V &to, typename std::enable_if::value && std::is_arithmetic::value && !std::is_same::value>::type* = 0) { if (id_ != id) { return; @@ -79,6 +77,16 @@ class attribute_reader : public basic_attribute_serializer this->success_ = true; } + template < class V > + void serialize(const char *id, V &to, typename std::enable_if::value && std::is_same::value>::type* = 0) + { + if (id_ != id) { + return; + } + to = from_ > 0; + this->success_ = true; + } + template < class V > void serialize(const char *id, V &to, typename std::enable_if::value && std::is_same::value >::type* = 0) { @@ -89,7 +97,7 @@ class attribute_reader : public basic_attribute_serializer this->success_ = true; } template < class V > - void serialize(const char *, V &, typename std::enable_if<(!std::is_arithmetic::value || !std::is_arithmetic::value) && !std::is_same::value >::type* = 0) {} + void serialize(const char *, V &, typename std::enable_if<(!std::is_arithmetic::value || !std::is_arithmetic::value) && !std::is_same::value >::type* = 0) {} void serialize(const char *, char*, size_t) {} template < class HAS_ONE > void serialize(const char *, HAS_ONE &, cascade_type) {} @@ -100,25 +108,164 @@ class attribute_reader : public basic_attribute_serializer const T &from_; }; -//template < class T > -//class attribute_reader::value || std::is_base_of::value>::type> -//{ -//public: -// attribute_reader(const std::string &id, const T &from) -// : basic_attribute_serializer(id) -// , from_(from) -// {} -// -// ~attribute_reader() {} -// -// template < class V > -// void serialize(const char *id, ) -// void serialize(const char *id, char *x) { -// -// } -//private: -// const T &from_; -//}; +template <> +class attribute_reader : public basic_attribute_serializer +{ +public: + attribute_reader(const std::string &id, bool from) + : basic_attribute_serializer(id) + , from_(from) + {} + + template < class V > + void serialize(const char *id, V &to, typename std::enable_if::value && !std::is_same::value>::type* = 0) + { + if (id_ != id) { + return; + } + to = (from_ > 0); + this->success_ = true; + } + + template < class V > + void serialize(const char *id, V &to, typename std::enable_if::value>::type* = 0) + { + if (id_ != id) { + return; + } + to = (from_ > 0.0f); + this->success_ = true; + } + + template < class V > + void serialize(const char *id, V &to, typename std::enable_if::value && std::is_same::value>::type* = 0) + { + if (id_ != id) { + return; + } + to = from_; + this->success_ = true; + } + + template < class V > + void serialize(const char *, V &, typename std::enable_if::value>::type* = 0) {} + void serialize(const char *, char*, size_t) {} + template < class HAS_ONE > + void serialize(const char *, HAS_ONE &, cascade_type) {} + template < class HAS_MANY > + void serialize(const char *, HAS_MANY &, const char *, const char *) {} + +private: + bool from_; +}; + +template < class T > +class attribute_reader> : public basic_attribute_serializer +{ +public: + attribute_reader(const std::string &id, const object_ptr &from) + : basic_attribute_serializer(id) + , from_(from) + {} + + template < class V > + void serialize(V &obj) + { + access::serialize(*this, obj); + } + + template < class V > + void serialize(const char *, V &) {} + void serialize(const char *, char*, std::size_t) {} + + template < class V > + void serialize(const char *, belongs_to &x, cascade_type, typename std::enable_if::value>::type* = 0) + { + x = from_; + this->success_ = true; + } + template < class V > + void serialize(const char *, has_one &x, cascade_type, typename std::enable_if::value>::type* = 0) + { + x = from_; + this->success_ = true; + } + + template < class HAS_MANY > + void serialize(const char *, HAS_MANY &, const char *, const char *) {} + +private: + const object_ptr &from_; +}; + +template < class T, class Enabled = void > +class has_many_attribute_reader; + +template < class T > +class has_many_attribute_reader::value>::type> : public basic_attribute_serializer +{ +public: + has_many_attribute_reader(const std::string &id, const T &from) + : basic_attribute_serializer(id) + , from_(from) + {} + + template < class V > + void serialize(V &obj) + { + access::serialize(*this, obj); + } + + template < class V > + void serialize(const char *, V &) {} + void serialize(const char *, char*, std::size_t) {} + + template < class HAS_ONE > + void serialize(const char *, HAS_ONE &, cascade_type) { } + + template class C> + void serialize(const char *, has_many &x, const char *, const char *) + { + x.push_back(from_); + this->success_ = true; + } + +private: + const T &from_; +}; + +template < class T > +class has_many_attribute_reader> : public basic_attribute_serializer +{ +public: + has_many_attribute_reader(const std::string &id, const object_ptr &from) + : basic_attribute_serializer(id) + , from_(from) + {} + + template < class V > + void serialize(V &obj) + { + access::serialize(*this, obj); + } + + template < class V > + void serialize(const char *, V &) {} + void serialize(const char *, char*, std::size_t) {} + + template < class HAS_ONE > + void serialize(const char *, HAS_ONE &, cascade_type) { } + + template class C> + void serialize(const char *, has_many &x, const char *, const char *) + { + x.push_back(from_); + this->success_ = true; + } + +private: + const object_ptr &from_; +}; template <> class attribute_reader : public basic_attribute_serializer @@ -190,7 +337,7 @@ class attribute_writer : public basic_attribute_serializer ~attribute_writer() {} template < class V > - void serialize(const char *id, V &from, typename std::enable_if< std::is_arithmetic::value && std::is_arithmetic::value >::type* = 0) + void serialize(const char *id, V &from, typename std::enable_if< std::is_arithmetic::value && std::is_arithmetic::value && !std::is_same::value>::type* = 0) { if (id_ != id) { return; @@ -199,6 +346,16 @@ class attribute_writer : public basic_attribute_serializer success_ = true; } + template < class V > + void serialize(const char *id, V &from, typename std::enable_if::value && std::is_arithmetic::value && std::is_same::value>::type* = 0) + { + if (id_ != id) { + return; + } + to_ = (from ? 1 : 0); + success_ = true; + } + template < class V > void serialize(const char *id, V &from, typename std::enable_if::value && std::is_same::value >::type* = 0) { @@ -222,6 +379,104 @@ class attribute_writer : public basic_attribute_serializer size_t precision_; }; +template < class T > +class attribute_writer> : public basic_attribute_serializer +{ +public: + attribute_writer(const std::string &id, object_ptr &to) + : basic_attribute_serializer(id) + , to_(to) + {} + + template < class V > + void serialize(const char *, V &) {} + void serialize(const char *, char*, size_t) {} + + template < class V > + void serialize(const char *, belongs_to &x, cascade_type, typename std::enable_if::value>::type* = 0) + { + to_ = x; + } + template < class V > + void serialize(const char *, has_one &x, cascade_type, typename std::enable_if::value>::type* = 0) + { + to_ = x; + } + + template < class HAS_MANY > + void serialize(const char *, HAS_MANY &, const char *, const char *) {} + +private: + object_ptr &to_; +}; + +template < class T, class Enabled = void > +class has_many_attribute_writer; + +template < class T > +class has_many_attribute_writer::value>::type> : public basic_attribute_serializer +{ +public: + has_many_attribute_writer(const std::string &id, const T &to) + : basic_attribute_serializer(id) + , to_(to) + {} + + template < class V > + void serialize(V &obj) + { + access::serialize(*this, obj); + } + + template < class V > + void serialize(const char *, V &) {} + void serialize(const char *, char*, size_t) {} + + template < class HAS_ONE > + void serialize(const char *, HAS_ONE &, cascade_type) {} + + template class C> + void serialize(const char *, has_many &x, const char *, const char *) + { + x.remove(to_); + this->success_ = true; + } +private: + const T &to_; +}; + +template < class T > +class has_many_attribute_writer> : public basic_attribute_serializer +{ +public: + has_many_attribute_writer(const std::string &id, const object_ptr &to) + : basic_attribute_serializer(id) + , to_(to) + {} + + template < class V > + void serialize(V &obj) + { + access::serialize(*this, obj); + } + + template < class V > + void serialize(const char *, V &) {} + void serialize(const char *, char*, size_t) {} + + template < class HAS_ONE > + void serialize(const char *, HAS_ONE &, cascade_type) {} + + template class C> + void serialize(const char *, has_many &x, const char *, const char *) + { + x.remove(to_); + this->success_ = true; + } +private: + const object_ptr &to_; +}; + template <> class attribute_writer : public basic_attribute_serializer { @@ -311,6 +566,22 @@ class attribute_writer : public basic_attribute_serializer success_ = true; } + template < class V > + void serialize(const char *id, belongs_to &x, cascade_type) + { + if (id_ != id) { + return; + } + std::stringstream to; + if (x.has_primary_key()) { + x.primary_key()->print(to); + } else { + to << x.id(); + } + to_ = to.str(); + success_ = true; + } + template < class V > void serialize(const char *id, has_one &x, cascade_type) { @@ -326,6 +597,7 @@ class attribute_writer : public basic_attribute_serializer to_ = to.str(); success_ = true; } + void serialize(const char*, abstract_has_many&, const char*, const char*) {} private: @@ -383,6 +655,8 @@ class attribute_writer void serialize(const char*, date&) {} void serialize(const char*, time&) {} template < class V > + void serialize(const char*, belongs_to &, cascade_type) {} + template < class V > void serialize(const char*, has_one &, cascade_type) {} void serialize(const char*, abstract_has_many&, const char*, const char*) {} template < class V > diff --git a/include/oos/object/basic_has_many.hpp b/include/oos/object/basic_has_many.hpp index 482d06198..571faa26e 100644 --- a/include/oos/object/basic_has_many.hpp +++ b/include/oos/object/basic_has_many.hpp @@ -14,6 +14,12 @@ namespace oos { namespace detail { class object_inserter; + +template class C, class Enabled = void> +class has_many_deleter; + +template class C, class Enabled = void> +class has_many_inserter; } /// @cond OOS_DEV @@ -63,6 +69,8 @@ class basic_has_many : public abstract_has_many typedef typename container_type::size_type size_type; /**< Shortcut to size type */ typedef typename container_type::iterator container_iterator; /**< Shortcut to container iterator */ + typedef std::function mark_modified_owner_func; /**< Shortcut to mark modified owner function */ + public: /** * @brief Returns the begin iterator of the container @@ -154,13 +162,6 @@ class basic_has_many : public abstract_has_many */ void append(const typename iterator::internal_type &item) { container_.push_back(item); } - /** - * @brief Returns true if the container uses a join table - * - * @return True if the container uses a join table - */ - bool has_join_table() const { return true; } - protected: /// @cond OOS_DEV @@ -170,7 +171,7 @@ class basic_has_many : public abstract_has_many object_proxy *owner_ = nullptr; std::shared_ptr owner_id_; - std::function mark_modified_owner_; + mark_modified_owner_func mark_modified_owner_; container_type container_; diff --git a/include/oos/object/belongs_to.hpp b/include/oos/object/belongs_to.hpp new file mode 100644 index 000000000..c32777cfb --- /dev/null +++ b/include/oos/object/belongs_to.hpp @@ -0,0 +1,13 @@ +#ifndef OOS_BELONGS_TO_HPP +#define OOS_BELONGS_TO_HPP + +#include "oos/object/object_ptr.hpp" + +namespace oos { + +template < class T > +using belongs_to = object_pointer; + +} + +#endif //OOS_BELONGS_TO_HPP diff --git a/include/oos/object/delete_action.hpp b/include/oos/object/delete_action.hpp index 702f0c3de..0af10e599 100644 --- a/include/oos/object/delete_action.hpp +++ b/include/oos/object/delete_action.hpp @@ -103,7 +103,6 @@ class OOS_OBJECT_API delete_action : public action template < class T, class S > static void restore_delete(byte_buffer &buffer, delete_action *act, object_store *store, S &serializer) { - // TODO: pass object store instance // check if there is an serializable with id in // serializable store object_proxy *proxy = action::find_proxy(store, act->id()); diff --git a/include/oos/object/generic_access.hpp b/include/oos/object/generic_access.hpp index 04ded72eb..b9c14fd24 100644 --- a/include/oos/object/generic_access.hpp +++ b/include/oos/object/generic_access.hpp @@ -23,13 +23,24 @@ namespace oos { * @param val The new value for the member. * @return True if the operation succeeds. */ - -template < typename O, class T, typename std::enable_if::value>::type* = nullptr > +template < typename O, class T > bool set(O *obj, const std::string &name, const T &val) { return set(*obj, name, val); } +template < typename O, class T > +bool append(O *obj, const std::string &name, const T &val) +{ + return append(*obj, name, val); +} + +template < typename O, class T > +bool remove(O *obj, const std::string &name, const T &val) +{ + return remove(*obj, name, val); +} + /** * Sets a value of an object member identified by * the given name. The object is passed by reference. @@ -42,7 +53,7 @@ bool set(O *obj, const std::string &name, const T &val) * @param val The new value for the member. * @return True if the operation succeeds. */ -template < typename O, class T, typename std::enable_if::value>::type* = nullptr > +template < typename O, class T > bool set(O &obj, const std::string &name, const T &val) { attribute_reader reader(name, val); @@ -50,6 +61,22 @@ bool set(O &obj, const std::string &name, const T &val) return reader.success(); } +template < typename O, class T > +bool append(O &obj, const std::string &name, const T &val) +{ + has_many_attribute_reader writer(name, val); + oos::access::serialize(writer, obj); + return writer.success(); +} + +template < typename O, class T > +bool remove(O &obj, const std::string &name, const T &val) +{ + has_many_attribute_writer writer(name, val); + oos::access::serialize(writer, obj); + return writer.success(); +} + /** * Sets string value of a member identified by * the given name. The value is passed as a diff --git a/include/oos/object/has_many_item.hpp b/include/oos/object/has_many_item.hpp index d522da469..ecbad938d 100644 --- a/include/oos/object/has_many_item.hpp +++ b/include/oos/object/has_many_item.hpp @@ -17,6 +17,12 @@ namespace oos { /// @cond OOS_DEV +template < class T, template class C > +class has_many_iterator; + +template < class T, template class C > +class const_has_many_iterator; + /** * @class has_many_item * @brief Holds item and owner id of a has many relation @@ -59,6 +65,11 @@ class has_many_item::value>::type } private: + template < class V, template class C > + friend class has_many_iterator; + template < class V, template class C > + friend class const_has_many_iterator; + has_one item_; }; @@ -104,6 +115,11 @@ class has_many_item::value>::type > return item_; } private: + template < class V, template class C > + friend class has_many_iterator; + template < class V, template class C > + friend class const_has_many_iterator; + T item_; }; diff --git a/include/oos/object/has_many_list.hpp b/include/oos/object/has_many_list.hpp index 008fc66b4..b841d2c81 100644 --- a/include/oos/object/has_many_list.hpp +++ b/include/oos/object/has_many_list.hpp @@ -85,6 +85,8 @@ class has_many_iterator */ has_many_iterator(const self &iter) : iter_(iter.iter_) {} + //has_many_iterator(self &&iter) = default; + //has_many_iterator& operator=(self &&iter) = default; /** * @brief Creates a has many iterator from given internal container iterator * @@ -128,6 +130,16 @@ class has_many_iterator */ bool operator!=(const self &i) const { return !this->operator==(i); } + /** + * @brief Compares less than iterator with another iterator. + * + * Compares iterator with another iterator. If other iterator isn't + * less than this iterator true es returned. + * + * @param i The iterator to compare with + * @return True if iterators isn't less than this itertor + */ + bool operator<(const self &i) const { return iter_ < i.iter_; } /** * @brief Returns the difference of two iterators a and b. * @@ -192,12 +204,11 @@ class has_many_iterator /** * Return the current value * represented by the iterator - * + * * @return The current value */ - value_type operator->() const { return get(); } - value_type operator*() const { return get(); } - value_type get() const { return (*iter_)->value(); } + value_type operator->() const { return (*iter_)->item_; } + value_type operator*() const { return (*iter_)->item_; } //@} private: @@ -298,6 +309,11 @@ class const_has_many_iterator */ const_has_many_iterator(const has_many_iterator &iter) : iter_(iter.iter_) {} + const_has_many_iterator(const self &iter) : iter_(iter.iter_) {} + + //const_has_many_iterator(self &&iter) = default; + //const_has_many_iterator& operator=(self &&iter) = default; + /** * @brief Copy assigns a new const has many iterator * @@ -348,6 +364,16 @@ class const_has_many_iterator */ bool operator!=(const self &i) const { return !this->operator==(i); } + /** + * @brief Compares less than iterator with another iterator. + * + * Compares iterator with another iterator. If other iterator isn't + * less than this iterator true es returned. + * + * @param i The iterator to compare with + * @return True if iterators isn't less than this itertor + */ + bool operator<(const self &i) const { return iter_ < i.iter_; } /** * @brief Pre increments self * @@ -422,6 +448,84 @@ class const_has_many_iterator const_container_iterator iter_; }; +namespace detail { + +template +class has_many_inserter::value>::type> +{ +public: + typedef T value_type; + typedef typename has_many_iterator_traits::relation_type relation_type; + typedef typename basic_has_many::mark_modified_owner_func mark_modified_owner_func; + + void insert(object_store &store, const relation_type &rtype, object_proxy &owner, const mark_modified_owner_func &mark_modified_owner) + { + prototype_iterator foreign_node_ = store.find(typeid(T).name()); + + auto i = foreign_node_->relation_info_map_.find(owner.node()->type_index()); + if (i != foreign_node_->relation_info_map_.end()) { + // set owner into value + store.on_update_relation_owner(i->second, rtype->value().proxy_ /*owner*/, &owner /*value*/); + } else { + store.insert(rtype); + } + mark_modified_owner(store, &owner); + } +}; + +template +class has_many_inserter::value>::type> +{ +public: + typedef T value_type; + typedef typename has_many_iterator_traits::relation_type relation_type; + typedef typename basic_has_many::mark_modified_owner_func mark_modified_owner_func; + + void insert(object_store &store, const relation_type &rtype, object_proxy &owner, const mark_modified_owner_func &mark_modified_owner) + { + store.insert(rtype); + mark_modified_owner(store, &owner); + } +}; + +template +class has_many_deleter::value>::type> +{ +public: + typedef T value_type; + typedef typename has_many_iterator_traits::relation_type relation_type; + + void remove(object_store &store, relation_type &rtype, object_proxy &owner) + { + prototype_iterator foreign_node_ = store.find(typeid(T).name()); + + auto i = foreign_node_->relation_info_map_.find(owner.node()->type_index()); + if (i != foreign_node_->relation_info_map_ + .end()) { +// store.remove(val); + // set owner into value + store.on_remove_relation_owner(i->second, rtype->value().proxy_ /*owner*/, &owner /*value*/); + } else { + store.remove(rtype); + } + } +}; + +template +class has_many_deleter::value>::type> +{ +public: + typedef T value_type; + typedef typename has_many_iterator_traits::relation_type relation_type; + + void remove(object_store &store, relation_type &rtype, object_proxy &) + { + store.remove(rtype); + } +}; + +} + /** * @brief Has many relation class using a std::list as container * @@ -481,8 +585,7 @@ class has_many : public basic_has_many item_type *item = this->create_item(value); relation_type iptr(item); if (this->ostore_) { - this->ostore_->insert(iptr); - this->mark_modified_owner_(*this->ostore_, this->owner_); + inserter_.insert(*this->ostore_, iptr, *this->owner_, this->mark_modified_owner_); } container_iterator i = pos.iter_; return iterator(this->container_.insert(i, iptr)); @@ -516,6 +619,35 @@ class has_many : public basic_has_many erase(this->begin(), this->end()); } + void remove(const value_type &value) + { + iterator first = this->begin(); + iterator last = this->end(); + while (first != last) { + iterator next = first; + ++next; + if (*first == value) { + erase(first); + } + first = next; + } + } + + template < class P > + void remove_if(P predicate) + { + iterator first = this->begin(); + iterator last = this->end(); + while (first != last) { + iterator next = first; + ++next; + if (predicate(*first)) { + erase(first); + } + first = next; + } + } + /** * @brief Erase the element at given position. * @@ -527,8 +659,8 @@ class has_many : public basic_has_many iterator erase(iterator i) { if (this->ostore_) { - relation_type iptr = i.relation_item(); - this->ostore_->remove(iptr); + relation_type iptr(*i.iter_); + deleter_.remove(*this->ostore_, iptr, *this->owner_); } container_iterator ci = this->container_.erase(i.iter_); return iterator(ci); @@ -551,8 +683,8 @@ class has_many : public basic_has_many iterator i = start; if (this->ostore_) { while (i != end) { - typename base::relation_type iptr = (i++).relation_item(); - this->ostore_->remove(iptr); + relation_type iptr = (i++).relation_item(); + deleter_.remove(*this->ostore_, iptr, *this->owner_); } } return iterator(this->container_.erase(start.iter_, end.iter_)); @@ -563,6 +695,10 @@ class has_many : public basic_has_many { return new item_type(this->owner_field_, this->item_field_, this->owner_id_, value); } + +private: + detail::has_many_inserter inserter_; + detail::has_many_deleter deleter_; }; } diff --git a/include/oos/object/has_many_vector.hpp b/include/oos/object/has_many_vector.hpp index d6ee84ac1..525c19252 100644 --- a/include/oos/object/has_many_vector.hpp +++ b/include/oos/object/has_many_vector.hpp @@ -7,8 +7,10 @@ #include "oos/object/basic_has_many.hpp" #include "oos/object/object_store.hpp" +#include "oos/object/generic_access.hpp" #include +#include namespace oos { @@ -62,13 +64,13 @@ class has_many_iterator typedef has_many_iterator_traits traits; typedef typename traits::item_type item_type; typedef typename traits::internal_type internal_type; - typedef typename traits::relation_type relation_type; typedef typename traits::container_type container_type; public: - typedef has_many_iterator self; /**< Shortcut value self */ + typedef has_many_iterator self; /**< Shortcut value self */ typedef typename traits::value_type value_type; /**< Shortcut value type */ - typedef typename traits::difference_type difference_type; /**< Shortcut to the difference type*/ + typedef typename traits::relation_type relation_type; /**< Shortcut to the relation type */ + typedef typename traits::difference_type difference_type; /**< Shortcut to the difference type */ typedef typename traits::container_iterator container_iterator; /**< Shortcut to the internal container iterator */ typedef typename traits::const_container_iterator const_container_iterator; /**< Shortcut to the internal const container iterator */ @@ -85,6 +87,8 @@ class has_many_iterator */ has_many_iterator(const self &iter) : iter_(iter.iter_) {} + //has_many_iterator(self &&iter) = default; + //has_many_iterator& operator=(self &&iter) = default; /** * @brief Creates a has many iterator from given internal container iterator * @@ -127,6 +131,17 @@ class has_many_iterator */ bool operator!=(const self &i) const { return !this->operator==(i); } + /** + * @brief Compares less than iterator with another iterator. + * + * Compares iterator with another iterator. If other iterator isn't + * less than this iterator true es returned. + * + * @param i The iterator to compare with + * @return True if iterators isn't less than this itertor + */ + bool operator<(const self &i) const { return iter_ < i.iter_; } + /** * @brief Returns the difference of two iterators a and b. * @@ -257,9 +272,8 @@ class has_many_iterator * * @return The current value */ - value_type operator->() const { return (*iter_)->value(); } - value_type operator*() const { return (*iter_)->value(); } - value_type get() const { return (*iter_)->value(); } + value_type operator->() const { return (*iter_)->item_; } + value_type operator*() const { return (*iter_)->item_; } //@} private: @@ -268,9 +282,17 @@ class has_many_iterator friend class basic_has_many; friend class object_serializer; friend class detail::object_inserter; + friend class detail::object_deleter; + template class C, class Enabled> + friend class detail::has_many_deleter; relation_type relation_item() const { return *iter_; } + void move(self &i) + { + *iter_ = std::move(*i.iter_); + } + container_iterator iter_; }; @@ -324,12 +346,12 @@ class const_has_many_iterator typedef const_has_many_iterator_traits traits; typedef typename traits::item_type item_type; typedef typename traits::internal_type internal_type; - typedef typename traits::relation_type relation_type; typedef typename traits::container_type container_type; public: typedef const_has_many_iterator self; /**< Shortcut value self */ typedef typename traits::value_type value_type; /**< Shortcut value type */ + typedef typename traits::relation_type relation_type; typedef typename traits::difference_type difference_type; /**< Shortcut to the difference type*/ typedef typename traits::container_iterator container_iterator; /**< Shortcut to the internal container iterator */ typedef typename traits::const_container_iterator const_container_iterator; /**< Shortcut to the internal const container iterator */ @@ -361,6 +383,10 @@ class const_has_many_iterator */ const_has_many_iterator(const has_many_iterator &iter) : iter_(iter.iter_) {} + const_has_many_iterator(const self &iter) : iter_(iter.iter_) {} + + //const_has_many_iterator(self &&iter) = default; + //const_has_many_iterator& operator=(self &&iter) = default; /** * @brief Copy assigns a new const has many iterator * @@ -411,6 +437,17 @@ class const_has_many_iterator */ bool operator!=(const self &i) const { return !this->operator==(i); } + /** + * @brief Compares less than iterator with another iterator. + * + * Compares iterator with another iterator. If other iterator isn't + * less than this iterator true es returned. + * + * @param i The iterator to compare with + * @return True if iterators isn't less than this itertor + */ + bool operator<(const self &i) const { return iter_ < i.iter_; } + /** * @brief Pre increments self * @@ -548,6 +585,89 @@ class const_has_many_iterator const_container_iterator iter_; }; +namespace detail { + +template +class has_many_inserter::value>::type> +{ +public: + typedef T value_type; + typedef typename has_many_iterator_traits::relation_type relation_type; + typedef typename basic_has_many::mark_modified_owner_func mark_modified_owner_func; + + void insert(object_store &store, const relation_type &rtype, object_proxy &owner, const mark_modified_owner_func &mark_modified_owner) + { + prototype_iterator foreign_node_ = store.find(typeid(T).name()); + + auto i = foreign_node_->relation_info_map_.find(owner.node()->type_index()); + if (i != foreign_node_->relation_info_map_.end()) { + // set owner into value + store.on_update_relation_owner(i->second, rtype->value().proxy_ /*owner*/, &owner /*value*/); + } else { + i = foreign_node_->relation_info_map_.find(foreign_node_->type_index()); + if (i != foreign_node_->relation_info_map_.end()) { + store.on_append_relation_item(*foreign_node_, rtype->value().proxy_, &owner); + } + store.insert(rtype); + } + mark_modified_owner(store, &owner); + } +}; + +template +class has_many_inserter::value>::type> +{ +public: + typedef T value_type; + typedef typename has_many_iterator_traits::relation_type relation_type; + typedef typename basic_has_many::mark_modified_owner_func mark_modified_owner_func; + + void insert(object_store &store, const relation_type &rtype, object_proxy &owner, const mark_modified_owner_func &mark_modified_owner) + { + store.insert(rtype); + mark_modified_owner(store, &owner); + } +}; + +template +class has_many_deleter::value>::type> +{ +public: + typedef T value_type; + typedef typename has_many_iterator_traits::relation_type relation_type; + + void remove(object_store &store, relation_type &rtype, object_proxy &owner) + { + prototype_iterator foreign_node_ = store.find(typeid(T).name()); + + auto i = foreign_node_->relation_info_map_.find(owner.node()->type_index()); + if (i != foreign_node_->relation_info_map_.end()) { + // set owner into value + store.on_remove_relation_owner(i->second, rtype->value().proxy_ /*owner*/, &owner /*value*/); + } else { + i = foreign_node_->relation_info_map_.find(foreign_node_->type_index()); + if (i != foreign_node_->relation_info_map_.end()) { + store.on_remove_relation_item(*foreign_node_, rtype->value().proxy_, &owner); + } + store.remove(rtype); + } + } +}; + +template +class has_many_deleter::value>::type> +{ +public: + typedef T value_type; + typedef typename has_many_iterator_traits::relation_type relation_type; + + void remove(object_store &store, relation_type &rtype, object_proxy &) + { + store.remove(rtype); + } +}; +} + /** * @brief Has many relation class using a std::vector as container * @@ -603,14 +723,10 @@ class has_many : public basic_has_many iterator insert(iterator pos, const value_type &value) { // create new has_many - if (indexed_) { - - } item_type *item = create_item(value); relation_type iptr(item); if (this->ostore_) { - this->ostore_->insert(iptr); - this->mark_modified_owner_(*this->ostore_, this->owner_); + inserter_.insert(*this->ostore_, iptr, *this->owner_, this->mark_modified_owner_); } return iterator(this->container_.insert(pos.iter_, iptr)); } @@ -626,39 +742,38 @@ class has_many : public basic_has_many } /** - * @brief Returns the element at specified location index. - * - * Returns the element at specified location index. - * No bounds checking is performed. - * - * @param index Indx of the element to return - * @return The requested element. + * @brief Clears the vector */ - value_type operator[](size_type index) + void clear() { - return this->container_[index]->value(); + erase(this->begin(), this->end()); } - /** - * @brief Returns the const element at specified location index. - * - * Returns the const element at specified location index. - * No bounds checking is performed. - * - * @param index Indx of the element to return - * @return The requested const element. - */ - const value_type operator[](size_type index) const + iterator remove(const value_type &value) { - return this->container_[index]->value(); + return remove_if([&value](const value_type &val) { + return val == value; + }); } - /** - * @brief Clears the vector - */ - void clear() + template < class P > + iterator remove_if(P predicate) { - erase(this->begin(), this->end()); + iterator first = this->begin(); + iterator last = this->end(); + first = std::find_if(first, last, predicate); + if (first == last) { + return first; + } else { + iterator result = first; + for (; first != last; ++first) { + if (!predicate(*first)) { + result.move(first); + ++result; + } + } + return erase(result, this->end()); + } } /** @@ -673,8 +788,8 @@ class has_many : public basic_has_many iterator erase(iterator i) { if (this->ostore_) { - relation_type iptr = i.relation_item(); - this->ostore_->remove(iptr); + relation_type iptr(*i.iter_); + deleter_.remove(*this->ostore_, iptr, *this->owner_); } container_iterator ci = this->container_.erase(i.iter_); return iterator(ci); @@ -697,8 +812,8 @@ class has_many : public basic_has_many iterator i = start; if (this->ostore_) { while (i != end) { - typename base::relation_type iptr = (i++).relation_item(); - this->ostore_->remove(iptr); + relation_type iptr = (i++).relation_item(); + deleter_.remove(*this->ostore_, iptr, *this->owner_); } } return iterator(this->container_.erase(start.iter_, end.iter_)); @@ -711,7 +826,8 @@ class has_many : public basic_has_many } private: - bool indexed_ = false; + detail::has_many_inserter inserter_; + detail::has_many_deleter deleter_; }; } diff --git a/include/oos/object/has_one.hpp b/include/oos/object/has_one.hpp index 7d9a1d288..f2cc6a6e1 100644 --- a/include/oos/object/has_one.hpp +++ b/include/oos/object/has_one.hpp @@ -7,4 +7,10 @@ #include "oos/object/object_ptr.hpp" +namespace oos { + +template < class T > +using has_one = object_pointer; + +} #endif //OOS_HAS_ONE_HPP diff --git a/include/oos/object/node_analyzer.hpp b/include/oos/object/node_analyzer.hpp new file mode 100644 index 000000000..7a3b006d1 --- /dev/null +++ b/include/oos/object/node_analyzer.hpp @@ -0,0 +1,108 @@ +#ifndef OOS_NODE_ANALYZER_HPP +#define OOS_NODE_ANALYZER_HPP + +#include "oos/object/has_one.hpp" +#include "oos/object/belongs_to.hpp" +#include "oos/object/basic_has_many.hpp" + +namespace oos { + +class prototype_node; + +namespace detail { + +class basic_node_analyzer +{ +public: + explicit basic_node_analyzer(prototype_node &node, object_store &store) + : node_(node), store_(store) + { } + +protected: + template < class V, class T > + void process_belongs_to(const char *id, belongs_to &x); + + template < class V, class T > + void process_has_one(const char *id, has_one &x); + + template < class V, class T, template class C > + void process_has_many(const prototype_iterator &pi, const char *id, has_many &x); + + template < class V, class T > + void register_has_many(const std::type_index &typeindex, const char *id, prototype_node *node); + +protected: + prototype_node &node_; + object_store &store_; +}; + +template < class T > struct observer_dummy {}; + +template < class T, template < class U = T > class O = observer_dummy > +class node_analyzer : public basic_node_analyzer +{ +public: + explicit node_analyzer(prototype_node &node, object_store &store, const std::vector*> &observer) + : basic_node_analyzer(node, store), observer_(observer) + { } + + ~node_analyzer() { } + + void analyze(); + + template + void serialize(V &x); + template + void serialize(const char *, V &) { } + void serialize(const char *, char *, size_t) { } + + template + void serialize(const char *id, belongs_to &x, cascade_type); + template + void serialize(const char *id, has_one &x, cascade_type); + template class C> + void serialize(const char *, has_many &, const char *, const char *, + typename std::enable_if::value>::type* = 0); + + template class C> + void serialize(const char *, has_many &, const char *, const char *, + typename std::enable_if::value>::type* = 0); + +private: + const std::vector*> &observer_; +}; + +template < class T > +class node_analyzer : public basic_node_analyzer +{ +public: + explicit node_analyzer(prototype_node &node, object_store &store) + : basic_node_analyzer(node, store) + { } + + ~node_analyzer() { } + + void analyze(); + + template + void serialize(V &x); + template + void serialize(const char *, V &) { } + void serialize(const char *, char *, size_t) { } + template + void serialize(const char *id, belongs_to &x, cascade_type); + template + void serialize(const char *id, has_one &x, cascade_type); + template class C> + void serialize(const char *, has_many &, const char *, const char *, + typename std::enable_if::value>::type* = 0); + + template class C> + void serialize(const char *, has_many &, const char *, const char *, + typename std::enable_if::value>::type* = 0); + +}; + +} +} +#endif //OOS_NODE_ANALYZER_HPP diff --git a/include/oos/object/node_analyzer.tpp b/include/oos/object/node_analyzer.tpp new file mode 100644 index 000000000..13b6ec377 --- /dev/null +++ b/include/oos/object/node_analyzer.tpp @@ -0,0 +1,245 @@ +#include "oos/object/node_analyzer.hpp" +#include "oos/object/prototype_iterator.hpp" +#include "oos/object/object_store.hpp" +#include "oos/object/generic_access.hpp" + +namespace oos { +namespace detail { + +template +void basic_node_analyzer::process_belongs_to(const char *id, belongs_to &x) +{ + prototype_iterator node = store_.find(x.type()); + if (node != store_.end()) { + // check if created from has_many + // check if node has_many relation for id (id == tablename) + auto i = node->relation_info_map_.find(node->type_index()); + if (i != node->relation_info_map_.end() && i->second.type == prototype_node::relation_info::HAS_MANY) { + // yes, node found! + // Todo: check if node is for has many item + // detach has_many_item node + if (i->second.node != nullptr) { + store_.detach(i->second.node); + } + i->second.node = nullptr; + } + } + object_store &store = store_; + node_.register_belongs_to(std::type_index(typeid(V)), + prototype_node::relation_info(id, + prototype_node::relation_info::BELONGS_TO, + [&store](object_proxy *proxy, const std::string &field, oos::object_proxy *owner) { + store.mark_modified(proxy); + oos::set(proxy->obj(), field, object_ptr(owner)); + }, [&store](object_proxy *proxy, const std::string &field, oos::object_proxy *) { + store.mark_modified(proxy); + oos::set(proxy->obj(), field, object_ptr()); + }, node.get())); +} + +template +void basic_node_analyzer::process_has_one(const char *, has_one &) +{ +// std::cout << "analyzing has_one field " << id << " (typeid: " << typeid(V).name() << ")\n"; +// prototype_iterator node = node_.tree()->find(x.type()); +// if (node == node_.tree()->end()) { +// // if there is no such prototype node +// // prepare insertion of new node +// node = node_.tree()->template prepare_attach(); +// } else if (!node->has_primary_key()) { +// throw_object_exception("serializable of type '" << x.type() << "' has no primary key"); +// } +} + +template class C> +void basic_node_analyzer::process_has_many(const prototype_iterator &pi, const char *id, has_many &) +{ + if (pi->type_index() == std::type_index(typeid(typename has_many::item_type))) { + // prototype is of type has_many_item + this->register_has_many(node_.type_index(), id, pi.get()); + store_.typeid_prototype_map_[typeid(typename has_many::item_type).name()].insert(std::make_pair(pi->type_, pi.get())); + } else { + // found corresponding belongs_to + auto j = pi->relation_info_map_.find(node_.type_index_); + if (j == pi->relation_info_map_.end()) { + // check for has many item + throw_object_exception("prototype already inserted: " << pi->type()); + } else if (j->second.type == prototype_node::relation_info::BELONGS_TO) { + // set missing node + j->second.node = &node_; + this->register_has_many(pi->type_index(), id, pi.get()); + } else if (j->second.type == prototype_node::relation_info::HAS_MANY) { + // handle has many + } + } +} + +template +void basic_node_analyzer::register_has_many(const std::type_index &typeindex, const char *id, prototype_node *node) +{ + object_store &store = store_; + node_.register_has_many(typeindex, + prototype_node::relation_info(id, + prototype_node::relation_info::HAS_MANY, + [&store](object_proxy *proxy, const std::string &field, oos::object_proxy *owner) { + store.mark_modified(proxy); + oos::append(proxy->obj(), field, object_ptr(owner)); + }, [&store](object_proxy *proxy, const std::string &field, oos::object_proxy *owner) { + store.mark_modified(proxy); + oos::remove(proxy->obj(), field, object_ptr(owner)); + }, node)); +} + +template class O> +void node_analyzer::analyze() +{ + T obj; + oos::access::serialize(*this, obj); +} + +template class O> +template +void node_analyzer::serialize(V &x) +{ + oos::access::serialize(*this, x); +} + +template class O> +template +void node_analyzer::serialize(const char *id, belongs_to &x, cascade_type) +{ + this->process_belongs_to(id, x); +} + +template class O> +template +void node_analyzer::serialize(const char *id, has_one &x, cascade_type) +{ + this->process_has_one(id, x); +} + +template class O> +template class C> +void node_analyzer::serialize(const char *id, has_many &x, const char *owner_column, const char *item_column, + typename std::enable_if::value>::type*) +{ + // attach relation table for has many relation + // check if has many item is already attached + // true: check owner and item field + // false: attach it + prototype_iterator pi = store_.find(id); + if (pi == store_.end()) { + std::vector::item_type>*> has_many_item_observer; + for (auto o : observer_) { + has_many_item_observer.push_back(new O::item_type>(o)); + } + + prototype_node *node = prototype_node::make_relation_node::item_type>(&store_, id, false, node_.type(), id, owner_column, item_column); + + pi = store_.attach::item_type>(node, nullptr, has_many_item_observer); + + this->register_has_many(node_.type_index(), id, pi.get()); + } else { + this->process_has_many(pi, id, x); + } +} + +template class O> +template class C> +void node_analyzer::serialize(const char *id, has_many &, const char *owner_column, const char *item_column, + typename std::enable_if::value>::type*) +{ + // attach relation table for has many relation + // check if has many item is already attached + // true: check owner and item field + // false: attach it + prototype_iterator pi = store_.find(id); + if (pi == store_.end()) { + std::vector::item_type>*> has_many_item_observer; + for (auto o : observer_) { + has_many_item_observer.push_back(new O::item_type>(o)); + } + + prototype_node *node = prototype_node::make_relation_node::item_type>(&store_, id, false, node_.type(), id, owner_column, item_column); + + pi = store_.attach::item_type>(node, nullptr, has_many_item_observer); + } else { + // throw exception + throw_object_exception("prototype already inserted: " << pi->type()); + } +} + +/* + * no observer version + */ +template +void node_analyzer::analyze() +{ + T obj; + oos::access::serialize(*this, obj); +} + +template +template +void node_analyzer::serialize(V &x) +{ + oos::access::serialize(*this, x); +} + +template +template +void node_analyzer::serialize(const char *id, belongs_to &x, cascade_type) +{ + this->process_belongs_to(id, x); +} + +template +template +void node_analyzer::serialize(const char *id, has_one &x, cascade_type) +{ + this->process_has_one(id, x); +} + +template +template class C> +void node_analyzer::serialize(const char *id, has_many &x, const char *owner_column, const char *item_column, + typename std::enable_if::value>::type*) +{ + // attach relation table for has many relation + // check if has many item is already attached + // true: check owner and item field + // false: attach it + prototype_iterator pi = store_.find(id); + if (pi == store_.end()) { + prototype_node *node = prototype_node::make_relation_node::item_type>(&store_, id, false, node_.type(), id, owner_column, item_column); + + pi = store_.attach::item_type>(node, nullptr); + + this->register_has_many(node_.type_index(), id, pi.get()); + } else { + this->process_has_many(pi, id, x); + } +} + +template +template class C> +void node_analyzer::serialize(const char *id, has_many &, const char *owner_column, const char *item_column, + typename std::enable_if::value>::type*) +{ + // attach relation table for has many relation + // check if has many item is already attached + // true: check owner and item field + // false: attach it + prototype_iterator pi = store_.find(id); + if (pi == store_.end()) { + prototype_node *node = prototype_node::make_relation_node::item_type>(&store_, id, false, node_.type(), id, owner_column, item_column); + + pi = store_.attach::item_type>(node, nullptr); + } else { + // throw exception + throw_object_exception("prototype already inserted: " << pi->type()); + } +} + +} +} \ No newline at end of file diff --git a/include/oos/object/object_deleter.hpp b/include/oos/object/object_deleter.hpp new file mode 100644 index 000000000..c133696e2 --- /dev/null +++ b/include/oos/object/object_deleter.hpp @@ -0,0 +1,135 @@ +#ifndef OOS_OBJECT_DELETER_HPP +#define OOS_OBJECT_DELETER_HPP + +#include "oos/object/has_one.hpp" +#include "oos/object/belongs_to.hpp" +#include "oos/object/basic_has_many.hpp" + +#ifdef _MSC_VER +#ifdef oos_object_EXPORTS + #define OOS_OBJECT_API __declspec(dllexport) + #define EXPIMP_OBJECT_TEMPLATE + #else + #define OOS_OBJECT_API __declspec(dllimport) + #define EXPIMP_OBJECT_TEMPLATE extern + #endif + #pragma warning(disable: 4251) + #pragma warning(disable: 4355) +#else +#define OOS_OBJECT_API +#endif + +namespace oos { + +class object_proxy; + +namespace detail { + +/** + * @cond OOS_DEV + * @class object_deleter + * @brief Checks if an serializable could be deleted + * + * This class checks wether a given serializable or a + * given object_list_base and their children objects + * could be deleted or not. + * If the check was successful, all the deletable serializable + * can be accepted via the iterators. + */ +class OOS_OBJECT_API object_deleter { + private: + struct OOS_OBJECT_API t_object_count { + typedef void (*t_remove_func)(object_proxy*, bool); + template < class T > + t_object_count(object_proxy *oproxy, bool ignr = true, T* = nullptr) + : proxy(oproxy) + , reference_counter(oproxy->reference_count()) + , ignore(ignr) + , remove_func(&remove_object) + {} + + void remove(bool notify); + + template + static void remove_object(object_proxy *proxy, bool notify); + + object_proxy *proxy; + unsigned long reference_counter; + bool ignore; + + t_remove_func remove_func; + }; + + private: + typedef std::map t_object_count_map; + + public: + typedef t_object_count_map::iterator iterator; + /**< Shortcut the serializable map iterator */ + typedef t_object_count_map::const_iterator const_iterator; /**< Shortcut the serializable map const_iterator */ + + /** + * Creates an instance of the object_deleter + */ + object_deleter() { } + + ~object_deleter() { } + + /** + * Checks wether the given serializable is deletable. + * + * @param proxy The object_proxy to be checked. + * @return True if the serializable could be deleted. + */ + template + bool is_deletable(object_proxy *proxy, T *o); + + /** + * Checks wether the given object_container is deletable. + * + * @param ovector The object_container to be checked. + * @return True if the object_container could be deleted. + */ +// bool is_deletable(object_container &oc); + + /** + * @brief Returns the first deletable serializable. + * + * If the check was made and was successful this + * returns the first deletable serializable. + */ + iterator begin(); + + /** + * @brief Returns the first deletable serializable. + * + * If the check was made and was successful this + * returns the last deletable serializable. + */ + iterator end(); + + template + void serialize(T &x) { oos::access::serialize(*this, x); } + + template + void serialize(const char *, T &) { } + void serialize(const char *, char *, size_t) { } + + template + void serialize(const char *, belongs_to &x, cascade_type cascade); + template + void serialize(const char *, has_one &x, cascade_type cascade); + template class C> + void serialize(const char *, basic_has_many &, const char *, const char *); + template + void serialize(const char *id, identifier &x); + + bool check_object_count_map() const; + + private: + t_object_count_map object_count_map; +}; + +} +} +#endif //OOS_OBJECT_DELETER_HPP diff --git a/include/oos/object/object_deleter.tpp b/include/oos/object/object_deleter.tpp new file mode 100644 index 000000000..425a0c895 --- /dev/null +++ b/include/oos/object/object_deleter.tpp @@ -0,0 +1,90 @@ +#include "oos/object/object_deleter.hpp" +#include "oos/object/object_store.hpp" +namespace oos { +namespace detail { + +template +void object_deleter::t_object_count::remove_object(object_proxy *proxy, bool notify) +{ + proxy->ostore()->remove(proxy, notify, false); +} + +template +bool object_deleter::is_deletable(object_proxy *proxy, T *o) { + object_count_map.clear(); + object_count_map.insert(std::make_pair(proxy->id(), t_object_count(proxy, false, (T*)proxy->obj()))); + + // start collecting information + oos::access::serialize(*this, *o); + + return check_object_count_map(); +} + +template +void object_deleter::serialize(const char *, belongs_to &x, cascade_type cascade) { + if (!x.ptr()) { + return; + } + std::pair ret = object_count_map.insert( + std::make_pair(x.proxy_->id(), t_object_count(x.proxy_, true, (T*)x.proxy_->obj())) + ); + --ret.first->second.reference_counter; + if (cascade & cascade_type::REMOVE) { + ret.first->second.ignore = false; + oos::access::serialize(*this, *(T*)x.ptr()); + } +} + +template +void object_deleter::serialize(const char *, has_one &x, cascade_type cascade) { + if (!x.ptr()) { + return; + } + std::pair ret = object_count_map.insert( + std::make_pair(x.proxy_->id(), t_object_count(x.proxy_, true, (T*)x.proxy_->obj())) + ); + --ret.first->second.reference_counter; + if (cascade & cascade_type::REMOVE) { + ret.first->second.ignore = false; + oos::access::serialize(*this, *(T*)x.ptr()); + } +} + +template class C> +void object_deleter::serialize(const char *, basic_has_many &x, const char *, const char *) +{ + typename basic_has_many::iterator first = x.begin(); + typename basic_has_many::iterator last = x.end(); + while (first != last) { + // Todo: get the real holder: on join table get has_many_item + typename basic_has_many::relation_type iptr = first.relation_item(); + ++first; + object_proxy *proxy = iptr.proxy_; + std::pair ret = object_count_map.insert( + std::make_pair(proxy->id(), t_object_count(proxy, false, (T*)proxy->obj())) + ); + /********** + * + * object is already in list and will + * be ignored on deletion so set + * ignore flag to false because this + * node must be deleted + * + **********/ + if (!ret.second && ret.first->second.ignore) { + ret.first->second.ignore = false; + } + + oos::access::serialize(*this, *iptr); + } +} + +template +void object_deleter::serialize(const char *id, identifier &x) +{ + T val = x.value(); + serialize(id, val); +} + +} +} \ No newline at end of file diff --git a/include/oos/object/object_holder.hpp b/include/oos/object/object_holder.hpp index ab5c3a89d..d36d4f55a 100644 --- a/include/oos/object/object_holder.hpp +++ b/include/oos/object/object_holder.hpp @@ -22,6 +22,8 @@ #include "oos/utils/cascade_type.hpp" #include "oos/utils/identifiable_holder.hpp" +#include "oos/object/object_holder_type.hpp" + #include namespace oos { @@ -30,6 +32,10 @@ namespace detail { class object_inserter; class object_deleter; class object_proxy_accessor; +template class C, class Enabled> +class has_many_inserter; +template class C, class Enabled> +class has_many_deleter; } class basic_identifier; @@ -38,11 +44,11 @@ class object_store; /** * @class object_holder - * @brief Base class for the serializable pointer and reference class + * @brief Base class for the object pointer and reference class * - * This is the base class for the serializable pointer + * This is the base class for the object pointer * and reference class. The class holds the proxy - * of the serializable and the id of the serializable. + * of the object and the id of the object. */ class OOS_OBJECT_API object_holder : public identifiable_holder { @@ -51,7 +57,7 @@ class OOS_OBJECT_API object_holder : public identifiable_holder * @brief Creates and empty base pointer. * * Creates and empty base pointer. The boolean - * tells the class if the serializable is handled + * tells the class if the object is handled * as a reference or an pointer. The difference * is that the reference couldn't be deleted * from the object_store and the pointer can. @@ -59,7 +65,7 @@ class OOS_OBJECT_API object_holder : public identifiable_holder * @param is_internal True if the pointer is used internal, which means * it is used to describe an entity. */ - explicit object_holder(bool is_internal); + explicit object_holder(object_holder_type holder_type); /** * Copies from another object_holder @@ -82,10 +88,10 @@ class OOS_OBJECT_API object_holder : public identifiable_holder * boolean tells the object_holder if it should be * handled as an internal. * - * @param is_internal If true the serializable is handled as an internal. + * @param is_internal If true the object is handled as an internal. * @param op The object_proxy of the object_holder */ - object_holder(bool is_internal, object_proxy *op); + object_holder(object_holder_type holder_type, object_proxy *op); /** * Destroys the object_holder @@ -131,24 +137,37 @@ class OOS_OBJECT_API object_holder : public identifiable_holder void reset(const std::shared_ptr &id); /** - * Returns if the serializable is loaded. + * Clears the currently set object + */ + void clear(); + + /** + * Returns true if object_holder doesn't + * holds an object + * + * @return True if object_holder doesn't holds an object + */ + bool empty() const; + + /** + * Returns if the object is loaded. * - * @return True if the serializable is loaded. + * @return True if the object is loaded. */ bool is_loaded() const; /** - * Returns the serializable id. + * Returns the object id. * - * @return The id of the serializable. + * @return The id of the object. */ unsigned long id() const; /** - * Sets the serializable id. If a proxy + * Sets the object id. If a proxy * is set an exception is thrown. * - * @param i The new serializable id + * @param i The new object id */ void id(unsigned long i); @@ -159,30 +178,30 @@ class OOS_OBJECT_API object_holder : public identifiable_holder object_store* store() const; /** - * Returns the serializable + * Returns the raw object pointer * - * @return The serializable. + * @return The raw object pointer. */ void* ptr(); /** - * Returns the serializable + * Returns the raw object pointer * - * @return The serializable. + * @return The raw object pointer. */ const void* ptr() const; /** - * Returns the serializable + * Returns the object pointer * - * @return The serializable. + * @return The object pointer. */ void* lookup_object(); /** - * Returns the serializable + * Returns the object pointer * - * @return The serializable. + * @return The object pointer. */ void* lookup_object() const; @@ -196,6 +215,11 @@ class OOS_OBJECT_API object_holder : public identifiable_holder */ bool is_internal() const; + bool is_belongs_to() const; + + bool is_has_one() const; + + bool is_object_ptr() const; /** * Returns true if the underlying object * is inserted in an object_store @@ -205,16 +229,16 @@ class OOS_OBJECT_API object_holder : public identifiable_holder bool is_inserted() const; /** - * Returns true if serializable has a primary key + * Returns true if object has a primary key * - * @return true if serializable has a primary key + * @return true if object has a primary key */ bool has_primary_key() const; /** - * Gets the primary key of the foreign serializable + * Gets the primary key of the foreign object * - * @return The primary key of the foreign serializable + * @return The primary key of the foreign object */ virtual std::shared_ptr primary_key() const; @@ -232,11 +256,13 @@ class OOS_OBJECT_API object_holder : public identifiable_holder */ virtual const char* type() const = 0; + object_holder_type holder_type() const; + /** - * Prints the underlaying serializable + * Prints the underlaying object * * @param out The output stream to write on. - * @param x The serializable pointer to print. + * @param x The object pointer to print. * @return The output stream. */ friend OOS_OBJECT_API std::ostream& operator<<(std::ostream &out, const object_holder &x); @@ -249,18 +275,22 @@ class OOS_OBJECT_API object_holder : public identifiable_holder friend class object_store; friend class object_container; friend class detail::object_proxy_accessor; + template class C, class Enabled > + friend class detail::has_many_inserter; + template class C, class Enabled > + friend class detail::has_many_deleter; // Todo: change interface to remove friend friend class session; // Todo: replace private access of proxy with call to reset friend class table_reader; - template < class T > friend class object_ptr; - template < class T > friend class has_one; + template < class T, object_holder_type OPT > friend class object_pointer; object_proxy *proxy_ = nullptr; + object_proxy *owner_ = nullptr; // only set if holder type is BELONGS_TO or HAS_MANY cascade_type cascade_ = cascade_type::NONE; - bool is_internal_ = false; + object_holder_type type_; bool is_inserted_ = false; unsigned long oid_ = 0; }; diff --git a/include/oos/object/object_holder_type.hpp b/include/oos/object/object_holder_type.hpp new file mode 100644 index 000000000..9a755d925 --- /dev/null +++ b/include/oos/object/object_holder_type.hpp @@ -0,0 +1,17 @@ +// +// Created by sascha on 1/6/17. +// + +#ifndef OOS_OBJECT_HOLDER_TYPE_HPP +#define OOS_OBJECT_HOLDER_TYPE_HPP + +namespace oos { + +enum class object_holder_type { + OBJECT_PTR, + HAS_ONE, + BELONGS_TO +}; + +} +#endif //OOS_OBJECT_HOLDER_TYPE_HPP diff --git a/include/oos/object/object_inserter.hpp b/include/oos/object/object_inserter.hpp new file mode 100644 index 000000000..eba86d209 --- /dev/null +++ b/include/oos/object/object_inserter.hpp @@ -0,0 +1,90 @@ +#ifndef OOS_OBJECT_INSERTER_HPP +#define OOS_OBJECT_INSERTER_HPP + +#include "oos/object/has_one.hpp" +#include "oos/object/belongs_to.hpp" +#include "oos/object/basic_has_many.hpp" + +#include + +#ifdef _MSC_VER +#ifdef oos_object_EXPORTS + #define OOS_OBJECT_API __declspec(dllexport) + #define EXPIMP_OBJECT_TEMPLATE + #else + #define OOS_OBJECT_API __declspec(dllimport) + #define EXPIMP_OBJECT_TEMPLATE extern + #endif + #pragma warning(disable: 4251) + #pragma warning(disable: 4355) +#else +#define OOS_OBJECT_API +#endif + +namespace oos { +namespace detail { + +/** + * @cond OOS_DEV + * @class object_inserter + * @brief Creates objects and object_lists + * + * When an serializable is inserted into the serializable store + * subsequently other serializable must be created and + * inserted into the serializable store. + * This class does these tasks. + */ +class OOS_OBJECT_API object_inserter { +public: + /** + * @brief Creates an object_inserter instance. + * + * An object_inserter instance ist created for a + * given object_store. The notify flag tells the + * object_inserter wether the observers should be + * notified or not. + * + * @param ostore The object_store. + */ + object_inserter(object_store &ostore); + + ~object_inserter(); + + template + void insert(object_proxy *proxy, T *o, bool notify); + + void reset(); + + template + void serialize(T &x); + + template + void serialize(const char *, T &) { } + void serialize(const char *, char *, size_t) { } + + template + void serialize(const char *, belongs_to &x, cascade_type cascade); + template + void serialize(const char *, has_one &x, cascade_type cascade); + + template class C> + void serialize(const char *id, basic_has_many &x, const char *owner_field, const char *item_field); + +private: + typedef std::set t_object_proxy_set; + + t_object_proxy_set object_proxies_; + + std::stack object_proxy_stack_; + + object_store &ostore_; + + std::function modified_marker_; + + bool notify_ = false; +}; + +} +} + +#endif //OOS_OBJECT_INSERTER_HPP diff --git a/include/oos/object/object_inserter.tpp b/include/oos/object/object_inserter.tpp new file mode 100644 index 000000000..568dddf2f --- /dev/null +++ b/include/oos/object/object_inserter.tpp @@ -0,0 +1,123 @@ +#include "oos/object/object_inserter.hpp" +#include "oos/object/object_store.hpp" + +namespace oos { +namespace detail { + +template +void object_inserter::insert(object_proxy *proxy, T *o, bool notify) +{ + + notify_ = notify; + + object_proxy_stack_.push(proxy); + + modified_marker_ = [](object_store &store, object_proxy *oproxy) { + store.mark_modified(oproxy); + }; + + if (proxy->obj()) { + oos::access::serialize(*this, *o); + } + object_proxy_stack_.pop(); +} + +template +void object_inserter::serialize(T &x) +{ + oos::access::serialize(*this, x); +} + +template +void object_inserter::serialize(const char *, belongs_to &x, cascade_type cascade) { + if (x.is_inserted() || (x.proxy_ && x.proxy_->obj() == nullptr)) { + return; + } + x.is_inserted_ = true; + x.cascade_ = cascade; + x.owner_ = object_proxy_stack_.top(); + // object was seen by inserter stop inserting + if (!object_proxies_.insert(x.proxy_).second) { + return; + } + + if (!x.proxy_) { + return; + } + + if (x.id()) { + // do the pointer count + object_proxy_stack_.push(x.proxy_); + oos::access::serialize(*this, *(T*)x.ptr()); + object_proxy_stack_.pop(); + } else { + // new object + ostore_.insert(x.proxy_, notify_); + } + ++(*x.proxy_); +} + +template +void object_inserter::serialize(const char *, has_one &x, cascade_type cascade) { + if (x.is_inserted() || (x.proxy_ && x.proxy_->obj() == nullptr)) { + return; + } + x.is_inserted_ = true; + x.cascade_ = cascade; + x.owner_ = object_proxy_stack_.top(); + // object was seen by inserter stop inserting + if (!object_proxies_.insert(x.proxy_).second) { + return; + } + + if (!x.proxy_) { + return; + } + + if (x.id()) { + // do the pointer count + object_proxy_stack_.push(x.proxy_); + oos::access::serialize(*this, *(T*)x.ptr()); + object_proxy_stack_.pop(); + } else { + // new object + ostore_.insert(x.proxy_, notify_); + } + ++(*x.proxy_); +} + +template class C> +void object_inserter::serialize(const char *, basic_has_many &x, const char*, const char*) +{ + // initialize the has many relation + // set identifier + // relation table name + // owner column name + // item column name + if (object_proxy_stack_.empty()) { + throw object_exception("no owner for has many relation"); + } + + if (x.ostore_) { + return; + } + object_proxy *proxy = object_proxy_stack_.top(); + x.owner_id_ = proxy->pk(); + x.owner_ = proxy; + x.ostore_ = &ostore_; + x.mark_modified_owner_ = modified_marker_; + + typename basic_has_many::iterator first = x.begin(); + typename basic_has_many::iterator last = x.end(); + + while (first != last) { + typename basic_has_many::relation_type i = (first++).relation_item(); + if (!i.is_inserted()) { + // item is not in store, insert it + ostore_.insert(i, false); + } + } +} + +} +} \ No newline at end of file diff --git a/include/oos/object/object_observer.hpp b/include/oos/object/object_observer.hpp deleted file mode 100644 index 709f52201..000000000 --- a/include/oos/object/object_observer.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of OpenObjectStore OOS. - * - * OpenObjectStore OOS is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OpenObjectStore OOS is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with OpenObjectStore OOS. If not, see . - */ - -#ifndef OBJECT_OBSERVER_HPP -#define OBJECT_OBSERVER_HPP - -namespace oos { - -class serializer; -class object_proxy; - -/** - * @class object_observer - * @brief Base class for serializable observer classes - * - * When interessted to observe insert, update and - * delete actions an observer class instance must be - * registered with serializable store. - * Use this class as base class for all observer classes. - */ -class OOS_OBJECT_API object_observer -{ -public: - virtual ~object_observer() {} - - /** - * @brief Called on serializable insertion. - * - * Called when an serializable is inserted - * into the object_store. - * - * @param proxy The proxy of the inserted serializable. - */ - virtual void on_insert(object_proxy *proxy) = 0; - - /** - * @brief Called on serializable update. - * - * Called when an serializable is updated - * in the object_store. - * - * @param proxy The proxy of the updated serializable. - */ - virtual void on_update(object_proxy *proxy) = 0; - - /** - * @brief Called on serializable deletion. - * - * Called when an serializable is deleted - * from the object_store. - * - * @param proxy The proxy of the deleted serializable. - */ - virtual void on_delete(object_proxy *proxy) = 0; -}; - -} - -#endif /* OBJECT_OBSERVER_HPP */ diff --git a/include/oos/object/object_proxy.hpp b/include/oos/object/object_proxy.hpp index 4f95b4371..712108758 100644 --- a/include/oos/object/object_proxy.hpp +++ b/include/oos/object/object_proxy.hpp @@ -34,6 +34,7 @@ #include "oos/utils/identifier_resolver.hpp" #include "oos/object/prototype_node.hpp" +#include "oos/object/object_holder_type.hpp" #include #include @@ -352,8 +353,7 @@ class OOS_OBJECT_API object_proxy friend class table_reader; friend class restore_visitor; friend class object_holder; - template < class T > friend class object_ptr; - template < class T > friend class has_one; + template < class T, object_holder_type OHT > friend class object_pointer; typedef void (*deleter)(void*); typedef const char* (*namer)(); @@ -378,13 +378,12 @@ class OOS_OBJECT_API object_proxy namer namer_; /**< The object classname function */ unsigned long oid = 0; /**< The id of the concrete or expected object. */ - unsigned long reference_counter_ = 0; object_store *ostore_ = nullptr; /**< The object_store to which the object_proxy belongs. */ prototype_node *node_ = nullptr; /**< The prototype_node containing the type of the object. */ - typedef std::set ptr_set_t; /**< Shortcut to the object_base_ptr_set. */ + typedef std::set ptr_set_t; /**< Shortcut to the object_holder set. */ ptr_set_t ptr_set_; /**< This set contains every object_holder pointing to this object_proxy. */ std::shared_ptr primary_key_ = nullptr; diff --git a/include/oos/object/object_ptr.hpp b/include/oos/object/object_ptr.hpp index 064df67bd..07ce20890 100644 --- a/include/oos/object/object_ptr.hpp +++ b/include/oos/object/object_ptr.hpp @@ -22,202 +22,75 @@ #include "oos/object/object_holder.hpp" #include "oos/object/transaction.hpp" #include "oos/utils/identifier_resolver.hpp" -#include "oos/object/has_one.hpp" #include #include namespace oos { -namespace detail { -class result_impl; -} - -template < class T > class object_ptr; - /** - * @brief The has_one holds a pointer to an serializable. + * @brief The object_pointer holds a pointer to an serializable. * @tparam T The type of the serializable. * - * The has_one holds a pointer to an object. The - * has_one is a wrapper class for the object class + * The object_pointer holds a pointer to an object. The + * object_pointer is a wrapper class for the object class * It has a reference count mechanism. * The objects inserted into the object_store are returned - * as a has_one and should be used through the - * has_one class. + * as a object_pointer and should be used through the + * object_pointer class. */ -template < class T > -class has_one : public object_holder -{ -public: - typedef has_one self; /**< Shortcut for self class. */ - -public: - /** - * Create an empty has_one - */ - has_one() - : object_holder(true) - {} - - /** - * Create an hs_one from an object - * - * @param o The object. - */ - has_one(T *o) - : object_holder(true, new object_proxy(o)) - {} - - /** - * Create an has_one from an object_proxy - * - * @param proxy The object_proxy. - */ - has_one(object_proxy *proxy) - : object_holder(true, proxy) - {} - - /** - * Copies has_one from object_ptr - * - * @param x The object_ptr to copy - */ - has_one(const object_ptr &x); - - /** - * Assigns has_one from object_ptr - * - * @param x The object_ptr to assign - * @return Reference to assigned has_one - */ - has_one& operator=(const object_ptr &x); - - //@{ - /** - * @brief Return the pointer to the object of type T. - * - * Return the pointer to the object of type T. If there - * isn't a valid object nullptr is returned. - * - * @return The pointer to the object of type T. - */ - T* operator->() - { - return get(); - } - - const T* operator->() const - { - return get(); - } - - T* get() - { - return static_cast(proxy_->obj()); - } - - const T* get() const - { - return static_cast(proxy_->obj()); - } - //@} - - /** - * Return the type string of the serializable - * - * @return The type string of the serializable. - */ - const char* type() const - { - return classname_.c_str(); - } - - /** - * Creates a new identifier, represented by the identifier - * of the underlaying type. - * - * @return A new identifier. - */ - basic_identifier* create_identifier() const - { - return self::identifier_->clone(); - } - -private: - friend class object_deleter; - -private: - static std::string classname_; - static std::unique_ptr identifier_; -}; - -template < class T > -std::string has_one::classname_ = typeid(T).name(); - -template < class T > -std::unique_ptr has_one::identifier_(identifier_resolver::resolve()); - - -/** - * @brief The object_ptr holds a pointer to an serializable. - * @tparam T The type of the serializable. - * - * The object_ptr holds a pointer to an object. The - * object_ptr is a wrapper class for the object class - * It has a reference count mechanism. - * The objects inserted into the object_store are returned - * as a object_ptr and should be used through the - * object_ptr class. - */ -template < class T > -class object_ptr : public object_holder +template < class T, object_holder_type OPT > +class object_pointer : public object_holder { public: typedef T object_type; /**< Shortcut for serializable type. */ - typedef object_ptr self; /**< Shortcut for self class. */ + typedef object_pointer self; /**< Shortcut for self class. */ public: /** - * Create an empty object_ptr + * Create an empty object_pointer */ - object_ptr() - : object_holder(false) + object_pointer() + : object_holder(OPT) {} /** - * Copies object_ptr + * Copies object_pointer * - * @param x The object_ptr to copy + * @param x The object_pointer to copy */ - object_ptr(const self &x) - : object_holder(x.is_internal_, x.proxy_) + object_pointer(const self &x) + : object_holder(x.type_, x.proxy_) {} /** - * Create an object_ptr from an object + * Create an object_pointer from an object * * @param o The object. */ - object_ptr(T *o) - : object_holder(false, new object_proxy(o)) + object_pointer(T *o) + : object_holder(OPT, new object_proxy(o)) {} /** - * Create an object_ptr from an object_proxy + * Create an object_pointer from an object_proxy * * @param proxy The object_proxy. */ - explicit object_ptr(object_proxy *proxy) - : object_holder(false, proxy) + explicit object_pointer(object_proxy *proxy) + : object_holder(OPT, proxy) {} /** - * @brief Creates an object_ptr from the given has_one object - * @param x The has_one object to created the object_ptr from + * @brief Creates an object_pointer from the given object_pointer object + * + * @param x The object_pointer object to created the object_pointer from */ - object_ptr(const has_one &x) - : object_holder(false, x.proxy_) - {} + template < object_holder_type OOPT > + object_pointer(const object_pointer &x) + : object_holder(OPT) + { + reset(x.proxy_, x.cascade_); + } /** * Assign operator. @@ -231,15 +104,28 @@ class object_ptr : public object_holder } /** - * @brief Copy assignes an object_ptr from the given has_one object - * @param x The has_one object to created the object_ptr from - * @return A reference to the created object_ptr + * @brief Copy assignes an object_pointer from the given has_one object + * @param x The has_one object to created the object_pointer from + * @return A reference to the created object_pointer + */ + self& operator=(const self &x) + { + reset(x.proxy_, x.cascade_); + return *this; + } + + /** + * @brief Copy assignes an object_pointer from the given has_one object + * @param x The has_one object to created the object_pointer from + * @return A reference to the created object_pointer */ - self& operator=(has_one &x) + template < object_holder_type OOPT > + self& operator=(object_pointer &x) { - reset(x.proxy_); + reset(x.proxy_, x.cascade_); return *this; } + /** * Return the type string of the object * @@ -318,23 +204,13 @@ class object_ptr : public object_holder }; template < class T > -std::string object_ptr::classname_ = typeid(T).name(); +using object_ptr = object_pointer; -template < class T > -std::unique_ptr object_ptr::identifier_(identifier_resolver::resolve()); +template < class T, object_holder_type OPT > +std::string object_pointer::classname_ = typeid(T).name(); - -template < class T > -has_one::has_one(const object_ptr &x) - : object_holder(true, x.proxy_) -{} - -template < class T > -has_one& has_one::operator=(const object_ptr &x) -{ - reset(x.proxy_, x.cascade_); - return *this; -} +template < class T, object_holder_type OPT > +std::unique_ptr object_pointer::identifier_(identifier_resolver::resolve()); } diff --git a/include/oos/object/object_serializer.hpp b/include/oos/object/object_serializer.hpp index 656ba2fd5..c0974c6fa 100644 --- a/include/oos/object/object_serializer.hpp +++ b/include/oos/object/object_serializer.hpp @@ -37,6 +37,7 @@ #include "oos/utils/identifier.hpp" #include "oos/object/has_one.hpp" +#include "oos/object/belongs_to.hpp" #include "oos/object/basic_has_many.hpp" #include @@ -161,6 +162,41 @@ class OOS_OBJECT_API object_serializer } } + template < class T > + void serialize(const char* id, belongs_to &x, cascade_type cascade) + { + if (restore) { + /*************** + * + * extract id and type of serializable from buffer + * try to find serializable on serializable store + * if found check type if wrong type throw error + * else create serializable and set extracted id + * insert serializable into serializable store + * + ***************/ + // Todo: correct implementation + + unsigned long oid = 0; + serialize(id, oid); + std::string type; + serialize(id, type); + + if (oid > 0) { + object_proxy *oproxy = find_proxy(oid); + if (!oproxy) { + oproxy = new object_proxy(new T, oid, ostore_); + insert_proxy(oproxy); + } + x.reset(oproxy, cascade); + } + } else { + unsigned long oid = x.id(); + serialize(id, oid); + serialize(id, const_cast(x.type()), strlen(x.type())); + } + } + template < class T > void serialize(const char* id, has_one &x, cascade_type cascade) { diff --git a/include/oos/object/object_store.hpp b/include/oos/object/object_store.hpp index fe94206b9..4375d3a21 100644 --- a/include/oos/object/object_store.hpp +++ b/include/oos/object/object_store.hpp @@ -20,8 +20,12 @@ #include "oos/object/prototype_iterator.hpp" #include "oos/object/object_exception.hpp" -#include "oos/object/object_observer.hpp" +#include "oos/object/object_store_observer.hpp" +#include "oos/object/object_inserter.hpp" +#include "oos/object/object_deleter.hpp" +#include "oos/object/node_analyzer.hpp" #include "oos/object/has_one.hpp" +#include "oos/object/belongs_to.hpp" #include "oos/object/object_serializer.hpp" #include "oos/object/basic_has_many.hpp" #include "oos/object/transaction.hpp" @@ -85,209 +89,6 @@ class OOS_OBJECT_API modified_marker t_marker marker_; }; -/** - * @cond OOS_DEV - * @class object_inserter - * @brief Creates objects and object_lists - * - * When an serializable is inserted into the serializable store - * subsequently other serializable must be created and - * inserted into the serializable store. - * This class does these tasks. - */ -class OOS_OBJECT_API object_inserter { -public: - /** - * @brief Creates an object_inserter instance. - * - * An object_inserter instance ist created for a - * given object_store. The notify flag tells the - * object_inserter wether the observers should be - * notified or not. - * - * @param ostore The object_store. - */ - object_inserter(object_store &ostore); - - ~object_inserter(); - - template - void insert(object_proxy *proxy, T *o, bool notify); - - void reset(); - - template - void serialize(T &x); - - template - void serialize(const char *, T &) { } - void serialize(const char *, char *, size_t) { } - -// template < class T > -// void serialize(const char *, object_ptr &x) - template - void serialize(const char *, has_one &x, cascade_type cascade); - - template class C> - void serialize(const char *id, basic_has_many &x, const char *owner_field, const char *item_field); - -private: - typedef std::set t_object_proxy_set; - - t_object_proxy_set object_proxies_; - - std::stack object_proxy_stack_; - - object_store &ostore_; - - std::function modified_marker_; - - bool notify_ = false; -}; - -/** - * @cond OOS_DEV - * @class object_deleter - * @brief Checks if an serializable could be deleted - * - * This class checks wether a given serializable or a - * given object_list_base and their children objects - * could be deleted or not. - * If the check was successful, all the deletable serializable - * can be accepted via the iterators. - */ -class OOS_OBJECT_API object_deleter { -private: - struct OOS_OBJECT_API t_object_count { - typedef void (*t_remove_func)(object_proxy*, bool); - template < class T > - t_object_count(object_proxy *oproxy, bool ignr = true, T* = nullptr) - : proxy(oproxy) - , reference_counter(oproxy->reference_count()) - , ignore(ignr) - , remove_func(&remove_object) - {} - - void remove(bool notify); - - template - static void remove_object(object_proxy *proxy, bool notify); - - object_proxy *proxy; - unsigned long reference_counter; - bool ignore; - - t_remove_func remove_func; - }; - -private: - typedef std::map t_object_count_map; - -public: - typedef t_object_count_map::iterator iterator; - /**< Shortcut the serializable map iterator */ - typedef t_object_count_map::const_iterator const_iterator; /**< Shortcut the serializable map const_iterator */ - - /** - * Creates an instance of the object_deleter - */ - object_deleter() { } - - ~object_deleter() { } - - /** - * Checks wether the given serializable is deletable. - * - * @param proxy The object_proxy to be checked. - * @return True if the serializable could be deleted. - */ - template - bool is_deletable(object_proxy *proxy, T *o); - - /** - * Checks wether the given object_container is deletable. - * - * @param ovector The object_container to be checked. - * @return True if the object_container could be deleted. - */ -// bool is_deletable(object_container &oc); - - /** - * @brief Returns the first deletable serializable. - * - * If the check was made and was successful this - * returns the first deletable serializable. - */ - iterator begin(); - - /** - * @brief Returns the first deletable serializable. - * - * If the check was made and was successful this - * returns the last deletable serializable. - */ - iterator end(); - - template - void serialize(T &x) { oos::access::serialize(*this, x); } - - template - void serialize(const char *, T &) { } - void serialize(const char *, char *, size_t) { } - - template - void serialize(const char *, has_one &x, cascade_type cascade); - template class C> - void serialize(const char *, basic_has_many &, const char *, const char *); - template - void serialize(const char *id, identifier &x); - - bool check_object_count_map() const; - -private: - t_object_count_map object_count_map; -}; - -template < class T, template < class ... > class ON_ATTACH > -class node_analyzer { -public: - node_analyzer(prototype_node &node, const ON_ATTACH &on_attach) - : node_(node) - , on_attach_(on_attach) - { } - - ~node_analyzer() { } - - template - void serialize(V &x); - template - void serialize(const char *, V &) { } - void serialize(const char *, char *, size_t) { } - template - void serialize(const char *id, has_one &x, cascade_type); - template class C> - void serialize(const char *, has_many &, const char *, const char *); - -private: - prototype_node &node_; - ON_ATTACH on_attach_; -}; - -struct basic_on_attach {}; - -template < class T > -struct null_on_attach : public basic_on_attach -{ - null_on_attach() {} - template < class V > - null_on_attach(const null_on_attach &) {} - null_on_attach& operator=(const null_on_attach &) { return *this; } - template < class V > - null_on_attach& operator=(const null_on_attach &) { return *this; } - - void operator()(prototype_node*) const {} -}; - } /// @endcond @@ -325,67 +126,48 @@ class OOS_OBJECT_API object_store */ ~object_store(); - /** - * Inserts a new object prototype into the prototype tree. The prototype - * constist of a unique type name. To know where the new - * prototype is inserted into the hierarchy the type name of the parent - * node is also given. - * - * @tparam T The type of the prototype node - * @param type The unique name of the type. - * @param abstract Indicates if the producers serializable is treated as an abstract node. - * @param parent The name of the parent type. - * @return Returns new inserted prototype iterator. - */ - template< class T, template < class ... > class ON_ATTACH = detail::null_on_attach, typename = typename std::enable_if>::value>::type > - prototype_iterator attach(const char *type, bool abstract = false, const char *parent = nullptr, const ON_ATTACH &on_attach = ON_ATTACH()); + template class O > + prototype_iterator attach(const char *type, std::initializer_list*> observer); + + template + prototype_iterator attach(const char *type, bool abstract = false, const char *parent = nullptr); + + template class O > + prototype_iterator attach(const char *type, bool abstract, const char *parent, const std::vector*> &observer); + + template class O > + prototype_iterator attach(const char *type, bool abstract, const char *parent, std::initializer_list*> observer); + + template class O > + prototype_iterator attach(const char *type, std::initializer_list*> observer); /** * Inserts a new object prototype into the prototype tree. The prototype - * constist of a unique type name. To know where the new + * consists of a unique type name. To know where the new * prototype is inserted into the hierarchy the type name of the parent * node is also given. * parameter. - * + * * @tparam T The type of the prototype node * @tparam S The type of the parent prototype node * @param type The unique name of the type. * @param abstract Indicates if the producers serializable is treated as an abstract node. * @return Returns new inserted prototype iterator. */ - template class ON_ATTACH = detail::null_on_attach, typename = typename std::enable_if>::value>::type > - prototype_iterator attach(const char *type, bool abstract = false, const ON_ATTACH &on_attach = ON_ATTACH()); + template + prototype_iterator attach(const char *type, bool abstract = false); - /** - * Inserts a new object prototype into the prototype tree. The prototype - * constist of a unique type name (generated from typeid). To know where the new - * prototype is inserted into the hierarchy the type name of the parent - * node is also given. - * - * @tparam T The type of the prototype node - * @param abstract Indicates if the producers serializable is treated as an abstract node. - * @param parent The name of the parent type. - * @return Returns new inserted prototype iterator. - */ - template - prototype_iterator prepare_attach(bool abstract = false, const char *parent = nullptr); + template class O > + prototype_iterator attach(const char *type, bool abstract, std::initializer_list*> observer); - /** - * Inserts a new object prototype into the prototype tree. The prototype - * constist of a unique type name (generated from typeid). To know where the new - * prototype is inserted into the hierarchy the typeid of the parent - * node is also given. - * - * @tparam T The type of the prototype node - * @tparam S The type of the parent prototype node - * @param abstract Indicates if the producers serializable is treated as an abstract node. - * @return Returns new inserted prototype iterator. - */ - template - prototype_iterator prepare_attach(bool abstract = false); + template < class T > + prototype_iterator attach(prototype_node *node, const char *parent = nullptr); + + template < class T, template < class V = T > class O > + prototype_iterator attach(prototype_node *node, const char *parent, std::vector*> observer); /** - * Removes an serializable prototype from the prototype tree. All children + * Removes an object prototype from the prototype tree. All children * nodes and all objects are also removed. * * @param type The name of the type to remove. @@ -605,7 +387,7 @@ class OOS_OBJECT_API object_store } iterator node = find(proxy->classname()); if (node == end()) { - throw object_exception("couldn't find object type"); + throw_object_exception("couldn't find object type"); } // check if proxy/object is already inserted if (proxy->ostore() != nullptr && proxy->id() > 0) { @@ -850,6 +632,14 @@ class OOS_OBJECT_API object_store */ sequencer_impl_ptr exchange_sequencer(const sequencer_impl_ptr &seq); + void on_update_relation_owner(prototype_node::relation_info &info, object_proxy *owner, object_proxy *value); + + void on_remove_relation_owner(prototype_node::relation_info &info, object_proxy *owner, object_proxy *value); + + void on_append_relation_item(prototype_node &node, object_proxy *owner, object_proxy *value); + + void on_remove_relation_item(prototype_node &node, object_proxy *owner, object_proxy *value); + transaction current_transaction(); bool has_transaction() const; @@ -863,7 +653,8 @@ class OOS_OBJECT_API object_store friend class object_holder; friend class object_proxy; friend class prototype_node; - template < class T, template < class ... > class ON_ATTACH > + friend class detail::basic_node_analyzer; + template < class T, template < class U = T > class O > friend class detail::node_analyzer; friend class transaction; template < class T, template class C > @@ -871,14 +662,14 @@ class OOS_OBJECT_API object_store private: - template < class T, typename = typename std::enable_if< std::is_same>::value >::type > - prototype_iterator attach(const char *id, abstract_has_many *container) - { - temp_container_ = container; - prototype_iterator i = attach(id); - temp_container_ = nullptr; - return i; - } +// template < class T, template < class V = T > class ... O, typename = typename std::enable_if< std::is_same>::value >::type > +// prototype_iterator attach(const char *id, abstract_has_many *container) +// { +// temp_container_ = container; +// prototype_iterator i = attach(id); +// temp_container_ = nullptr; +// return i; +// } /** * Clears a prototype_node and its @@ -938,34 +729,21 @@ class OOS_OBJECT_API object_store */ prototype_node *remove_prototype_node(prototype_node *node, bool is_root); - /** - * Get or create a prototype node - * - * @param producer The producer of the concrete object - * @param type The type name of the node - * @param abstract indicates wether the representing object is abstract - * @tparam T Type of the node - * @return The prototype node - */ - template - prototype_node *acquire(const char *type, bool abstract); - - /** - * Initializes a prototype node - * - * @param node The node to initialize - * @return iterator representing the prototype node - */ - template class ON_ATTACH> - iterator initialize(prototype_node *node, const ON_ATTACH &on_attach); - -// object_proxy *initialze_proxy(object_proxy *oproxy, prototype_iterator &node, bool notify); + template < class T > + prototype_node* attach_node(prototype_node *node, const char *parent); prototype_node* find_parent(const char *name) const; void push_transaction(const transaction &tr); void pop_transaction(); + bool is_relation_notification_enabled(); + void enable_relation_notification(); + void disable_relation_notification(); + + template < class T > + void validate(prototype_node *node); + private: typedef std::unordered_map t_prototype_map; // typeid -> [name -> prototype] @@ -980,17 +758,11 @@ class OOS_OBJECT_API object_store // typeid to prototype node map t_typeid_prototype_map typeid_prototype_map_; - // prepared prototype nodes - t_prototype_map prepared_prototype_map_; - typedef std::unordered_map t_object_proxy_map; t_object_proxy_map object_map_; sequencer seq_; - typedef std::list t_observer_list; - t_observer_list observer_list_; - detail::object_deleter object_deleter_; detail::object_inserter object_inserter_; @@ -998,114 +770,24 @@ class OOS_OBJECT_API object_store abstract_has_many *temp_container_ = nullptr; std::stack transactions_; -}; - -template class ON_ATTACH, typename Enabled > -object_store::iterator object_store::attach(const char *type, bool abstract, const char *parent, const ON_ATTACH &on_attach) -{ - // set node to root node - prototype_node *parent_node = find_parent(parent); - /* - * try to insert new prototype node - */ - const char *name = typeid(T).name(); - prototype_node *node = nullptr; - t_prototype_map::iterator i = prepared_prototype_map_.find(name); - if (i != prepared_prototype_map_.end()) { - // found a prepared node - node = i->second; - node->type_.assign(type); - prepared_prototype_map_.erase(i); - } else { - node = acquire(type, abstract); - // insert node - if (parent_node != nullptr) { - parent_node->insert(node); - } else { - last_->prev->append(node); - } - // Analyze primary and foreign keys of node - basic_identifier *id = identifier_resolver::resolve(); - if (id) { - id->isolate(); - node->id_.reset(id); - } - } - - // store prototype in map - // Todo: check return value - prototype_map_.insert(std::make_pair(node->type_, node))/*.first*/; - typeid_prototype_map_[typeid(T).name()].insert(std::make_pair(node->type_, node)); - - on_attach(node); - - return initialize(node, on_attach); -} - -template class ON_ATTACH, typename Enabled > -object_store::iterator object_store::attach(const char *type, bool abstract, const ON_ATTACH &on_attach) -{ - return attach(type, abstract, typeid(S).name(), on_attach); -} -template -prototype_iterator object_store::prepare_attach(bool abstract, const char *parent) -{ - prototype_node *parent_node = find_parent(parent); - - if (typeid_prototype_map_.find(typeid(T).name()) != typeid_prototype_map_.end()) { - throw_object_exception("attach: object type " << typeid(T).name() << " already in attached"); - } - - t_prototype_map::iterator i = prepared_prototype_map_.find(typeid(T).name()); - if (i != prepared_prototype_map_.end()) { - // there is already a prepared node for this type - return prototype_iterator(i->second); - } - - std::unique_ptr node(new prototype_node(this, "", typeid(T), abstract)); - - node->initialize(this, "", abstract); - - if (parent_node != nullptr) { - parent_node->insert(node.get()); - } else { - last_->prev->append(node.get()); - } - - // store only in prepared prototype map - // Todo: check return value - prepared_prototype_map_.insert(std::make_pair(typeid(T).name(), node.get())); - // Analyze primary and foreign keys of node - std::unique_ptr id(identifier_resolver::resolve()); - if (id) { - id->isolate(); - node->id_.reset(id.release()); - } - - return prototype_iterator(node.release()); -} - -template -prototype_iterator object_store::prepare_attach(bool abstract) -{ - return prepare_attach(abstract, typeid(T).name()); -} - -template -prototype_node *object_store::acquire(const char *type, bool abstract) + // relation notification related + bool relation_notification_ = true; +}; +template < class T > +void object_store::validate(prototype_node *node) { + std::unique_ptr nptr(node); // try to find node in prepared map const char *name = typeid(T).name(); - prototype_node *node = nullptr; - t_prototype_map::iterator i = prototype_map_.find(type); + t_prototype_map::iterator i = prototype_map_.find(node->type_); if (i != prototype_map_.end()) { - throw_object_exception("prototype already inserted: " << type); + throw_object_exception("prototype already inserted: " << node->type_.c_str()); } // try to find by typeid name i = prototype_map_.find(name); if (i != prototype_map_.end()) { - throw_object_exception("prototype already inserted: " << type); + throw_object_exception("prototype already inserted: " << node->type_.c_str()); } /* * no typeid found, seems to be @@ -1113,266 +795,144 @@ prototype_node *object_store::acquire(const char *type, bool abstract) * to be sure check in typeid map */ t_typeid_prototype_map::iterator j = typeid_prototype_map_.find(name); - if (j != typeid_prototype_map_.end() && j->second.find(type) != j->second.end()) { + if (j != typeid_prototype_map_.end() && j->second.find(node->type_) != j->second.end()) { /* unexpected found the * typeid check for type * throw exception */ throw object_exception("unexpectly found prototype"); - } else { - /* insert new prototype and add to - * typeid map - */ - node = new prototype_node(this, type, typeid(T), abstract); } - return node; + nptr.release(); } -template class ON_ATTACH> -object_store::iterator object_store::initialize(prototype_node *node, const ON_ATTACH &on_attach) +template class O > +object_store::iterator object_store::attach(const char *type, std::initializer_list*> observer) { - // Check if nodes serializable has 'to-many' relations - // Analyze primary and foreign keys of node - detail::node_analyzer analyzer(*node, on_attach); - T obj; - oos::access::serialize(analyzer, obj); - - while (!node->foreign_key_ids.empty()) { - auto i = node->foreign_key_ids.front(); - node->foreign_key_ids.pop_front(); - prototype_node *foreign_node = i.first; - std::shared_ptr fk(node->id_->clone()); - foreign_node->foreign_keys.insert(std::make_pair(i.second, fk)); - } - - return prototype_iterator(node); + return attach(type, false, nullptr, observer); } -namespace detail { +template +object_store::iterator object_store::attach(const char *type, bool abstract, const char *parent) +{ + prototype_node *node = new prototype_node(this, type, new T, abstract); + + return attach(node, parent); +} -template class ON_ATTACH> -template < class V > -void node_analyzer::serialize(V &x) +template class O > +object_store::iterator object_store::attach(const char *type, bool abstract, const char *parent, std::initializer_list*> observer) { - oos::access::serialize(*this, x); + return attach(type, abstract, parent, std::vector*>(observer)); } -template class ON_ATTACH> -template -void node_analyzer::serialize(const char *id, has_one &x, cascade_type) +template class O> +object_store::iterator object_store::attach(const char *type, bool abstract, const char *parent, const std::vector*> &observer) { - prototype_iterator node = node_.tree()->find(x.type()); - if (node == node_.tree()->end()) { - // if there is no such prototype node - // prepare insertion of new node - node = node_.tree()->template prepare_attach(); - if (node_.tree()->temp_container_) { - node->prepare_foreign_key(&node_, node_.tree()->temp_container_->item_field().c_str()); - } else { - node->prepare_foreign_key(&node_, id); - } - } else if (!node->has_primary_key()) { - throw_object_exception("serializable of type '" << x.type() << "' has no primary key"); - } else { - // node is inserted/attached; store it in nodes foreign key map - std::shared_ptr fk(node->id()->clone()); - node_.register_foreign_key(id, fk); - } + prototype_node *node = new prototype_node(this, type, new T, abstract); + + return attach(node, parent, observer); } -template class ON_ATTACH> -template class C> -void node_analyzer::serialize(const char *id, has_many &x, const char *, const char *) -//void node_analyzer::serialize(const char *id, has_many &x, const char */*owner_field*/, const char */*item_field*/) +template < class T > +prototype_node* object_store::attach_node(prototype_node *node, const char *parent) { - // item column column names -// x.owner_field(owner_field); -// x.item_field(item_field); - // Todo: distinguish between join table and no join table - if (x.has_join_table()) { - // attach relation table for has many relation - // check if has many item is already attached - // true: check owner and item field - // false: attach it - prototype_iterator pi = node_.tree()->find(id); - if (pi == node_.tree()->end()) { - pi = node_.tree()->template attach::item_type, ON_ATTACH>(id, false, nullptr, on_attach_); - } else if (pi->type_index() == std::type_index(typeid(typename has_many::item_type))) { - // prototype is of type has_many_item - throw_object_exception("many to many relations are not supported by now"); - } else { - // throw exception - throw_object_exception("prototype already inserted: " << pi->type()); - } - // insert the relation - // add container node to item node - pi->register_relation(node_.type(), &node_, id); + std::unique_ptr nptr(node); + // set node to root node + prototype_node *parent_node = find_parent(parent); + /* + * try to insert new prototype node + */ + // insert node + if (parent_node != nullptr) { + parent_node->insert(node); } else { - throw object_exception("has_many without join table not supported"); + last_->prev->append(node); } + // Analyze primary and foreign keys of node + basic_identifier *id = identifier_resolver::resolve(); + if (id) { + id->isolate(); + node->id_.reset(id); + } + // store prototype in map + // Todo: check return value + prototype_map_.insert(std::make_pair(node->type_, node))/*.first*/; + typeid_prototype_map_[typeid(T).name()].insert(std::make_pair(node->type_, node)); + + return nptr.release(); } -template < class T > -void modified_marker::marker_func(object_store &store, object_proxy &proxy) +template +object_store::iterator object_store::attach(const char *type, bool abstract) { - store.mark_modified(&proxy); + return attach(type, abstract, typeid(S).name()); } -template -void object_inserter::insert(object_proxy *proxy, T *o, bool notify) +template class O > +object_store::iterator object_store::attach(const char *type, std::initializer_list*> observer) { - - notify_ = notify; - - object_proxy_stack_.push(proxy); - - modified_marker_ = [](object_store &store, object_proxy *oproxy) { - store.mark_modified(oproxy); - }; - - if (proxy->obj()) { - oos::access::serialize(*this, *o); - } - object_proxy_stack_.pop(); + return attach(type, false, observer); } -template -void object_inserter::serialize(T &x) +template class O > +object_store::iterator object_store::attach(const char *type, bool abstract, std::initializer_list*> observer) { - oos::access::serialize(*this, x); + return attach(type, abstract, typeid(S).name(), observer); } -template -void object_inserter::serialize(const char *, has_one &x, cascade_type cascade) { - if (x.is_inserted() || (x.proxy_ && x.proxy_->obj() == nullptr)) { - return; - } - x.is_inserted_ = true; - x.cascade_ = cascade; - // object was seen by inserter stop inserting - if (!object_proxies_.insert(x.proxy_).second) { - return; - } +template < class T > +prototype_iterator object_store::attach(prototype_node *node, const char *parent) +{ + // Check if nodes object has 'to-many' relations + // Analyze primary and foreign keys of node + detail::node_analyzer analyzer(*node, *this); + analyzer.analyze(); - if (!x.proxy_) { - return; - } + validate(node); - if (x.id()) { - // do the pointer count - object_proxy_stack_.push(x.proxy_); - oos::access::serialize(*this, *(T*)x.ptr()); - object_proxy_stack_.pop(); - } else { - // new object - ostore_.insert(x.proxy_, notify_); - } - ++(*x.proxy_); + attach_node(node, parent); + + node->on_attach(); + + + return prototype_iterator(node); } -template class C> -void object_inserter::serialize(const char *, basic_has_many &x, const char*, const char*) +template < class T, template < class V = T > class O > +prototype_iterator object_store::attach(prototype_node *node, const char *parent, std::vector*> observer) { - // initialize the has many relation - // set identifier - // relation table name - // owner column name - // item column name - if (object_proxy_stack_.empty()) { - throw object_exception("no owner for has many relation"); + for(auto o : observer) { + node->register_observer(o); } + // Check if nodes object has 'to-many' relations + // Analyze primary and foreign keys of node + detail::node_analyzer analyzer(*node, *this, observer); + analyzer.analyze(); - if (x.ostore_) { - return; - } - object_proxy *proxy = object_proxy_stack_.top(); - x.owner_id_ = proxy->pk(); - x.owner_ = proxy; - x.ostore_ = &ostore_; - x.mark_modified_owner_ = modified_marker_; - - typename basic_has_many::iterator first = x.begin(); - typename basic_has_many::iterator last = x.end(); - - while (first != last) { - typename basic_has_many::relation_type i = (first++).relation_item(); - if (!i.is_inserted()) { - // item is not in store, insert it - ostore_.insert(i, false); - } - } -} + validate(node); -template -void object_deleter::t_object_count::remove_object(object_proxy *proxy, bool notify) -{ - proxy->ostore()->remove(proxy, notify, false); -} + attach_node(node, parent); -template -bool object_deleter::is_deletable(object_proxy *proxy, T *o) { - object_count_map.clear(); - object_count_map.insert(std::make_pair(proxy->id(), t_object_count(proxy, false, (T*)proxy->obj()))); + node->on_attach(); - // start collecting information - oos::access::serialize(*this, *o); - return check_object_count_map(); + return prototype_iterator(node); } -template -void object_deleter::serialize(const char *, has_one &x, cascade_type cascade) { - if (!x.ptr()) { - return; - } - std::pair ret = object_count_map.insert( - std::make_pair(x.proxy_->id(), t_object_count(x.proxy_, true, (T*)x.proxy_->obj())) - ); - --ret.first->second.reference_counter; - if (cascade & cascade_type::REMOVE) { - ret.first->second.ignore = false; - oos::access::serialize(*this, *(T*)x.ptr()); - } -} +namespace detail { -template class C> -void object_deleter::serialize(const char *, basic_has_many &x, const char *, const char *) +template < class T > +void modified_marker::marker_func(object_store &store, object_proxy &proxy) { - typename basic_has_many::iterator first = x.begin(); - typename basic_has_many::iterator last = x.end(); - while (first != last) { - // Todo: get the real holder: on join table get has_many_item - typename basic_has_many::relation_type iptr = first.relation_item(); - ++first; - object_proxy *proxy = iptr.proxy_; - std::pair ret = object_count_map.insert( - std::make_pair(proxy->id(), t_object_count(proxy, false, (T*)proxy->obj())) - ); - /********** - * - * object is already in list and will - * be ignored on deletion so set - * ignore flag to false because this - * node must be deleted - * - **********/ - if (!ret.second && ret.first->second.ignore) { - ret.first->second.ignore = false; - } - - oos::access::serialize(*this, *iptr); - } + store.mark_modified(&proxy); } -template -void object_deleter::serialize(const char *id, identifier &x) -{ - T val = x.value(); - serialize(id, val); } } -} +#include "oos/object/node_analyzer.tpp" +#include "oos/object/object_inserter.tpp" +#include "oos/object/object_deleter.tpp" #endif /* OBJECT_STORE_HPP */ diff --git a/include/oos/object/object_store_observer.hpp b/include/oos/object/object_store_observer.hpp new file mode 100644 index 000000000..550849765 --- /dev/null +++ b/include/oos/object/object_store_observer.hpp @@ -0,0 +1,120 @@ +/* + * This file is part of OpenObjectStore OOS. + * + * OpenObjectStore OOS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenObjectStore OOS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenObjectStore OOS. If not, see . + */ + +#ifndef OBJECT_STORE_OBSERVER_HPP +#define OBJECT_STORE_OBSERVER_HPP + +namespace oos { + +class object_proxy; +class prototype_node; + +/** + * Base class for all object store observer + */ +class basic_object_store_observer +{ +protected: + basic_object_store_observer(const std::type_index &tindex) + : type_index_(tindex) + {} + +public: + virtual ~basic_object_store_observer() {} + + const std::type_index& index() const { return type_index_; } + +private: + std::type_index type_index_; +}; + +/** + * @class object_store_observer + * @tparam T Type of the object store observer + * @brief Base class for object observer classes + * + * When interested to observe + * - attach (prototype_node) + * - detach (prototype_node) + * - insert (object) + * - update (object) + * - delete (object) + * actions an observer class instance must be + * registered with object store. + * Use this class as base class for all observer classes. + */ +template < class T > +class object_store_observer : public basic_object_store_observer +{ +public: + + object_store_observer() : basic_object_store_observer(std::type_index(typeid(T))) {} + + /** + * @brief Called on prototype_node attach + * + * When a prototype node is attached to the object store + * this is called after the attaching succeeded. + * + * @param node The attached prototype node + */ + virtual void on_attach(prototype_node &node, T &prototype) = 0; + + /** + * @brief Called on prototype_node detach + * + * When a prototype node is detached from the object store + * this is called before the detaching succeeded. + * + * @param node The to be detached prototype node + */ + virtual void on_detach(prototype_node &node, T &prototype) = 0; + + /** + * @brief Called on object insertion. + * + * Called when an object is inserted + * into the object_store. + * + * @param proxy The proxy of the inserted object. + */ + virtual void on_insert(object_proxy &proxy) = 0; + + /** + * @brief Called on object update. + * + * Called when an object is updated + * in the object_store. + * + * @param proxy The proxy of the updated object. + */ + virtual void on_update(object_proxy &proxy) = 0; + + /** + * @brief Called on object deletion. + * + * Called when an object is deleted + * from the object_store. + * + * @param proxy The proxy of the deleted object. + */ + virtual void on_delete(object_proxy &proxy) = 0; +}; + +} + +#endif /* OBJECT_STORE_OBSERVER_HPP */ diff --git a/include/oos/object/prototype_iterator.hpp b/include/oos/object/prototype_iterator.hpp index 628de6c0f..7f76142f7 100644 --- a/include/oos/object/prototype_iterator.hpp +++ b/include/oos/object/prototype_iterator.hpp @@ -144,7 +144,7 @@ void decrement(); private: friend class const_prototype_iterator; -pointer node_; +pointer node_ = nullptr; }; class OOS_OBJECT_API const_prototype_iterator diff --git a/include/oos/object/prototype_node.hpp b/include/oos/object/prototype_node.hpp index f2596025e..b885d2e61 100644 --- a/include/oos/object/prototype_node.hpp +++ b/include/oos/object/prototype_node.hpp @@ -35,6 +35,7 @@ #include "oos/utils/identifier.hpp" #include "oos/object/identifier_proxy_map.hpp" +#include "oos/object/object_store_observer.hpp" #include #include @@ -47,6 +48,15 @@ namespace oos { class object_store; class object_proxy; +namespace detail { +template < class T, template class C, class Enabled > +class has_many_inserter; +template < class T, template class C, class Enabled > +class has_many_deleter; +class basic_node_analyzer; +template < class T, template < class V = T > class O > +class node_analyzer; +} /** * @class prototype_node * @brief Holds the prototype of a concrete serializable. @@ -69,6 +79,47 @@ class OOS_OBJECT_API prototype_node prototype_node& operator=(const prototype_node&) = delete; public: + struct relation_info + { + enum relation_type { + BELONGS_TO, HAS_ONE, HAS_MANY + }; + + typedef std::function modify_value_func; + relation_info(const std::string &n, + relation_type t, + const modify_value_func &insert_func, + const modify_value_func &remove_func, + prototype_node *pn) + : name(n), type(t), insert_value(insert_func), remove_value(remove_func), node(pn) + {} + std::string name; + relation_type type; + std::function insert_value; + std::function remove_value; + prototype_node *node; + }; + + typedef std::unordered_map relation_map; + + struct relation_node_info + { + std::string owner_type_; + std::string relation_id_; + std::string owner_id_column_; + std::string item_id_column_; + }; + +public: + + template < class T > + static prototype_node* make_node(object_store *store, const char *type, bool abstract = false); + + template < class T > + static prototype_node* make_relation_node(object_store *store, const char *type, bool abstract, + const char *owner_type, const char *relation_id, + const char *owner_column, const char *item_column); + prototype_node(); /** @@ -83,16 +134,21 @@ class OOS_OBJECT_API prototype_node * @param typeinfo The typeinfo of this node. * @param abstract Tells the node if its prototype is abstract. */ - prototype_node(object_store *tree, const char *type, const std::type_info &typeinfo, bool abstract = false) + template < class T > + prototype_node(object_store *tree, const char *type, T *proto, bool abstract = false) : tree_(tree) , first(new prototype_node) , last(new prototype_node) , type_(type) , abstract_(abstract) - , type_index_(typeinfo) + , type_index_(typeid(T)) + , deleter_(&destroy) + , notifier_(¬ify_observer) + , prototype(proto) { first->next = last.get(); last->prev = first.get(); +// std::cout << "creating node " << type << "/" << type_index_.name() << "\n"; } @@ -254,40 +310,6 @@ class OOS_OBJECT_API prototype_node */ basic_identifier* id() const; - /** - * @brief Returns the count of relations - * @return The count of relations - */ - size_t relation_count() const; - - /** - * @brief Returns true if a relation with the given name exists - * - * Returns true if a relation with the given name exists in the - * object prototype. - * - * @param relation_name The name of the relation to be checked - * @return True if a relation with the given name exists - */ - bool has_relation(const std::string &relation_name) const; - - /** - * @brief Returns the count of foreign keys - * @return The count of foreign keys - */ - size_t foreign_key_count() const; - - /** - * @brief Returns true if a foreign key with the given name exists - * - * Returns true if a foreign key with the given name exists in the - * object prototype. - * - * @param foreign_key_name The name of the foreign key to be checked - * @return True if a relation with the given name exists - */ - bool has_foreign_key(const std::string &foreign_key_name) const; - /** * @brief Returns true if the node represents an abstract object * @return True if the node represents an abstract object @@ -301,13 +323,21 @@ class OOS_OBJECT_API prototype_node */ std::type_index type_index() const; - /// @cond OOS_DEV - - void register_foreign_key(const char *id, const std::shared_ptr &foreign_key); - void register_relation(const char *type, prototype_node *node, const char *id); - void prepare_foreign_key(prototype_node *master_node, const char *id); + /** + * Returns true if this node represents a relation + * node (has_many_item). + * + * @return True if this node represents a relation node. + */ + bool is_relation_node() const; - /// @endcond + /** + * Returns the relation node info. This struct contains + * only valid data if this node represents a n relation + * node (has_many_item). + * @return + */ + const relation_node_info& node_info() const; /** * Prints the node in graphviz layout to the stream. @@ -327,8 +357,19 @@ class OOS_OBJECT_API prototype_node */ object_proxy* find_proxy(const std::shared_ptr &pk); + void register_belongs_to(const std::type_index &tindex, const prototype_node::relation_info &relation_info); + void register_has_many(const std::type_index &tindex, const prototype_node::relation_info &relation_info); + private: + enum notification_type { + ATTACH, + DETACH, + INSERT, + UPDATE, + REMOVE + }; + /** * @internal * @@ -350,8 +391,71 @@ class OOS_OBJECT_API prototype_node */ void adjust_left_marker(prototype_node *root, object_proxy *old_proxy, object_proxy *new_proxy); + void register_observer(basic_object_store_observer *obs) + { + if (type_index_ != obs->index()) { + std::cout << "not same type\n"; + throw std::runtime_error("not same type"); + } + observer_list.push_back(obs); + } + + typedef std::list t_observer_list; + typedef void (*deleter)(void*, t_observer_list&); + typedef void (*notifier)(notification_type, prototype_node&, void*, basic_object_store_observer*); + + template + static void destroy(void* p, t_observer_list &ol) + { + delete (T*)p; + for(auto i : ol) { + delete (object_store_observer*)i; + } + } + + void on_attach() + { + notify(ATTACH); + } + + void on_detach() + { + notify(DETACH); + } + + void notify(notification_type t) { + if (!notifier_) { + return; + } + for (auto i : observer_list) { + notifier_(t, *this, prototype, i); + } + } + + template < typename T > + static void notify_observer(notification_type t, prototype_node &pt, void *p, basic_object_store_observer *obs) + { + switch (t) { + case ATTACH: + static_cast*>(obs)->on_attach(pt, *(T*)p); + break; + case DETACH: + static_cast*>(obs)->on_detach(pt, *(T*)p); + break; + case INSERT: + break; + case UPDATE: + break; + case REMOVE: + break; + default: + break; + } + } + private: friend class prototype_tree; + friend class object_holder; friend class object_store; template < class T > friend class object_view; @@ -359,15 +463,15 @@ class OOS_OBJECT_API prototype_node friend class const_object_view_iterator; template < class T > friend class object_view_iterator; - - /* - * The field_prototype_map_t contains a map - * of all foreign key field relations, where each value - * is a pair of the foreign prototype and its - * corresponding field - */ - typedef std::pair prototype_field_info_t; /**< Shortcut for prototype fieldname pair. */ - typedef std::map field_prototype_map_t; /**< Holds the fieldname and the prototype_node. */ + template < class T, template class C > + friend class has_many; + template < class T, template class C, class Enabled > + friend class detail::has_many_inserter; + template < class T, template class C, class Enabled > + friend class detail::has_many_deleter; + friend class detail::basic_node_analyzer; + template < class T, template < class U = T > class O > + friend class detail::node_analyzer; object_store *tree_ = nullptr; /**< The prototype tree to which the node belongs */ @@ -378,14 +482,6 @@ class OOS_OBJECT_API prototype_node std::unique_ptr first; /**< The first children node */ std::unique_ptr last; /**< The last children node */ - /* this map holds information about - * all prototypes in which this prototype - * is used as a child item (to many - * relation). The string tells the name - * of the attribute - */ - field_prototype_map_t relations; /**< Map holding relation information for type. */ - object_proxy *op_first = nullptr; /**< The marker of the first list node. */ object_proxy *op_marker = nullptr; /**< The marker of the last list node of the own elements. */ object_proxy *op_last = nullptr; /**< The marker of the last list node of all elements. */ @@ -399,6 +495,12 @@ class OOS_OBJECT_API prototype_node std::type_index type_index_; /**< type index of the represented object type */ + t_observer_list observer_list; + deleter deleter_ = nullptr; + notifier notifier_ = nullptr; + + void* prototype = nullptr; + /** * Holds the primary keys of all proxies in this node */ @@ -409,22 +511,36 @@ class OOS_OBJECT_API prototype_node */ std::unique_ptr id_; - /** - * a list of prototype_node and ids for - * which the relation map is yet to be filled - * once the object type is really inserted - * this list is processed - */ - typedef std::list > t_node_id_list; - t_node_id_list foreign_key_ids; /**< The foreign key id list */ +// relation_map belongs_to_map_; +// relation_map has_one_map_; +// relation_map has_many_map_; - /** - * a list of all foreign keys inside nodes object - */ - typedef std::unordered_map > t_foreign_key_map; - t_foreign_key_map foreign_keys; /**< The foreign key map */ + relation_map relation_info_map_; + + bool is_relation_node_ = false; + relation_node_info relation_node_info_; }; +template +prototype_node *prototype_node::make_node(object_store *store, const char *type, bool abstract) +{ + return new prototype_node(store, type, new T, abstract); +} + +template +prototype_node *prototype_node::make_relation_node(object_store *store, const char *type, bool abstract, + const char *owner_type, const char *relation_id, + const char *owner_column, const char *item_column) +{ + prototype_node *node = make_node(store, type, abstract); + node->relation_node_info_.owner_type_.assign(owner_type); + node->relation_node_info_.relation_id_.assign(relation_id); + node->relation_node_info_.owner_id_column_.assign(owner_column); + node->relation_node_info_.item_id_column_.assign(item_column); + node->is_relation_node_ = true; + return node; +} + } #endif /* PROTOTYPE_NODE_HPP */ diff --git a/include/oos/object/transaction.hpp b/include/oos/object/transaction.hpp index ece5155fd..5e1230948 100644 --- a/include/oos/object/transaction.hpp +++ b/include/oos/object/transaction.hpp @@ -289,7 +289,7 @@ void transaction::on_update(object_proxy *proxy) std::shared_ptr ua(new update_action(proxy, (T*)proxy->obj())); backup(ua, proxy); } else { - // An serializable with that id already exists + // An object with that id already exists // do nothing because the serializable is already // backed up } diff --git a/include/oos/orm/identifier_binder.hpp b/include/oos/orm/identifier_binder.hpp index 2d1a92aae..a064ac208 100644 --- a/include/oos/orm/identifier_binder.hpp +++ b/include/oos/orm/identifier_binder.hpp @@ -37,8 +37,9 @@ class identifier_binder template < class V > void serialize(const char *, identifier &x); - template < class V, typename = typename std::enable_if::value>::type > - void serialize(const char *, V &, cascade_type) { } +// template < class V, typename = typename std::enable_if::value>::type > + template < class HAS_ONE > + void serialize(const char *, HAS_ONE &, cascade_type) { } void serialize(const char *, char *, size_t) { } diff --git a/include/oos/orm/persistence.hpp b/include/oos/orm/persistence.hpp index 03017f967..2714e8fff 100644 --- a/include/oos/orm/persistence.hpp +++ b/include/oos/orm/persistence.hpp @@ -24,23 +24,13 @@ #include "oos/orm/table.hpp" #include "oos/orm/relation_table.hpp" +#include "oos/orm/persistence_observer.hpp" #include #include namespace oos { -namespace detail { - -/// @cond OOS_DEV - -template < class T > -struct persistence_on_attach; - -/// @endcond - -} - /** * @brief Represents the persistence layer for a database and an object_store * @@ -68,11 +58,12 @@ class OOS_ORM_API persistence * @param dns The database connection string */ explicit persistence(const std::string &dns); + ~persistence(); /** * Inserts a new object prototype into the prototype tree. The prototype - * constist of a unique type name. To know where the new + * consists of a unique type name. To know where the new * prototype is inserted into the hierarchy the type name of the parent * node is also given. * @@ -87,7 +78,7 @@ class OOS_ORM_API persistence /** * Inserts a new object prototype into the prototype tree. The prototype - * constist of a unique type name. To know where the new + * consists of a unique type name. To know where the new * prototype is inserted into the hierarchy the type name of the parent * node is also given. * parameter. @@ -101,6 +92,14 @@ class OOS_ORM_API persistence template void attach(const char *type, bool abstract = false); + /** + * Removes an object prototype from the prototype tree. All children + * nodes and all objects are also removed. + * + * @param type The name of the type to remove. + */ + void detach(const char *type); + /** * Checks if the given entity as * table exists @@ -108,7 +107,7 @@ class OOS_ORM_API persistence * @tparam T entity type class * @return True if table exists */ - template < class T > + template bool exists() { t_table_map::iterator i = tables_.find(store_.type()); @@ -170,32 +169,33 @@ class OOS_ORM_API persistence * * @return A reference to the object_store. */ - object_store& store(); + object_store &store(); /** * @brief Return a const reference to the underlaying object_store * * @return A const reference to the object_store. */ - const object_store& store() const; + const object_store &store() const; /** * @brief Return a reference to the underlaying database connection * * @return A reference to the database connection. */ - connection& conn(); + connection &conn(); /** * @brief Return a const reference to the underlaying database connection * * @return A const reference to the database connection. */ - const connection& conn() const; + const connection &conn() const; private: - template < class T > - friend struct detail::persistence_on_attach; + template + friend + class persistence_observer; private: connection connection_; @@ -208,142 +208,152 @@ namespace detail { /// @cond OOS_DEV -struct basic_persistence_on_attach : public detail::basic_on_attach -{ - basic_persistence_on_attach(persistence &p) : persistence_(p) {} - basic_persistence_on_attach(const basic_persistence_on_attach &x) : persistence_(x.persistence_) {} - basic_persistence_on_attach& operator=(const basic_persistence_on_attach &x) { persistence_ = x.persistence_; return *this; } - - std::reference_wrapper persistence_; -}; - -template < class T > -struct persistence_on_attach : public basic_persistence_on_attach -{ - using basic_persistence_on_attach::basic_persistence_on_attach; - - persistence_on_attach(persistence &p) : basic_persistence_on_attach(p) {} - - template < class V > - persistence_on_attach(const persistence_on_attach &x); - - template < class V > - persistence_on_attach& operator=(const persistence_on_attach &x) { persistence_ = x.persistence_; return *this; } - - void operator()(prototype_node *node) const; -}; - -//template <> -template < class T > -struct persistence_on_attach> : public basic_persistence_on_attach -{ - using basic_persistence_on_attach::basic_persistence_on_attach; - - typedef has_many_item relation_type; - - persistence_on_attach(persistence &p) : basic_persistence_on_attach(p) {} - - persistence_on_attach(const persistence_on_attach &x) - : basic_persistence_on_attach(x) - , relation_(x.relation_) - { } - - template < class V > - persistence_on_attach(const persistence_on_attach &x); - - template < class V > - persistence_on_attach& operator=(const persistence_on_attach &x) { persistence_ = x.persistence_; return *this; } - - void operator()(prototype_node *node) const; - - - template < class V > - void serialize(T &obj) - { - oos::access::serialize(*this, obj); - } - - template < class V > - void serialize(const char *, identifier &) - { - std::shared_ptr id(new identifier); - id->as_value(true); - relation_.owner(id); - } - - template < class V > - void serialize(const char*, V &) {} - - template < class V > - void serialize(const char*, V &, size_t) {} - - template < class HAS_ONE > - void serialize(const char*, HAS_ONE &, cascade_type) {} - - template < class HAS_MANY > - void serialize(const char *id, HAS_MANY &, const char *owner_field, const char *item_field) - { - owner_id_column_.assign(owner_field); - item_id_column_.assign(item_field); - relation_id_.assign(id); - } - - relation_type relation_; - std::string owner_type_; - std::string relation_id_; - std::string owner_id_column_; - std::string item_id_column_; -}; - -template -template -persistence_on_attach::persistence_on_attach(const persistence_on_attach &x) - : basic_persistence_on_attach(x.persistence_) -{ } +//struct basic_persistence_on_attach : public detail::basic_on_attach +//{ +// basic_persistence_on_attach(persistence &p) : persistence_(p) {} +// basic_persistence_on_attach(const basic_persistence_on_attach &x) : persistence_(x.persistence_) {} +// basic_persistence_on_attach& operator=(const basic_persistence_on_attach &x) { persistence_ = x.persistence_; return *this; } +// +// std::reference_wrapper persistence_; +//}; +// +//template < class T > +//struct persistence_on_attach : public basic_persistence_on_attach +//{ +// using basic_persistence_on_attach::basic_persistence_on_attach; +// +// persistence_on_attach(persistence &p) : basic_persistence_on_attach(p) {} +// +// template < class V > +// persistence_on_attach(const persistence_on_attach &x); +// +// template < class V > +// persistence_on_attach& operator=(const persistence_on_attach &x) { persistence_ = x.persistence_; return *this; } +// +// void operator()(prototype_node *node) const; +//}; +// +////template <> +//template < class T > +//struct persistence_on_attach> : public basic_persistence_on_attach +//{ +// using basic_persistence_on_attach::basic_persistence_on_attach; +// +// typedef has_many_item relation_type; +// +// persistence_on_attach(persistence &p) : basic_persistence_on_attach(p) {} +// +// persistence_on_attach(const persistence_on_attach &x) +// : basic_persistence_on_attach(x) +// , relation_(x.relation_) +// { } +// +// template < class V > +// persistence_on_attach(const persistence_on_attach &x); +// +// template < class V > +// persistence_on_attach& operator=(const persistence_on_attach &x) { persistence_ = x.persistence_; return *this; } +// +// void operator()(prototype_node *node) const; +// +// +// template < class V > +// void serialize(T &obj) +// { +// oos::access::serialize(*this, obj); +// } +// +// template < class V > +// void serialize(const char *, identifier &) +// { +// std::shared_ptr id(new identifier); +// id->as_value(true); +// relation_.owner(id); +// } +// +// template < class V > +// void serialize(const char*, V &) {} +// +// template < class V > +// void serialize(const char*, V &, size_t) {} +// +// template < class HAS_ONE > +// void serialize(const char*, HAS_ONE &, cascade_type) {} +// +// template < class HAS_MANY > +// void serialize(const char *id, HAS_MANY &, const char *owner_field, const char *item_field) +// { +// owner_id_column_.assign(owner_field); +// item_id_column_.assign(item_field); +// relation_id_.assign(id); +// } +// +// relation_type relation_; +// std::string owner_type_; +// std::string relation_id_; +// std::string owner_id_column_; +// std::string item_id_column_; +//}; +// +//template +//template +//persistence_on_attach::persistence_on_attach(const persistence_on_attach &x) +// : basic_persistence_on_attach(x.persistence_) +//{ } +// +//template +//void persistence_on_attach::operator()(prototype_node *node) const +//{ +// if (persistence_.get().tables_.find(node->type()) != persistence_.get().tables_.end()) { +// return; +// } +// std::cout << "node type: " << node->type() << "\n"; +// persistence_.get().tables_.insert(std::make_pair(node->type(), std::make_shared>(node, persistence_))); +//} +// +//template +//template +//persistence_on_attach>::persistence_on_attach(const persistence_on_attach &x) +// : basic_persistence_on_attach(x.persistence_) +//{ +// V owner; +// owner_type_ = persistence_.get().store().find(typeid(V).name())->type(); +// oos::access::serialize(*this, owner); +//} +// +////template <> +//template +//void persistence_on_attach>::operator()(prototype_node *node) const +//{ +// if (persistence_.get().tables_.find(node->type()) != persistence_.get().tables_.end()) { +// return; +// } +// std::cout << "node relation type: " << node->type() << "\n"; +// persistence_.get().tables_.insert(std::make_pair( +// node->type(), std::make_shared>( +// node, persistence_, relation_, owner_type_, relation_id_, owner_id_column_, item_id_column_ +// ))); +//} -template -void persistence_on_attach::operator()(prototype_node *node) const -{ - persistence_.get().tables_.insert(std::make_pair(node->type(), std::make_shared>(node, persistence_))); -} +/// @endcond -template -template -persistence_on_attach>::persistence_on_attach(const persistence_on_attach &x) - : basic_persistence_on_attach(x.persistence_) -{ - V owner; - owner_type_ = persistence_.get().store().find(typeid(V).name())->type(); - oos::access::serialize(*this, owner); } - -//template <> -template -void persistence_on_attach>::operator()(prototype_node *node) const -{ - if (persistence_.get().tables_.find(node->type()) != persistence_.get().tables_.end()) { - return; - } - persistence_.get().tables_.insert(std::make_pair( - node->type(), std::make_shared>( - node, persistence_, relation_, owner_type_, relation_id_, owner_id_column_, item_id_column_ - ))); } -/// @endcond +#include "oos/orm/persistence_observer.tpp" -} +namespace oos { template void persistence::attach(const char *type, bool abstract, const char *parent) { - store_.attach(type, abstract, parent, detail::persistence_on_attach(*this)); + store_.attach(type, abstract, parent, { new persistence_observer(*this) }); } template void persistence::attach(const char *type, bool abstract) { - store_.attach(type, abstract, detail::persistence_on_attach(*this)); + store_.attach(type, abstract, { new persistence_observer(*this) }); } } diff --git a/include/oos/orm/persistence_observer.hpp b/include/oos/orm/persistence_observer.hpp new file mode 100644 index 000000000..82b95fa93 --- /dev/null +++ b/include/oos/orm/persistence_observer.hpp @@ -0,0 +1,65 @@ +#ifndef OOS_PERSISTENCE_OBSERVER_HPP +#define OOS_PERSISTENCE_OBSERVER_HPP + +#include "oos/object/object_store_observer.hpp" +#include "oos/object/has_many_item.hpp" + +namespace oos { + +class persistence; + +template < class T > +class persistence_observer : public object_store_observer +{ +public: + + template < class V > + persistence_observer(const persistence_observer *x) + : persistence_ (x->persistence_) + {} + + persistence_observer(persistence &p) : persistence_(p) {} + + void on_attach(prototype_node &node, T &proto) override; + void on_detach(prototype_node &node, T &proto) override; + + void on_insert(object_proxy &) override {} + void on_update(object_proxy &) override {} + void on_delete(object_proxy &) override {} + +private: + template < class V > + friend class persistence_observer; + + persistence& persistence_; +}; + +template < class T > +class persistence_observer> : public object_store_observer> +{ +public: + + persistence_observer(persistence &p) : persistence_(p) {} + + typedef has_many_item relation_type; + + template < class V > + persistence_observer(const persistence_observer *x); + + void on_attach(prototype_node &node, has_many_item &proto) override; + void on_detach(prototype_node &node, has_many_item &proto) override; + + void on_insert(object_proxy &) override {} + void on_update(object_proxy &) override {} + void on_delete(object_proxy &) override {} + +private: + template < class V > + friend class persistence_observer; + + persistence& persistence_; + relation_type relation_; +}; + +} +#endif //OOS_PERSISTENCE_OBSERVER_HPP diff --git a/include/oos/orm/persistence_observer.tpp b/include/oos/orm/persistence_observer.tpp new file mode 100644 index 000000000..2c656da9c --- /dev/null +++ b/include/oos/orm/persistence_observer.tpp @@ -0,0 +1,62 @@ +#include "oos/orm/persistence_observer.hpp" +#include "oos/orm/persistence.hpp" + +namespace oos { +template +void persistence_observer::on_attach(prototype_node &node, T &/*proto*/) +{ + if (persistence_.tables_.find(node.type()) != persistence_.tables_.end()) { + return; + } +// std::cout << "node type: " << node.type() << "\n"; + persistence_.tables_.insert(std::make_pair(node.type(), std::make_shared>(&node, persistence_))); + +} + +template +void persistence_observer::on_detach(prototype_node &node, T &) +{ + auto i = persistence_.tables_.find(node.type()); + if (i == persistence_.tables_.end()) { + return; + } +// std::cout << "detach node type: " << node.type() << "\n"; + persistence_.tables_.erase(i); +} + +template +template < class V > +persistence_observer>::persistence_observer(const persistence_observer *x) + : persistence_ (x->persistence_) +{ + std::shared_ptr id(identifier_resolver::resolve()); + id->as_value(true); + relation_.owner(id); +} + +template +void persistence_observer>::on_attach(prototype_node &node, oos::has_many_item &) +{ + if (persistence_.tables_.find(node.type()) != persistence_.tables_.end()) { + return; + } +// std::cout << "node relation type: " << node.type() << "\n"; + persistence_.tables_.insert(std::make_pair( + node.type(), std::make_shared>( + &node, persistence_, relation_, node.node_info().owner_type_, node.node_info().relation_id_, + node.node_info().owner_id_column_, node.node_info().item_id_column_ + ))); +} + +template +void persistence_observer>::on_detach(prototype_node &node, oos::has_many_item &) +{ + auto i = persistence_.tables_.find(node.type()); + if (i == persistence_.tables_.end()) { + return; + } +// std::cout << "detach node type: " << node.type() << "\n"; + persistence_.tables_.erase(i); +} + +} \ No newline at end of file diff --git a/include/oos/orm/relation_resolver.hpp b/include/oos/orm/relation_resolver.hpp index cf3b24f79..1d147b048 100644 --- a/include/oos/orm/relation_resolver.hpp +++ b/include/oos/orm/relation_resolver.hpp @@ -51,6 +51,43 @@ class relation_resolver void serialize(const char *, char *, size_t) { } + template < class V > + void serialize(const char *, belongs_to &x, cascade_type cascade) + { + std::shared_ptr pk = x.primary_key(); + if (!pk) { + return; + } + + // get node of object type + prototype_iterator node = store_->find(x.type()); + + object_proxy *proxy = node->find_proxy(pk); + if (proxy) { + /** + * find proxy in node map + * if proxy can be found object was + * already read - replace proxy + */ + x.reset(proxy, cascade); + } else { + /** + * if proxy can't be found we create + * a proxy and store it in tables + * proxy map. it will be used when + * table is read. + */ + proxy = new object_proxy(pk, (T*)nullptr, node.get()); + basic_table::t_table_map::iterator j = table_.find_table(node->type()); + + if (j == table_.end_table()) { + throw_object_exception("unknown table " << node->type()); + } + j->second->identifier_proxy_map_.insert(std::make_pair(pk, proxy)); + x.reset(proxy, cascade); + } + } + template < class V > void serialize(const char *, has_one &x, cascade_type cascade) { @@ -110,7 +147,7 @@ class relation_resolver */ if (j->second->is_loaded()) { // relation table is loaded - std::cout << "Todo: relation table [" << id << "/" << j->second->name() << "] loaded; append all elements for owner " << *id_ << "\n"; +// std::cout << "Todo: relation table [" << id << "/" << j->second->name() << "] loaded; append all elements for owner " << *id_ << "\n"; auto i = table_.has_many_relations_.find(id); // get relation items for id/relation if (i != table_.has_many_relations_.end()) { diff --git a/include/oos/orm/table.hpp b/include/oos/orm/table.hpp index 057336844..ed6725718 100644 --- a/include/oos/orm/table.hpp +++ b/include/oos/orm/table.hpp @@ -135,8 +135,10 @@ class table : public basic_table { query q(name()); insert_ = q.insert().prepare(conn); +// std::cout << "INSERT: " << insert_.str() << "\n"; column id = detail::identifier_column_resolver::resolve(); update_ = q.update().where(id == 1).prepare(conn); +// std::cout << "UPDATE: " << update_.str() << "\n"; delete_ = q.remove().where(id == 1).prepare(conn); select_ = q.select().prepare(conn); } diff --git a/include/oos/sql/query.hpp b/include/oos/sql/query.hpp index e09b70ae9..3f7adc087 100644 --- a/include/oos/sql/query.hpp +++ b/include/oos/sql/query.hpp @@ -515,6 +515,13 @@ class query : public detail::basic_query reset_query(query_command); return *this; } + result execute() + { + if (!conn_.is_open()) { + throw std::logic_error("connection is not open"); + } + return execute(conn_); + } /** * Executes the current query and * returns a new result serializable. @@ -528,6 +535,15 @@ class query : public detail::basic_query return conn.execute(sql_); } + statement prepare() + { + if (!conn_.is_open()) { + throw std::logic_error("connection is not open"); + } + return prepare(conn_); + + } + /** * Creates and returns a prepared * statement based on the current query. diff --git a/include/oos/sql/statement.hpp b/include/oos/sql/statement.hpp index a78335650..7152ebb95 100644 --- a/include/oos/sql/statement.hpp +++ b/include/oos/sql/statement.hpp @@ -117,7 +117,7 @@ class statement */ result execute() { - //std::cout << "SQL: " << p->str() << '\n'; +// std::cout << "SQL: " << p->str() << '\n'; //std::cout.flush(); return result(p->execute()); } @@ -257,6 +257,7 @@ class statement */ result execute() { +// std::cout << "SQL: " << p->str() << '\n'; return result(p->execute(), prototype_); } diff --git a/include/oos/unit/test_suite.hpp b/include/oos/unit/test_suite.hpp index 945931b9d..0986f7268 100644 --- a/include/oos/unit/test_suite.hpp +++ b/include/oos/unit/test_suite.hpp @@ -146,6 +146,7 @@ class OOS_UNIT_API test_suite test_suite_cmd cmd = UNKNOWN; /**< Test suite command to be executed */ bool initialized = false; /**< Indicates wether the suite is initialized or not */ bool brief = false; /**< If true for LIST command only a brief list is printed */ + bool quiet = false; /**< If true no output is written to stdout */ std::vector unit_args; /**< Vector of test unit arguments */ }; @@ -157,10 +158,11 @@ class OOS_UNIT_API test_suite struct unit_executer : public std::unary_function { - unit_executer(summary &s); + unit_executer(summary &s, bool q); void operator()(test_suite::value_type &x); bool succeeded; + bool quiet; summary &summary_; }; @@ -290,6 +292,13 @@ class OOS_UNIT_API test_suite */ const test_suite_args& test_args() const; + /** + * Disables any output + * + * @param q If true a output is disabled + */ + void quiet(bool q = true); + private: test_suite_args args_; t_unit_test_map unit_test_map_; diff --git a/include/oos/unit/unit_test.hpp b/include/oos/unit/unit_test.hpp index a458f6412..06b26b065 100644 --- a/include/oos/unit/unit_test.hpp +++ b/include/oos/unit/unit_test.hpp @@ -292,8 +292,10 @@ class OOS_UNIT_API unit_test * * Each test method of the test_unit is * executed. + * + * @param quiet If true no output is written to stdout */ - bool execute(); + bool execute(bool quiet = false); /** * @brief Executes a concrete test method. @@ -303,8 +305,9 @@ class OOS_UNIT_API unit_test * a warning is displayed. * * @param test Name of the test to execute. + * @param quiet If true no output is written to stdout */ - bool execute(const std::string &test); + bool execute(const std::string &test, bool quiet = false); /** * @brief List all tests. @@ -877,7 +880,7 @@ class OOS_UNIT_API unit_test } test_func_info; private: - void execute(test_func_info &test_info); + void execute(test_func_info &test_info, bool quiet); private: std::string name_; diff --git a/include/oos/utils/serializer.hpp b/include/oos/utils/serializer.hpp index f32985634..627179a3f 100644 --- a/include/oos/utils/serializer.hpp +++ b/include/oos/utils/serializer.hpp @@ -20,7 +20,7 @@ #include "oos/utils/cascade_type.hpp" #include "oos/utils/varchar.hpp" -#include "access.hpp" +#include "oos/utils/access.hpp" #include #include diff --git a/include/oos/utils/string.hpp b/include/oos/utils/string.hpp index c822de689..3586a4b11 100644 --- a/include/oos/utils/string.hpp +++ b/include/oos/utils/string.hpp @@ -70,7 +70,7 @@ struct OOS_UTILS_API date_format /** * date format string representing the ISO8601 format */ - static constexpr const char* ISO8601 = "%F"; + static constexpr const char* ISO8601 = "%Y-%m-%d"; #endif }; diff --git a/src/db/mssql/mssql_connection.cpp b/src/db/mssql/mssql_connection.cpp index 7a9d60018..a850ee55a 100644 --- a/src/db/mssql/mssql_connection.cpp +++ b/src/db/mssql/mssql_connection.cpp @@ -224,7 +224,8 @@ std::vector mssql_connection::describe(const std::string &table) #else strcpy((char*)buf, table.c_str()); #endif - SQLColumns(stmt, NULL, 0, NULL, 0, buf, SQL_NTS, NULL, 0); + ret = SQLColumns(stmt, NULL, 0, NULL, 0, buf, SQL_NTS, NULL, 0); + throw_error(ret, SQL_HANDLE_STMT, stmt, "mssql", "error on executing column description"); //std::unique_ptr res(static_cast(execute(stmt))); // bind to columns we need (column name, data type of column and index) @@ -238,22 +239,22 @@ std::vector mssql_connection::describe(const std::string &table) // column name ret = SQLBindCol(stmt, 4, SQL_C_CHAR, column, sizeof(column), &indicator[0]); - throw_error(ret, SQL_HANDLE_DBC, connection_, "mssql", "error on sql columns statement (column)"); + throw_error(ret, SQL_HANDLE_STMT, stmt, "mssql", "error on sql columns statement (column)"); // data type - ret = SQLBindCol(stmt, 5, SQL_C_LONG, &data_type, sizeof(data_type), &indicator[1]); - throw_error(ret, SQL_HANDLE_DBC, connection_, "mssql", "error on sql columns statement (data type)"); + ret = SQLBindCol(stmt, 5, SQL_C_SSHORT, &data_type, sizeof(data_type), &indicator[1]); + throw_error(ret, SQL_HANDLE_STMT, stmt, "mssql", "error on sql columns statement (data type)"); // type name ret = SQLBindCol(stmt, 6, SQL_C_CHAR, type, sizeof(type), &indicator[2]); - throw_error(ret, SQL_HANDLE_DBC, connection_, "mssql", "error on sql columns statement (type name)"); + throw_error(ret, SQL_HANDLE_STMT, stmt, "mssql", "error on sql columns statement (type name)"); // size - ret = SQLBindCol(stmt, 7, SQL_C_LONG, &size, sizeof(size), &indicator[3]); - throw_error(ret, SQL_HANDLE_DBC, connection_, "mssql", "error on sql columns statement (data size)"); + ret = SQLBindCol(stmt, 7, SQL_C_SLONG, &size, sizeof(size), &indicator[3]); + throw_error(ret, SQL_HANDLE_STMT, stmt, "mssql", "error on sql columns statement (data size)"); // nullable - ret = SQLBindCol(stmt, 11, SQL_C_LONG, ¬_null, 0, &indicator[4]); - throw_error(ret, SQL_HANDLE_DBC, connection_, "mssql", "error on sql columns statement (not null)"); + ret = SQLBindCol(stmt, 11, SQL_C_SSHORT, ¬_null, 0, &indicator[4]); + throw_error(ret, SQL_HANDLE_STMT, stmt, "mssql", "error on sql columns statement (not null)"); // index (1 based) - ret = SQLBindCol(stmt, 17, SQL_C_LONG, &pos, 0, &indicator[5]); - throw_error(ret, SQL_HANDLE_DBC, connection_, "mssql", "error on sql columns statement (pos)"); + ret = SQLBindCol(stmt, 17, SQL_C_SLONG, &pos, 0, &indicator[5]); + throw_error(ret, SQL_HANDLE_STMT, stmt, "mssql", "error on sql columns statement (pos)"); std::vector fields; diff --git a/src/db/mssql/mssql_statement.cpp b/src/db/mssql/mssql_statement.cpp index f530e7ef7..844350761 100644 --- a/src/db/mssql/mssql_statement.cpp +++ b/src/db/mssql/mssql_statement.cpp @@ -358,7 +358,7 @@ void mssql_statement::bind_value(const char *val, size_t size, size_t index) host_data_.push_back(v); - SQLRETURN ret = SQLBindParameter(stmt_, (SQLUSMALLINT)index, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, size, 0, v->data, v->len, NULL); + SQLRETURN ret = SQLBindParameter(stmt_, (SQLUSMALLINT)index, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, size, 0, v->data, v->len, NULL); throw_error(ret, SQL_HANDLE_STMT, stmt_, "mssql", "couldn't bind parameter"); } @@ -458,9 +458,11 @@ int mssql_statement::type2sql(data_type type) case data_type::type_double: return SQL_DOUBLE; case data_type::type_char_pointer: - return SQL_LONGVARCHAR; + return SQL_VARCHAR; + //return SQL_LONGVARCHAR; case data_type::type_varchar: - return SQL_LONGVARCHAR; + return SQL_VARCHAR; + //return SQL_LONGVARCHAR; case data_type::type_text: return SQL_LONGVARCHAR; case data_type::type_date: diff --git a/src/db/mysql/mysql_statement.cpp b/src/db/mysql/mysql_statement.cpp index bff2a6c9c..7e71ac6e4 100644 --- a/src/db/mysql/mysql_statement.cpp +++ b/src/db/mysql/mysql_statement.cpp @@ -46,7 +46,7 @@ mysql_statement::mysql_statement(mysql_connection &db, const oos::sql &stmt) if (host_size) { host_array = new MYSQL_BIND[host_size]; memset(host_array, 0, host_size * sizeof(MYSQL_BIND)); - length_vector.assign(host_size, 0); + is_null_vector.assign(host_size, false); } int res = mysql_stmt_prepare(stmt_, str().c_str(), (unsigned long)str().size()); @@ -104,87 +104,87 @@ detail::result_impl* mysql_statement::execute() void mysql_statement::serialize(const char *, char &x) { - bind_value(host_array[host_index], MYSQL_TYPE_TINY, x); + bind_value(host_index, MYSQL_TYPE_TINY, x); ++host_index; } void mysql_statement::serialize(const char *, short &x) { - bind_value(host_array[host_index], MYSQL_TYPE_SHORT, x); + bind_value(host_index, MYSQL_TYPE_SHORT, x); ++host_index; } void mysql_statement::serialize(const char *, int &x) { - bind_value(host_array[host_index], MYSQL_TYPE_LONG, x); + bind_value(host_index, MYSQL_TYPE_LONG, x); ++host_index; } void mysql_statement::serialize(const char *, long &x) { - //bind_value(host_array[host_index], MYSQL_TYPE_LONGLONG, x); - bind_value(host_array[host_index], MYSQL_TYPE_LONG, x); + //bind_value(host_index, MYSQL_TYPE_LONGLONG, x); + bind_value(host_index, MYSQL_TYPE_LONG, x); ++host_index; } void mysql_statement::serialize(const char *, unsigned char &x) { - bind_value(host_array[host_index], MYSQL_TYPE_TINY, x); + bind_value(host_index, MYSQL_TYPE_TINY, x); ++host_index; } void mysql_statement::serialize(const char *, unsigned short &x) { - bind_value(host_array[host_index], MYSQL_TYPE_SHORT, x); + bind_value(host_index, MYSQL_TYPE_SHORT, x); ++host_index; } void mysql_statement::serialize(const char *, unsigned int &x) { - bind_value(host_array[host_index], MYSQL_TYPE_LONG, x); + bind_value(host_index, MYSQL_TYPE_LONG, x); ++host_index; } void mysql_statement::serialize(const char *, unsigned long &x) { - //bind_value(host_array[host_index], MYSQL_TYPE_LONGLONG, x); - bind_value(host_array[host_index], MYSQL_TYPE_LONG, x); + //bind_value(host_index, MYSQL_TYPE_LONGLONG, x); + bind_value(host_index, MYSQL_TYPE_LONG, x); ++host_index; } void mysql_statement::serialize(const char *, bool &x) { - bind_value(host_array[host_index], MYSQL_TYPE_TINY, x); + bind_value(host_index, MYSQL_TYPE_TINY, x); ++host_index; } void mysql_statement::serialize(const char *, float &x) { - bind_value(host_array[host_index], MYSQL_TYPE_FLOAT, x); + bind_value(host_index, MYSQL_TYPE_FLOAT, x); ++host_index; } void mysql_statement::serialize(const char *, double &x) { - bind_value(host_array[host_index], MYSQL_TYPE_DOUBLE, x); + bind_value(host_index, MYSQL_TYPE_DOUBLE, x); ++host_index; } void mysql_statement::serialize(const char *, char *x, size_t s) { - bind_value(host_array[host_index], MYSQL_TYPE_VAR_STRING, x, s); + bind_value(host_index, MYSQL_TYPE_VAR_STRING, x, s); ++host_index; } void mysql_statement::serialize(const char *, std::string &x) { - bind_value(host_array[host_index], MYSQL_TYPE_STRING, x.data(), x.size()); + bind_value(host_index, MYSQL_TYPE_STRING, x.data(), x.size()); ++host_index; } void mysql_statement::serialize(const char *, oos::date &x) { - bind_value(host_array[host_index], MYSQL_TYPE_DATE, x); + bind_value(host_index, MYSQL_TYPE_DATE, x); ++host_index; } @@ -195,16 +195,16 @@ void mysql_statement::serialize(const char *, oos::time &x) // doesn't support fractional seconds // so we use a datetime string here std::string tstr = to_string(x, "%FT%T"); - bind_value(host_array[host_index], MYSQL_TYPE_VAR_STRING, tstr.c_str(), tstr.size()); + bind_value(host_index, MYSQL_TYPE_VAR_STRING, tstr.c_str(), tstr.size()); #else - bind_value(host_array[host_index], MYSQL_TYPE_TIMESTAMP, x); + bind_value(host_index, MYSQL_TYPE_TIMESTAMP, x); #endif ++host_index; } void mysql_statement::serialize(const char *, varchar_base &x) { - bind_value(host_array[host_index], MYSQL_TYPE_VAR_STRING, x.c_str(), x.size()); + bind_value(host_index, MYSQL_TYPE_VAR_STRING, x.c_str(), x.size()); ++host_index; } @@ -215,13 +215,17 @@ void mysql_statement::serialize(const char *id, basic_identifier &x) void mysql_statement::serialize(const char *id, identifiable_holder &x, cascade_type) { +// std::cout << "binding [" << id << "] (index: " << host_index << ")\n"; if (x.has_primary_key()) { x.primary_key()->serialize(id, *this); + } else { + bind_null(host_index++); } } -void mysql_statement::bind_value(MYSQL_BIND &bind, enum_field_types, char x) +void mysql_statement::bind_value(std::size_t index, enum_field_types, char x) { + MYSQL_BIND &bind = host_array[index]; if (bind.buffer == nullptr) { size_t s = sizeof(char); bind.buffer = new char[s]; @@ -229,11 +233,13 @@ void mysql_statement::bind_value(MYSQL_BIND &bind, enum_field_types, char x) } *static_cast(bind.buffer) = x; bind.buffer_type = MYSQL_TYPE_VAR_STRING; - bind.is_null = 0; + is_null_vector[index] = false; + bind.is_null = &is_null_vector[index]; } -void mysql_statement::bind_value(MYSQL_BIND &bind, enum_field_types, unsigned char x) +void mysql_statement::bind_value(std::size_t index, enum_field_types, unsigned char x) { + MYSQL_BIND &bind = host_array[index]; if (bind.buffer == nullptr) { size_t s = sizeof(unsigned char); bind.buffer = new unsigned char[s]; @@ -241,11 +247,14 @@ void mysql_statement::bind_value(MYSQL_BIND &bind, enum_field_types, unsigned ch } *static_cast(bind.buffer) = x; bind.buffer_type = MYSQL_TYPE_VAR_STRING; - bind.is_null = 0; + is_null_vector[index] = false; + bind.is_null = &is_null_vector[index]; } -void mysql_statement::bind_value(MYSQL_BIND &bind, enum_field_types type, const oos::date &x) +void mysql_statement::bind_value(std::size_t index, enum_field_types type, const oos::date &x) { +// std::cout << "binding [" << x << "] (index: " << index << ")\n"; + MYSQL_BIND &bind = host_array[index]; if (bind.buffer == nullptr) { size_t s = sizeof(MYSQL_TIME); bind.buffer = new char[s]; @@ -254,7 +263,8 @@ void mysql_statement::bind_value(MYSQL_BIND &bind, enum_field_types type, const memset(bind.buffer, 0, sizeof(MYSQL_TIME)); bind.buffer_type = type; bind.length = 0; - bind.is_null = 0; + is_null_vector[index] = false; + bind.is_null = &is_null_vector[index]; MYSQL_TIME *mt = static_cast(bind.buffer); mt->day = (unsigned int)x.day(); mt->month = (unsigned int)x.month(); @@ -262,8 +272,9 @@ void mysql_statement::bind_value(MYSQL_BIND &bind, enum_field_types type, const mt->time_type = MYSQL_TIMESTAMP_DATE; } -void mysql_statement::bind_value(MYSQL_BIND &bind, enum_field_types type, const oos::time &x) +void mysql_statement::bind_value(std::size_t index, enum_field_types type, const oos::time &x) { + MYSQL_BIND &bind = host_array[index]; if (bind.buffer == nullptr) { size_t s = sizeof(MYSQL_TIME); bind.buffer = new char[s]; @@ -272,7 +283,8 @@ void mysql_statement::bind_value(MYSQL_BIND &bind, enum_field_types type, const memset(bind.buffer, 0, sizeof(MYSQL_TIME)); bind.buffer_type = type; bind.length = 0; - bind.is_null = 0; + is_null_vector[index] = false; + bind.is_null = &is_null_vector[index]; MYSQL_TIME *mt = static_cast(bind.buffer); mt->day = (unsigned int)x.day(); mt->month = (unsigned int)x.month(); @@ -284,9 +296,11 @@ void mysql_statement::bind_value(MYSQL_BIND &bind, enum_field_types type, const mt->time_type = MYSQL_TIMESTAMP_DATETIME; } -void mysql_statement::bind_value(MYSQL_BIND &bind, enum_field_types type, const char *value, size_t) +void mysql_statement::bind_value(std::size_t index, enum_field_types type, const char *value, size_t) { - size_t len(strlen(value) + 1); +// std::cout << "binding [" << value << "] (index: " << index << ")\n"; + MYSQL_BIND &bind = host_array[index]; + std::size_t len(strlen(value) + 1); if (bind.buffer_length < len) { // reallocate memory delete [] static_cast(bind.buffer); @@ -305,7 +319,15 @@ void mysql_statement::bind_value(MYSQL_BIND &bind, enum_field_types type, const strncpy(static_cast(bind.buffer), value, len); #endif bind.buffer_type = type; - bind.is_null = 0; + is_null_vector[index] = false; + bind.is_null = &is_null_vector[index]; +} + +void mysql_statement::bind_null(std::size_t index) +{ + MYSQL_BIND &bind = host_array[index]; + is_null_vector[index] = true; + bind.is_null = &is_null_vector[index]; } } diff --git a/src/db/sqlite/sqlite_prepared_result.cpp b/src/db/sqlite/sqlite_prepared_result.cpp index 5c3af2fb9..4269bce0b 100644 --- a/src/db/sqlite/sqlite_prepared_result.cpp +++ b/src/db/sqlite/sqlite_prepared_result.cpp @@ -3,6 +3,7 @@ #include "oos/utils/date.hpp" #include "oos/utils/time.hpp" #include "oos/utils/varchar.hpp" +#include "oos/utils/string.hpp" #include "oos/utils/basic_identifier.hpp" #include @@ -158,9 +159,9 @@ void sqlite_prepared_result::serialize(const char *, char *x, size_t s) void sqlite_prepared_result::serialize(const char *id, oos::date &x) { - double val = 0; + std::string val; serialize(id, val); - x.set(static_cast(val)); + x = oos::date::parse(val, date_format::ISO8601); } void sqlite_prepared_result::serialize(const char *id, oos::time &x) diff --git a/src/db/sqlite/sqlite_statement.cpp b/src/db/sqlite/sqlite_statement.cpp index d985951be..29aa228b4 100644 --- a/src/db/sqlite/sqlite_statement.cpp +++ b/src/db/sqlite/sqlite_statement.cpp @@ -196,6 +196,8 @@ void sqlite_statement::serialize(const char *id, identifiable_holder &x, cascade { if (x.has_primary_key()) { x.primary_key()->serialize(id, *this); + } else { + sqlite3_bind_null(stmt_, (int)++host_index); } } diff --git a/src/object/CMakeLists.txt b/src/object/CMakeLists.txt index 65ae858db..c4bc1fba6 100644 --- a/src/object/CMakeLists.txt +++ b/src/object/CMakeLists.txt @@ -14,6 +14,8 @@ SET(SOURCES delete_action.cpp basic_has_many_item.cpp object_proxy_accessor.cpp + object_deleter.cpp + object_inserter.cpp ) SET(HEADER @@ -27,7 +29,7 @@ SET(HEADER ${CMAKE_SOURCE_DIR}/include/oos/object/object_proxy.hpp ${CMAKE_SOURCE_DIR}/include/oos/object/object_serializer.hpp ${CMAKE_SOURCE_DIR}/include/oos/object/prototype_node.hpp - ${CMAKE_SOURCE_DIR}/include/oos/object/object_observer.hpp + ${CMAKE_SOURCE_DIR}/include/oos/object/object_store_observer.hpp ${CMAKE_SOURCE_DIR}/include/oos/object/object_expression.hpp ${CMAKE_SOURCE_DIR}/include/oos/object/attribute_serializer.hpp ${CMAKE_SOURCE_DIR}/include/oos/object/prototype_iterator.hpp @@ -50,7 +52,16 @@ SET(HEADER ${CMAKE_SOURCE_DIR}/include/oos/object/has_many_item.hpp ${CMAKE_SOURCE_DIR}/include/oos/object/basic_has_many_item.hpp ${CMAKE_SOURCE_DIR}/include/oos/object/identifier_proxy_map.hpp - ${CMAKE_SOURCE_DIR}/include/oos/object/object_proxy_accessor.hpp) + ${CMAKE_SOURCE_DIR}/include/oos/object/object_proxy_accessor.hpp + ${CMAKE_SOURCE_DIR}/include/oos/object/belongs_to.hpp + ${CMAKE_SOURCE_DIR}/include/oos/object/object_holder_type.hpp + ${CMAKE_SOURCE_DIR}/include/oos/object/object_inserter.hpp + ${CMAKE_SOURCE_DIR}/include/oos/object/object_inserter.tpp + ${CMAKE_SOURCE_DIR}/include/oos/object/object_deleter.hpp + ${CMAKE_SOURCE_DIR}/include/oos/object/object_deleter.tpp + ${CMAKE_SOURCE_DIR}/include/oos/object/node_analyzer.hpp + ${CMAKE_SOURCE_DIR}/include/oos/object/node_analyzer.tpp +) ADD_LIBRARY(oos-object SHARED ${SOURCES} ${HEADER}) diff --git a/src/object/object_deleter.cpp b/src/object/object_deleter.cpp new file mode 100644 index 000000000..715ccbd66 --- /dev/null +++ b/src/object/object_deleter.cpp @@ -0,0 +1,39 @@ +#include "oos/object/object_deleter.hpp" + +namespace oos { +namespace detail { + +void object_deleter::t_object_count::remove(bool notify) +{ + remove_func(proxy, notify); +} + +object_deleter::iterator object_deleter::begin() +{ + return object_count_map.begin(); +} + +object_deleter::iterator object_deleter::end() +{ + return object_count_map.end(); +} + +bool object_deleter::check_object_count_map() const +{ + // check the reference and pointer counter of collected objects + const_iterator first = object_count_map.begin(); + const_iterator last = object_count_map.end(); + while (first != last) { + if (first->second.ignore) { + ++first; + } else if (first->second.reference_counter == 0) { + ++first; + } else { + return false; + } + } + return true; +} + +} +} diff --git a/src/object/object_holder.cpp b/src/object/object_holder.cpp index 19195f8a0..c2ce6a779 100644 --- a/src/object/object_holder.cpp +++ b/src/object/object_holder.cpp @@ -4,12 +4,13 @@ #include "oos/object/object_holder.hpp" #include "oos/object/object_proxy.hpp" +#include "oos/object/object_store.hpp" #include "oos/object/object_exception.hpp" namespace oos { -object_holder::object_holder(bool is_internal) - : is_internal_(is_internal) +object_holder::object_holder(object_holder_type holder_type) + : type_(holder_type) {} object_holder::object_holder(const object_holder &x) @@ -31,9 +32,9 @@ object_holder::operator=(const object_holder &x) return *this; } -object_holder::object_holder(bool is_internal, object_proxy *op) +object_holder::object_holder(object_holder_type holder_type, object_proxy *op) : proxy_(op) - , is_internal_(is_internal) + , type_(holder_type) , oid_(0) { if (proxy_) { @@ -45,7 +46,7 @@ object_holder::object_holder(bool is_internal, object_proxy *op) object_holder::~object_holder() { if (proxy_) { - if (is_internal_ && is_inserted_) { + if (is_internal() && is_inserted_) { --(*proxy_); } proxy_->remove(this); @@ -76,8 +77,10 @@ void object_holder::reset(object_proxy *proxy, cascade_type cascade) } if (proxy_) { oid_ = 0; - if (is_internal_ && is_inserted_ && proxy_->ostore_) { + if (is_internal() && is_inserted_ && proxy_->ostore_) { --(*proxy_); + proxy_->ostore_->on_remove_relation_item(*proxy_->node_, proxy_, owner_); +// proxy_->node_->notify_delete_relation(owner_, proxy); } proxy_->remove(this); /* @@ -92,13 +95,25 @@ void object_holder::reset(object_proxy *proxy, cascade_type cascade) cascade_ = cascade; if (proxy_) { oid_ = proxy_->id(); - if (is_internal_ && is_inserted_ && proxy_->ostore_) { + if (is_internal() && is_inserted_ && proxy_->ostore_) { ++(*proxy_); + proxy_->ostore_->on_append_relation_item(*proxy_->node_, proxy_, owner_); +// proxy_->node_->notify_insert_relation(owner_, proxy); } proxy_->add(this); } } +void object_holder::clear() +{ + reset(nullptr, cascade_type::ALL); +} + +bool object_holder::empty() const +{ + return proxy_ == nullptr; +} + void object_holder::reset(const std::shared_ptr &id) { if (proxy_ && !proxy_->pk()->is_same_type(*id)) { @@ -161,10 +176,24 @@ void*object_holder::lookup_object() const return proxy_ ? proxy_->obj() : nullptr; } -bool -object_holder::is_internal() const +bool object_holder::is_belongs_to() const +{ + return type_ == object_holder_type::BELONGS_TO; +} + +bool object_holder::is_has_one() const { - return is_internal_; + return type_ == object_holder_type::HAS_ONE; +} + +bool object_holder::is_object_ptr() const +{ + return type_ == object_holder_type::OBJECT_PTR; +} + +bool object_holder::is_internal() const +{ + return type_ == object_holder_type::BELONGS_TO || type_ == object_holder_type::HAS_ONE; } bool object_holder::is_inserted() const @@ -187,6 +216,11 @@ unsigned long object_holder::reference_count() const return (proxy_ ? proxy_->reference_counter_ : 0UL); } +object_holder_type object_holder::holder_type() const +{ + return type_; +} + std::ostream& operator<<(std::ostream &out, const object_holder &x) { if (x.proxy_) { diff --git a/src/object/object_inserter.cpp b/src/object/object_inserter.cpp new file mode 100644 index 000000000..61f1f94bc --- /dev/null +++ b/src/object/object_inserter.cpp @@ -0,0 +1,20 @@ +#include "oos/object/object_inserter.hpp" + +namespace oos { +namespace detail { + +object_inserter::object_inserter(object_store &ostore) + : ostore_(ostore) { } + +object_inserter::~object_inserter() { } + +void object_inserter::reset() +{ + object_proxies_.clear(); + while (!object_proxy_stack_.empty()) { + object_proxy_stack_.pop(); + } +} + +} +} diff --git a/src/object/object_proxy.cpp b/src/object/object_proxy.cpp index 4394afdbb..45c953abf 100644 --- a/src/object/object_proxy.cpp +++ b/src/object/object_proxy.cpp @@ -27,10 +27,6 @@ object_proxy::object_proxy(const std::shared_ptr &pk) : primary_key_(pk) {} -//object_proxy::object_proxy(unsigned long i) -// : oid(i) -//{} - object_proxy::~object_proxy() { if (ostore_ && id() > 0) { @@ -42,7 +38,7 @@ object_proxy::~object_proxy() } ostore_ = 0; for (ptr_set_t::iterator i = ptr_set_.begin(); i != ptr_set_.end(); ++i) { - (*i)->proxy_ = 0; + (*i)->proxy_ = nullptr; } } @@ -85,34 +81,6 @@ void object_proxy::unlink() node_ = 0; } -//void object_proxy::link_ref() -//{ -// if (obj_) { -// ++ref_count_; -// } -//} -// -//void object_proxy::unlink_ref() -//{ -// if (obj_) { -// --ref_count_; -// } -//} -// -//void object_proxy::link_ptr() -//{ -// if (obj_) { -// ++ptr_count_; -// } -//} -// -//void object_proxy::unlink_ptr() -//{ -// if (obj_) { -// --ptr_count_; -// } -//} - unsigned long object_proxy::operator++() { return ++reference_counter_; @@ -148,16 +116,6 @@ object_proxy *object_proxy::prev() const return prev_; } -//unsigned long object_proxy::ref_count() const -//{ -// return ref_count_; -//} -// -//unsigned long object_proxy::ptr_count() const -//{ -// return ptr_count_; -//} - unsigned long object_proxy::reference_count() const { return reference_counter_; diff --git a/src/object/object_store.cpp b/src/object/object_store.cpp index 2b8649265..c2a3f07e6 100644 --- a/src/object/object_store.cpp +++ b/src/object/object_store.cpp @@ -17,32 +17,13 @@ #include "oos/object/object_store.hpp" -#include #include -#include using namespace std; using namespace std::placeholders; namespace oos { -namespace detail { - -object_inserter::object_inserter(object_store &ostore) - : ostore_(ostore) { } - -object_inserter::~object_inserter() { } - -void object_inserter::reset() -{ - object_proxies_.clear(); - while (!object_proxy_stack_.empty()) { - object_proxy_stack_.pop(); - } -} - -} - object_store::object_store() : first_(new prototype_node) , last_(new prototype_node) @@ -67,6 +48,9 @@ void object_store::detach(const char *type) if (!node) { throw object_exception("unknown prototype type"); } + + node->on_detach(); + remove_prototype_node(node, node->depth == 0); } @@ -75,6 +59,9 @@ object_store::iterator object_store::detach(const prototype_iterator &i) if (i == end() || i.get() == nullptr) { throw object_exception("invalid prototype iterator"); } + + i->on_detach(); + return remove_prototype_node(i.get(), i->depth == 0); } @@ -128,7 +115,6 @@ void object_store::clear(bool full) while (first != last) { (first++)->clear(false); } -// prototype_tree_.begin()->clear(true); } object_map_.clear(); } @@ -302,6 +288,44 @@ sequencer_impl_ptr object_store::exchange_sequencer(const sequencer_impl_ptr &se return seq_.exchange_sequencer(seq); } +void object_store::on_update_relation_owner(prototype_node::relation_info &info, object_proxy *owner, object_proxy *value) +{ + if (!is_relation_notification_enabled()) { + return; + } + disable_relation_notification(); + info.insert_value(owner, info.name, value); + enable_relation_notification(); +} + +void object_store::on_remove_relation_owner(prototype_node::relation_info &info, object_proxy *owner, object_proxy *value) +{ + if (!is_relation_notification_enabled()) { + return; + } + disable_relation_notification(); + info.remove_value(owner, info.name, value); + enable_relation_notification(); +} + +void object_store::on_append_relation_item(prototype_node &node, object_proxy *owner, object_proxy *value) +{ + auto i = node.relation_info_map_.find(node.type_index()); + if (i == node.relation_info_map_.end()) { + return; + } + on_update_relation_owner(i->second, owner, value); +} + +void object_store::on_remove_relation_item(prototype_node &node, object_proxy *owner, object_proxy *value) +{ + auto i = node.relation_info_map_.find(node.type_index()); + if (i == node.relation_info_map_.end()) { + return; + } + on_remove_relation_owner(i->second, owner, value); +} + prototype_node* object_store::find_prototype_node(const char *type) const { // check for null if (type == 0) { @@ -339,7 +363,8 @@ prototype_node* object_store::find_prototype_node(const char *type) const { } } -prototype_node* object_store::remove_prototype_node(prototype_node *node, bool is_root) { +prototype_node* object_store::remove_prototype_node(prototype_node *node, bool is_root) +{ // remove (and delete) from tree (deletes subsequently all child nodes // for each child call remove_prototype(child); prototype_node *next = node->next_node(node); @@ -397,64 +422,30 @@ void object_store::pop_transaction() transactions_.pop(); } -transaction object_store::current_transaction() -{ - return transactions_.top(); -} - -bool object_store::has_transaction() const +bool object_store::is_relation_notification_enabled() { - return !transactions_.empty(); + return relation_notification_; } -//transaction& object_store::begin_transaction() -//{ -// transactions_.push(transaction(*this)); -// return transactions_.top(); -//} - -//transaction& object_store::begin_transaction(const std::shared_ptr &obsvr) -//{ -// transaction tr(*this, obsvr); -// tr.begin(); -// return transactions_.top(); -//} - -namespace detail { - -void object_deleter::t_object_count::remove(bool notify) +void object_store::enable_relation_notification() { - remove_func(proxy, notify); + relation_notification_ = true; } -object_deleter::iterator object_deleter::begin() +void object_store::disable_relation_notification() { - return object_count_map.begin(); + relation_notification_ = false; } -object_deleter::iterator object_deleter::end() +transaction object_store::current_transaction() { - return object_count_map.end(); + return transactions_.top(); } -bool object_deleter::check_object_count_map() const +bool object_store::has_transaction() const { - // check the reference and pointer counter of collected objects - const_iterator first = object_count_map.begin(); - const_iterator last = object_count_map.end(); - while (first != last) { - if (first->second.ignore) { - ++first; - } else if (first->second.reference_counter == 0) { -// } else if (first->second.ref_count == 0 && first->second.ptr_count == 0) { - ++first; - } else { - return false; - } - } - return true; + return !transactions_.empty(); } -} } diff --git a/src/object/prototype_iterator.cpp b/src/object/prototype_iterator.cpp index 868ca1b9f..b431a9dcc 100644 --- a/src/object/prototype_iterator.cpp +++ b/src/object/prototype_iterator.cpp @@ -6,7 +6,6 @@ namespace oos { prototype_iterator::prototype_iterator() - : node_(0) {} prototype_iterator::prototype_iterator(prototype_iterator::pointer node) diff --git a/src/object/prototype_node.cpp b/src/object/prototype_node.cpp index 376f0fa95..7ab445036 100644 --- a/src/object/prototype_node.cpp +++ b/src/object/prototype_node.cpp @@ -30,7 +30,11 @@ prototype_node::prototype_node() {} prototype_node::~prototype_node() -{} +{ + if (deleter_) { + deleter_(prototype, observer_list); + } +} void prototype_node::initialize(object_store *tree, const char *type, bool abstract) { @@ -329,25 +333,25 @@ basic_identifier *prototype_node::id() const return id_.get(); } -size_t prototype_node::relation_count() const -{ - return relations.size(); -} - -bool prototype_node::has_relation(const std::string &relation_name) const -{ - return relations.find(relation_name) != relations.end(); -} - -size_t prototype_node::foreign_key_count() const -{ - return foreign_keys.size(); -} - -bool prototype_node::has_foreign_key(const std::string &foreign_key_name) const -{ - return foreign_keys.find(foreign_key_name) != foreign_keys.end(); -} +//size_t prototype_node::relation_count() const +//{ +// return relations.size(); +//} +// +//bool prototype_node::has_relation(const std::string &relation_name) const +//{ +// return relations.find(relation_name) != relations.end(); +//} + +//size_t prototype_node::foreign_key_count() const +//{ +// return foreign_keys.size(); +//} +// +//bool prototype_node::has_foreign_key(const std::string &foreign_key_name) const +//{ +// return foreign_keys.find(foreign_key_name) != foreign_keys.end(); +//} bool prototype_node::is_abstract() const { @@ -359,21 +363,6 @@ std::type_index prototype_node::type_index() const return type_index_; } -void prototype_node::register_foreign_key(const char *id, const std::shared_ptr &foreign_key) -{ - foreign_keys.insert(std::make_pair(id, foreign_key)); -} - -void prototype_node::register_relation(const char *type, prototype_node *node, const char *id) -{ - relations.insert(std::make_pair(type, std::make_pair(node, id))); -} - -void prototype_node::prepare_foreign_key(prototype_node *master_node, const char *id) -{ - foreign_key_ids.push_back(std::make_pair(master_node, id)); -} - object_proxy *prototype_node::find_proxy(const std::shared_ptr &pk) { detail::t_identifier_map::iterator i = std::find_if(id_map_.begin(), id_map_.end(), [pk](const detail::t_identifier_map::value_type &x) { @@ -383,6 +372,16 @@ object_proxy *prototype_node::find_proxy(const std::shared_ptr return (i != id_map_.end() ? i->second : nullptr); } +void prototype_node::register_belongs_to(const std::type_index &tindex, const prototype_node::relation_info &relation_info) +{ + relation_info_map_.insert(std::make_pair(tindex, relation_info)); +} + +void prototype_node::register_has_many(const std::type_index &tindex, const prototype_node::relation_info &relation_info) +{ + relation_info_map_.insert(std::make_pair(tindex, relation_info)); +} + /* * adjust the marker of all predeccessor nodes * self and last marker @@ -415,6 +414,16 @@ void prototype_node::adjust_right_marker(prototype_node *root, object_proxy* old } } +bool prototype_node::is_relation_node() const +{ + return is_relation_node_; +} + +const prototype_node::relation_node_info &prototype_node::node_info() const +{ + return relation_node_info_; +} + std::ostream& operator <<(std::ostream &os, const prototype_node &pn) { if (pn.parent) { @@ -437,15 +446,6 @@ std::ostream& operator <<(std::ostream &os, const prototype_node &pn) iop = iop->next(); } os << "|{size|" << i << "}"; - - os << "|{relations}"; - // list relations - prototype_node::field_prototype_map_t::const_iterator first = pn.relations.begin(); - prototype_node::field_prototype_map_t::const_iterator last = pn.relations.end(); - while (first != last) { - os << "|{parent node type|" << first->first << "|node|" << first->second.first->type() << "|foreign field|" << first->second.second << "}"; - ++first; - } os << "}\"]\n"; return os; } diff --git a/src/orm/CMakeLists.txt b/src/orm/CMakeLists.txt index 74c0c0d6d..475018b98 100644 --- a/src/orm/CMakeLists.txt +++ b/src/orm/CMakeLists.txt @@ -12,7 +12,10 @@ SET(HEADER ${CMAKE_SOURCE_DIR}/include/oos/orm/identifier_column_resolver.hpp ${CMAKE_SOURCE_DIR}/include/oos/orm/relation_table.hpp ${CMAKE_SOURCE_DIR}/include/oos/orm/relation_resolver.hpp - ${CMAKE_SOURCE_DIR}/include/oos/orm/relation_item_appender.hpp) + ${CMAKE_SOURCE_DIR}/include/oos/orm/relation_item_appender.hpp + ${CMAKE_SOURCE_DIR}/include/oos/orm/persistence_observer.hpp + ${CMAKE_SOURCE_DIR}/include/oos/orm/persistence_observer.tpp +) ADD_LIBRARY(oos-orm SHARED ${SOURCES} ${HEADER}) diff --git a/src/orm/persistence.cpp b/src/orm/persistence.cpp index cd778f626..39794c459 100644 --- a/src/orm/persistence.cpp +++ b/src/orm/persistence.cpp @@ -19,6 +19,11 @@ persistence::~persistence() connection_.close(); } +void persistence::detach(const char *type) +{ + store_.detach(type); +} + void persistence::create() { for (t_table_map::value_type &val : tables_) { @@ -78,5 +83,4 @@ const connection &persistence::conn() const { return connection_; } - } \ No newline at end of file diff --git a/src/sql/value_column_serializer.cpp b/src/sql/value_column_serializer.cpp index 5f78ed528..cf69aaffb 100644 --- a/src/sql/value_column_serializer.cpp +++ b/src/sql/value_column_serializer.cpp @@ -107,6 +107,8 @@ void value_column_serializer::serialize(const char *id, identifiable_holder &x, { if (x.has_primary_key()) { x.primary_key()->serialize(id, *this); + } else { + cols_->push_back(std::make_shared>(id, new null_value)); } } diff --git a/src/unit/test_suite.cpp b/src/unit/test_suite.cpp index eabdd6840..6f23e6b56 100644 --- a/src/unit/test_suite.cpp +++ b/src/unit/test_suite.cpp @@ -15,15 +15,18 @@ void test_suite::register_unit(unit_test *utest) unit_test_map_.insert(std::make_pair(utest->name(), unit_test_ptr(utest))); } -test_suite::unit_executer::unit_executer(summary &sum) +test_suite::unit_executer::unit_executer(summary &sum, bool q) : succeeded(true) + , quiet(q) , summary_(sum) {} void test_suite::unit_executer::operator()(test_suite::value_type &x) { - std::cout << "[" << x.second->caption() << "]\n"; - bool result = x.second->execute(); + if (!quiet) { + std::cout << "[" << x.second->caption() << "]\n"; + } + bool result = x.second->execute(quiet); std::for_each(x.second->test_func_infos_.begin(), x.second->test_func_infos_.end(), [this](const unit_test::test_func_info &info) { summary_.evaluate(info); }); @@ -124,7 +127,9 @@ bool test_suite::run() if (args_.initialized) { switch (args_.cmd) { case LIST: - std::for_each(unit_test_map_.begin(), unit_test_map_.end(), unit_lister(std::cout, args_.brief)); + if (!args_.quiet) { + std::for_each(unit_test_map_.begin(), unit_test_map_.end(), unit_lister(std::cout, args_.brief)); + } break; case EXECUTE: summary_.reset(); @@ -136,13 +141,17 @@ bool test_suite::run() result = succeeded; } } - std::cout << summary_; + if (!args_.quiet) { + std::cout << summary_; + } return result; } else { // execute all test units - unit_executer ue(summary_); + unit_executer ue(summary_, args_.quiet); std::for_each(unit_test_map_.begin(), unit_test_map_.end(), std::ref(ue)); - std::cout << summary_; + if (!args_.quiet) { + std::cout << summary_; + } return ue.succeeded; } default: @@ -158,11 +167,15 @@ bool test_suite::run(const std::string &unit) { t_unit_test_map::const_iterator i = unit_test_map_.find(unit); if (i == unit_test_map_.end()) { - std::cout << "couldn't find test unit [" << unit << "]\n"; + if (!args_.quiet) { + std::cout << "couldn't find test unit [" << unit << "]\n"; + } return false; } else { - std::cout << "[" << i->second->caption() << "]\n"; - bool succeeded = i->second->execute(); + if (!args_.quiet) { + std::cout << "[" << i->second->caption() << "]\n"; + } + bool succeeded = i->second->execute(args_.quiet); std::for_each(i->second->test_func_infos_.begin(), i->second->test_func_infos_.end(), [this](const unit_test::test_func_info &info) { summary_.evaluate(info); }); @@ -190,10 +203,12 @@ bool test_suite::run(const std::string &unit, const std::string &test) { t_unit_test_map::const_iterator i = unit_test_map_.find(unit); if (i == unit_test_map_.end()) { - std::cout << "couldn't find test unit [" << unit << "]\n"; + if (!args_.quiet) { + std::cout << "couldn't find test unit [" << unit << "]\n"; + } return false; } else { - bool succeeded = i->second->execute(test); + bool succeeded = i->second->execute(test, args_.quiet); auto j = std::find_if(i->second->test_func_infos_.begin(), i->second->test_func_infos_.end(), [test](const unit_test::t_test_func_info_vector::value_type &x) { return x.name == test; }); @@ -236,6 +251,11 @@ const test_suite::test_suite_args& test_suite::test_args() const return args_; } +void test_suite::quiet(bool q) +{ + args_.quiet = q; +} + std::ostream& operator<<(std::ostream& out, const test_suite::summary &s) { out << "summary for " << s.tests << " tests with " << s.asserts << " asserts: (succeeded: " << s.succeeded << "), (failures: " << s.failures << "), (errors: " << s.errors << ")\n"; diff --git a/src/unit/unit_test.cpp b/src/unit/unit_test.cpp index 7844ca1a4..e8855d6ea 100644 --- a/src/unit/unit_test.cpp +++ b/src/unit/unit_test.cpp @@ -47,12 +47,12 @@ std::string unit_test::caption() const return caption_; } -bool unit_test::execute() +bool unit_test::execute(bool quiet) { // execute each test bool succeeded = true; std::for_each(test_func_infos_.begin(), test_func_infos_.end(), [&](t_test_func_info_vector::value_type &info) { - execute(info); + execute(info, quiet); if (succeeded && !info.succeeded) { succeeded = false; } @@ -60,17 +60,19 @@ bool unit_test::execute() return succeeded; } -bool unit_test::execute(const std::string &test) +bool unit_test::execute(const std::string &test, bool quiet) { t_test_func_info_vector::iterator i = std::find_if(test_func_infos_.begin(), test_func_infos_.end(), [test](const t_test_func_info_vector::value_type &x) { return x.name == test; }); if (i == test_func_infos_.end()) { - std::cout << "couldn't find test [" << test << "] of unit [" << caption_ << "]\n"; + if (!quiet) { + std::cout << "couldn't find test [" << test << "] of unit [" << caption_ << "]\n"; + } return false; } else { - execute(*i); + execute(*i, quiet); return i->succeeded; } } @@ -142,10 +144,12 @@ void unit_test::info(const std::string &msg) std::cout << "INFO: " << msg; } -void unit_test::execute(test_func_info &test_info) +void unit_test::execute(test_func_info &test_info, bool quiet) { initialize(); - std::cout << std::left << std::setw(70) << test_info.caption << " ... " << std::flush; + if (!quiet) { + std::cout << std::left << std::setw(70) << test_info.caption << " ... " << std::flush; + } long dur(0L); try { @@ -164,6 +168,9 @@ void unit_test::execute(test_func_info &test_info) test_info.message = ex.what(); } finalize(); + if (quiet) { + return; + } if (test_info.succeeded) { std::cout << "PASS (" << test_info.assertion_count + test_info.error_count << " assertions) (" << (double)(dur)/1000.0 << "ms)\n"; } else { diff --git a/src/utils/date.cpp b/src/utils/date.cpp index 6fe0edfd9..84089969a 100644 --- a/src/utils/date.cpp +++ b/src/utils/date.cpp @@ -171,7 +171,7 @@ date date::parse(const std::string &dstr, const char *format) { struct tm t; detail::strptime(dstr.c_str(), format, &t); - return std::move(date(t.tm_mday, t.tm_mon + 1, t.tm_year + 1900)); + return date(t.tm_mday, t.tm_mon + 1, t.tm_year + 1900); } void date::set(const char *datestr, const char *format) diff --git a/src/utils/time.cpp b/src/utils/time.cpp index 69d32d7d7..5ad85f2b2 100644 --- a/src/utils/time.cpp +++ b/src/utils/time.cpp @@ -191,7 +191,7 @@ time time::parse(const std::string &tstr, const char *format) tv.tv_sec = mktime(&tm); tv.tv_usec = usec; #endif - return std::move(oos::time(tv)); + return oos::time(tv); } void time::set(int year, int month, int day, int hour, int min, int sec, long millis) diff --git a/test/Item.hpp b/test/Item.hpp index 988ec225a..5f4df94aa 100644 --- a/test/Item.hpp +++ b/test/Item.hpp @@ -21,6 +21,7 @@ #include "oos/utils/base_class.hpp" #include "oos/object/object_ptr.hpp" #include "oos/object/has_one.hpp" +#include "oos/object/belongs_to.hpp" #include "oos/object/has_many.hpp" #include "oos/utils/time.hpp" @@ -293,7 +294,7 @@ class person { private: oos::identifier id_; - oos::varchar<256> name_; + oos::varchar<255> name_; oos::date birthdate_; unsigned int height_ = 0; @@ -333,104 +334,65 @@ class person unsigned int height() const { return height_; } void height(unsigned int height) { height_ = height; } }; -/* -class department; + +struct department; class employee : public person { public: - typedef oos::object_ref dep_ref; - -private: - dep_ref dep_; + oos::belongs_to department_; public: employee() {} - employee(const std::string &name_) : person(name_) {} - employee(const std::string &name_, const dep_ref &dep) - : person(name_) - , dep_(dep) + employee(const std::string &name) : person(name, oos::date(17, 12, 1983), 183) {} + employee(const std::string &name, const oos::object_ptr &dep) + : person(name, oos::date(17, 12, 1983), 183) + , department_(dep) {} - virtual void deserialize(oos::deserializer &deserializer) - { - person::deserialize(deserializer); - deserializer.read("department", dep_); - } - virtual void serialize(oos::serializer &serializer) const + template < class SERIALIZER > + void serialize(SERIALIZER &serializer) { - person::serialize(serializer); - serializer.write("department", dep_); + serializer.serialize(*oos::base_class(this)); + serializer.serialize("department", department_, oos::cascade_type::NONE); } - dep_ref dep() const { return dep_; } - void dep(const dep_ref &d) { dep_ = d; } + oos::object_ptr dep() { return department_; } + void dep(const oos::object_ptr &d) { department_ = d; } }; -class department : public oos::serializable +struct department { -public: - typedef oos::object_ref emp_ref; - typedef oos::object_list emp_list_t; - typedef emp_list_t::size_type size_type; - typedef emp_list_t::iterator iterator; - typedef emp_list_t::const_iterator const_iterator; + oos::identifier id; + oos::varchar<255> name; + oos::has_many employees; -private: - oos::identifier id_; - std::string name_; - emp_list_t emp_list_; - -public: - department() - : emp_list_(&employee::dep) - {} - department(const std::string &name) - : name_(name) - , emp_list_(&employee::dep) + department() {} + department(const std::string &n) + : name(n) {} - virtual ~department() {} + ~department() {} - virtual void deserialize(oos::deserializer &deserializer) - { - deserializer.read("id", id_); - deserializer.read("name", name_); - deserializer.read("employee_department", emp_list_); - } - virtual void serialize(oos::serializer &serializer) const - { - serializer.write("id", id_); - serializer.write("name", name_); - serializer.write("employee_department", emp_list_); - } - - unsigned long id() const { return id_.value(); } - std::string name() const { return name_; } - void name(const std::string &name) { name_ = name; } - - void add(const emp_ref &b) + template < class SERIALIZER > + void serialize(SERIALIZER &serializer) { - emp_list_.push_back(b); + serializer.serialize("id", id); + serializer.serialize("name", name); + serializer.serialize("employee" , employees, "department", "id"); + // name of table, container, name of member + // to serialize } - - iterator begin() { return emp_list_.begin(); } - const_iterator begin() const { return emp_list_.begin(); } - - iterator end() { return emp_list_.end(); } - const_iterator end() const { return emp_list_.end(); } - - iterator erase(iterator i) { return emp_list_.erase(i); } - - size_type size() const { return emp_list_.size(); } - bool empty() const { return emp_list_.empty(); } }; -*/ + class course; class student : public person { public: + student() {} + student(const std::string &name, const oos::date &bdate = oos::date(), unsigned h = 170) : person(name, bdate, h) {} + template < class SERIALIZER > void serialize(SERIALIZER &serializer) { @@ -446,6 +408,7 @@ class course public: course() {} + course(const std::string &t) : title(t) {} template < class SERIALIZER > void serialize(SERIALIZER &serializer) diff --git a/test/object/HasManyListUnitTest.cpp b/test/object/HasManyListUnitTest.cpp index 357aa8c4f..05066475b 100644 --- a/test/object/HasManyListUnitTest.cpp +++ b/test/object/HasManyListUnitTest.cpp @@ -14,6 +14,8 @@ HasManyListUnitTest::HasManyListUnitTest() { add_test("join", std::bind(&HasManyListUnitTest::test_join_table, this), "test list join table"); add_test("const_iterator", std::bind(&HasManyListUnitTest::test_const_iterator, this), "test list const iterator"); + add_test("remove_scalar", std::bind(&HasManyListUnitTest::test_remove_scalar, this), "test list remove scalar elements"); + add_test("remove_object", std::bind(&HasManyListUnitTest::test_remove_object, this), "test list remove object elements"); add_test("erase_scalar", std::bind(&HasManyListUnitTest::test_erase_scalar, this), "test list erase scalar elements"); add_test("erase_object", std::bind(&HasManyListUnitTest::test_erase_object, this), "test list erase object elements"); add_test("int", std::bind(&HasManyListUnitTest::test_integer, this), "test list of ints"); @@ -162,6 +164,66 @@ void HasManyListUnitTest::test_erase_object() UNIT_ASSERT_EQUAL(i->name, "i4", "name must be 'i4'"); } +void HasManyListUnitTest::test_remove_scalar() +{ + object_store store; + + store.attach("many_ints"); + + object_ptr mptr = store.insert(new many_ints); + + mptr->ints.push_back(1); + + UNIT_ASSERT_EQUAL(1U, mptr->ints.size(), "size should be 1 (one)"); + + mptr->ints.push_back(7); + mptr->ints.push_back(90); + + UNIT_ASSERT_EQUAL(3U, mptr->ints.size(), "size should be 3 (three)"); + + mptr->ints.remove(1); + + UNIT_ASSERT_EQUAL(2U, mptr->ints.size(), "size should be 2 (two)"); + + mptr->ints.remove_if([](const int &val) { + return val == 90; + }); + + UNIT_ASSERT_EQUAL(1U, mptr->ints.size(), "size should be 1 (one)"); +} + +void HasManyListUnitTest::test_remove_object() +{ + object_store store; + + store.attach("item"); + store.attach("owner"); + + object_ptr o = store.insert(new owner("hans")); + object_ptr i1 = store.insert(new item("i1")); + object_ptr i2 = store.insert(new item("i2")); + + UNIT_ASSERT_EQUAL("i1", i1->name, "name should be 'i1'"); + + o->items.push_back(i1); + + UNIT_ASSERT_EQUAL(1U, o->items.size(), "size should be 1 (one)"); + + o->items.push_back(i2); + + UNIT_ASSERT_EQUAL(2U, o->items.size(), "size should be 2 (two)"); + + o->items.remove(i2); + + UNIT_ASSERT_EQUAL(1U, o->items.size(), "size should be 1 (one)"); + + o->items.remove_if([&i1](const object_ptr &x) { + return i1 == x; + }); + + UNIT_ASSERT_EQUAL(0U, o->items.size(), "size should be 0 (zero)"); + UNIT_ASSERT_TRUE(o->items.empty(), "list should be empty"); +} void HasManyListUnitTest::test_integer() { diff --git a/test/object/HasManyListUnitTest.hpp b/test/object/HasManyListUnitTest.hpp index a91849ab4..1144681e4 100644 --- a/test/object/HasManyListUnitTest.hpp +++ b/test/object/HasManyListUnitTest.hpp @@ -17,6 +17,8 @@ class HasManyListUnitTest : public oos::unit_test void test_const_iterator(); void test_erase_scalar(); void test_erase_object(); + void test_remove_scalar(); + void test_remove_object(); void test_integer(); }; diff --git a/test/object/HasManyVectorUnitTest.cpp b/test/object/HasManyVectorUnitTest.cpp index 594353c79..5623aa3fc 100644 --- a/test/object/HasManyVectorUnitTest.cpp +++ b/test/object/HasManyVectorUnitTest.cpp @@ -14,6 +14,8 @@ HasManyVectorUnitTest::HasManyVectorUnitTest() { add_test("join", std::bind(&HasManyVectorUnitTest::test_join_table, this), "test vector join table"); add_test("const_iterator", std::bind(&HasManyVectorUnitTest::test_const_iterator, this), "test vector const iterator"); + add_test("remove_scalar", std::bind(&HasManyVectorUnitTest::test_remove_scalar, this), "test vector remove scalar elements"); + add_test("remove_object", std::bind(&HasManyVectorUnitTest::test_remove_object, this), "test vector remove object elements"); add_test("erase_scalar", std::bind(&HasManyVectorUnitTest::test_erase_scalar, this), "test vector erase scalar elements"); add_test("erase_object", std::bind(&HasManyVectorUnitTest::test_erase_object, this), "test vector erase object elements"); add_test("int", std::bind(&HasManyVectorUnitTest::test_integer, this), "test vector of ints"); @@ -154,6 +156,54 @@ void HasManyVectorUnitTest::test_erase_object() UNIT_ASSERT_EQUAL(i->name, "i4", "name must be 'i4'"); } +void HasManyVectorUnitTest::test_remove_scalar() +{ + object_store store; + + store.attach("many_ints"); + + object_ptr mptr = store.insert(new many_ints); + + mptr->ints.push_back(1); + + UNIT_ASSERT_EQUAL(1U, mptr->ints.size(), "size should be 1 (one)"); + + mptr->ints.push_back(7); + mptr->ints.push_back(90); + + UNIT_ASSERT_EQUAL(3U, mptr->ints.size(), "size should be 3 (three)"); + + mptr->ints.remove(7); + + UNIT_ASSERT_EQUAL(2U, mptr->ints.size(), "size should be 2 (two)"); +} + +void HasManyVectorUnitTest::test_remove_object() +{ + object_store store; + + store.attach("item"); + store.attach("owner"); + + object_ptr o = store.insert(new owner("hans")); + object_ptr i1 = store.insert(new item("i1")); + object_ptr i2 = store.insert(new item("i2")); + + UNIT_ASSERT_EQUAL("i1", i1->name, "name should be 'i1'"); + + o->items.push_back(i1); + + UNIT_ASSERT_EQUAL(1U, o->items.size(), "size should be 1 (one)"); + + o->items.push_back(i2); + + UNIT_ASSERT_EQUAL(2U, o->items.size(), "size should be 2 (two)"); + + o->items.remove(i1); + + UNIT_ASSERT_EQUAL(1U, o->items.size(), "size should be 1 (one)"); +} + void HasManyVectorUnitTest::test_integer() { object_store store; diff --git a/test/object/HasManyVectorUnitTest.hpp b/test/object/HasManyVectorUnitTest.hpp index 7f94ffd27..963b64f84 100644 --- a/test/object/HasManyVectorUnitTest.hpp +++ b/test/object/HasManyVectorUnitTest.hpp @@ -77,6 +77,8 @@ class HasManyVectorUnitTest : public oos::unit_test void test_const_iterator(); void test_erase_scalar(); void test_erase_object(); + void test_remove_scalar(); + void test_remove_object(); void test_integer(); }; diff --git a/test/object/ObjectStoreTestUnit.cpp b/test/object/ObjectStoreTestUnit.cpp index 2761adaf2..c6b266827 100644 --- a/test/object/ObjectStoreTestUnit.cpp +++ b/test/object/ObjectStoreTestUnit.cpp @@ -16,23 +16,23 @@ using namespace std; ObjectStoreTestUnit::ObjectStoreTestUnit() : unit_test("store", "ObjectStore Test Unit") { - add_test("version", std::bind(&ObjectStoreTestUnit::version_test, this), "test oos version"); - add_test("optr", std::bind(&ObjectStoreTestUnit::optr_test, this), "test optr behaviour"); - add_test("expression", std::bind(&ObjectStoreTestUnit::expression_test, this), "test object expressions"); - add_test("set", std::bind(&ObjectStoreTestUnit::set_test, this), "access object values via set interface"); - add_test("get", std::bind(&ObjectStoreTestUnit::get_test, this), "access object values via get interface"); + add_test("version", std::bind(&ObjectStoreTestUnit::test_version, this), "test oos version"); + add_test("optr", std::bind(&ObjectStoreTestUnit::test_optr, this), "test optr behaviour"); + add_test("expression", std::bind(&ObjectStoreTestUnit::test_expression, this), "test object expressions"); + add_test("set", std::bind(&ObjectStoreTestUnit::test_set, this), "access object values via set interface"); + add_test("get", std::bind(&ObjectStoreTestUnit::test_get, this), "access object values via get interface"); add_test("serializer", std::bind(&ObjectStoreTestUnit::test_serializer, this), "serializer test"); add_test("identifier_serializer", std::bind(&ObjectStoreTestUnit::test_identifier_serializer, this), "identifier serializer test"); - add_test("reference_counter", std::bind(&ObjectStoreTestUnit::reference_counter, this), "reference counter test"); - add_test("simple", std::bind(&ObjectStoreTestUnit::simple_object, this), "create and delete one object"); - add_test("with_sub", std::bind(&ObjectStoreTestUnit::object_with_sub_object, this), "create and delete object with sub object"); - add_test("multiple_simple", std::bind(&ObjectStoreTestUnit::multiple_simple_objects, this), "create and delete multiple objects"); - add_test("multiple_object_with_sub", std::bind(&ObjectStoreTestUnit::multiple_object_with_sub_objects, this), "create and delete multiple objects with sub object"); - add_test("delete", std::bind(&ObjectStoreTestUnit::delete_object, this), "object deletion test"); - add_test("hierarchy", std::bind(&ObjectStoreTestUnit::hierarchy, this), "object hierarchy test"); - add_test("view", std::bind(&ObjectStoreTestUnit::view_test, this), "object view test"); - add_test("clear", std::bind(&ObjectStoreTestUnit::clear_test, this), "object store clear test"); - add_test("generic", std::bind(&ObjectStoreTestUnit::generic_test, this), "generic object access test"); + add_test("reference_counter", std::bind(&ObjectStoreTestUnit::test_reference_counter, this), "reference counter test"); + add_test("simple", std::bind(&ObjectStoreTestUnit::test_simple_object, this), "create and delete one object"); + add_test("with_sub", std::bind(&ObjectStoreTestUnit::test_object_with_sub_object, this), "create and delete object with sub object"); + add_test("multiple_simple", std::bind(&ObjectStoreTestUnit::test_multiple_simple_objects, this), "create and delete multiple objects"); + add_test("multiple_object_with_sub", std::bind(&ObjectStoreTestUnit::test_multiple_object_with_sub_objects, this), "create and delete multiple objects with sub object"); + add_test("delete", std::bind(&ObjectStoreTestUnit::test_delete_object, this), "object deletion test"); + add_test("hierarchy", std::bind(&ObjectStoreTestUnit::test_hierarchy, this), "object hierarchy test"); + add_test("view", std::bind(&ObjectStoreTestUnit::test_view, this), "object view test"); + add_test("clear", std::bind(&ObjectStoreTestUnit::test_clear, this), "object store clear test"); + add_test("generic", std::bind(&ObjectStoreTestUnit::test_generic, this), "generic object access test"); add_test("structure", std::bind(&ObjectStoreTestUnit::test_structure, this), "object transient structure test"); add_test("structure_cyclic", std::bind(&ObjectStoreTestUnit::test_structure_cyclic, this), "object transient cyclic structure test"); add_test("structure_container", std::bind(&ObjectStoreTestUnit::test_structure_container, this), "object transient container structure test"); @@ -41,8 +41,9 @@ ObjectStoreTestUnit::ObjectStoreTestUnit() add_test("remove", std::bind(&ObjectStoreTestUnit::test_remove, this), "object remove test"); add_test("pk", std::bind(&ObjectStoreTestUnit::test_primary_key, this), "object proxy primary key test"); add_test("has_many", std::bind(&ObjectStoreTestUnit::test_has_many, this), "has many test"); -// add_test("has_many_to_many", std::bind(&ObjectStoreTestUnit::test_has_many_to_many, this), "has many to many test"); - add_test("on_attach", std::bind(&ObjectStoreTestUnit::test_on_attach, this), "test on attach callback"); + add_test("has_many_to_many", std::bind(&ObjectStoreTestUnit::test_has_many_to_many, this), "has many to many test"); + add_test("belongs_to", std::bind(&ObjectStoreTestUnit::test_belongs_to, this), "test belongs to behaviour"); + add_test("observer", std::bind(&ObjectStoreTestUnit::test_observer, this), "test observer functionality"); } struct basic_test_pair @@ -77,16 +78,7 @@ struct test_pair : public basic_test_pair size_t size; }; -void -ObjectStoreTestUnit::initialize() -{ - ostore_.attach("item"); - ostore_.attach >("object_item"); - ostore_.attach("object_item_ptr_list"); -} - -void -ObjectStoreTestUnit::finalize() +void ObjectStoreTestUnit::finalize() { ostore_.clear(true); } @@ -99,8 +91,7 @@ struct item_counter : public std::unary_function("item"); + typedef object_ptr item_ptr; item_ptr item_null; @@ -129,9 +122,12 @@ void ObjectStoreTestUnit::optr_test() UNIT_ASSERT_NOT_NULL(item.store(), "item must be internal"); } -void -ObjectStoreTestUnit::expression_test() +void ObjectStoreTestUnit::test_expression() { + ostore_.attach("item"); + ostore_.attach >("object_item"); + ostore_.attach("object_item_ptr_list"); + typedef object_ptr > object_item_ptr; typedef object_ptr item_ptr; typedef object_ptr itemlist_ptr; @@ -161,7 +157,7 @@ ObjectStoreTestUnit::expression_test() std::unique_ptr exp(make_expression(z == 4)); - object_ptr> optr = itemlist->begin().get(); + object_ptr> optr = *itemlist->begin(); (*exp)(optr); @@ -203,8 +199,7 @@ ObjectStoreTestUnit::expression_test() UNIT_ASSERT_EQUAL((*j)->get_string(), "ObjectItem", "couldn't find item 'ObjectItem'"); } -void -ObjectStoreTestUnit::test_serializer() +void ObjectStoreTestUnit::test_serializer() { char c = 'c'; float f = 1.55f; @@ -307,8 +302,11 @@ void ObjectStoreTestUnit::test_identifier_serializer() } } -void ObjectStoreTestUnit::reference_counter() +void ObjectStoreTestUnit::test_reference_counter() { + ostore_.attach("item"); + ostore_.attach >("object_item"); + Item *i = new Item("Item", 7); typedef object_ptr item_ptr; @@ -339,8 +337,7 @@ void ObjectStoreTestUnit::reference_counter() } -void -ObjectStoreTestUnit::set_test() +void ObjectStoreTestUnit::test_set() { oos::date dt(15, 9, 1972); oos::time t(2008, 12, 27, 13, 6, 57, 4711); @@ -390,8 +387,7 @@ ObjectStoreTestUnit::set_test() // UNIT_ASSERT_EQUAL("elefant", i.get_string(), "invalid value"); } -void -ObjectStoreTestUnit::get_test() +void ObjectStoreTestUnit::test_get() { test_pair c('c'); test_pair b(true); @@ -459,9 +455,10 @@ ObjectStoreTestUnit::get_test() UNIT_ASSERT_EQUAL(timeval.result, timeval.expected, "invalid value"); } -void -ObjectStoreTestUnit::simple_object() +void ObjectStoreTestUnit::test_simple_object() { + ostore_.attach("item"); + Item *a = ostore_.create(); UNIT_ASSERT_NOT_NULL(a, "couldn't create object of type "); @@ -477,9 +474,11 @@ ObjectStoreTestUnit::simple_object() ostore_.remove(simple); } -void -ObjectStoreTestUnit::object_with_sub_object() +void ObjectStoreTestUnit::test_object_with_sub_object() { + ostore_.attach("item"); + ostore_.attach >("object_item"); + ObjectItem *s = ostore_.create>(); UNIT_ASSERT_NOT_NULL(s, "couldn't create object of type "); @@ -500,9 +499,10 @@ ObjectStoreTestUnit::object_with_sub_object() ostore_.remove(ows); } -void -ObjectStoreTestUnit::multiple_simple_objects() +void ObjectStoreTestUnit::test_multiple_simple_objects() { + ostore_.attach("item"); + typedef object_ptr item_ptr; size_t elem_size = 10; @@ -521,9 +521,11 @@ ObjectStoreTestUnit::multiple_simple_objects() UNIT_ASSERT_EQUAL(elem_size, simple_view.size(), "expected size of view isn't 10"); } -void -ObjectStoreTestUnit::multiple_object_with_sub_objects() +void ObjectStoreTestUnit::test_multiple_object_with_sub_objects() { + ostore_.attach("item"); + ostore_.attach >("object_item"); + typedef object_ptr > ows_ptr; // create 10 objects @@ -542,9 +544,11 @@ ObjectStoreTestUnit::multiple_object_with_sub_objects() UNIT_ASSERT_EQUAL(elem_size, withsub_view.size(), "expected size of view isn't 10"); } -void -ObjectStoreTestUnit::delete_object() +void ObjectStoreTestUnit::test_delete_object() { + ostore_.attach("item"); + ostore_.attach >("object_item"); + typedef ObjectItem TestItem; typedef object_ptr test_item_ptr; typedef object_ptr item_ptr; @@ -575,9 +579,9 @@ ObjectStoreTestUnit::delete_object() ostore_.remove(item); } -void -ObjectStoreTestUnit::hierarchy() +void ObjectStoreTestUnit::test_hierarchy() { + ostore_.attach("item"); ostore_.attach("ITEM_A"); ostore_.attach("ITEM_B"); ostore_.attach("ITEM_C"); @@ -671,9 +675,10 @@ ObjectStoreTestUnit::hierarchy() } } -void -ObjectStoreTestUnit::view_test() +void ObjectStoreTestUnit::test_view() { + ostore_.attach("item"); + for (int i = 0; i < 10; ++i) { std::stringstream str; str << "Item " << i+1; @@ -697,9 +702,10 @@ ObjectStoreTestUnit::view_test() UNIT_ASSERT_GREATER(item->id(), 0UL, "invalid item"); } -void -ObjectStoreTestUnit::clear_test() +void ObjectStoreTestUnit::test_clear() { + ostore_.attach("item"); + for (int i = 0; i < 10; ++i) { std::stringstream str; str << "Item " << i+1; @@ -733,8 +739,7 @@ ObjectStoreTestUnit::clear_test() UNIT_ASSERT_TRUE(first == last, "prototype iterator must be the same"); } -void -ObjectStoreTestUnit::generic_test() +void ObjectStoreTestUnit::test_generic() { test_pair c('c'); test_pair b(true); @@ -828,10 +833,42 @@ ObjectStoreTestUnit::generic_test() oos::set(*item, "val_time", timeval.expected); oos::get(*item, "val_time", timeval.result); UNIT_ASSERT_EQUAL(timeval.result, timeval.expected, "not expected result value"); + + master m1("master 1"); + + object_ptr c1(new child("child 1")); + object_ptr child_result; + + oos::set(m1, "child", c1); + oos::get(m1, "child", child_result); + UNIT_ASSERT_EQUAL(c1->name, child_result->name, "not expected result value"); + + oos::set(m1, "child", object_ptr()); + oos::get(m1, "child", child_result); + UNIT_ASSERT_TRUE(child_result.get() == nullptr, "not expected result value"); + + children_vector cv("children vector"); + + UNIT_ASSERT_TRUE(cv.children.empty(), "vector must be empty"); + oos::append(cv, "children", c1); + UNIT_ASSERT_FALSE(cv.children.empty(), "vector must not be empty"); + oos::remove(cv, "children", c1); + UNIT_ASSERT_TRUE(cv.children.empty(), "vector must be empty"); + // Todo: oos::remove, oos::begin, oos::end, oos::size, oos::empty for generic access has_many + // see: https://tartanllama.github.io/c++/2017/01/03/deduction-on-the-left/ for begin and end +// +// auto i = oos::begin(cv, "children"); +// auto i = oos::end(cv, "children"); +// +// auto size = oos::size(cv, "children"); +// bool is_empty = oos::empty(cv, "children"); } void ObjectStoreTestUnit::test_structure() { + ostore_.attach("item"); + ostore_.attach >("object_item"); + typedef ObjectItem object_item_t; typedef object_ptr object_item_ptr; typedef object_ptr item_ptr; @@ -932,6 +969,8 @@ void ObjectStoreTestUnit::test_structure_container() void ObjectStoreTestUnit::test_transient_optr() { + ostore_.attach("item"); + typedef object_ptr item_ptr; item_ptr item(new Item("item", 5)); @@ -943,6 +982,8 @@ void ObjectStoreTestUnit::test_transient_optr() void ObjectStoreTestUnit::test_insert() { + ostore_.attach("item"); + UNIT_ASSERT_EXCEPTION(ostore_.insert((Item *)nullptr), object_exception, "object is null", "null shouldn't be insertable"); std::unique_ptr ic(new ItemC); @@ -955,6 +996,8 @@ void ObjectStoreTestUnit::test_insert() void ObjectStoreTestUnit::test_remove() { + ostore_.attach("item"); + typedef object_ptr item_ptr; item_ptr item; @@ -1000,69 +1043,157 @@ void ObjectStoreTestUnit::test_has_many_to_many() ostore_.attach("course"); UNIT_ASSERT_EQUAL(4UL, ostore_.size(), "unexpected size"); + + auto george = ostore_.insert(new student("george")); + auto jane = ostore_.insert(new student("jane")); + auto algebra = ostore_.insert(new course("algebra")); + auto art = ostore_.insert(new course("art")); + + UNIT_ASSERT_TRUE(george->courses.empty(), "georges courses must be empty"); + UNIT_ASSERT_TRUE(jane->courses.empty(), "janes courses must be empty"); + UNIT_ASSERT_TRUE(algebra->students.empty(), "there must be no students in algebra"); + UNIT_ASSERT_TRUE(art->students.empty(), "there must be no students in art"); + + art->students.push_back(jane); + + UNIT_ASSERT_FALSE(art->students.empty(), "there must not be students in art"); + UNIT_ASSERT_EQUAL(art->students.size(), 1UL, "there must be one student in art course"); + UNIT_ASSERT_EQUAL(art->students.front()->name(), jane->name(), "arts student must be jane"); + UNIT_ASSERT_FALSE(jane->courses.empty(), "janes courses must not be empty"); + UNIT_ASSERT_EQUAL(jane->courses.size(), 1UL, "jane must've took one course"); + UNIT_ASSERT_EQUAL(jane->courses.front()->title, art->title, "janes course must be art"); + + jane->courses.erase(jane->courses.begin()); + + UNIT_ASSERT_TRUE(jane->courses.empty(), "janes courses must be empty"); + UNIT_ASSERT_TRUE(art->students.empty(), "there must be no students in art"); + + george->courses.push_back(algebra); + + UNIT_ASSERT_FALSE(algebra->students.empty(), "there must not be students in algebra"); + UNIT_ASSERT_EQUAL(algebra->students.size(), 1UL, "there must be one student in algebra course"); + UNIT_ASSERT_EQUAL(algebra->students.front()->name(), george->name(), "algebras student must be george"); + UNIT_ASSERT_FALSE(george->courses.empty(), "georges courses must not be empty"); + UNIT_ASSERT_EQUAL(george->courses.size(), 1UL, "george must've took one course"); + UNIT_ASSERT_EQUAL(george->courses.front()->title, algebra->title, "georges course must be algebra"); + + algebra->students.clear(); + + UNIT_ASSERT_TRUE(george->courses.empty(), "georges courses must be empty"); + UNIT_ASSERT_TRUE(algebra->students.empty(), "there must be no students in algebra"); } -std::vector table_names = {}; +void ObjectStoreTestUnit::test_belongs_to() +{ + ostore_.attach("person"); + ostore_.attach("department"); + ostore_.attach("employee"); + + // expected prototypes + // person, employee and department + + auto george = ostore_.insert(new employee("george")); + auto jane = ostore_.insert(new employee("jane")); + auto dep = ostore_.insert(new department("insurance")); + + UNIT_ASSERT_TRUE(dep->employees.empty(), "there must be no employees"); + UNIT_ASSERT_TRUE(george->dep().empty(), "there must not be an department"); + UNIT_ASSERT_TRUE(jane->dep().empty(), "there must not be an department"); + + // department is automatically set + dep->employees.push_back(george); + + UNIT_ASSERT_EQUAL(dep->employees.size(), 1UL, "there must be one employee"); + UNIT_ASSERT_EQUAL(dep->employees.front()->name(), "george", "expected name must be george"); + UNIT_ASSERT_FALSE(george->dep().empty(), "department must not be empty"); + UNIT_ASSERT_EQUAL(george->dep()->name, dep->name, "names must be equal"); + + // jane is automatically added to deps employee list + jane->dep(dep); + + UNIT_ASSERT_EQUAL(dep->employees.size(), 2UL, "there must be two employees"); + + // remove george + auto i = dep->employees.begin(); + i = dep->employees.erase(i); -struct on_attach_base : public oos::detail::basic_on_attach + UNIT_ASSERT_EQUAL(dep->employees.size(), 1UL, "there must be one employee"); + UNIT_ASSERT_TRUE(george->dep().empty(), "there must not be an department"); + UNIT_ASSERT_EQUAL(dep->employees.front()->name(), "jane", "expected name must be jane"); + + jane->department_.clear(); + + UNIT_ASSERT_TRUE(dep->employees.empty(), "there must be no employees"); +} + +struct basic_logger { - std::string name; - on_attach_base() {} - on_attach_base(const std::string &n) : name(n) {} - on_attach_base& operator=(const on_attach_base &x) { name = x.name; return *this; } + static std::vector nodes; }; +std::vector basic_logger::nodes = std::vector(); + template < class T > -struct on_attach : public on_attach_base +struct logger : public object_store_observer, public basic_logger { - using on_attach_base::on_attach_base; - - on_attach() {} - - template < class V > - on_attach(const on_attach &x) : on_attach_base(x.name) {} + logger() {} template < class V > - on_attach& operator=(const on_attach &x) + logger(const logger *) {} + void on_attach(prototype_node &node, T &) override { - name = x.name; return *this; + nodes.push_back(node.type()); } - void operator()(prototype_node *node) const + void on_detach(prototype_node &node, T &) override { - table_names.push_back(node->type()); + nodes.push_back(node.type()); } + + void on_insert(object_proxy &) override {} + void on_update(object_proxy &) override {} + void on_delete(object_proxy &) override {} }; -//template <> -template < class T > -struct on_attach> : public on_attach_base +void ObjectStoreTestUnit::test_observer() { - using on_attach_base::on_attach_base; + ostore_.attach("person", { new logger }); + ostore_.attach("employee", { new logger }); + ostore_.attach("department", { new logger }); + ostore_.attach("book", { new logger }); + ostore_.attach("book_list", { new logger }); - typedef has_many_item type; + std::vector result({ + "person", + "employee", + "department", + "book", + "books", + "book_list" + }); - template < class V > - on_attach(const on_attach &x) : on_attach_base(x.name) {} - - template < class V > - on_attach& operator=(const on_attach &x) { name = x.name; return *this; } - - void operator()(prototype_node *node) const - { - table_names.push_back(node->type()); - } -}; + UNIT_ASSERT_TRUE(basic_logger::nodes == result, "vectors must be equal"); -void ObjectStoreTestUnit::test_on_attach() -{ - ostore_.attach("book", false, nullptr); - ostore_.attach("book_list", false, nullptr); + basic_logger::nodes.clear(); - UNIT_ASSERT_EQUAL(3UL, table_names.size(), "size mustbe three"); + ostore_.clear(true); - UNIT_ASSERT_EQUAL("book", table_names[0], "type must be book"); - UNIT_ASSERT_EQUAL("book_list", table_names[1], "type must be book_list"); - UNIT_ASSERT_EQUAL("books", table_names[2], "type must be books"); + ostore_.attach("person", { new logger }); + ostore_.attach("department", { new logger }); + ostore_.attach("employee", { new logger }); + ostore_.attach("book_list", { new logger }); + ostore_.attach("book", { new logger }); + + result = { + "person", + "employee", + "department", + "employee", + "employee", + "books", + "book_list", + "book" + }; + + UNIT_ASSERT_TRUE(basic_logger::nodes == result, "vectors must be equal"); } - diff --git a/test/object/ObjectStoreTestUnit.hpp b/test/object/ObjectStoreTestUnit.hpp index 79759dc7b..6161d8c6c 100644 --- a/test/object/ObjectStoreTestUnit.hpp +++ b/test/object/ObjectStoreTestUnit.hpp @@ -13,26 +13,25 @@ class ObjectStoreTestUnit : public oos::unit_test public: ObjectStoreTestUnit(); - virtual void initialize(); virtual void finalize(); - void version_test(); - void optr_test(); - void expression_test(); - void set_test(); - void get_test(); + void test_version(); + void test_optr(); + void test_expression(); + void test_set(); + void test_get(); void test_serializer(); void test_identifier_serializer(); - void reference_counter(); - void simple_object(); - void object_with_sub_object(); - void multiple_simple_objects(); - void multiple_object_with_sub_objects(); - void delete_object(); - void hierarchy(); - void view_test(); - void clear_test(); - void generic_test(); + void test_reference_counter(); + void test_simple_object(); + void test_object_with_sub_object(); + void test_multiple_simple_objects(); + void test_multiple_object_with_sub_objects(); + void test_delete_object(); + void test_hierarchy(); + void test_view(); + void test_clear(); + void test_generic(); void test_structure(); void test_structure_cyclic(); void test_structure_container(); @@ -42,7 +41,8 @@ class ObjectStoreTestUnit : public oos::unit_test void test_primary_key(); void test_has_many(); void test_has_many_to_many(); - void test_on_attach(); + void test_belongs_to(); + void test_observer(); private: oos::object_store ostore_; diff --git a/test/object/PrototypeTreeTest.cpp b/test/object/PrototypeTreeTest.cpp index 9576553f9..6d9d9959f 100644 --- a/test/object/PrototypeTreeTest.cpp +++ b/test/object/PrototypeTreeTest.cpp @@ -23,7 +23,6 @@ PrototypeTreeTestUnit::PrototypeTreeTestUnit() add_test("child_of", std::bind(&PrototypeTreeTestUnit::test_child_of, this), "test child of element"); add_test("traverse", std::bind(&PrototypeTreeTestUnit::test_traverse, this), "test traversing the prototype tree"); add_test("const_traverse", std::bind(&PrototypeTreeTestUnit::test_const_traverse, this), "test const traversing the prototype tree"); - add_test("relations", std::bind(&PrototypeTreeTestUnit::test_relations, this), "test relations"); } void PrototypeTreeTestUnit::test_empty() @@ -242,53 +241,3 @@ void PrototypeTreeTestUnit::test_const_traverse() UNIT_ASSERT_TRUE(first == ptree.begin(), "expected prototype iterator to be begin()"); } - -void PrototypeTreeTestUnit::test_relations() -{ - object_store ptree; - - object_store::const_iterator children_vector_node = ptree.attach("children_vector"); - object_store::const_iterator master_node = ptree.attach("master"); - object_store::const_iterator child_node = ptree.attach("child"); - - // get the relation table for children - // holding id of children_vector (container) - // and id of child (value) - object_store::const_iterator children_node = ptree.find("children"); - - UNIT_ASSERT_EQUAL(child_node->relation_count(), 0UL, "relations must be empty"); - UNIT_ASSERT_EQUAL(child_node->foreign_key_count(), 0UL, "foreign keys must be empty"); - - UNIT_ASSERT_EQUAL(children_vector_node->relation_count(), 0UL, "relations must be empty"); - UNIT_ASSERT_EQUAL(children_vector_node->foreign_key_count(), 0UL, "foreign keys must be empty"); - - UNIT_ASSERT_EQUAL(master_node->relation_count(), 0UL, "relations must be empty"); - UNIT_ASSERT_GREATER(master_node->foreign_key_count(), 0UL, "foreign key must not be empty"); - UNIT_ASSERT_EQUAL(master_node->foreign_key_count(), 1UL, "there must be one foreign key"); - -// prototype_node::t_foreign_key_map::const_iterator j = master_node->foreign_keys.find("child"); - UNIT_ASSERT_TRUE(master_node->has_foreign_key("child"), "iterator must not be end"); -// UNIT_ASSERT_FALSE(j == master_node->foreign_keys.end(), "iterator must not be end"); - - UNIT_ASSERT_NOT_EQUAL(children_node->relation_count(), 0UL, "relations must not be empty"); - UNIT_ASSERT_EQUAL(children_node->relation_count(), 1UL, "there must be one relation"); -// prototype_node::field_prototype_map_t::const_iterator i = children_node->relations.find("children_vector"); - UNIT_ASSERT_TRUE(children_node->has_relation("children_vector"), "iterator must not be end"); -// UNIT_ASSERT_FALSE(i == children_node->relations.end(), "iterator must not be end"); - - UNIT_ASSERT_NOT_EQUAL(children_node->foreign_key_count(), 0UL, "foreign keys must not be empty"); - // Todo: foreign key maybe take the id of the parent node in account - UNIT_ASSERT_EQUAL(children_node->foreign_key_count(), 1UL, "there must be one foreign keys"); -// UNIT_ASSERT_EQUAL(children_node->foreign_key_count(), 2UL, "there must be two foreign keys"); -// j = children_node->foreign_keys.find("container"); -// UNIT_ASSERT_TRUE(children_node->has_foreign_key("owner_id"), "iterator must not be end"); -// j = children_node->foreign_keys.find("value"); -// UNIT_ASSERT_FALSE(children_node->has_foreign_key("item_id"), "foreign item key must not be 'item_id'"); -// UNIT_ASSERT_TRUE(children_node->has_foreign_key("child_id"), "foreign item key must be 'child_id'"); - - std::ofstream out("graph.dot", ios::out | ios::trunc); - - ptree.dump(out); - - out.close(); -} diff --git a/test/object/PrototypeTreeTest.hpp b/test/object/PrototypeTreeTest.hpp index 65f2af602..46dfd447d 100644 --- a/test/object/PrototypeTreeTest.hpp +++ b/test/object/PrototypeTreeTest.hpp @@ -20,7 +20,6 @@ class PrototypeTreeTestUnit : public oos::unit_test void test_child_of(); void test_traverse(); void test_const_traverse(); - void test_relations(); }; #endif /* PROTOTYPE_TREE_TESTUNIT_HPP */ diff --git a/test/orm/OrmTestUnit.cpp b/test/orm/OrmTestUnit.cpp index 4e3c4e3d9..2a36d59f8 100644 --- a/test/orm/OrmTestUnit.cpp +++ b/test/orm/OrmTestUnit.cpp @@ -18,16 +18,17 @@ OrmTestUnit::OrmTestUnit(const std::string &prefix, const std::string &dns) : unit_test(prefix + "_orm", prefix + " orm test unit") , dns_(dns) { - add_test("create", std::bind(&OrmTestUnit::test_create, this), "test orm create table"); - add_test("insert", std::bind(&OrmTestUnit::test_insert, this), "test orm insert into table"); - add_test("select", std::bind(&OrmTestUnit::test_select, this), "test orm select a table"); - add_test("update", std::bind(&OrmTestUnit::test_update, this), "test orm update on table"); - add_test("delete", std::bind(&OrmTestUnit::test_delete, this), "test orm delete from table"); - add_test("load", std::bind(&OrmTestUnit::test_load, this), "test orm load from table"); - add_test("load_has_one", std::bind(&OrmTestUnit::test_load_has_one, this), "test orm load has one relation from table"); - add_test("load_has_many", std::bind(&OrmTestUnit::test_load_has_many, this), "test orm load has many from table"); - add_test("load_has_many_int", std::bind(&OrmTestUnit::test_load_has_many_int, this), "test orm load has many int from table"); - add_test("has_many_delete", std::bind(&OrmTestUnit::test_has_many_delete, this), "test orm has many delete item"); + add_test("create", std::bind(&OrmTestUnit::test_create, this), "test create table"); + add_test("insert", std::bind(&OrmTestUnit::test_insert, this), "test insert into table"); + add_test("select", std::bind(&OrmTestUnit::test_select, this), "test select a table"); + add_test("update", std::bind(&OrmTestUnit::test_update, this), "test update on table"); + add_test("delete", std::bind(&OrmTestUnit::test_delete, this), "test delete from table"); + add_test("load", std::bind(&OrmTestUnit::test_load, this), "test load from table"); + add_test("load_has_one", std::bind(&OrmTestUnit::test_load_has_one, this), "test load has one relation from table"); + add_test("load_has_many", std::bind(&OrmTestUnit::test_load_has_many, this), "test load has many from table"); + add_test("load_has_many_int", std::bind(&OrmTestUnit::test_load_has_many_int, this), "test load has many int from table"); + add_test("has_many_delete", std::bind(&OrmTestUnit::test_has_many_delete, this), "test has many delete item"); + add_test("belongs_to", std::bind(&OrmTestUnit::test_belongs_to, this), "test belongs to"); } void OrmTestUnit::test_create() @@ -99,7 +100,7 @@ void OrmTestUnit::test_select() for (std::string name : names) { auto pptr = s.insert(new person(name, oos::date(18, 5, 1980), 180)); - UNIT_EXPECT_GREATER(pptr->id(), 0UL, "is must be greater zero"); + UNIT_EXPECT_GREATER(pptr->id(), 0UL, "is must be greater zero"); } auto view = s.select(); @@ -442,3 +443,52 @@ void OrmTestUnit::test_has_many_delete() p.drop(); } + +void OrmTestUnit::test_belongs_to() +{ + oos::persistence p(dns_); + + p.attach("person"); + p.attach("department"); + p.attach("employee"); + + p.create(); + + oos::session s(p); + + auto george = s.insert(new employee("george")); + auto jane = s.insert(new employee("jane")); + auto dep = s.insert(new department("insurance")); + + UNIT_ASSERT_TRUE(dep->employees.empty(), "there must be no employees"); + UNIT_ASSERT_TRUE(george->dep().empty(), "there must not be an department"); + UNIT_ASSERT_TRUE(jane->dep().empty(), "there must not be an department"); + + // department is automatically set + s.push_back(dep->employees, george); + + UNIT_ASSERT_EQUAL(dep->employees.size(), 1UL, "there must be one employee"); + UNIT_ASSERT_EQUAL(dep->employees.front()->name(), "george", "expected name must be george"); + UNIT_ASSERT_FALSE(george->dep().empty(), "department must not be empty"); + UNIT_ASSERT_EQUAL(george->dep()->name, dep->name, "names must be equal"); + + // jane is automatically added to deps employee list + jane->dep(dep); + s.update(jane); + + UNIT_ASSERT_EQUAL(dep->employees.size(), 2UL, "there must be two employees"); + + // remove george + s.erase(dep->employees, dep->employees.begin()); + + UNIT_ASSERT_EQUAL(dep->employees.size(), 1UL, "there must be one employee"); + UNIT_ASSERT_TRUE(george->dep().empty(), "there must not be an department"); + UNIT_ASSERT_EQUAL(dep->employees.front()->name(), "jane", "expected name must be jane"); +// + jane->department_.clear(); + s.update(jane); +// + UNIT_ASSERT_TRUE(dep->employees.empty(), "there must be no employees"); + + p.drop(); +} diff --git a/test/orm/OrmTestUnit.hpp b/test/orm/OrmTestUnit.hpp index dbc928fb1..975b6045c 100644 --- a/test/orm/OrmTestUnit.hpp +++ b/test/orm/OrmTestUnit.hpp @@ -23,6 +23,7 @@ class OrmTestUnit : public oos::unit_test void test_load_has_many(); void test_load_has_many_int(); void test_has_many_delete(); + void test_belongs_to(); private: std::string dns_; diff --git a/test/sql/QueryTestUnit.cpp b/test/sql/QueryTestUnit.cpp index bfd32ef0d..8f53adc11 100644 --- a/test/sql/QueryTestUnit.cpp +++ b/test/sql/QueryTestUnit.cpp @@ -23,6 +23,7 @@ QueryTestUnit::QueryTestUnit(const std::string &name, const std::string &msg, co add_test("bind_tablename", std::bind(&QueryTestUnit::test_bind_tablename, this), "test bind tablenames"); add_test("describe", std::bind(&QueryTestUnit::test_describe, this), "test describe table"); add_test("identifier", std::bind(&QueryTestUnit::test_identifier, this), "test sql identifier"); + add_test("update", std::bind(&QueryTestUnit::test_update, this), "test direct sql update statement"); add_test("create", std::bind(&QueryTestUnit::test_create, this), "test direct sql create statement"); add_test("create_anonymous", std::bind(&QueryTestUnit::test_anonymous_create, this), "test direct sql create statement via row (anonymous)"); add_test("insert_anonymous", std::bind(&QueryTestUnit::test_anonymous_insert, this), "test direct sql insert statement via row (anonymous)"); @@ -375,6 +376,56 @@ void QueryTestUnit::test_create() res = q.drop().execute(connection_); } +void QueryTestUnit::test_update() +{ + connection_.open(); + + query q(connection_, "person"); + + // create item table and insert item + result res(q.create().execute()); + + std::vector names({ "hans", "otto", "georg", "hilde" }); + + unsigned long id(0); + for (std::string name : names) { + person p(name, oos::date(12, 3, 1980), 180); + p.id(++id); + q.insert(p).execute(); + } + + column name("name"); + res = q.select().where(name == "hans").execute(); + + auto first = res.begin(); + auto last = res.end(); + + while (first != last) { + std::unique_ptr item(first.release()); + + UNIT_ASSERT_EQUAL(item->name(), "hans", "expected name must be 'Hans'"); + UNIT_ASSERT_EQUAL(item->height(), 180U, "expected height must be 180"); + UNIT_ASSERT_EQUAL(item->birthdate(), oos::date(12, 3, 1980), "expected birthdate is 12.3.1980"); + + ++first; + } + + person hans("hans", oos::date(15, 6, 1990), 165); + hans.id(1); + column idcol("id"); + q.update(hans).where(idcol == 1).execute(); + + res = q.select().where(name == "hans").execute(); + + for (auto i : res) { + UNIT_ASSERT_EQUAL(i->name(), "hans", "expected name must be 'Hans'"); + UNIT_ASSERT_EQUAL(i->height(), 165U, "expected height must be 180"); + UNIT_ASSERT_EQUAL(i->birthdate(), oos::date(15, 6, 1990), "expected birthdate is 12.3.1980"); + } + + q.drop().execute(); +} + void QueryTestUnit::test_anonymous_create() { connection_.open(); @@ -514,23 +565,27 @@ void QueryTestUnit::test_statement_update() { connection_.open(); - query q("item"); + query q(connection_, "person"); - statement stmt(q.create().prepare(connection_)); + statement stmt(q.create().prepare()); - result res(stmt.execute()); + result res(stmt.execute()); - oos::identifier id(23); - auto itime = time_val_; - Item hans("Hans", 4711); - hans.id(id.value()); - hans.set_time(itime); - stmt = q.insert(hans).prepare(connection_); + std::vector names({ "hans", "otto", "georg", "hilde" }); - stmt.bind(0, &hans); - res = stmt.execute(); + unsigned long id(0); + for (std::string name : names) { + person p(name, oos::date(12, 3, 1980), 180); + p.id(++id); + stmt = q.insert(p).prepare(); + stmt.bind(0, &p); + stmt.execute(); + } - stmt = q.select().prepare(connection_); + column name("name"); + stmt = q.select().where(name == "").prepare(); + oos::varchar<255> hname("hans"); + stmt.bind(0, hname); res = stmt.execute(); // UNIT_ASSERT_EQUAL(res.size(), 1UL, "expected size must be one (1)"); @@ -539,52 +594,30 @@ void QueryTestUnit::test_statement_update() auto last = res.end(); while (first != last) { - std::unique_ptr item(first.release()); - UNIT_ASSERT_EQUAL(item->id(), 23UL, "expected id must be 23"); - UNIT_ASSERT_EQUAL(item->get_string(), "Hans", "expected name must be 'Hans'"); - UNIT_EXPECT_EQUAL(item->get_int(), 4711, "expected integer must be 4711"); - UNIT_EXPECT_EQUAL(item->get_time(), itime, "expected time is invalid"); + std::unique_ptr p(first.release()); + UNIT_EXPECT_EQUAL(p->id(), 1UL, "expected id must be 1"); + UNIT_ASSERT_EQUAL(p->name(), "hans", "expected name must be 'hans'"); + UNIT_ASSERT_EQUAL(p->height(), 180U, "expected height must be 180"); + UNIT_ASSERT_EQUAL(p->birthdate(), oos::date(12, 3, 1980), "expected birthdate is 12.3.1980"); ++first; } // auto id_cond = id_condition_builder::build(); oos::column idcol("id"); - int i815 = 815; - stmt = q.update({{"val_int", i815}}).where(idcol == 7).prepare(connection_); - size_t pos = 0; - pos = stmt.bind(pos, i815); - unsigned long hid = hans.id(); - stmt.bind(pos, hid); - - res = stmt.execute(); - - stmt = q.select().prepare(connection_); - res = stmt.execute(); - -// UNIT_ASSERT_EQUAL(res.size(), 1UL, "expected size must be one (1)"); - - first = res.begin(); - last = res.end(); - - while (first != last) { - std::unique_ptr item(first.release()); - UNIT_EXPECT_EQUAL(item->id(), 23UL, "expected id must be 23"); - UNIT_ASSERT_EQUAL(item->get_string(), "Hans", "expected name must be 'Hans'"); - UNIT_EXPECT_EQUAL(item->get_int(), 815, "expected integer must be 815"); - UNIT_EXPECT_EQUAL(item->get_time(), itime, "expected time is invalid"); - ++first; - } + person hans("hans", oos::date(15, 6, 1990), 165); + hans.id(1); - hans.set_int(4711); - stmt = q.update(hans).where(idcol == 7).prepare(connection_); - pos = 0; + stmt = q.update(hans).where(idcol == 7).prepare(); + size_t pos = 0; pos = stmt.bind(pos, &hans); + unsigned long hid = 1; stmt.bind(pos, hid); res = stmt.execute(); - stmt = q.select().prepare(connection_); + stmt = q.select().where(name == "").prepare(); + stmt.bind(0, hname); res = stmt.execute(); // UNIT_ASSERT_EQUAL(res.size(), 1UL, "expected size must be one (1)"); @@ -593,15 +626,15 @@ void QueryTestUnit::test_statement_update() last = res.end(); while (first != last) { - std::unique_ptr item(first.release()); - UNIT_EXPECT_EQUAL(item->id(), 23UL, "expected id must be 23"); - UNIT_EXPECT_EQUAL(item->get_string(), "Hans", "expected name must be 'Hans'"); - UNIT_EXPECT_EQUAL(item->get_int(), 4711, "expected integer must be 4711"); - UNIT_EXPECT_EQUAL(item->get_time(), itime, "expected time is invalid"); + std::unique_ptr p(first.release()); + UNIT_EXPECT_EQUAL(p->id(), 1UL, "expected id must be 1"); + UNIT_ASSERT_EQUAL(p->name(), "hans", "expected name must be 'hans'"); + UNIT_ASSERT_EQUAL(p->height(), 165U, "expected height must be 180"); + UNIT_ASSERT_EQUAL(p->birthdate(), oos::date(15, 6, 1990), "expected birthdate is 12.3.1980"); ++first; } - stmt = q.drop().prepare(connection_); + stmt = q.drop().prepare(); res = stmt.execute(); diff --git a/test/sql/QueryTestUnit.hpp b/test/sql/QueryTestUnit.hpp index 5fe7a3a2e..077711e48 100644 --- a/test/sql/QueryTestUnit.hpp +++ b/test/sql/QueryTestUnit.hpp @@ -30,6 +30,7 @@ class QueryTestUnit : public oos::unit_test void test_describe(); void test_identifier(); void test_create(); + void test_update(); void test_anonymous_create(); void test_anonymous_insert(); void test_anonymous_update(); diff --git a/test/unit/TestSuiteTestUnit.cpp b/test/unit/TestSuiteTestUnit.cpp index b0300febf..907f0fce9 100644 --- a/test/unit/TestSuiteTestUnit.cpp +++ b/test/unit/TestSuiteTestUnit.cpp @@ -80,6 +80,7 @@ bool TestSuiteTestUnit::test_list() }; ts.init(2, argv1); + ts.quiet(); const oos::test_suite::test_suite_args &args = ts.test_args(); @@ -121,6 +122,7 @@ bool TestSuiteTestUnit::test_method() }; ts.init(3, argv); + ts.quiet(); const oos::test_suite::test_suite_args &args = ts.test_args(); @@ -153,6 +155,7 @@ bool TestSuiteTestUnit::test_unit() }; ts.init(3, argv); + ts.quiet(); const oos::test_suite::test_suite_args &args = ts.test_args(); @@ -180,6 +183,7 @@ bool TestSuiteTestUnit::test_suite() }; ts.init(3, argv); + ts.quiet(); const oos::test_suite::test_suite_args &args = ts.test_args();