diff options
-rw-r--r-- | odb/mysql/forward.hxx | 4 | ||||
-rw-r--r-- | odb/mysql/statement.cxx | 255 | ||||
-rw-r--r-- | odb/mysql/statement.hxx | 223 | ||||
-rw-r--r-- | odb/mysql/statement.txx | 45 |
4 files changed, 470 insertions, 57 deletions
diff --git a/odb/mysql/forward.hxx b/odb/mysql/forward.hxx index 41baa1c..701627b 100644 --- a/odb/mysql/forward.hxx +++ b/odb/mysql/forward.hxx @@ -14,6 +14,10 @@ namespace odb class connection; class connection_factory; class transaction; + class binding; + + template <typename T> + class object_statements; } } diff --git a/odb/mysql/statement.cxx b/odb/mysql/statement.cxx index 68449ae..e008d17 100644 --- a/odb/mysql/statement.cxx +++ b/odb/mysql/statement.cxx @@ -3,10 +3,15 @@ // copyright : Copyright (c) 2005-2010 Code Synthesis Tools CC // license : GNU GPL v2; see accompanying LICENSE file +#include <mysql/mysqld_error.h> // ER_DUP_ENTRY + #include <new> // std::bad_alloc #include <odb/mysql/statement.hxx> #include <odb/mysql/connection.hxx> +#include <odb/mysql/exceptions.hxx> + +using namespace std; namespace odb { @@ -22,7 +27,7 @@ namespace odb stmt_ = mysql_stmt_init (conn_.handle ()); if (stmt_ == 0) - throw std::bad_alloc (); + throw bad_alloc (); } statement:: @@ -31,6 +36,254 @@ namespace odb mysql_stmt_close (stmt_); } + // insert_statement + // + + insert_statement:: + ~insert_statement () + { + } + + insert_statement:: + insert_statement (connection& conn, + const string& query, + binding& image) + : statement (conn), image_ (image), version_ (0) + { + if (mysql_stmt_prepare (stmt_, query.c_str (), query.size ()) != 0) + throw database_exception (stmt_); + } + + void insert_statement:: + execute () + { + if (mysql_stmt_reset (stmt_)) + throw database_exception (stmt_); + + if (version_ != image_.version) + { + if (mysql_stmt_bind_param (stmt_, image_.bind)) + throw database_exception (stmt_); + + version_ = image_.version; + } + + if (mysql_stmt_execute (stmt_)) + { + unsigned int e (mysql_stmt_errno (stmt_)); + + if (e == ER_DUP_ENTRY) + throw object_already_persistent (); + else + throw database_exception (stmt_); + } + + /* + @@ Should I throw unknown error or some such? + + if (mysql_stmt_affected_rows (stmt_) != 1) + throw object_already_persistent (); + */ + } + + // select_statement + // + + select_statement:: + ~select_statement () + { + } + + select_statement:: + select_statement (connection& conn, + const string& query, + binding& id, + binding& image) + : statement (conn), + id_ (id), + id_version_ (0), + image_ (image), + image_version_ (0) + { + if (mysql_stmt_prepare (stmt_, query.c_str (), query.size ()) != 0) + throw database_exception (stmt_); + } + + select_statement::result select_statement:: + execute () + { + if (mysql_stmt_reset (stmt_)) + throw database_exception (stmt_); + + if (id_version_ != id_.version) + { + if (mysql_stmt_bind_param (stmt_, id_.bind)) + throw database_exception (stmt_); + + id_version_ = id_.version; + } + + if (image_version_ != image_.version) + { + if (mysql_stmt_bind_result (stmt_, image_.bind)) + throw database_exception (stmt_); + + image_version_ = image_.version; + } + + if (mysql_stmt_execute (stmt_)) + throw database_exception (stmt_); + + int r (mysql_stmt_fetch (stmt_)); + + switch (r) + { + case 0: + { + return success; + } + case 1: + { + throw database_exception (stmt_); + } + case MYSQL_NO_DATA: + { + free_result (); + return no_data; + } + case MYSQL_DATA_TRUNCATED: + { + return truncated; + } + } + } + + void select_statement:: + refetch () + { + // Re-fetch columns that were truncated. + // + for (size_t i (0); i < image_.count; ++i) + { + if (*image_.bind[i].error) + { + *image_.bind[i].error = 0; + + if (mysql_stmt_fetch_column ( + stmt_, image_.bind + i, static_cast<unsigned int> (i), 0)) + throw database_exception (stmt_); + } + } + } + + void select_statement:: + free_result () + { + if (mysql_stmt_free_result (stmt_)) + throw database_exception (stmt_); + } + + // update_statement + // + + update_statement:: + ~update_statement () + { + } + + update_statement:: + update_statement (connection& conn, + const string& query, + binding& id, + binding& image) + : statement (conn), + id_ (id), + id_version_ (0), + image_ (image), + image_version_ (0) + { + if (mysql_stmt_prepare (stmt_, query.c_str (), query.size ()) != 0) + throw database_exception (stmt_); + } + + void update_statement:: + execute () + { + if (mysql_stmt_reset (stmt_)) + throw database_exception (stmt_); + + if (image_version_ != image_.version || id_version_ != id_.version) + { + // Here we assume that the last element in image_.bind is the + // id parameter. + // + if (mysql_stmt_bind_param (stmt_, image_.bind)) + throw database_exception (stmt_); + + id_version_ = id_.version; + image_version_ = image_.version; + } + + if (mysql_stmt_execute (stmt_)) + throw database_exception (stmt_); + + my_ulonglong r (mysql_stmt_affected_rows (stmt_)); + + if (r > 0) + return; + + if (r == 0) + throw object_not_persistent (); + else + throw database_exception (stmt_); + } + + // delete_statement + // + + delete_statement:: + ~delete_statement () + { + } + + delete_statement:: + delete_statement (connection& conn, + const string& query, + binding& id) + : statement (conn), id_ (id), version_ (0) + { + if (mysql_stmt_prepare (stmt_, query.c_str (), query.size ()) != 0) + throw database_exception (stmt_); + } + + void delete_statement:: + execute () + { + if (mysql_stmt_reset (stmt_)) + throw database_exception (stmt_); + + if (version_ != id_.version) + { + if (mysql_stmt_bind_param (stmt_, id_.bind)) + throw database_exception (stmt_); + + version_ = id_.version; + } + + if (mysql_stmt_execute (stmt_)) + throw database_exception (stmt_); + + my_ulonglong r (mysql_stmt_affected_rows (stmt_)); + + if (r > 0) + return; + + if (r == 0) + throw object_not_persistent (); + else + throw database_exception (stmt_); + } + // object_statements_base // diff --git a/odb/mysql/statement.hxx b/odb/mysql/statement.hxx index ab6ab9b..4925269 100644 --- a/odb/mysql/statement.hxx +++ b/odb/mysql/statement.hxx @@ -10,6 +10,7 @@ #include <map> #include <string> +#include <cstddef> // std::size_t #include <typeinfo> #include <odb/forward.hxx> @@ -22,6 +23,23 @@ namespace odb { class connection; + class binding + { + public: + binding (MYSQL_BIND* b, std::size_t n) + : bind (b), count (n), version (0) + { + } + + MYSQL_BIND* bind; + std::size_t count; + std::size_t version; + + private: + binding (const binding&); + binding& operator= (const binding&); + }; + class statement: public shared_base { public: @@ -36,27 +54,15 @@ namespace odb MYSQL_STMT* stmt_; }; - template <typename T> class insert_statement: public statement { public: - typedef T object_type; - typedef odb::object_traits<T> object_traits; - typedef typename object_traits::image_type image_type; - - public: virtual ~insert_statement (); insert_statement (connection& conn, const std::string& query, - image_type&); - - image_type& - image () - { - return image_; - } + binding& image); void execute (); @@ -66,8 +72,97 @@ namespace odb insert_statement& operator= (const insert_statement&); private: - image_type& image_; - MYSQL_BIND bind_[object_traits::column_count]; + binding& image_; + std::size_t version_; + }; + + class select_statement: public statement + { + public: + virtual + ~select_statement (); + + select_statement (connection& conn, + const std::string& query, + binding& id, + binding& image); + enum result + { + success, + no_data, + truncated + }; + + // You are expected to call free_result() if this function + // returns success or truncated. + // + result + execute (); + + void + refetch (); + + void + free_result (); + + + private: + select_statement (const select_statement&); + select_statement& operator= (const select_statement&); + + private: + binding& id_; + std::size_t id_version_; + + binding& image_; + std::size_t image_version_; + }; + + class update_statement: public statement + { + public: + virtual + ~update_statement (); + + update_statement (connection& conn, + const std::string& query, + binding& id, + binding& image); + void + execute (); + + private: + update_statement (const update_statement&); + update_statement& operator= (const update_statement&); + + private: + binding& id_; + std::size_t id_version_; + + binding& image_; + std::size_t image_version_; + }; + + class delete_statement: public statement + { + public: + virtual + ~delete_statement (); + + delete_statement (connection& conn, + const std::string& query, + binding& id); + + void + execute (); + + private: + delete_statement (const delete_statement&); + delete_statement& operator= (const delete_statement&); + + private: + binding& id_; + std::size_t version_; }; // Statement cache. @@ -93,27 +188,111 @@ namespace odb class object_statements: public object_statements_base { public: - typedef typename object_traits<T>::image_type image_type; + typedef odb::object_traits<T> object_traits; + typedef typename object_traits::image_type image_type; + typedef typename object_traits::id_image_type id_image_type; + + object_statements (connection&); + + image_type& + image () + { + return image_; + } + + binding& + image_binding () + { + return image_binding_; + } + + my_bool* + image_error () + { + return image_error_; + } + + id_image_type& + id_image () + { + return id_image_; + } - object_statements (connection& conn) - : object_statements_base (conn) + binding& + id_image_binding () { + return id_image_binding_; } - insert_statement<T>& + insert_statement& insert () { if (insert_ == 0) insert_.reset ( - new (shared) insert_statement<T> ( - conn_, object_traits<T>::insert_query, image_)); + new (shared) insert_statement ( + conn_, object_traits::insert_query, image_binding_)); return *insert_; } + select_statement& + select () + { + if (select_ == 0) + select_.reset ( + new (shared) select_statement ( + conn_, + object_traits::select_query, + id_image_binding_, + image_binding_)); + + return *select_; + } + + update_statement& + update () + { + if (update_ == 0) + update_.reset ( + new (shared) update_statement ( + conn_, + object_traits::update_query, + id_image_binding_, + image_binding_)); + + return *update_; + } + + delete_statement& + delete_ () + { + if (del_ == 0) + del_.reset ( + new (shared) delete_statement ( + conn_, + object_traits::delete_query, + id_image_binding_)); + + return *del_; + } + private: + // The last element is the id parameter. The update statement + // depends on this being one contiguous arrays. + // + MYSQL_BIND image_bind_[object_traits::column_count + 1]; + image_type image_; - odb::shared_ptr<insert_statement<T> > insert_; + my_bool image_error_[object_traits::column_count]; + binding image_binding_; + + id_image_type id_image_; + binding id_image_binding_; + + odb::shared_ptr<insert_statement> insert_; + odb::shared_ptr<select_statement> select_; + odb::shared_ptr<update_statement> update_; + odb::shared_ptr<delete_statement> del_; }; struct type_info_comparator diff --git a/odb/mysql/statement.txx b/odb/mysql/statement.txx index 4f82942..95579b4 100644 --- a/odb/mysql/statement.txx +++ b/odb/mysql/statement.txx @@ -6,48 +6,25 @@ #include <cstddef> // std::size_t #include <cstring> // std::memset -#include <odb/mysql/exceptions.hxx> - namespace odb { namespace mysql { - template <typename T> - insert_statement<T>:: - ~insert_statement () - { - } - - template <typename T> - insert_statement<T>:: - insert_statement (connection& conn, - const std::string& query, - image_type& image) - : statement (conn), image_ (image) - { - if (mysql_stmt_prepare (stmt_, query.c_str (), query.size ()) != 0) - throw database_exception (stmt_); - - std::memset (bind_, 0, sizeof (bind_)); - } + // object_statements + // template <typename T> - void insert_statement<T>:: - execute () + object_statements<T>:: + object_statements (connection& conn) + : object_statements_base (conn), + image_binding_ (image_bind_, object_traits::column_count), + id_image_binding_ (image_bind_ + object_traits::column_count, 1) { - if (mysql_stmt_reset (stmt_)) - throw database_exception (stmt_); - - object_traits::bind (bind_, image_); - - if (mysql_stmt_bind_param (stmt_, bind_)) - throw database_exception (stmt_); - - if (mysql_stmt_execute (stmt_)) - throw database_exception (stmt_); + std::memset (image_bind_, 0, sizeof (image_bind_)); + std::memset (image_error_, 0, sizeof (image_error_)); - if (mysql_stmt_affected_rows (stmt_) != 1) - throw object_already_persistent (); + for (std::size_t i (0); i < object_traits::column_count; ++i) + image_bind_[i].error = image_error_ + i; } } } |