fix(Data::Session): Set autoCommit to false in Session #4167 #4143

This commit is contained in:
Alex Fabijanic 2023-10-25 22:40:41 +02:00
parent e47b92d641
commit bd06526ee0
11 changed files with 126 additions and 120 deletions

View File

@ -1275,14 +1275,10 @@ void SQLExecutor::sessionPool(const std::string& connectString, int minSessions,
{
SessionPool pool("ODBC", connectString, 1, 4, 2, 10);
pool.setFeature("f1", true);
assertTrue (pool.getFeature("f1"));
try { pool.getFeature("g1"); fail ("must fail"); }
try { pool.getFeature("g1"); fail ("getting an unsuported feature must fail"); }
catch ( Poco::NotFoundException& ) { }
pool.setProperty("p1", 1);
assertTrue (1 == Poco::AnyCast<int>(pool.getProperty("p1")));
try { pool.getProperty("r1"); fail ("must fail"); }
try { pool.getProperty("r1"); fail ("getting an unsuported property must fail"); }
catch ( Poco::NotFoundException& ) { }
assertTrue (pool.capacity() == 4);
@ -1294,14 +1290,15 @@ void SQLExecutor::sessionPool(const std::string& connectString, int minSessions,
assertTrue (pool.allocated() == pool.used() + pool.idle());
Session s1(pool.get());
assertTrue (s1.getFeature("f1"));
assertTrue (1 == Poco::AnyCast<int>(s1.getProperty("p1")));
try { pool.setFeature("f1", true); fail ("setting an unsuported feature must fail"); }
catch (Poco::InvalidAccessException&) { }
catch (Poco::NotImplementedException&) { }
catch (Poco::Data::NotSupportedException&) { }
try { pool.setFeature("f1", true); fail ("must fail"); }
catch ( Poco::InvalidAccessException& ) { }
try { pool.setProperty("p1", 1); fail ("must fail"); }
catch ( Poco::InvalidAccessException& ) { }
try { pool.setProperty("p1", 1); fail ("setting an unsuported property must fail"); }
catch (Poco::InvalidAccessException&) { }
catch (Poco::NotImplementedException&) { }
catch (Poco::Data::NotSupportedException&) { }
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 1);
@ -1311,10 +1308,7 @@ void SQLExecutor::sessionPool(const std::string& connectString, int minSessions,
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
Session s2(pool.get("f1", false));
assertTrue (!s2.getFeature("f1"));
assertTrue (1 == Poco::AnyCast<int>(s2.getProperty("p1")));
Session s2(pool.get());
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 2);
assertTrue (pool.idle() == 0);
@ -1323,32 +1317,7 @@ void SQLExecutor::sessionPool(const std::string& connectString, int minSessions,
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
{
Session s3(pool.get("p1", 2));
assertTrue (s3.getFeature("f1"));
assertTrue (2 == Poco::AnyCast<int>(s3.getProperty("p1")));
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 3);
assertTrue (pool.idle() == 0);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 1);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
}
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 3);
assertTrue (pool.idle() == 1);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 2);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
Session s4(pool.get());
assertTrue (s4.getFeature("f1"));
assertTrue (1 == Poco::AnyCast<int>(s4.getProperty("p1")));
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 3);
assertTrue (pool.idle() == 0);
@ -1358,7 +1327,6 @@ void SQLExecutor::sessionPool(const std::string& connectString, int minSessions,
assertTrue (pool.allocated() == pool.used() + pool.idle());
Session s5(pool.get());
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 4);
assertTrue (pool.idle() == 0);
@ -1419,13 +1387,10 @@ void SQLExecutor::sessionPool(const std::string& connectString, int minSessions,
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
s6.setFeature("connected", false);
assertTrue (pool.dead() == 1);
s6.close();
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 2);
assertTrue (pool.idle() == 0);
assertTrue (pool.allocated() == 3);
assertTrue (pool.idle() == 1);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 2);
assertTrue (pool.dead() == 0);
@ -1447,7 +1412,9 @@ void SQLExecutor::sessionPool(const std::string& connectString, int minSessions,
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 0);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
assertTrue (pool.allocated() == 0);
assertTrue (pool.used() == 0);
assertTrue (pool.idle() == 0);
}
}
@ -4214,9 +4181,8 @@ void SQLExecutor::sessionTransaction(const std::string& connect)
}
Session local("odbc", connect);
local.setFeature("autoCommit", true);
std::string funct = "transaction()";
std::string funct = "sessionTransaction()";
std::vector<std::string> lastNames;
std::vector<std::string> firstNames;
std::vector<std::string> addresses;
@ -4240,10 +4206,13 @@ void SQLExecutor::sessionTransaction(const std::string& connect)
session().setFeature("autoCommit", false);
assertTrue (!session().isTransaction());
auto ti = session().getTransactionIsolation();
// these are just calls to check the transactional capabilities of the session
// they will print diagnostics to stderr if a transaction isolation level is not supported
setTransactionIsolation(session(), Session::TRANSACTION_READ_UNCOMMITTED);
setTransactionIsolation(session(), Session::TRANSACTION_REPEATABLE_READ);
setTransactionIsolation(session(), Session::TRANSACTION_SERIALIZABLE);
setTransactionIsolation(session(), Session::TRANSACTION_READ_COMMITTED);
session().begin();
@ -4291,7 +4260,9 @@ void SQLExecutor::sessionTransaction(const std::string& connect)
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
assertTrue (2 == count);
// restore the original transaction state
session().setFeature("autoCommit", autoCommit);
setTransactionIsolation(session(), ti);
}
@ -4306,6 +4277,9 @@ void SQLExecutor::transaction(const std::string& connect)
Session local("odbc", connect);
local.setFeature("autoCommit", true);
bool autoCommit = session().getFeature("autoCommit");
auto ti = session().getTransactionIsolation();
setTransactionIsolation(session(), Session::TRANSACTION_READ_COMMITTED);
if (local.hasTransactionIsolation(Session::TRANSACTION_READ_UNCOMMITTED))
setTransactionIsolation(local, Session::TRANSACTION_READ_UNCOMMITTED);
@ -4329,8 +4303,6 @@ void SQLExecutor::transaction(const std::string& connect)
int count = 0, locCount = 0;
std::string result;
bool autoCommit = session().getFeature("autoCommit");
session().setFeature("autoCommit", true);
assertTrue (!session().isTransaction());
session().setFeature("autoCommit", false);
@ -4454,7 +4426,9 @@ void SQLExecutor::transaction(const std::string& connect)
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
assertTrue (2 == count);
// restore the original transaction state
session().setFeature("autoCommit", autoCommit);
setTransactionIsolation(session(), ti);
}

View File

@ -115,7 +115,7 @@ public:
{
}
bool hasFeature(const std::string& name)
bool hasFeature(const std::string& name) const
/// Looks a feature up in the features map
/// and returns true if there is one.
{
@ -140,7 +140,7 @@ public:
else throw NotSupportedException(name);
}
bool getFeature(const std::string& name)
bool getFeature(const std::string& name) const
/// Looks a feature up in the features map
/// and calls the feature's getter, if there is one.
{
@ -148,14 +148,14 @@ public:
if (it != _features.end())
{
if (it->second.getter)
return (static_cast<C*>(this)->*it->second.getter)(name);
return (static_cast<const C*>(this)->*it->second.getter)(name);
else
throw NotImplementedException("get", name);
}
else throw NotSupportedException(name);
}
bool hasProperty(const std::string& name)
bool hasProperty(const std::string& name) const
/// Looks a property up in the properties map
/// and returns true if there is one.
{
@ -180,7 +180,7 @@ public:
else throw NotSupportedException(name);
}
Poco::Any getProperty(const std::string& name)
Poco::Any getProperty(const std::string& name) const
/// Looks a property up in the properties map
/// and calls the property's getter, if there is one.
{
@ -188,7 +188,7 @@ public:
if (it != _properties.end())
{
if (it->second.getter)
return (static_cast<C*>(this)->*it->second.getter)(name);
return (static_cast<const C*>(this)->*it->second.getter)(name);
else
throw NotImplementedException("set", name);
}

View File

@ -62,12 +62,12 @@ public:
bool hasTransactionIsolation(Poco::UInt32) const;
bool isTransactionIsolation(Poco::UInt32) const;
const std::string& connectorName() const;
bool hasFeature(const std::string& name);
bool hasFeature(const std::string& name) const;
void setFeature(const std::string& name, bool state);
bool getFeature(const std::string& name);
bool hasProperty(const std::string& name);
bool getFeature(const std::string& name) const;
bool hasProperty(const std::string& name) const;
void setProperty(const std::string& name, const Poco::Any& value);
Poco::Any getProperty(const std::string& name);
Poco::Any getProperty(const std::string& name) const;
protected:
SessionImpl* access() const;

View File

@ -151,6 +151,12 @@ class Data_API Session
/// If the formatting will occur and the percent sign is part of the query itself, it can be passed to the query by entering it twice (%%).
/// However, if no formatting is used, one percent sign is sufficient as the string will be passed unaltered.
/// For complete list of supported data types with their respective specifications, see the documentation for format in Foundation.
///
/// Transactions are supported via the begin(), commit() and rollback() methods.
/// If the session is in autocommit mode, begin() will temporarily disable autocommit mode for the duration of the transaction.
/// Calls to either commit() or rollback() will re-enable it.
/// Poco::Data::Transaction, a convenient session wrapper class, automates this process and provides RAII-based transactional behavior.
/// For more information, see Transation class documentation.
{
public:
static const std::size_t LOGIN_TIMEOUT_DEFAULT = SessionImpl::LOGIN_TIMEOUT_DEFAULT;
@ -160,7 +166,7 @@ public:
static const Poco::UInt32 TRANSACTION_SERIALIZABLE = 0x00000008L;
Session(Poco::AutoPtr<SessionImpl> ptrImpl);
/// Creates the Session.
/// Creates the Session from SessionImpl.
Session(const std::string& connector,
const std::string& connectionString,
@ -221,6 +227,13 @@ public:
bool isGood();
/// Returns true iff the session is good and can be used, false otherwise.
bool isAutocommit() const;
/// Returns true iff the session is in autocommit mode, false otherwise.
/// If the session does not support autocommit, it is assumed not to
/// be in auto commit mode.
/// This function looks for the "autoCommit" feature and returns its value.
/// It is recommended for all back-end implementations to support this feature.
void setLoginTimeout(std::size_t timeout);
/// Sets the session login timeout value.
@ -235,12 +248,19 @@ public:
void begin();
/// Starts a transaction.
/// If `session` is in autocommit mode, it is switched to manual commit mode
/// for the duration of the transaction and reverted back to the original mode
/// after transaction completes (on rollback or commit() call).
void commit();
/// Commits and ends a transaction.
/// If `session` was in autocommit mode when the transaction started (begin() call),
/// it is switched back to autocommit mode.
void rollback();
/// Rolls back and ends a transaction.
/// If `session` was in autocommit mode when the transaction started (begin() call),
/// it is switched back to autocommit mode.
bool canTransact();
/// Returns true if session has transaction capabilities.
@ -273,7 +293,7 @@ public:
/// Utility function that teturns the URI formatted from supplied
/// arguments as "connector:///connectionString".
bool hasFeature(const std::string& name);
bool hasFeature(const std::string& name) const;
/// Returns true if session has the named feature.
void setFeature(const std::string& name, bool state);
@ -294,7 +314,7 @@ public:
/// Throws a NotSupportedException if the requested feature is
/// not supported by the underlying implementation.
bool hasProperty(const std::string& name);
bool hasProperty(const std::string& name) const;
/// Returns true if session has the named property.
void setProperty(const std::string& name, const Poco::Any& value);
@ -322,13 +342,21 @@ private:
Session();
Poco::AutoPtr<SessionImpl> _pImpl;
StatementCreator _statementCreator;
StatementCreator _statementCreator;
bool _wasAutoCommit = false;
};
//
// inlines
//
inline bool Session::isAutocommit() const
{
return hasFeature("autoCommit") && getFeature("autoCommit");
}
inline SharedPtr<StatementImpl> Session::createStatementImpl()
{
return _pImpl->createStatementImpl();
@ -389,24 +417,6 @@ inline std::size_t Session::getConnectionTimeout()
}
inline void Session::begin()
{
return _pImpl->begin();
}
inline void Session::commit()
{
return _pImpl->commit();
}
inline void Session::rollback()
{
return _pImpl->rollback();
}
inline bool Session::canTransact()
{
return _pImpl->canTransact();
@ -462,7 +472,7 @@ inline std::string Session::uri() const
}
inline bool Session::hasFeature(const std::string& name)
inline bool Session::hasFeature(const std::string& name) const
{
return _pImpl->hasFeature(name);
}
@ -480,7 +490,7 @@ inline bool Session::getFeature(const std::string& name) const
}
inline bool Session::hasProperty(const std::string& name)
inline bool Session::hasProperty(const std::string& name) const
{
return _pImpl->hasProperty(name);
}

View File

@ -146,7 +146,7 @@ public:
std::string uri() const;
/// Returns the URI for this session.
virtual bool hasFeature(const std::string& name) = 0;
virtual bool hasFeature(const std::string& name) const = 0;
/// Returns true if session has the named feature.
virtual void setFeature(const std::string& name, bool state) = 0;
@ -158,7 +158,7 @@ public:
/// Throws a NotSupportedException if the requested feature is
/// not supported by the underlying implementation.
virtual bool getFeature(const std::string& name) = 0;
virtual bool getFeature(const std::string& name) const = 0;
/// Look up the state of a feature.
///
/// Features are a generic extension mechanism for session implementations.
@ -167,7 +167,7 @@ public:
/// Throws a NotSupportedException if the requested feature is
/// not supported by the underlying implementation.
virtual bool hasProperty(const std::string& name) = 0;
virtual bool hasProperty(const std::string& name) const = 0;
/// Returns true if session has the named feature.
virtual void setProperty(const std::string& name, const Poco::Any& value) = 0;
@ -179,7 +179,7 @@ public:
/// Throws a NotSupportedException if the requested property is
/// not supported by the underlying implementation.
virtual Poco::Any getProperty(const std::string& name) = 0;
virtual Poco::Any getProperty(const std::string& name) const = 0;
/// Look up the value of a property.
///
/// Properties are a generic extension mechanism for session implementations.

View File

@ -146,13 +146,13 @@ public:
void setFeature(const std::string& name, bool state);
/// Sets feature for all the sessions.
bool getFeature(const std::string& name);
bool getFeature(const std::string& name) const;
/// Returns the requested feature.
void setProperty(const std::string& name, const Poco::Any& value);
/// Sets property for all sessions.
Poco::Any getProperty(const std::string& name);
Poco::Any getProperty(const std::string& name) const;
/// Returns the requested property.
void shutdown();

View File

@ -39,9 +39,6 @@ class Data_API Transaction
public:
Transaction(Poco::Data::Session& session, Poco::Logger* pLogger = 0);
/// Creates the Transaction and starts it, using the given database session and logger.
/// If `session` is in autocommit mode, it is switched to manual commit mode
/// for the duration of the transaction and reverted back to the original mode
/// after transaction completes.
Transaction(Poco::Data::Session& session, bool start);
/// Creates the Transaction, using the given database session.
@ -159,7 +156,6 @@ private:
/// Otherwise does nothing.
Session _rSession;
bool _autoCommit = false;
Logger* _pLogger = nullptr;
};

View File

@ -166,7 +166,7 @@ const std::string& PooledSessionImpl::connectorName() const
}
bool PooledSessionImpl::hasFeature(const std::string& name)
bool PooledSessionImpl::hasFeature(const std::string& name) const
{
return access()->hasFeature(name);
}
@ -178,13 +178,13 @@ void PooledSessionImpl::setFeature(const std::string& name, bool state)
}
bool PooledSessionImpl::getFeature(const std::string& name)
bool PooledSessionImpl::getFeature(const std::string& name) const
{
return access()->getFeature(name);
}
bool PooledSessionImpl::hasProperty(const std::string& name)
bool PooledSessionImpl::hasProperty(const std::string& name) const
{
return access()->hasProperty(name);
}
@ -196,7 +196,7 @@ void PooledSessionImpl::setProperty(const std::string& name, const Poco::Any& va
}
Poco::Any PooledSessionImpl::getProperty(const std::string& name)
Poco::Any PooledSessionImpl::getProperty(const std::string& name) const
{
return access()->getProperty(name);
}

View File

@ -25,9 +25,9 @@ namespace Data {
Session::Session(Poco::AutoPtr<SessionImpl> pImpl):
_pImpl(pImpl),
_statementCreator(pImpl)
_statementCreator(pImpl),
_wasAutoCommit(false)
{
poco_check_ptr (pImpl.get());
}
@ -40,8 +40,7 @@ Session::Session(const std::string& connector,
}
Session::Session(const std::string& connection,
std::size_t timeout)
Session::Session(const std::string& connection, std::size_t timeout)
{
Session newSession(SessionFactory::instance().create(connection, timeout));
swap(newSession);
@ -50,14 +49,16 @@ Session::Session(const std::string& connection,
Session::Session(const Session& other):
_pImpl(other._pImpl),
_statementCreator(other._statementCreator)
_statementCreator(other._statementCreator),
_wasAutoCommit(other._wasAutoCommit)
{
}
Session::Session(Session&& other) noexcept:
_pImpl(std::move(other._pImpl)),
_statementCreator(std::move(other._statementCreator))
_statementCreator(std::move(other._statementCreator)),
_wasAutoCommit(other._wasAutoCommit)
{
}
@ -78,6 +79,7 @@ Session& Session::operator = (Session&& other) noexcept
{
_pImpl = std::move(other._pImpl);
_statementCreator = std::move(other._statementCreator);
_wasAutoCommit = other._wasAutoCommit;
return *this;
}
@ -87,6 +89,40 @@ void Session::swap(Session& other)
using std::swap;
swap(_statementCreator, other._statementCreator);
swap(_pImpl, other._pImpl);
swap(_wasAutoCommit, other._wasAutoCommit);
}
void Session::begin()
{
if (isAutocommit())
{
setFeature("autoCommit", false);
_wasAutoCommit = true;
}
return _pImpl->begin();
}
void Session::commit()
{
_pImpl->commit();
if (_wasAutoCommit)
{
setFeature("autoCommit", true);
_wasAutoCommit = false;
}
}
void Session::rollback()
{
_pImpl->rollback();
if (_wasAutoCommit)
{
setFeature("autoCommit", true);
_wasAutoCommit = false;
}
}

View File

@ -178,7 +178,7 @@ void SessionPool::setFeature(const std::string& name, bool state)
}
bool SessionPool::getFeature(const std::string& name)
bool SessionPool::getFeature(const std::string& name) const
{
if (_shutdown) throw InvalidAccessException("Session pool has been shut down.");
@ -205,7 +205,7 @@ void SessionPool::setProperty(const std::string& name, const Poco::Any& value)
}
Poco::Any SessionPool::getProperty(const std::string& name)
Poco::Any SessionPool::getProperty(const std::string& name) const
{
Poco::Mutex::ScopedLock lock(_mutex);
PropertyMap::ConstIterator it = _propertyMap.find(name);

View File

@ -22,7 +22,6 @@ namespace Data {
Transaction::Transaction(Poco::Data::Session& rSession, Poco::Logger* pLogger):
_rSession(rSession),
_autoCommit(_rSession.hasFeature("autoCommit") ? _rSession.getFeature("autoCommit") : false),
_pLogger(pLogger)
{
begin();
@ -31,7 +30,6 @@ Transaction::Transaction(Poco::Data::Session& rSession, Poco::Logger* pLogger):
Transaction::Transaction(Poco::Data::Session& rSession, bool start):
_rSession(rSession),
_autoCommit(_rSession.hasFeature("autoCommit") ? _rSession.getFeature("autoCommit") : false),
_pLogger(0)
{
if (start) begin();
@ -73,11 +71,7 @@ Transaction::~Transaction()
void Transaction::begin()
{
if (!_rSession.isTransaction())
{
if (_autoCommit)
_rSession.setFeature("autoCommit", false);
_rSession.begin();
}
else
throw InvalidAccessException("Transaction in progress.");
}
@ -122,8 +116,6 @@ void Transaction::commit()
_pLogger->debug("Committing transaction.");
_rSession.commit();
if (_autoCommit)
_rSession.setFeature("autoCommit", true);
}
@ -133,8 +125,6 @@ void Transaction::rollback()
_pLogger->debug("Rolling back transaction.");
_rSession.rollback();
if (_autoCommit)
_rSession.setFeature("autoCommit", true);
}