added Poco::Data::Session::isGood()

This commit is contained in:
Günter Obiltschnig 2020-01-13 20:13:27 +01:00
parent c04a1f28d9
commit 43b79ffdbe
13 changed files with 200 additions and 44 deletions

View File

@ -44,6 +44,9 @@ public:
MySQLException(const MySQLException& exc); MySQLException(const MySQLException& exc);
/// Creates MySQLException. /// Creates MySQLException.
MySQLException(const std::string& msg, int code);
/// Creates MySQLException.
~MySQLException() noexcept; ~MySQLException() noexcept;
/// Destroys MySQLexception. /// Destroys MySQLexception.

View File

@ -70,6 +70,9 @@ public:
void reset(); void reset();
/// Reset connection with dababase and clears session state, but without disconnecting /// Reset connection with dababase and clears session state, but without disconnecting
bool ping();
/// Checks if the connection is alive.
operator MYSQL* (); operator MYSQL* ();
private: private:

View File

@ -54,10 +54,10 @@ public:
/// for compress and auto-reconnect correct values are true/false /// for compress and auto-reconnect correct values are true/false
/// for port - numeric in decimal notation /// for port - numeric in decimal notation
/// ///
~SessionImpl(); ~SessionImpl();
/// Destroys the SessionImpl. /// Destroys the SessionImpl.
Poco::SharedPtr<Poco::Data::StatementImpl> createStatementImpl(); Poco::SharedPtr<Poco::Data::StatementImpl> createStatementImpl();
/// Returns an MySQL StatementImpl /// Returns an MySQL StatementImpl
@ -69,10 +69,22 @@ public:
void reset(); void reset();
/// Reset connection with dababase and clears session state, but without disconnecting /// Reset connection with dababase and clears session state, but without disconnecting
bool isConnected() const; bool isConnected() const;
/// Returns true if connected, false otherwise. /// Returns true if connected, false otherwise.
bool isGood() const;
/// Returns true iff the database session is good.
/// For the session to be considered good:
/// - it must be connected
/// - and it's last error code must be 0,
/// or mysql_ping() must be okay.
///
/// Furthermore, if the "failIfInnoReadOnly" property
/// has been set to true, the innodb_read_only setting
/// must be false. The flag is only checked if the
/// session has a non-zero error code.
void setConnectionTimeout(std::size_t timeout); void setConnectionTimeout(std::size_t timeout);
/// Sets the session connection timeout value. /// Sets the session connection timeout value.
@ -81,13 +93,13 @@ public:
void begin(); void begin();
/// Starts a transaction /// Starts a transaction
void commit(); void commit();
/// Commits and ends a transaction /// Commits and ends a transaction
void rollback(); void rollback();
/// Aborts a transaction /// Aborts a transaction
bool canTransact() const; bool canTransact() const;
/// Returns true if session has transaction capabilities. /// Returns true if session has transaction capabilities.
@ -107,7 +119,7 @@ public:
bool isTransactionIsolation(Poco::UInt32 ti) const; bool isTransactionIsolation(Poco::UInt32 ti) const;
/// Returns true iff the transaction isolation level corresponds /// Returns true iff the transaction isolation level corresponds
/// to the supplied bitmask. /// to the supplied bitmask.
void autoCommit(const std::string&, bool val); void autoCommit(const std::string&, bool val);
/// Sets autocommit property for the session. /// Sets autocommit property for the session.
@ -116,10 +128,24 @@ public:
void setInsertId(const std::string&, const Poco::Any&); void setInsertId(const std::string&, const Poco::Any&);
/// Try to set insert id - do nothing. /// Try to set insert id - do nothing.
Poco::Any getInsertId(const std::string&) const; Poco::Any getInsertId(const std::string&) const;
/// Get insert id /// Get insert id
void setFailIfInnoReadOnly(const std::string&, bool value);
/// Sets the "failIfInnoReadOnly" feature. If set, isGood() will
/// return false if the database is in read-only mode.
bool getFailIfInnoReadOnly(const std::string&) const;
/// Returns the state of the "failIfInnoReadOnly" feature.
void setLastError(int err);
/// Sets an error code. If a non-zero error code is set, the session
/// is considered bad.
int getLastError() const;
/// Returns the last set error code.
SessionHandle& handle(); SessionHandle& handle();
// Get handle // Get handle
@ -158,7 +184,9 @@ private:
mutable SessionHandle _handle; mutable SessionHandle _handle;
bool _connected; bool _connected;
bool _inTransaction; bool _inTransaction;
bool _failIfInnoReadOnly;
std::size_t _timeout; std::size_t _timeout;
mutable int _lastError;
Poco::FastMutex _mutex; Poco::FastMutex _mutex;
}; };
@ -183,6 +211,30 @@ inline Poco::Any SessionImpl::getInsertId(const std::string&) const
} }
inline void SessionImpl::setFailIfInnoReadOnly(const std::string&, bool value)
{
_failIfInnoReadOnly = value;
}
inline bool SessionImpl::getFailIfInnoReadOnly(const std::string&) const
{
return _failIfInnoReadOnly;
}
inline void SessionImpl::setLastError(int err)
{
_lastError = err;
}
inline int SessionImpl::getLastError() const
{
return _lastError;
}
inline SessionHandle& SessionImpl::handle() inline SessionHandle& SessionImpl::handle()
{ {
return _handle; return _handle;
@ -207,12 +259,6 @@ inline bool SessionImpl::isTransactionIsolation(Poco::UInt32 ti) const
} }
inline bool SessionImpl::isConnected() const
{
return _connected;
}
inline std::size_t SessionImpl::getConnectionTimeout() const inline std::size_t SessionImpl::getConnectionTimeout() const
{ {
return _timeout; return _timeout;

View File

@ -32,6 +32,11 @@ MySQLException::MySQLException(const MySQLException& exc) : Poco::Data::DataExce
} }
MySQLException::MySQLException(const std::string& msg, int code) : Poco::Data::DataException(std::string("[MySQL]: ") + msg, code)
{
}
MySQLException::~MySQLException() noexcept MySQLException::~MySQLException() noexcept
{ {
} }

View File

@ -21,10 +21,10 @@ namespace MySQL {
MySQLStatementImpl::MySQLStatementImpl(SessionImpl& h) : MySQLStatementImpl::MySQLStatementImpl(SessionImpl& h) :
Poco::Data::StatementImpl(h), Poco::Data::StatementImpl(h),
_stmt(h.handle()), _stmt(h.handle()),
_pBinder(new Binder), _pBinder(new Binder),
_pExtractor(new Extractor(_stmt, _metadata)), _pExtractor(new Extractor(_stmt, _metadata)),
_hasNext(NEXT_DONTKNOW) _hasNext(NEXT_DONTKNOW)
{ {
} }
@ -46,13 +46,13 @@ int MySQLStatementImpl::affectedRowCount() const
return _stmt.getAffectedRowCount(); return _stmt.getAffectedRowCount();
} }
const MetaColumn& MySQLStatementImpl::metaColumn(std::size_t pos) const const MetaColumn& MySQLStatementImpl::metaColumn(std::size_t pos) const
{ {
return _metadata.metaColumn(pos); return _metadata.metaColumn(pos);
} }
bool MySQLStatementImpl::hasNext() bool MySQLStatementImpl::hasNext()
{ {
if (_hasNext == NEXT_DONTKNOW) if (_hasNext == NEXT_DONTKNOW)
@ -79,11 +79,11 @@ bool MySQLStatementImpl::hasNext()
return false; return false;
} }
std::size_t MySQLStatementImpl::next() std::size_t MySQLStatementImpl::next()
{ {
if (!hasNext()) if (!hasNext())
throw StatementException("No data received"); throw StatementException("No data received");
Poco::Data::AbstractExtractionVec::iterator it = extractions().begin(); Poco::Data::AbstractExtractionVec::iterator it = extractions().begin();
Poco::Data::AbstractExtractionVec::iterator itEnd = extractions().end(); Poco::Data::AbstractExtractionVec::iterator itEnd = extractions().end();
@ -119,12 +119,21 @@ bool MySQLStatementImpl::canCompile() const
void MySQLStatementImpl::compileImpl() void MySQLStatementImpl::compileImpl()
{ {
_metadata.reset(); try
_stmt.prepare(toString()); {
_metadata.init(_stmt); _metadata.reset();
_stmt.prepare(toString());
_metadata.init(_stmt);
if (_metadata.columnsReturned() > 0) if (_metadata.columnsReturned() > 0)
_stmt.bindResult(_metadata.row()); _stmt.bindResult(_metadata.row());
}
catch (MySQLException& exc)
{
static_cast<SessionImpl&>(session()).setLastError(exc.code());
throw;
}
static_cast<SessionImpl&>(session()).setLastError(0);
} }
@ -141,8 +150,17 @@ void MySQLStatementImpl::bindImpl()
} }
_stmt.bindParams(_pBinder->getBindArray(), _pBinder->size()); _stmt.bindParams(_pBinder->getBindArray(), _pBinder->size());
_stmt.execute(); try
{
_stmt.execute();
}
catch (MySQLException& exc)
{
static_cast<SessionImpl&>(session()).setLastError(exc.code());
throw;
}
_hasNext = NEXT_DONTKNOW; _hasNext = NEXT_DONTKNOW;
static_cast<SessionImpl&>(session()).setLastError(0);
} }

View File

@ -42,23 +42,23 @@ public:
if (pthread_key_create(&_key, &ThreadCleanupHelper::cleanup) != 0) if (pthread_key_create(&_key, &ThreadCleanupHelper::cleanup) != 0)
throw Poco::SystemException("cannot create TLS key for mysql cleanup"); throw Poco::SystemException("cannot create TLS key for mysql cleanup");
} }
void init() void init()
{ {
if (pthread_setspecific(_key, reinterpret_cast<void*>(1))) if (pthread_setspecific(_key, reinterpret_cast<void*>(1)))
throw Poco::SystemException("cannot set TLS key for mysql cleanup"); throw Poco::SystemException("cannot set TLS key for mysql cleanup");
} }
static ThreadCleanupHelper& instance() static ThreadCleanupHelper& instance()
{ {
return *_sh.get(); return *_sh.get();
} }
static void cleanup(void* data) static void cleanup(void* data)
{ {
mysql_thread_end(); mysql_thread_end();
} }
private: private:
pthread_key_t _key; pthread_key_t _key;
static Poco::SingletonHolder<ThreadCleanupHelper> _sh; static Poco::SingletonHolder<ThreadCleanupHelper> _sh;
@ -192,4 +192,11 @@ void SessionHandle::reset()
} }
bool SessionHandle::ping()
{
int rc = mysql_ping(_pHandle);
return rc == 0;
}
} } } // namespace Poco::Data::MySQL } } } // namespace Poco::Data::MySQL

View File

@ -49,10 +49,13 @@ SessionImpl::SessionImpl(const std::string& connectionString, std::size_t loginT
_connector("MySQL"), _connector("MySQL"),
_handle(0), _handle(0),
_connected(false), _connected(false),
_inTransaction(false) _inTransaction(false),
_failIfInnoReadOnly(false),
_lastError(0)
{ {
addProperty("insertId", &SessionImpl::setInsertId, &SessionImpl::getInsertId); addProperty("insertId", &SessionImpl::setInsertId, &SessionImpl::getInsertId);
setProperty("handle", static_cast<MYSQL*>(_handle)); setProperty("handle", static_cast<MYSQL*>(_handle));
addFeature("failIfInnoReadOnly", &SessionImpl::setFailIfInnoReadOnly, &SessionImpl::getFailIfInnoReadOnly);
open(); open();
} }
@ -129,7 +132,7 @@ void SessionImpl::open(const std::string& connect)
else if (!options["auto-reconnect"].empty()) else if (!options["auto-reconnect"].empty())
throw MySQLException("create session: specify correct auto-reconnect option (true or false) or skip it"); throw MySQLException("create session: specify correct auto-reconnect option (true or false) or skip it");
#ifdef MYSQL_SECURE_AUTH #ifdef MYSQL_SECURE_AUTH
if (options["secure-auth"] == "true") if (options["secure-auth"] == "true")
_handle.options(MYSQL_SECURE_AUTH, true); _handle.options(MYSQL_SECURE_AUTH, true);
else if (options["secure-auth"] == "false") else if (options["secure-auth"] == "false")
@ -266,6 +269,50 @@ void SessionImpl::reset()
} }
inline bool SessionImpl::isConnected() const
{
return _connected;
}
bool SessionImpl::isGood() const
{
if (_connected)
{
if (_lastError)
{
if (_failIfInnoReadOnly)
{
try
{
int ro = 0;
if (0 == getSetting("innodb_read_only", ro))
{
_lastError = 0;
return true;
}
}
catch (...)
{
}
return false;
}
else
{
if (_handle.ping())
{
_lastError = 0;
return true;
}
return false;
}
}
else return true;
}
else return false;
}
void SessionImpl::close() void SessionImpl::close()
{ {
if (_connected) if (_connected)

View File

@ -52,6 +52,7 @@ public:
void close(); void close();
void reset(); void reset();
bool isConnected() const; bool isConnected() const;
bool isGood() const;
void setConnectionTimeout(std::size_t timeout); void setConnectionTimeout(std::size_t timeout);
std::size_t getConnectionTimeout() const; std::size_t getConnectionTimeout() const;
bool canTransact() const; bool canTransact() const;

View File

@ -218,6 +218,9 @@ public:
void reconnect(); void reconnect();
/// Closes the session and opens it. /// Closes the session and opens it.
bool isGood();
/// Returns true iff the session is good and can be used, false otherwise.
void setLoginTimeout(std::size_t timeout); void setLoginTimeout(std::size_t timeout);
/// Sets the session login timeout value. /// Sets the session login timeout value.
@ -350,6 +353,12 @@ inline void Session::reconnect()
} }
inline bool Session::isGood()
{
return _pImpl->isGood();
}
inline void Session::setLoginTimeout(std::size_t timeout) inline void Session::setLoginTimeout(std::size_t timeout)
{ {
_pImpl->setLoginTimeout(timeout); _pImpl->setLoginTimeout(timeout);

View File

@ -35,7 +35,7 @@ class StatementImpl;
class Data_API SessionImpl: public Poco::RefCountedObject class Data_API SessionImpl: public Poco::RefCountedObject
/// Interface for Session functionality that subclasses must extend. /// Interface for Session functionality that subclasses must extend.
/// SessionImpl objects are noncopyable. /// SessionImpl objects are noncopyable.
{ {
public: public:
@ -65,10 +65,10 @@ public:
virtual void open(const std::string& connectionString = "") = 0; virtual void open(const std::string& connectionString = "") = 0;
/// Opens the session using the supplied string. /// Opens the session using the supplied string.
/// Can also be used with default empty string to reconnect /// Can also be used with default empty string to reconnect
/// a disconnected session. /// a disconnected session.
/// If the connection is not established within requested timeout /// If the connection is not established within requested timeout
/// (specified in seconds), a ConnectionFailedException is thrown. /// (specified in seconds), a ConnectionFailedException is thrown.
/// Zero timout means indefinite /// Zero timout means indefinite
virtual void close() = 0; virtual void close() = 0;
@ -77,6 +77,11 @@ public:
virtual bool isConnected() const = 0; virtual bool isConnected() const = 0;
/// Returns true if session is connected, false otherwise. /// Returns true if session is connected, false otherwise.
virtual bool isGood() const;
/// Returns true if session is good and can be used, false otherwise.
///
/// The default implementation returns result of isConnected().
void setLoginTimeout(std::size_t timeout); void setLoginTimeout(std::size_t timeout);
/// Sets the session login timeout value. /// Sets the session login timeout value.
@ -144,7 +149,7 @@ public:
/// ///
/// Throws a NotSupportedException if the requested feature is /// Throws a NotSupportedException if the requested feature is
/// not supported by the underlying implementation. /// not supported by the underlying implementation.
virtual bool getFeature(const std::string& name) = 0; virtual bool getFeature(const std::string& name) = 0;
/// Look up the state of a feature. /// Look up the state of a feature.
/// ///

View File

@ -66,6 +66,12 @@ bool PooledSessionImpl::isConnected() const
} }
bool PooledSessionImpl::isGood() const
{
return access()->isGood();
}
void PooledSessionImpl::setConnectionTimeout(std::size_t timeout) void PooledSessionImpl::setConnectionTimeout(std::size_t timeout)
{ {
return access()->setConnectionTimeout(timeout); return access()->setConnectionTimeout(timeout);
@ -160,7 +166,7 @@ const std::string& PooledSessionImpl::connectorName() const
} }
void PooledSessionImpl::setFeature(const std::string& name, bool state) void PooledSessionImpl::setFeature(const std::string& name, bool state)
{ {
access()->setFeature(name, state); access()->setFeature(name, state);
} }

View File

@ -20,7 +20,7 @@ namespace Poco {
namespace Data { namespace Data {
SessionImpl::SessionImpl(const std::string& connectionString, std::size_t timeout): SessionImpl::SessionImpl(const std::string& connectionString, std::size_t timeout):
_connectionString(connectionString), _connectionString(connectionString),
_loginTimeout(timeout) _loginTimeout(timeout)
{ {
@ -49,4 +49,10 @@ void SessionImpl::setConnectionString(const std::string& connectionString)
} }
bool SessionImpl::isGood() const
{
return isConnected();
}
} } // namespace Poco::Data } } // namespace Poco::Data

View File

@ -100,7 +100,7 @@ void SessionPool::purgeDeadSessions()
SessionList::iterator it = _idleSessions.begin(); SessionList::iterator it = _idleSessions.begin();
for (; it != _idleSessions.end(); ) for (; it != _idleSessions.end(); )
{ {
if (!(*it)->session()->isConnected()) if (!(*it)->session()->isGood())
{ {
it = _idleSessions.erase(it); it = _idleSessions.erase(it);
--_nSessions; --_nSessions;
@ -139,7 +139,7 @@ int SessionPool::dead()
SessionList::iterator itEnd = _activeSessions.end(); SessionList::iterator itEnd = _activeSessions.end();
for (; it != itEnd; ++it) for (; it != itEnd; ++it)
{ {
if (!(*it)->session()->isConnected()) if (!(*it)->session()->isGood())
++count; ++count;
} }
@ -233,7 +233,7 @@ void SessionPool::putBack(PooledSessionHolderPtr pHolder)
SessionList::iterator it = std::find(_activeSessions.begin(), _activeSessions.end(), pHolder); SessionList::iterator it = std::find(_activeSessions.begin(), _activeSessions.end(), pHolder);
if (it != _activeSessions.end()) if (it != _activeSessions.end())
{ {
if (pHolder->session()->isConnected()) if (pHolder->session()->isGood())
{ {
pHolder->session()->reset(); pHolder->session()->reset();
@ -271,7 +271,7 @@ void SessionPool::onJanitorTimer(Poco::Timer&)
SessionList::iterator it = _idleSessions.begin(); SessionList::iterator it = _idleSessions.begin();
while (_nSessions > _minSessions && it != _idleSessions.end()) while (_nSessions > _minSessions && it != _idleSessions.end())
{ {
if ((*it)->idle() > _idleTime || !(*it)->session()->isConnected()) if ((*it)->idle() > _idleTime || !(*it)->session()->isGood())
{ {
try { (*it)->session()->close(); } try { (*it)->session()->close(); }
catch (...) { } catch (...) { }