From 534da0d899b11f6ca1fac161ea8901a62e5c07dc Mon Sep 17 00:00:00 2001
From: Boris Kolpackov <boris@codesynthesis.com>
Date: Wed, 14 Aug 2013 15:16:09 +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/sqlite/database.hxx                      |  14 +++
 odb/sqlite/database.ixx                      |  14 +++
 odb/sqlite/forward.hxx                       |   3 +
 odb/sqlite/polymorphic-object-statements.hxx |  39 ++++--
 odb/sqlite/section-statements.hxx            | 182 +++++++++++++++++++++++++++
 odb/sqlite/section-statements.txx            |  42 +++++++
 odb/sqlite/simple-object-statements.hxx      | 100 ++++++++-------
 odb/sqlite/simple-object-statements.txx      |   1 -
 8 files changed, 337 insertions(+), 58 deletions(-)
 create mode 100644 odb/sqlite/section-statements.hxx
 create mode 100644 odb/sqlite/section-statements.txx

diff --git a/odb/sqlite/database.hxx b/odb/sqlite/database.hxx
index 0471b64..6039f23 100644
--- a/odb/sqlite/database.hxx
+++ b/odb/sqlite/database.hxx
@@ -147,6 +147,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>
@@ -217,6 +223,14 @@ namespace odb
       void
       update (const typename object_traits<T>::pointer_type& obj_ptr);
 
+      // Update a section of an object. Throws the section_not_loaded
+      // exception if the 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.
       //
diff --git a/odb/sqlite/database.ixx b/odb/sqlite/database.ixx
index fd2063e..c2fd1a5 100644
--- a/odb/sqlite/database.ixx
+++ b/odb/sqlite/database.ixx
@@ -108,6 +108,13 @@ namespace odb
     }
 
     template <typename T>
+    inline void database::
+    load (T& obj, section& s)
+    {
+      return load_<T, id_sqlite> (obj, s);
+    }
+
+    template <typename T>
     inline typename object_traits<T>::pointer_type database::
     find (const typename object_traits<T>::id_type& id)
     {
@@ -249,6 +256,13 @@ namespace odb
 
     template <typename T>
     inline void database::
+    update (const T& obj, const section& s)
+    {
+      update_<T, id_sqlite> (obj, s);
+    }
+
+    template <typename T>
+    inline void database::
     erase (const typename object_traits<T>::id_type& id)
     {
       return erase_<T, id_sqlite> (id);
diff --git a/odb/sqlite/forward.hxx b/odb/sqlite/forward.hxx
index 316fbbc..456ff43 100644
--- a/odb/sqlite/forward.hxx
+++ b/odb/sqlite/forward.hxx
@@ -70,6 +70,9 @@ namespace odb
     template <typename T>
     class smart_container_statements;
 
+    template <typename T, typename ST>
+    class section_statements;
+
     class query_base;
     class query_params;
   }
diff --git a/odb/sqlite/polymorphic-object-statements.hxx b/odb/sqlite/polymorphic-object-statements.hxx
index f932234..fa5f6af 100644
--- a/odb/sqlite/polymorphic-object-statements.hxx
+++ b/odb/sqlite/polymorphic-object-statements.hxx
@@ -171,8 +171,8 @@ namespace odb
       base_statements_type;
 
       typedef
-      typename object_traits::container_statement_cache_type
-      container_statement_cache_type;
+      typename object_traits::extra_statement_cache_type
+      extra_statement_cache_type;
 
       typedef sqlite::insert_statement insert_statement_type;
       typedef sqlite::select_statement select_statement_type;
@@ -282,6 +282,10 @@ namespace odb
       binding&
       id_image_binding () {return root_statements_.id_image_binding ();}
 
+      binding&
+      optimistic_id_image_binding () {
+        return root_statements_.optimistic_id_image_binding ();}
+
       // Statements.
       //
       insert_statement_type&
@@ -348,31 +352,40 @@ namespace odb
         return *erase_;
       }
 
-      // Container statement cache.
+      // Extra (container, section) statement cache.
       //
-      container_statement_cache_type&
-      container_statment_cache ()
+      extra_statement_cache_type&
+      extra_statement_cache ()
       {
-        return container_statement_cache_.get (conn_, id_image_binding ());
+        return extra_statement_cache_.get (
+          conn_,
+          image_,
+          id_image_binding (),
+          &id_image_binding ()); // Note, not id+version.
       }
 
     public:
-      // select = total - id + base::select
+      // select = total - id - separate_load + base::select
       // insert = total - inverse
-      // update = total - inverse - id - readonly
+      // update = total - inverse - id - readonly - separate_update
       //
       static const std::size_t id_column_count =
         object_traits::id_column_count;
 
       static const std::size_t select_column_count =
-        object_traits::column_count - id_column_count +
+        object_traits::column_count -
+        id_column_count -
+        object_traits::separate_load_column_count +
         base_statements_type::select_column_count;
 
       static const std::size_t insert_column_count =
-        object_traits::column_count - object_traits::inverse_column_count;
+        object_traits::column_count -
+        object_traits::inverse_column_count;
 
       static const std::size_t update_column_count = insert_column_count -
-        object_traits::id_column_count - object_traits::readonly_column_count;
+        object_traits::id_column_count -
+        object_traits::readonly_column_count -
+        object_traits::separate_update_column_count;
 
     private:
       polymorphic_derived_object_statements (
@@ -385,8 +398,8 @@ namespace odb
       root_statements_type& root_statements_;
       base_statements_type& base_statements_;
 
-      container_statement_cache_ptr<container_statement_cache_type>
-      container_statement_cache_;
+      extra_statement_cache_ptr<extra_statement_cache_type, image_type>
+      extra_statement_cache_;
 
       image_type image_;
 
diff --git a/odb/sqlite/section-statements.hxx b/odb/sqlite/section-statements.hxx
new file mode 100644
index 0000000..3f0d9c4
--- /dev/null
+++ b/odb/sqlite/section-statements.hxx
@@ -0,0 +1,182 @@
+// file      : odb/sqlite/section-statements.hxx
+// copyright : Copyright (c) 2005-2013 Code Synthesis Tools CC
+// license   : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_SECTION_STATEMENTS_HXX
+#define ODB_SQLITE_SECTION_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/sqlite-types.hxx>
+#include <odb/sqlite/binding.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+  namespace sqlite
+  {
+    class connection;
+
+    // Template argument is the section traits type.
+    //
+    template <typename T, typename ST>
+    class section_statements
+    {
+    public:
+      typedef ST traits;
+
+      typedef typename traits::image_type image_type;
+
+      typedef sqlite::select_statement select_statement_type;
+      typedef sqlite::update_statement update_statement_type;
+
+      typedef sqlite::connection connection_type;
+
+      section_statements (connection_type&,
+                          image_type&,
+                          binding& id, binding& idv);
+
+      connection_type&
+      connection () {return conn_;}
+
+      image_type&
+      image () {return image_;}
+
+      const binding&
+      id_binding () {return id_binding_;}
+
+      // Id and optimistic concurrency version (if any).
+      //
+      const binding&
+      idv_binding () {return idv_binding_;}
+
+      // Select binding.
+      //
+      std::size_t
+      select_image_version () const { return select_image_version_;}
+
+      void
+      select_image_version (std::size_t v) {select_image_version_ = v;}
+
+      binding&
+      select_image_binding () {return select_image_binding_;}
+
+      bool*
+      select_image_truncated () {return select_image_truncated_;}
+
+      // Update binding.
+      //
+      std::size_t
+      update_image_version () const { return update_image_version_;}
+
+      void
+      update_image_version (std::size_t v) {update_image_version_ = v;}
+
+      std::size_t
+      update_id_binding_version () const { return update_id_binding_version_;}
+
+      void
+      update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+      binding&
+      update_image_binding () {return update_image_binding_;}
+
+      //
+      // Statements.
+      //
+
+      select_statement_type&
+      select_statement ()
+      {
+        if (select_ == 0)
+          select_.reset (
+            new (details::shared) select_statement_type (
+              conn_,
+              traits::select_statement,
+              id_binding_,
+              select_image_binding_));
+
+        return *select_;
+      }
+
+      update_statement_type&
+      update_statement ()
+      {
+        if (update_ == 0)
+          update_.reset (
+            new (details::shared) update_statement_type (
+              conn_,
+              traits::update_statement,
+              update_image_binding_));
+
+        return *update_;
+      }
+
+    public:
+      static const std::size_t id_column_count = traits::id_column_count;
+      static const std::size_t managed_optimistic_load_column_count =
+        traits::managed_optimistic_load_column_count;
+      static const std::size_t managed_optimistic_update_column_count =
+        traits::managed_optimistic_update_column_count;
+      static const std::size_t select_column_count = traits::load_column_count;
+      static const std::size_t update_column_count =
+        traits::update_column_count;
+
+    private:
+      section_statements (const section_statements&);
+      section_statements& operator= (const section_statements&);
+
+    protected:
+      connection_type& conn_;
+
+      // These come from object_statements.
+      //
+      image_type& image_;
+      binding& id_binding_;
+      binding& idv_binding_;
+
+      // Select binding.
+      //
+      std::size_t select_image_version_;
+
+      static const std::size_t select_bind_count =
+        select_column_count != 0 || managed_optimistic_load_column_count != 0
+        ? select_column_count + managed_optimistic_load_column_count
+        : 1;
+
+      binding select_image_binding_;
+      bind select_image_bind_[select_bind_count];
+      bool select_image_truncated_[select_bind_count];
+
+      // Update binding.
+      //
+      std::size_t update_image_version_;
+      std::size_t update_id_binding_version_;
+
+      static const std::size_t update_bind_count =
+        update_column_count != 0 || managed_optimistic_update_column_count != 0
+        ? update_column_count + id_column_count +
+          managed_optimistic_update_column_count
+        : 1;
+
+      binding update_image_binding_;
+      bind update_image_bind_[update_bind_count];
+
+      details::shared_ptr<select_statement_type> select_;
+      details::shared_ptr<update_statement_type> update_;
+    };
+  }
+}
+
+#include <odb/sqlite/section-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_SECTION_STATEMENTS_HXX
diff --git a/odb/sqlite/section-statements.txx b/odb/sqlite/section-statements.txx
new file mode 100644
index 0000000..b6945d9
--- /dev/null
+++ b/odb/sqlite/section-statements.txx
@@ -0,0 +1,42 @@
+// file      : odb/sqlite/section-statements.txx
+// copyright : Copyright (c) 2005-2013 Code Synthesis Tools CC
+// license   : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+  namespace sqlite
+  {
+    template <typename T, typename ST>
+    section_statements<T, ST>::
+    section_statements (connection_type& conn,
+                        image_type& im,
+                        binding& id, binding& idv)
+        : conn_ (conn),
+          image_ (im),
+          id_binding_ (id),
+          idv_binding_ (idv),
+          select_image_binding_ (select_image_bind_,
+                                 select_column_count +
+                                 managed_optimistic_load_column_count),
+          update_image_binding_ (update_image_bind_,
+                                 update_column_count + id_column_count +
+                                 managed_optimistic_update_column_count)
+    {
+      select_image_version_ = 0;
+      update_image_version_ = 0;
+      update_id_binding_version_ = 0;
+
+      std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+      std::memset (
+        select_image_truncated_, 0, sizeof (select_image_truncated_));
+      std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+      for (std::size_t i (0);
+           i < select_column_count + managed_optimistic_load_column_count;
+           ++i)
+        select_image_bind_[i].truncated = select_image_truncated_ + i;
+    }
+  }
+}
diff --git a/odb/sqlite/simple-object-statements.hxx b/odb/sqlite/simple-object-statements.hxx
index 2c5b321..a1e7678 100644
--- a/odb/sqlite/simple-object-statements.hxx
+++ b/odb/sqlite/simple-object-statements.hxx
@@ -29,58 +29,59 @@ namespace odb
 {
   namespace sqlite
   {
-    // The container_statement_cache class is only defined (and used) in
+    // The extra_statement_cache class is only defined (and used) in
     // the generated source file. However, object_statements may be
     // referenced from another source file in the case of a polymorphic
-    // hierarchy (though in this case the container statement cache is
+    // hierarchy (though in this case the extra statement cache is
     // not used). As a result, we cannot have a by-value member and
     // instead will store a pointer and lazily allocate the cache if
     // and when needed. We will also need to store a pointer to the
     // deleter function which will be initialized during allocation
     // (at that point we know that the cache class is defined).
     //
-    template <typename T>
-    struct container_statement_cache_ptr
+    template <typename T, typename I>
+    struct extra_statement_cache_ptr
     {
+      typedef I image_type;
       typedef sqlite::connection connection_type;
 
-      container_statement_cache_ptr (): p_ (0) {}
-      ~container_statement_cache_ptr ()
+      extra_statement_cache_ptr (): p_ (0) {}
+      ~extra_statement_cache_ptr ()
       {
         if (p_ != 0)
-          (this->*deleter_) (0, 0);
+          (this->*deleter_) (0, 0, 0, 0);
       }
 
       T&
-      get (connection_type& c, binding& id)
+      get (connection_type& c, image_type& im, binding& id, binding* idv)
       {
         if (p_ == 0)
-          allocate (&c, &id);
+          allocate (&c, &im, &id, (idv != 0 ? idv : &id));
 
         return *p_;
       }
 
     private:
       void
-      allocate (connection_type*, binding*);
+      allocate (connection_type*, image_type*, binding*, binding*);
 
     private:
       T* p_;
-      void (container_statement_cache_ptr::*deleter_) (
-        connection_type*, binding*);
+      void (extra_statement_cache_ptr::*deleter_) (
+        connection_type*, image_type*, binding*, binding*);
     };
 
-    template <typename T>
-    void container_statement_cache_ptr<T>::
-    allocate (connection_type* c, binding* id)
+    template <typename T, typename I>
+    void extra_statement_cache_ptr<T, I>::
+    allocate (connection_type* c, image_type* im, binding* id, binding* idv)
     {
       // To reduce object code size, this function acts as both allocator
       // and deleter.
       //
       if (p_ == 0)
       {
-        p_ = new T (*c, *id);
-        deleter_ = &container_statement_cache_ptr<T>::allocate;
+        p_ = new T (*c, *im, *id, *idv);
+        deleter_ = &extra_statement_cache_ptr<T, I>::allocate;
       }
       else
         delete p_;
@@ -156,9 +157,11 @@ namespace odb
 
       optimistic_data (bind*);
 
+      binding*
+      id_image_binding () {return &id_image_binding_;}
+
       // The id + optimistic column binding.
       //
-      std::size_t id_image_version_;
       binding id_image_binding_;
 
       details::shared_ptr<delete_statement> erase_;
@@ -168,6 +171,9 @@ namespace odb
     struct optimistic_data<T, false>
     {
       optimistic_data (bind*) {}
+
+      binding*
+      id_image_binding () {return 0;}
     };
 
     template <typename T>
@@ -186,8 +192,8 @@ namespace odb
       pointer_cache_traits;
 
       typedef
-      typename object_traits::container_statement_cache_type
-      container_statement_cache_type;
+      typename object_traits::extra_statement_cache_type
+      extra_statement_cache_type;
 
       typedef sqlite::insert_statement insert_statement_type;
       typedef sqlite::select_statement select_statement_type;
@@ -327,14 +333,10 @@ namespace odb
       binding&
       id_image_binding () {return id_image_binding_;}
 
-      // Optimistic id + managed column image binding.
+      // Optimistic id + managed column image binding. It points to
+      // the same suffix as id binding and they are always updated
+      // at the same time.
       //
-      std::size_t
-      optimistic_id_image_version () const {return od_.id_image_version_;}
-
-      void
-      optimistic_id_image_version (std::size_t v) {od_.id_image_version_ = v;}
-
       binding&
       optimistic_id_image_binding () {return od_.id_image_binding_;}
 
@@ -416,35 +418,42 @@ namespace odb
         return *od_.erase_;
       }
 
-      // Container statement cache.
+      // Extra (container, section) statement cache.
       //
-      container_statement_cache_type&
-      container_statment_cache ()
+      extra_statement_cache_type&
+      extra_statement_cache ()
       {
-        return container_statement_cache_.get (conn_, id_image_binding_);
+        return extra_statement_cache_.get (
+          conn_, image_, id_image_binding_, od_.id_image_binding ());
       }
 
     public:
-      // select = total
+      // select = total - separate_load
       // insert = total - inverse - managed_optimistic
       // update = total - inverse - managed_optimistic - id - readonly
+      //   - separate_update
       //
-      static const std::size_t select_column_count =
-        object_traits::column_count;
-
-      static const std::size_t insert_column_count =
-        object_traits::column_count - object_traits::inverse_column_count -
-        object_traits::managed_optimistic_column_count;
-
-      static const std::size_t update_column_count = insert_column_count -
-        object_traits::id_column_count - object_traits::readonly_column_count;
-
       static const std::size_t id_column_count =
         object_traits::id_column_count;
 
       static const std::size_t managed_optimistic_column_count =
         object_traits::managed_optimistic_column_count;
 
+      static const std::size_t select_column_count =
+        object_traits::column_count -
+        object_traits::separate_load_column_count;
+
+      static const std::size_t insert_column_count =
+        object_traits::column_count -
+        object_traits::inverse_column_count -
+        object_traits::managed_optimistic_column_count;
+
+      static const std::size_t update_column_count =
+        insert_column_count -
+        id_column_count -
+        object_traits::readonly_column_count -
+        object_traits::separate_update_column_count;
+
     private:
       object_statements (const object_statements&);
       object_statements& operator= (const object_statements&);
@@ -457,8 +466,11 @@ namespace odb
       clear_delayed_ ();
 
     private:
-      container_statement_cache_ptr<container_statement_cache_type>
-      container_statement_cache_;
+      template <typename T1>
+      friend class polymorphic_derived_object_statements;
+
+      extra_statement_cache_ptr<extra_statement_cache_type, image_type>
+      extra_statement_cache_;
 
       image_type image_;
 
diff --git a/odb/sqlite/simple-object-statements.txx b/odb/sqlite/simple-object-statements.txx
index f0e6138..15689c6 100644
--- a/odb/sqlite/simple-object-statements.txx
+++ b/odb/sqlite/simple-object-statements.txx
@@ -25,7 +25,6 @@ namespace odb
             object_traits::id_column_count +
             object_traits::managed_optimistic_column_count)
     {
-      id_image_version_ = 0;
     }
 
     //
-- 
cgit v1.1