From b820128512133c443799b27265e28a27622497e6 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov 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/pgsql/object-statements.hxx | 87 +++++++++++++++++++++++++++++++++++++---- odb/pgsql/object-statements.txx | 32 +++++++++++++-- odb/pgsql/statement.cxx | 48 +++++++++++------------ 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 + struct optimistic_data; + + template + struct optimistic_data + { + typedef T object_type; + typedef odb::object_traits 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 erase_; + }; + + template + struct optimistic_data + { + optimistic_data (bind*, char**, int*, int*) {} + }; + template 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 od_; + details::shared_ptr persist_; details::shared_ptr find_; details::shared_ptr 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 + optimistic_data:: + 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 -#include // object_not_persistent - #include #include #include @@ -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 (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 (current_bind.buffer); + n.values[i] = static_cast (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 (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: -- cgit v1.1