From 91830e3bd38a05c73d03a5dfb88997799d44274b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 6 May 2013 12:05:39 +0200 Subject: 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. --- odb/database.hxx | 22 +++++++++ odb/database.ixx | 14 ++++++ odb/database.txx | 35 +++++++++++++++ odb/exceptions.cxx | 12 +++++ odb/exceptions.hxx | 17 +++++++ odb/forward.hxx | 2 + odb/makefile | 1 + odb/polymorphic-info.hxx | 63 ++++++++++++++++++++++++-- odb/polymorphic-map.hxx | 34 +++++++++++++- odb/section.cxx | 28 ++++++++++++ odb/section.hxx | 114 +++++++++++++++++++++++++++++++++++++++++++++++ odb/traits.hxx | 48 ++++++++++++++++++++ odb/vector-traits.hxx | 3 ++ odb/vector-traits.txx | 28 ++++++++++++ 14 files changed, 416 insertions(+), 5 deletions(-) create mode 100644 odb/section.cxx create mode 100644 odb/section.hxx 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::id_type& id, T& object); + // Load (or reload, if it is already loaded) a section of an object. + // + template + void + load (T& object, section&); + // Reload an object. // template @@ -153,6 +159,14 @@ namespace odb void update (const typename object_traits::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 + 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 void + load_ (T&, section&); + + template + void reload_ (T&); template @@ -414,6 +432,10 @@ namespace odb template void + update_ (const T&, const section&); + + template + void erase_ (const typename object_traits::id_type&); template 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 + inline void database:: + load (T& obj, section& s) + { + return load_ (obj, s); + } + + template inline typename object_traits::pointer_type database:: find (const typename object_traits::id_type& id) { @@ -340,6 +347,13 @@ namespace odb template inline void database:: + update (const T& obj, const section& s) + { + update_ (obj, s); + } + + template + inline void database:: erase (const typename object_traits::id_type& id) { return erase_ (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 #include #include #include @@ -96,6 +97,20 @@ namespace odb template void database:: + load_ (T& obj, section& s) + { + connection_type& c (transaction::current ().connection ()); + + // T is always object_type. + // + if (object_traits_impl::load (c, obj, s)) + s.reset (true, false); // Loaded, unchanged. + else + throw section_not_in_object (); + } + + template + 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 + 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::update (t.connection (), obj, s)) + { + if (s.changed ()) + s.reset (true, false, &t); // Clear the change flag. + } + else + throw section_not_in_object (); + } + + template struct database::query_ { template 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 +#include // std::size_t #include -#include // database +#include // database, connection #include namespace odb @@ -17,9 +18,20 @@ namespace odb template 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 @@ -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::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* b, + const section_functions* s, const discriminator_type& d, create_function cf, dispatch_function df, delayed_loader_function dlf) - : polymorphic_abstract_info (t, b), + : polymorphic_abstract_info (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 - bool dispatch_impl ( + bool + dispatch_impl ( typename polymorphic_concrete_info< typename object_traits::root_type>::call_type c, database& db, @@ -235,6 +236,37 @@ namespace odb return r; } + + template + void + section_load_impl (odb::connection& conn, + typename object_traits::root_type& obj, + bool top) + { + typedef object_traits_impl derived_traits; + typedef typename derived_traits::statements_type statements_type; + typedef typename statements_type::connection_type connection_type; + + connection_type& c (static_cast (conn)); + statements_type& sts (c.statement_cache ().template find_object ()); + + ST::load (sts.extra_statement_cache (), static_cast (obj), top); + } + + template + void + section_update_impl (odb::connection& conn, + const typename object_traits::root_type& obj) + { + typedef object_traits_impl derived_traits; + typedef typename derived_traits::statements_type statements_type; + typedef typename statements_type::connection_type connection_type; + + connection_type& c (static_cast (conn)); + statements_type& sts (c.statement_cache ().template find_object ()); + + ST::update (sts.extra_statement_cache (), static_cast (obj)); + } } #include 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 + +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 (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 + +#include +#include + +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 (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 + +#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
{}; + template // // 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 { }; + + // + // Get root image from a polymorphic image chain. + // + + template + struct root_image_impl + { + typedef root_image_impl 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 + struct root_image_impl + { + typedef typename T::image_type image_type; + + static image_type& + get (image_type& i) {return i;} + }; + + template + struct root_image + { + typedef root_image_impl 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 + struct root_image + { + typedef typename T::image_type image_type; + + static image_type& + get (image_type& i) {return i;} + }; } #include 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 + bool access::container_traits >:: + 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 void access::container_traits >:: update (const container_type& c, const functions& f) { -- cgit v1.1