From 8bff3a2fc0ccce05abef7972beefadcd2534bdcd Mon Sep 17 00:00:00 2001
From: Boris Kolpackov <boris@codesynthesis.com>
Date: Tue, 1 Nov 2011 12:41:02 +0200
Subject: Implement support for optimistic concurrency

New pragmas: optimistic, version. New test: optimistic. New database
function: reload().
---
 odb/sqlite/object-statements.hxx | 75 +++++++++++++++++++++++++++++++++++++---
 odb/sqlite/object-statements.txx | 21 +++++++++--
 odb/sqlite/statement.cxx         |  6 ++--
 odb/sqlite/statement.hxx         |  3 +-
 4 files changed, 94 insertions(+), 11 deletions(-)

diff --git a/odb/sqlite/object-statements.hxx b/odb/sqlite/object-statements.hxx
index 53445b8..34a6a5e 100644
--- a/odb/sqlite/object-statements.hxx
+++ b/odb/sqlite/object-statements.hxx
@@ -106,6 +106,31 @@ namespace odb
       bool locked_;
     };
 
+    template <typename T, bool optimistic>
+    struct optimistic_data;
+
+    template <typename T>
+    struct optimistic_data<T, true>
+    {
+      typedef T object_type;
+      typedef odb::object_traits<object_type> object_traits;
+
+      optimistic_data (bind*);
+
+      // The id + optimistic column binding.
+      //
+      std::size_t id_image_version_;
+      binding id_image_binding_;
+
+      details::shared_ptr<delete_statement> erase_;
+    };
+
+    template <typename T>
+    struct optimistic_data<T, false>
+    {
+      optimistic_data (bind*) {}
+    };
+
     template <typename T>
     class object_statements: public object_statements_base
     {
@@ -257,6 +282,17 @@ namespace odb
       binding&
       id_image_binding () {return id_image_binding_;}
 
+      // Optimistic id + managed column image binding.
+      //
+      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_;}
+
       // Statements.
       //
       insert_statement_type&
@@ -328,6 +364,23 @@ namespace odb
         return *erase_;
       }
 
+      delete_statement_type&
+      optimistic_erase_statement ()
+      {
+        if (od_.erase_ == 0)
+        {
+          od_.erase_.reset (
+            new (details::shared) delete_statement_type (
+              conn_,
+              object_traits::optimistic_erase_statement,
+              od_.id_image_binding_));
+
+          od_.erase_->cached (true);
+        }
+
+        return *od_.erase_;
+      }
+
       // Container statement cache.
       //
       container_statement_cache_type&
@@ -349,14 +402,15 @@ namespace odb
 
     private:
       // select = total
-      // insert = total - inverse
-      // update = total - inverse - id - readonly
+      // insert = total - inverse - managed_optimistic
+      // update = total - inverse - managed_optimistic - id - readonly
       //
       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::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;
@@ -364,6 +418,9 @@ namespace odb
       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;
+
     private:
       container_statement_cache_type container_statement_cache_;
 
@@ -385,12 +442,16 @@ namespace odb
       // Update binding. Note that the id suffix is bound to id_image_
       // below instead of image_ which makes this binding effectively
       // bound to two images. As a result, we have to track versions
-      // for both of them.
+      // for both of them. If this object uses optimistic concurrency,
+      // then the binding for the managed column (version, timestamp,
+      // etc) comes after the id and the image for such a column is
+      // stored as part of the id image.
       //
       std::size_t update_image_version_;
       std::size_t update_id_image_version_;
       binding update_image_binding_;
-      bind update_image_bind_[update_column_count + id_column_count];
+      bind update_image_bind_[update_column_count + id_column_count +
+                              managed_optimistic_column_count];
 
       // Id image binding (only used as a parameter). Uses the suffix in
       // the update bind.
@@ -399,6 +460,10 @@ namespace odb
       std::size_t id_image_version_;
       binding id_image_binding_;
 
+      // Extra data for objects with optimistic concurrency support.
+      //
+      optimistic_data<T, managed_optimistic_column_count != 0> od_;
+
       details::shared_ptr<insert_statement_type> persist_;
       details::shared_ptr<select_statement_type> find_;
       details::shared_ptr<update_statement_type> update_;
diff --git a/odb/sqlite/object-statements.txx b/odb/sqlite/object-statements.txx
index 7c5c178..7132296 100644
--- a/odb/sqlite/object-statements.txx
+++ b/odb/sqlite/object-statements.txx
@@ -17,6 +17,21 @@ namespace odb
   namespace sqlite
   {
     //
+    // optimistic_data
+    //
+
+    template <typename T>
+    optimistic_data<T, true>::
+    optimistic_data (bind* b)
+        : id_image_binding_ (
+            b,
+            object_traits::id_column_count +
+            object_traits::managed_optimistic_column_count)
+    {
+      id_image_version_ = 0;
+    }
+
+    //
     // object_statements
     //
 
@@ -34,9 +49,11 @@ namespace odb
           select_image_binding_ (select_image_bind_, select_column_count),
           insert_image_binding_ (insert_image_bind_, insert_column_count),
           update_image_binding_ (update_image_bind_,
-                                 update_column_count + id_column_count),
+                                 update_column_count + id_column_count +
+                                 managed_optimistic_column_count),
           id_image_binding_ (update_image_bind_ + update_column_count,
-                             id_column_count)
+                             id_column_count),
+          od_ (update_image_bind_ + update_column_count)
     {
       image_.version = 0;
       select_image_version_ = 0;
diff --git a/odb/sqlite/statement.cxx b/odb/sqlite/statement.cxx
index a7ace7a..a33ebcb 100644
--- a/odb/sqlite/statement.cxx
+++ b/odb/sqlite/statement.cxx
@@ -389,7 +389,7 @@ namespace odb
     {
     }
 
-    void update_statement::
+    unsigned long long update_statement::
     execute ()
     {
       bind_param (param_.bind, param_.count);
@@ -410,8 +410,8 @@ namespace odb
       if (e != SQLITE_DONE)
         translate_error (e, conn_);
 
-      if (sqlite3_changes (conn_.handle ()) == 0)
-        throw object_not_persistent ();
+      return static_cast<unsigned long long> (
+        sqlite3_changes (conn_.handle ()));
     }
 
     // delete_statement
diff --git a/odb/sqlite/statement.hxx b/odb/sqlite/statement.hxx
index 2bf770c..4fad1a3 100644
--- a/odb/sqlite/statement.hxx
+++ b/odb/sqlite/statement.hxx
@@ -311,7 +311,8 @@ namespace odb
       update_statement (connection& conn,
                         const std::string& statement,
                         binding& param);
-      void
+
+      unsigned long long
       execute ();
 
     private:
-- 
cgit v1.1