From: Date: September 25 2007 4:42pm Subject: Error num in BadQuery patch List-Archive: http://lists.mysql.com/plusplus/7051 Message-Id: <5AA52B773286DA4E83B1F2D034FFED3724727B@mailexchange.klausatlanta.local> MIME-Version: 1.0 Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: quoted-printable 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 =3D !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. =20 class MYSQLPP_EXPORT BadQuery : public Exception { public: - /// \brief Create exception object, taking C string - explicit BadQuery(const char* w =3D "") : - Exception(w) + int errnum; ///< error number associated with execption +=09 + /// \brief Create exception object, taking C string and error number + explicit BadQuery(const char* w =3D "", int err =3D 0) : + Exception(w), errnum(err) { } =20 - /// \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 =3D 0) : + Exception(w), errnum(err) { } +=09 }; =20 =20 /// \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(); } =20 - std::string Query::error() { return conn_->error(); } =20 @@ -95,13 +94,13 @@ bool Query::exec(const std::string& str) { success_ =3D !mysql_real_query(&conn_->mysql_, str.data(), static_cast(str.length())); if (!success_ && throw_exceptions()) { - throw BadQuery(error()); + throw BadQuery(error(),errnum()); } else { return success_; } } =20 @@ -149,13 +148,13 @@ =20 unlock(); if (success_) { return ResNSel(conn_); } else if (throw_exceptions()) { - throw BadQuery(error()); + throw BadQuery(error(),errnum()); } else { return ResNSel(); } } =20 @@ -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(); } } =20 @@ -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()); }=20 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(); } } =20 --- 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=3D(const Query& rhs); =20 + /// \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=20 + /// 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" =20 +#include + #include #include #include =20 namespace mysqlpp { =20 @@ -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 =3D 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 =3D 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; } } =20