List:MySQL++« Previous MessageNext Message »
From:Jim Wallace Date:September 26 2007 1:29pm
Subject:RE: Error num in BadQuery patch
View as plain text  
Some of you may have thought this was pretty bogus.  I was thinking that
too when doing to more testing, but digging in found the patch is still
valid.

However, what I thought was causing mysql_errno to be cleared was
incorrect.  I thought the only API called after the error was
mysql_error. Instead it is a successful rollback in ~Transaction that
sets mysql_errno to zero.  If you are using the Transaction class and
get a BadQuery exception, the query/conn.errnum() in the catch is *not*
that of the problem, but of the rollback.  My change is still quite
useful since it captures the errnum associated with the error string in
the BadQuery instead of losing it.  Here's the test case

try
{
	Transaction trans(con);
	query.execute( ... insert dupe key ... )
	trans.commit();
}
catch ( BadQuery e ) // tran is destroyed when goes out of scope,
calling rollback,
			   //                      which sets errnum to
zero
{
	// prints correct error message
	cerr << "BadQuery Error: " << e2.what() << endl;		

	// at this point we've lost the errnum for the real problem
since
	// the rollback call was successful so errnum() 
	// is always zero (unless it was a connection issue or something
	// that also caused the rollback to fail)
	cerr << "Error num is " << con.errnum() << endl; 
	
}

-----Original Message-----
From: Jim Wallace 
Sent: Tuesday, September 25, 2007 10:43 AM
To: 'plusplus@stripped'
Subject: Error num in BadQuery patch

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;
 		}
 	}
 
Thread
RELEASE: v2.3.2Warren Young11 Jul
  • Getting errnum() in exception?Jim Wallace20 Sep
    • Re: Getting errnum() in exception?Warren Young22 Sep
  • Building the head of SVN with MSVC?Jim Wallace25 Sep
    • Re: Building the head of SVN with MSVC?Warren Young26 Sep
  • Error num in BadQuery patchJim Wallace25 Sep
    • RE: Error num in BadQuery patchJim Wallace26 Sep
      • Re: Error num in BadQuery patchWarren Young26 Sep
    • Re: Error num in BadQuery patchWarren Young26 Sep
  • v2.3.2 and execute?Jim Wallace5 Oct
    • Re: v2.3.2 and execute?Warren Young5 Oct
  • Building svn tip on WindowsJim Wallace24 Oct
    • Re: Building svn tip on WindowsWarren Young25 Oct
  • Patch for better exception use -- BadQuery w/Errnum patch part 1Jim Wallace24 Oct
  • Better exception usage -- BadQuery w/Errnum patch part 2Jim Wallace24 Oct
  • Expose errnum() in query -- BadQuery w/Errnum patch part 3Jim Wallace24 Oct
    • Re: Expose errnum() in query -- BadQuery w/Errnum patch part 3Warren Young25 Oct
      • RE: Expose errnum() in query -- BadQuery w/Errnum patch part 3Jim Wallace25 Oct
  • BadQuery w/Errnum patch part 4Jim Wallace24 Oct
    • Re: BadQuery w/Errnum patch part 4Warren Young25 Oct
      • RE: BadQuery w/Errnum patch part 4Jim Wallace25 Oct
  • Sample files -- BadQuery w/Errnum patch part 4Jim Wallace24 Oct
    • Re: Sample files -- BadQuery w/Errnum patch part 4Warren Young25 Oct
      • RE: Sample files -- BadQuery w/Errnum patch part 4Jim Wallace25 Oct
        • Re: Sample files -- BadQuery w/Errnum patch part 4Warren Young25 Oct
          • RE: Sample files -- BadQuery w/Errnum patch part 4Jim Wallace25 Oct
          • RE: Sample files -- BadQuery w/Errnum patch part 4Jim Wallace25 Oct
            • Re: Sample files -- BadQuery w/Errnum patch part 4Warren Young25 Oct
              • RE: Sample files -- BadQuery w/Errnum patch part 4Jim Wallace25 Oct
      • RE: Sample files -- BadQuery w/Errnum patch part 4Jim Wallace25 Oct