diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2013-05-06 12:05:39 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2013-08-14 15:18:23 +0200 |
commit | 91830e3bd38a05c73d03a5dfb88997799d44274b (patch) | |
tree | 399f1fc61ce959ac63b8b7ebaf5e3c58f80d8a7d | |
parent | 72b262ee4f375089256f6438152bd323df1ff5a3 (diff) |
Add support for object sections
Sections are an optimization mechanism that allows the partitioning of
data members of a persistent class into groups that can be separately
loaded and/or updated.
-rw-r--r-- | odb/database.hxx | 22 | ||||
-rw-r--r-- | odb/database.ixx | 14 | ||||
-rw-r--r-- | odb/database.txx | 35 | ||||
-rw-r--r-- | odb/exceptions.cxx | 12 | ||||
-rw-r--r-- | odb/exceptions.hxx | 17 | ||||
-rw-r--r-- | odb/forward.hxx | 2 | ||||
-rw-r--r-- | odb/makefile | 1 | ||||
-rw-r--r-- | odb/polymorphic-info.hxx | 63 | ||||
-rw-r--r-- | odb/polymorphic-map.hxx | 34 | ||||
-rw-r--r-- | odb/section.cxx | 28 | ||||
-rw-r--r-- | odb/section.hxx | 114 | ||||
-rw-r--r-- | odb/traits.hxx | 48 | ||||
-rw-r--r-- | odb/vector-traits.hxx | 3 | ||||
-rw-r--r-- | odb/vector-traits.txx | 28 |
14 files changed, 416 insertions, 5 deletions
diff --git a/odb/database.hxx b/odb/database.hxx index 79fa622..6c70da7 100644 --- a/odb/database.hxx +++ b/odb/database.hxx @@ -83,6 +83,12 @@ namespace odb void load (const typename object_traits<T>::id_type& id, T& object); + // Load (or reload, if it is already loaded) a section of an object. + // + template <typename T> + void + load (T& object, section&); + // Reload an object. // template <typename T> @@ -153,6 +159,14 @@ namespace odb void update (const typename object_traits<T>::pointer_type& obj_ptr); + // Update a section of an object. Throws section_not_loaded exception + // if section is not loaded. Note also that this function does not + // clear the changed flag if it is set. + // + template <typename T> + void + update (const T& object, const section&); + // Make the object transient. Throw object_not_persistent if not // found. // @@ -394,6 +408,10 @@ namespace odb template <typename T, database_id DB> void + load_ (T&, section&); + + template <typename T, database_id DB> + void reload_ (T&); template <typename T, database_id DB> @@ -414,6 +432,10 @@ namespace odb template <typename T, database_id DB> void + update_ (const T&, const section&); + + template <typename T, database_id DB> + void erase_ (const typename object_traits<T>::id_type&); template <typename T, database_id DB> diff --git a/odb/database.ixx b/odb/database.ixx index b7c1917..066afd6 100644 --- a/odb/database.ixx +++ b/odb/database.ixx @@ -199,6 +199,13 @@ namespace odb } template <typename T> + inline void database:: + load (T& obj, section& s) + { + return load_<T, id_common> (obj, s); + } + + template <typename T> inline typename object_traits<T>::pointer_type database:: find (const typename object_traits<T>::id_type& id) { @@ -340,6 +347,13 @@ namespace odb template <typename T> inline void database:: + update (const T& obj, const section& s) + { + update_<T, id_common> (obj, s); + } + + template <typename T> + inline void database:: erase (const typename object_traits<T>::id_type& id) { return erase_<T, id_common> (id); diff --git a/odb/database.txx b/odb/database.txx index 46ec752..5d4829a 100644 --- a/odb/database.txx +++ b/odb/database.txx @@ -2,6 +2,7 @@ // copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC // license : GNU GPL v2; see accompanying LICENSE file +#include <odb/section.hxx> #include <odb/exceptions.hxx> #include <odb/no-op-cache-traits.hxx> #include <odb/pointer-traits.hxx> @@ -96,6 +97,20 @@ namespace odb template <typename T, database_id DB> void database:: + load_ (T& obj, section& s) + { + connection_type& c (transaction::current ().connection ()); + + // T is always object_type. + // + if (object_traits_impl<T, DB>::load (c, obj, s)) + s.reset (true, false); // Loaded, unchanged. + else + throw section_not_in_object (); + } + + template <typename T, database_id DB> + void database:: reload_ (T& obj) { // T should be object_type (cannot be const). We also don't need to @@ -106,6 +121,26 @@ namespace odb } template <typename T, database_id DB> + void database:: + update_ (const T& obj, const section& s) + { + if (!s.loaded ()) + throw section_not_loaded (); + + transaction& t (transaction::current ()); + + // T is always object_type. + // + if (object_traits_impl<T, DB>::update (t.connection (), obj, s)) + { + if (s.changed ()) + s.reset (true, false, &t); // Clear the change flag. + } + else + throw section_not_in_object (); + } + + template <typename T, database_id DB> struct database::query_<T, DB, class_object> { template <typename Q> diff --git a/odb/exceptions.cxx b/odb/exceptions.cxx index 1ee17e5..a5292bc 100644 --- a/odb/exceptions.cxx +++ b/odb/exceptions.cxx @@ -185,4 +185,16 @@ namespace odb { return what_.c_str (); } + + const char* section_not_loaded:: + what () const throw () + { + return "section is not loaded"; + } + + const char* section_not_in_object:: + what () const throw () + { + return "section instance is not part of an object (section was copied?)"; + } } diff --git a/odb/exceptions.hxx b/odb/exceptions.hxx index 2cefea7..14f4f86 100644 --- a/odb/exceptions.hxx +++ b/odb/exceptions.hxx @@ -208,6 +208,20 @@ namespace odb std::string what_; }; + // Section exceptions. + // + struct LIBODB_EXPORT section_not_loaded: odb::exception + { + virtual const char* + what () const throw (); + }; + + struct LIBODB_EXPORT section_not_in_object: odb::exception + { + virtual const char* + what () const throw (); + }; + namespace common { using odb::null_pointer; @@ -235,6 +249,9 @@ namespace odb using odb::unknown_schema; using odb::unknown_schema_version; + + using odb::section_not_loaded; + using odb::section_not_in_object; } } diff --git a/odb/forward.hxx b/odb/forward.hxx index a09f08d..f8b3420 100644 --- a/odb/forward.hxx +++ b/odb/forward.hxx @@ -34,11 +34,13 @@ namespace odb class transaction; class statement; class session; + class section; namespace common { using odb::schema_version; using odb::session; + using odb::section; } namespace core diff --git a/odb/makefile b/odb/makefile index 6a2cca4..5a58103 100644 --- a/odb/makefile +++ b/odb/makefile @@ -15,6 +15,7 @@ prepared-query.cxx \ query-dynamic.cxx \ result.cxx \ schema-catalog.cxx \ +section.cxx \ session.cxx \ statement.cxx \ tracer.cxx \ diff --git a/odb/polymorphic-info.hxx b/odb/polymorphic-info.hxx index 2011133..7762525 100644 --- a/odb/polymorphic-info.hxx +++ b/odb/polymorphic-info.hxx @@ -7,9 +7,10 @@ #include <odb/pre.hxx> +#include <cstddef> // std::size_t #include <typeinfo> -#include <odb/forward.hxx> // database +#include <odb/forward.hxx> // database, connection #include <odb/traits.hxx> namespace odb @@ -17,9 +18,20 @@ namespace odb template <typename R> struct polymorphic_abstract_info { + typedef void (*section_load) (odb::connection&, R&, bool top); + typedef void (*section_update) (odb::connection&, const R&); + + struct section_functions + { + section_load load; + section_update update; + }; + + public: polymorphic_abstract_info (const std::type_info& t, - const polymorphic_abstract_info* b) - : type (t), base (b) {} + const polymorphic_abstract_info* b, + const section_functions* s) + : type (t), base (b), sections (s) {} bool derived (const polymorphic_abstract_info& b) const @@ -31,9 +43,48 @@ namespace odb return false; } + // Find the "most overridden" section functions. + // + section_load + find_section_load (std::size_t index) const + { + for (const polymorphic_abstract_info* b (this); b != 0; b = b->base) + if (b->sections != 0 && b->sections[index].load != 0) + return b->sections[index].load; + + return 0; + } + + section_update + find_section_update (std::size_t index) const + { + for (const polymorphic_abstract_info* b (this); b != 0; b = b->base) + if (b->sections != 0 && b->sections[index].update != 0) + return b->sections[index].update; + + return 0; + } + + bool + final_section_update (const polymorphic_abstract_info& i, + std::size_t index) const + { + return i.sections != 0 && + i.sections[index].update != 0 && + i.sections[index].update == find_section_update (index); + } + public: const std::type_info& type; const polymorphic_abstract_info* base; + + // Sections. + // + // There could be "concrete" (i.e., not overridden) section in an + // abstract class. Which means the section table has to be in + // abstract_info. + // + const section_functions* sections; }; template <typename R> @@ -47,6 +98,9 @@ namespace odb typedef typename root_traits::pointer_type pointer_type; typedef typename root_traits::discriminator_type discriminator_type; + typedef typename polymorphic_abstract_info<R>::section_functions + section_functions; + enum call_type { call_callback, // arg points to callback event. @@ -67,11 +121,12 @@ namespace odb public: polymorphic_concrete_info (const std::type_info& t, const polymorphic_abstract_info<R>* b, + const section_functions* s, const discriminator_type& d, create_function cf, dispatch_function df, delayed_loader_function dlf) - : polymorphic_abstract_info<R> (t, b), + : polymorphic_abstract_info<R> (t, b, s), discriminator (d), create (cf), dispatch (df), delayed_loader (dlf) { diff --git a/odb/polymorphic-map.hxx b/odb/polymorphic-map.hxx index 5c3e434..3c0883f 100644 --- a/odb/polymorphic-map.hxx +++ b/odb/polymorphic-map.hxx @@ -149,7 +149,8 @@ namespace odb }; template <typename T, database_id DB> - bool dispatch_impl ( + bool + dispatch_impl ( typename polymorphic_concrete_info< typename object_traits<T>::root_type>::call_type c, database& db, @@ -235,6 +236,37 @@ namespace odb return r; } + + template <typename T, database_id DB, typename ST> + void + section_load_impl (odb::connection& conn, + typename object_traits<T>::root_type& obj, + bool top) + { + typedef object_traits_impl<T, DB> derived_traits; + typedef typename derived_traits::statements_type statements_type; + typedef typename statements_type::connection_type connection_type; + + connection_type& c (static_cast<connection_type&> (conn)); + statements_type& sts (c.statement_cache ().template find_object<T> ()); + + ST::load (sts.extra_statement_cache (), static_cast<T&> (obj), top); + } + + template <typename T, database_id DB, typename ST> + void + section_update_impl (odb::connection& conn, + const typename object_traits<T>::root_type& obj) + { + typedef object_traits_impl<T, DB> derived_traits; + typedef typename derived_traits::statements_type statements_type; + typedef typename statements_type::connection_type connection_type; + + connection_type& c (static_cast<connection_type&> (conn)); + statements_type& sts (c.statement_cache ().template find_object<T> ()); + + ST::update (sts.extra_statement_cache (), static_cast<const T&> (obj)); + } } #include <odb/polymorphic-map.ixx> diff --git a/odb/section.cxx b/odb/section.cxx new file mode 100644 index 0000000..9ef9c7d --- /dev/null +++ b/odb/section.cxx @@ -0,0 +1,28 @@ +// file : odb/section.cxx +// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include <odb/section.hxx> + +namespace odb +{ + void section:: + disarm () + { + transaction& t (transaction::current ()); + t.callback_unregister (this); + state_.armed = 0; + } + + void section:: + transacion_callback (unsigned short event, void* key, unsigned long long) + { + section& s (*static_cast<section*> (key)); + + if (event == transaction::event_rollback && s.state_.restore) + s.state_.changed = 1; + + s.state_.armed = 0; + s.state_.restore = 0; + } +} diff --git a/odb/section.hxx b/odb/section.hxx new file mode 100644 index 0000000..e32996c --- /dev/null +++ b/odb/section.hxx @@ -0,0 +1,114 @@ +// file : odb/section.hxx +// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_SECTION_HXX +#define ODB_SECTION_HXX + +#include <odb/pre.hxx> + +#include <odb/transaction.hxx> +#include <odb/details/export.hxx> + +namespace odb +{ + class LIBODB_EXPORT section + { + public: + // Load state. + // + bool + loaded () const {return state_.loaded;} + + // Mark a loaded section as not loaded. This, for example, can be + // useful if you don't want the section to be reloaded during the + // object reload. + // + void + unload () + { + state_.loaded = 0; + state_.changed = 0; + state_.restore = 0; + } + + // Change state. + // + bool + changed () const {return state_.changed;} + + // Mark the section as changed. + // + void + change () + { + state_.changed = 1; + state_.restore = 0; + } + + // User data. 4 bits of custom state. + // + unsigned char + user_data () const {return state_.user;} + + void + user_data (unsigned char u) {state_.user = u;} + + public: + section () + { + state_.loaded = 0; + state_.changed = 0; + state_.armed = 0; + state_.restore = 0; + } + + ~section () + { + if (state_.armed) + disarm (); + } + + // Implementation details. + // + public: + // Arm the callback and set the restore flag if transaction is not NULL. + // + void + reset (bool l = false, bool c = false, transaction* t = 0) const + { + state_.loaded = l; + state_.changed = c; + + if (t != 0 && !state_.armed) + { + t->callback_register (&transacion_callback, + const_cast<section*> (this)); + state_.armed = 1; + } + + state_.restore = (t != 0); + } + + private: + void + disarm (); + + static void + transacion_callback (unsigned short, void* key, unsigned long long); + + private: + mutable struct + { + unsigned char loaded : 1; + unsigned char changed : 1; + unsigned char armed : 1; // Transaction callback is armed. + unsigned char restore: 1; // Restore changed flag on rollback. + unsigned char user : 4; // User data. + } state_; + }; +} + +#include <odb/post.hxx> + +#endif // ODB_SECTION_HXX diff --git a/odb/traits.hxx b/odb/traits.hxx index 0fd11e4..495d972 100644 --- a/odb/traits.hxx +++ b/odb/traits.hxx @@ -181,6 +181,12 @@ namespace odb struct id_type {}; }; + // Specialization for section to allow instantiation of all the load() + // signature. + // + template <> + struct object_traits<section> {}; + template <typename T, database_id DB> // // If a C++ compiler issues an error pointing to this struct and @@ -271,6 +277,48 @@ namespace odb struct composite_value_traits: access::composite_value_traits<T, DB> { }; + + // + // Get root image from a polymorphic image chain. + // + + template <typename T, std::size_t d> + struct root_image_impl + { + typedef root_image_impl<typename T::base_traits, d - 1> base_type; + typedef typename base_type::image_type image_type; + + static image_type& + get (typename T::image_type& i) {return base_type::get (*i.base);} + }; + + template <typename T> + struct root_image_impl<T, 1> + { + typedef typename T::image_type image_type; + + static image_type& + get (image_type& i) {return i;} + }; + + template <typename T, bool p> + struct root_image + { + typedef root_image_impl<T, T::depth> impl_type; + typedef typename impl_type::image_type image_type; + + static image_type& + get (typename T::image_type& i) {return impl_type::get (i);} + }; + + template <typename T> + struct root_image<T, false> + { + typedef typename T::image_type image_type; + + static image_type& + get (image_type& i) {return i;} + }; } #include <odb/post.hxx> diff --git a/odb/vector-traits.hxx b/odb/vector-traits.hxx index d996a89..9f4186d 100644 --- a/odb/vector-traits.hxx +++ b/odb/vector-traits.hxx @@ -63,6 +63,9 @@ namespace odb c._start (); } + static bool + changed (const container_type&); + static void update (const container_type&, const functions&); diff --git a/odb/vector-traits.txx b/odb/vector-traits.txx index 66f757a..2844633 100644 --- a/odb/vector-traits.txx +++ b/odb/vector-traits.txx @@ -5,6 +5,34 @@ namespace odb { template <typename V, typename A LIBODB_VECTOR_ARG_DECL> + bool access::container_traits<vector<V, A LIBODB_VECTOR_ARG_USE> >:: + changed (const container_type& c) + { + // Because modifications can cancel each other (e.g., push and pop), + // it is tricky to keep track of whether there are any changes in + // the container. Instead, we are just going to examine each element + // just like update(). + // + + // We should either be tracking or summarily changed. + // + if (c._tracking ()) + { + const vector_impl& impl (c._impl ()); + + for (std::size_t i (0), n (impl.size ()); i < n; ++i) + { + if (impl.state (i) != vector_impl::state_unchanged) + return true; + } + } + else + return true; + + return false; + } + + template <typename V, typename A LIBODB_VECTOR_ARG_DECL> void access::container_traits<vector<V, A LIBODB_VECTOR_ARG_USE> >:: update (const container_type& c, const functions& f) { |