As for background of this, I needed it in two cases. 1) Detecting
duplicate keys, and 2) adding retry logic in the event of a deadlock
(and, yes, I know how and why they should be avoided but fault-tolerant
code is good). I have created a sample to test this with a deadlock.
Quite simply, this gets the errnum from the API so it can be checked
when a BadQuery exception is caught. You can not call the connection's
errnum after the exception is thrown since when BadQuery is constructed
it calls the API to get the error as a string, which clears the error
number.
This is against the 2.3.2 codebase since I couldn't get the head of svn
to build. I'd be happy to re-do the patch against the head if you think
this is a useful feature, after I can build the head. This is my first
attempt at this, so any constructive criticism is welcome.
jmw
--- connection.cpp 2007-07-11 17:37:16.000000000 -0400
+++ connection.cpp 2007-09-25 07:31:39.000000000 -0400
@@ -300,21 +300,21 @@
bool suc = !mysql_reload(&mysql_);
if (throw_exceptions() && !suc) {
// Reloading grant tables through this API isn't
precisely a
// query, but it's acceptable to signal errors
with BadQuery
// because the new mechanism is the FLUSH
PRIVILEGES query.
// A program won't have to change when doing it
the new way.
- throw BadQuery(error());
+ throw BadQuery(error(),errnum());
}
else {
return suc;
}
}
else {
if (throw_exceptions()) {
- throw BadQuery("MySQL++ connection not
established");
+ throw BadQuery("MySQL++ connection not
established",errnum());
}
else {
return false;
}
}
}
--- exceptions.h 2007-07-11 17:37:16.000000000 -0400
+++ exceptions.h 2007-09-25 08:54:07.000000000 -0400
@@ -242,23 +242,26 @@
/// SQL query. In v1.7, it was used as a more generic exception type,
/// for no particularly good reason.
class MYSQLPP_EXPORT BadQuery : public Exception
{
public:
- /// \brief Create exception object, taking C string
- explicit BadQuery(const char* w = "") :
- Exception(w)
+ int errnum; ///< error number associated with execption
+
+ /// \brief Create exception object, taking C string and error
number
+ explicit BadQuery(const char* w = "", int err = 0) :
+ Exception(w), errnum(err)
{
}
- /// \brief Create exception object, taking C++ string
- explicit BadQuery(const std::string& w) :
- Exception(w)
+ /// \brief Create exception object, taking C++ string and error
number
+ explicit BadQuery(const std::string& w, int err = 0) :
+ Exception(w), errnum(err)
{
}
+
};
/// \brief Exception thrown when there is a problem establishing the
/// database server connection. It's also thrown if
/// Connection::shutdown() fails.
--- query.cpp 2007-07-11 17:37:16.000000000 -0400
+++ query.cpp 2007-09-25 09:05:12.000000000 -0400
@@ -81,13 +81,12 @@
my_ulonglong
Query::affected_rows() const
{
return conn_->affected_rows();
}
-
std::string
Query::error()
{
return conn_->error();
}
@@ -95,13 +94,13 @@
bool
Query::exec(const std::string& str)
{
success_ = !mysql_real_query(&conn_->mysql_, str.data(),
static_cast<unsigned long>(str.length()));
if (!success_ && throw_exceptions()) {
- throw BadQuery(error());
+ throw BadQuery(error(),errnum());
}
else {
return success_;
}
}
@@ -149,13 +148,13 @@
unlock();
if (success_) {
return ResNSel(conn_);
}
else if (throw_exceptions()) {
- throw BadQuery(error());
+ throw BadQuery(error(),errnum());
}
else {
return ResNSel();
}
}
@@ -463,13 +462,13 @@
// that doesn't always return results. While it's better to use
// exec*() in that situation, it's legal to call store()
instead,
// and sometimes you have no choice. For example, if the SQL
comes
// from outside the program so you can't predict whether there
will
// be results.
if (conn_->errnum() && throw_exceptions()) {
- throw BadQuery(error());
+ throw BadQuery(error(),errnum());
}
else {
return Result();
}
}
@@ -510,25 +509,25 @@
else {
// Result set is null, but throw an exception
only i it is
// null because of some error. If not, it's
just an empty
// result set, which is harmless. We return an
empty result
// set if exceptions are disabled, as well.
if (conn_->errnum() && throw_exceptions()) {
- throw BadQuery(error());
+ throw BadQuery(error(),errnum());
}
else {
return Result();
}
}
}
else {
// No more results, or some other error occurred.
unlock();
if (throw_exceptions()) {
if (ret > 0) {
- throw BadQuery(error());
+ throw BadQuery(error(),errnum());
}
else {
throw EndOfResultSets();
}
}
else {
@@ -630,13 +629,13 @@
// that doesn't always return results. While it's better to use
// exec*() in that situation, it's legal to call use() instead,
and
// sometimes you have no choice. For example, if the SQL comes
// from outside the program so you can't predict whether there
will
// be results.
if (conn_->errnum() && throw_exceptions()) {
- throw BadQuery(error());
+ throw BadQuery(error(),errnum());
}
else {
return ResUse();
}
}
--- query.h 2007-07-11 17:37:16.000000000 -0400
+++ query.h 2007-09-25 08:53:49.000000000 -0400
@@ -150,12 +150,20 @@
/// \brief Assign another query's state to this object
///
/// The same caveats apply to this operator as apply to the copy
/// ctor.
Query& operator=(const Query& rhs);
+ /// \brief Get the last error number.
+ ///
+ /// This just delegates to Connection::errnum(). Query has
nothing
+ /// extra to say, so use either, as makes sense in your program.
+ /// If you want the string *and* number you must
+ /// call errnum() before calling error() since error() clears
errnum()
+ int errnum() const { return conn_->errnum(); }
+
/// \brief Get the last error message that was set.
///
/// This class has an internal error message string, but if it
/// isn't set, we return the last error message that happened
/// on the connection we're bound to instead.
std::string error();
--- result.h 2007-07-11 17:37:16.000000000 -0400
+++ result.h 2007-09-25 09:04:38.000000000 -0400
@@ -35,12 +35,14 @@
#include "field_names.h"
#include "field_types.h"
#include "noexceptions.h"
#include "resiter.h"
#include "row.h"
+#include <mysqld_error.h>
+
#include <map>
#include <set>
#include <string>
namespace mysqlpp {
@@ -103,13 +105,13 @@
/// This is not a thin wrapper. It does a lot of error checking
before
/// returning the mysqlpp::Row object containing the row data.
Row fetch_row()
{
if (!result_) {
if (throw_exceptions()) {
- throw BadQuery("Results not fetched");
+ throw BadQuery("Results not
fetched",ER_UNKNOWN_ERROR);
}
else {
return Row();
}
}
MYSQL_ROW row = mysql_fetch_row(result_);
@@ -370,13 +372,13 @@
/// \link mysqlpp::ResUse parent class \endlink . Why this
cannot
/// actually \e be in our parent class is beyond me.
const Row fetch_row() const
{
if (!result_) {
if (throw_exceptions()) {
- throw BadQuery("Results not fetched");
+ throw BadQuery("Results not
fetched",ER_UNKNOWN_ERROR);
}
else {
return Row();
}
}
MYSQL_ROW row = mysql_fetch_row(result_);
--- row.cpp 2007-07-11 17:37:16.000000000 -0400
+++ row.cpp 2007-09-25 08:56:16.000000000 -0400
@@ -35,13 +35,13 @@
OptionalExceptions(te),
res_(r),
initialized_(false)
{
if (!d || !r) {
if (throw_exceptions()) {
- throw BadQuery("ROW or RES is NULL");
+ throw BadQuery("ROW or RES is
NULL",ER_INVALID_USE_OF_NULL);
}
else {
return;
}
}