aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2013-05-06 12:05:39 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2013-08-14 15:18:23 +0200
commit91830e3bd38a05c73d03a5dfb88997799d44274b (patch)
tree399f1fc61ce959ac63b8b7ebaf5e3c58f80d8a7d
parent72b262ee4f375089256f6438152bd323df1ff5a3 (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.hxx22
-rw-r--r--odb/database.ixx14
-rw-r--r--odb/database.txx35
-rw-r--r--odb/exceptions.cxx12
-rw-r--r--odb/exceptions.hxx17
-rw-r--r--odb/forward.hxx2
-rw-r--r--odb/makefile1
-rw-r--r--odb/polymorphic-info.hxx63
-rw-r--r--odb/polymorphic-map.hxx34
-rw-r--r--odb/section.cxx28
-rw-r--r--odb/section.hxx114
-rw-r--r--odb/traits.hxx48
-rw-r--r--odb/vector-traits.hxx3
-rw-r--r--odb/vector-traits.txx28
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)
{