aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--odb/mysql/forward.hxx4
-rw-r--r--odb/mysql/statement.cxx255
-rw-r--r--odb/mysql/statement.hxx223
-rw-r--r--odb/mysql/statement.txx45
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;
}
}
}