diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2011-11-01 12:41:02 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2011-11-01 12:41:02 +0200 |
commit | b820128512133c443799b27265e28a27622497e6 (patch) | |
tree | b17cfca63277072f100455e43164acd74c1bc5dd | |
parent | 1adbed32a10e327bc1ab108133fcd2961ea1e07e (diff) |
Implement support for optimistic concurrency
New pragmas: optimistic, version. New test: optimistic. New database
function: reload().
-rw-r--r-- | odb/pgsql/object-statements.hxx | 87 | ||||
-rw-r--r-- | odb/pgsql/object-statements.txx | 32 | ||||
-rw-r--r-- | odb/pgsql/statement.cxx | 48 | ||||
-rw-r--r-- | odb/pgsql/statement.hxx | 2 |
4 files changed, 133 insertions, 36 deletions
diff --git a/odb/pgsql/object-statements.hxx b/odb/pgsql/object-statements.hxx index f80ab42..040f7bd 100644 --- a/odb/pgsql/object-statements.hxx +++ b/odb/pgsql/object-statements.hxx @@ -106,6 +106,32 @@ 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*, char** nv, int* nl, int* nf); + + // The id + optimistic column binding. + // + std::size_t id_image_version_; + binding id_image_binding_; + native_binding id_image_native_binding_; + + details::shared_ptr<delete_statement> erase_; + }; + + template <typename T> + struct optimistic_data<T, false> + { + optimistic_data (bind*, char**, int*, int*) {} + }; + template <typename T> class object_statements: public object_statements_base { @@ -256,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& @@ -336,6 +373,25 @@ 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_name, + object_traits::optimistic_erase_statement, + object_traits::optimistic_erase_statement_types, + id_column_count + managed_optimistic_column_count, + od_.id_image_binding_, + od_.id_image_native_binding_)); + } + + return *od_.erase_; + } + // Container statement cache. // container_statement_cache_type& @@ -357,14 +413,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; @@ -372,6 +429,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_; @@ -397,16 +457,23 @@ 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]; native_binding update_image_native_binding_; - char* update_image_values_[update_column_count + id_column_count]; - int update_image_lengths_[update_column_count + id_column_count]; - int update_image_formats_[update_column_count + id_column_count]; + char* update_image_values_[update_column_count + id_column_count + + managed_optimistic_column_count]; + int update_image_lengths_[update_column_count + id_column_count + + managed_optimistic_column_count]; + int update_image_formats_[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. @@ -416,6 +483,10 @@ namespace odb binding id_image_binding_; native_binding id_image_native_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/pgsql/object-statements.txx b/odb/pgsql/object-statements.txx index 0e61715..697dd76 100644 --- a/odb/pgsql/object-statements.txx +++ b/odb/pgsql/object-statements.txx @@ -17,6 +17,25 @@ namespace odb namespace pgsql { // + // optimistic_data + // + + template <typename T> + optimistic_data<T, true>:: + optimistic_data (bind* b, char** nv, int* nl, int* nf) + : id_image_binding_ ( + b, + object_traits::id_column_count + + object_traits::managed_optimistic_column_count), + id_image_native_binding_ ( + nv, nl, nf, + object_traits::id_column_count + + object_traits::managed_optimistic_column_count) + { + id_image_version_ = 0; + } + + // // object_statements // @@ -41,11 +60,13 @@ namespace odb insert_column_count), // update update_image_binding_ (update_image_bind_, - update_column_count + id_column_count), + update_column_count + id_column_count + + managed_optimistic_column_count), update_image_native_binding_ (update_image_values_, update_image_lengths_, update_image_formats_, - update_column_count + id_column_count), + update_column_count + id_column_count + + managed_optimistic_column_count), // id id_image_binding_ (update_image_bind_ + update_column_count, id_column_count), @@ -53,7 +74,12 @@ namespace odb update_image_values_ + update_column_count, update_image_lengths_ + update_column_count, update_image_formats_ + update_column_count, - id_column_count) + id_column_count), + // optimistic data + od_ (update_image_bind_ + update_column_count, + update_image_values_ + update_column_count, + update_image_lengths_ + update_column_count, + update_image_formats_ + update_column_count) { image_.version = 0; select_image_version_ = 0; diff --git a/odb/pgsql/statement.cxx b/odb/pgsql/statement.cxx index f119fd0..049a00d 100644 --- a/odb/pgsql/statement.cxx +++ b/odb/pgsql/statement.cxx @@ -9,8 +9,6 @@ #include <libpq-fe.h> -#include <odb/exceptions.hxx> // object_not_persistent - #include <odb/pgsql/pgsql-oid.hxx> #include <odb/pgsql/statement.hxx> #include <odb/pgsql/connection.hxx> @@ -28,6 +26,26 @@ namespace odb { using namespace details; + static unsigned long long + affected_row_count (PGresult* h) + { + const char* s (PQcmdTuples (h)); + unsigned long long count; + + if (s[0] != '\0' && s[1] == '\0') + count = static_cast<unsigned long long> (s[0] - '0'); + else + { + // @@ Using stringstream conversion for now. See if we can optimize + // this (atoll possibly, even though it is not standard). + // + istringstream ss (s); + ss >> count; + } + + return count; + } + // // statement // @@ -102,7 +120,7 @@ namespace odb continue; } - n.values[i] = reinterpret_cast<char*> (current_bind.buffer); + n.values[i] = static_cast<char*> (current_bind.buffer); size_t l (0); @@ -547,7 +565,7 @@ namespace odb { } - void update_statement:: + unsigned long long update_statement:: execute () { bind_param (native_param_, param_); @@ -564,11 +582,7 @@ namespace odb if (!is_good_result (h)) translate_error (conn_, h); - // PQcmdTuples returns a string representing the number of matching - // rows found. "0" indicates no match. - // - if (PQcmdTuples (h)[0] == '0') - throw object_not_persistent (); + return affected_row_count (h); } // @@ -625,21 +639,7 @@ namespace odb if (!is_good_result (h)) translate_error (conn_, h); - const char* s (PQcmdTuples (h)); - unsigned long long count; - - if (s[0] != '\0' && s[1] == '\0') - count = static_cast<unsigned long long> (s[0] - '0'); - else - { - // @@ Using stringstream conversion for now. See if we can optimize - // this (atoll possibly, even though it is not standard). - // - istringstream ss (s); - ss >> count; - } - - return count; + return affected_row_count (h); } } } diff --git a/odb/pgsql/statement.hxx b/odb/pgsql/statement.hxx index 0432920..8beb01f 100644 --- a/odb/pgsql/statement.hxx +++ b/odb/pgsql/statement.hxx @@ -221,7 +221,7 @@ namespace odb binding& param, native_binding& native_param); - void + unsigned long long execute (); private: |