mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-17 11:05:03 +02:00
more callbacks and transaction tests
- added callbacks for commit and rollback with tests - added transaction tests
This commit is contained in:
@@ -141,7 +141,7 @@ public:
|
|||||||
SessionHandle& handle();
|
SessionHandle& handle();
|
||||||
// Get handle
|
// Get handle
|
||||||
|
|
||||||
const std::string& connectorName();
|
const std::string& connectorName() const;
|
||||||
/// Returns the name of the connector.
|
/// Returns the name of the connector.
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -208,7 +208,7 @@ inline SessionHandle& SessionImpl::handle()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline const std::string& SessionImpl::connectorName()
|
inline const std::string& SessionImpl::connectorName() const
|
||||||
{
|
{
|
||||||
return _connector;
|
return _connector;
|
||||||
}
|
}
|
||||||
|
@@ -122,7 +122,7 @@ public:
|
|||||||
bool isTransaction();
|
bool isTransaction();
|
||||||
/// Returns true iff a transaction is in progress.
|
/// Returns true iff a transaction is in progress.
|
||||||
|
|
||||||
const std::string& connectorName();
|
const std::string& connectorName() const;
|
||||||
/// Returns the name of the connector.
|
/// Returns the name of the connector.
|
||||||
|
|
||||||
bool canTransact();
|
bool canTransact();
|
||||||
@@ -263,7 +263,7 @@ inline bool SessionImpl::isAutoExtract(const std::string& name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline const std::string& SessionImpl::connectorName()
|
inline const std::string& SessionImpl::connectorName() const
|
||||||
{
|
{
|
||||||
return _connector;
|
return _connector;
|
||||||
}
|
}
|
||||||
|
@@ -52,6 +52,9 @@ struct sqlite3_stmt;
|
|||||||
|
|
||||||
|
|
||||||
namespace Poco {
|
namespace Poco {
|
||||||
|
|
||||||
|
class Mutex;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
namespace SQLite {
|
namespace SQLite {
|
||||||
|
|
||||||
@@ -125,7 +128,13 @@ public:
|
|||||||
/// Returns true iff the transaction isolation level corresponds
|
/// Returns true iff the transaction isolation level corresponds
|
||||||
/// to the supplied bitmask.
|
/// to the supplied bitmask.
|
||||||
|
|
||||||
const std::string& connectorName();
|
void autoCommit(const std::string&, bool val);
|
||||||
|
/// Sets autocommit property for the session.
|
||||||
|
|
||||||
|
bool isAutoCommit(const std::string& name="");
|
||||||
|
/// Returns autocommit property value.
|
||||||
|
|
||||||
|
const std::string& connectorName() const;
|
||||||
/// Returns the name of the connector.
|
/// Returns the name of the connector.
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -134,7 +143,7 @@ private:
|
|||||||
bool _connected;
|
bool _connected;
|
||||||
bool _isTransaction;
|
bool _isTransaction;
|
||||||
int _timeout;
|
int _timeout;
|
||||||
|
Mutex _mutex;
|
||||||
static const std::string DEFERRED_BEGIN_TRANSACTION;
|
static const std::string DEFERRED_BEGIN_TRANSACTION;
|
||||||
static const std::string COMMIT_TRANSACTION;
|
static const std::string COMMIT_TRANSACTION;
|
||||||
static const std::string ABORT_TRANSACTION;
|
static const std::string ABORT_TRANSACTION;
|
||||||
@@ -156,7 +165,7 @@ inline bool SessionImpl::isTransaction()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline const std::string& SessionImpl::connectorName()
|
inline const std::string& SessionImpl::connectorName() const
|
||||||
{
|
{
|
||||||
return _connector;
|
return _connector;
|
||||||
}
|
}
|
||||||
|
@@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
#include "Poco/Data/SQLite/SQLite.h"
|
#include "Poco/Data/SQLite/SQLite.h"
|
||||||
#include "Poco/Data/MetaColumn.h"
|
#include "Poco/Data/MetaColumn.h"
|
||||||
|
#include "Poco/Data/Session.h"
|
||||||
#include "Poco/Mutex.h"
|
#include "Poco/Mutex.h"
|
||||||
#include "Poco/Types.h"
|
#include "Poco/Types.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -57,7 +58,7 @@ namespace SQLite {
|
|||||||
|
|
||||||
|
|
||||||
class SQLite_API Utility
|
class SQLite_API Utility
|
||||||
/// Various utility functions for SQLite, mostly return code handling
|
/// Various utility functions for SQLite.
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const std::string SQLITE_DATE_FORMAT;
|
static const std::string SQLITE_DATE_FORMAT;
|
||||||
@@ -72,8 +73,14 @@ public:
|
|||||||
static const int OPERATION_DELETE;
|
static const int OPERATION_DELETE;
|
||||||
static const int OPERATION_UPDATE;
|
static const int OPERATION_UPDATE;
|
||||||
|
|
||||||
|
static sqlite3* dbHandle(const Session& session);
|
||||||
|
/// Returns native DB handle.
|
||||||
|
|
||||||
static std::string lastError(sqlite3* pDb);
|
static std::string lastError(sqlite3* pDb);
|
||||||
/// Retreives the last error code from sqlite and converts it to a string
|
/// Retreives the last error code from sqlite and converts it to a string.
|
||||||
|
|
||||||
|
static std::string lastError(const Session& session);
|
||||||
|
/// Retreives the last error code from sqlite and converts it to a string.
|
||||||
|
|
||||||
static void throwException(int rc, const std::string& addErrMsg = std::string());
|
static void throwException(int rc, const std::string& addErrMsg = std::string());
|
||||||
/// Throws for an error code the appropriate exception
|
/// Throws for an error code the appropriate exception
|
||||||
@@ -85,13 +92,25 @@ public:
|
|||||||
/// Loads the contents of a database file on disk into an opened
|
/// Loads the contents of a database file on disk into an opened
|
||||||
/// database in memory.
|
/// database in memory.
|
||||||
///
|
///
|
||||||
/// Returns true if succesful
|
/// Returns true if succesful.
|
||||||
|
|
||||||
|
static bool fileToMemory(const Session& session, const std::string& fileName);
|
||||||
|
/// Loads the contents of a database file on disk into an opened
|
||||||
|
/// database in memory.
|
||||||
|
///
|
||||||
|
/// Returns true if succesful.
|
||||||
|
|
||||||
static bool memoryToFile(const std::string& fileName, sqlite3* pInMemory);
|
static bool memoryToFile(const std::string& fileName, sqlite3* pInMemory);
|
||||||
/// Saves the contents of an opened database in memory to the
|
/// Saves the contents of an opened database in memory to the
|
||||||
/// database on disk.
|
/// database on disk.
|
||||||
///
|
///
|
||||||
/// Returns true if succesful
|
/// Returns true if succesful.
|
||||||
|
|
||||||
|
static bool memoryToFile(const std::string& fileName, const Session& session);
|
||||||
|
/// Saves the contents of an opened database in memory to the
|
||||||
|
/// database on disk.
|
||||||
|
///
|
||||||
|
/// Returns true if succesful.
|
||||||
|
|
||||||
static bool isThreadSafe();
|
static bool isThreadSafe();
|
||||||
/// Returns true if SQLite was compiled in multi-thread or serialized mode.
|
/// Returns true if SQLite was compiled in multi-thread or serialized mode.
|
||||||
@@ -121,27 +140,57 @@ public:
|
|||||||
static bool registerUpdateHandler(sqlite3* pDB, CBT callbackFn, T* pParam)
|
static bool registerUpdateHandler(sqlite3* pDB, CBT callbackFn, T* pParam)
|
||||||
/// Registers the callback for (1)(insert, delete, update), (2)(commit) or
|
/// Registers the callback for (1)(insert, delete, update), (2)(commit) or
|
||||||
/// or (3)(rollback) events. Only one function per group can be registered
|
/// or (3)(rollback) events. Only one function per group can be registered
|
||||||
/// at a time. Registration is thread-safe. Storage pointed to by pParam
|
/// at a time. Registration is not thread-safe. Storage pointed to by pParam
|
||||||
/// must remain valid as long as registration is active. Registering with
|
/// must remain valid as long as registration is active. Registering with
|
||||||
/// callbackFn set to zero disables notifications.
|
/// callbackFn set to zero disables notifications.
|
||||||
///
|
///
|
||||||
/// See http://www.sqlite.org/c3ref/update_hook.html and
|
/// See http://www.sqlite.org/c3ref/update_hook.html and
|
||||||
/// http://www.sqlite.org/c3ref/commit_hook.html for details.
|
/// http://www.sqlite.org/c3ref/commit_hook.html for details.
|
||||||
{
|
{
|
||||||
typedef std::map<sqlite3*, T*> RetMap;
|
typedef std::pair<typename CBT, T*> CBPair;
|
||||||
|
typedef std::multimap<sqlite3*, CBPair> CBMap;
|
||||||
|
typedef CBMap::iterator CBMapIt;
|
||||||
|
typedef std::pair<CBMapIt, CBMapIt> CBMapItPair;
|
||||||
|
|
||||||
Poco::Mutex::ScopedLock l(_mutex);
|
static CBMap retMap;
|
||||||
static RetMap retMap;
|
T* pRet = reinterpret_cast<T*>(eventHookRegister(pDB, callbackFn, pParam));
|
||||||
T* pRet = reinterpret_cast<T*>(eventHook(pDB, callbackFn, pParam));
|
|
||||||
|
|
||||||
bool success = false;
|
if (pRet == 0)
|
||||||
if (retMap.find(pDB) == retMap.end())
|
{
|
||||||
success = (pRet == 0);
|
if (retMap.find(pDB) == retMap.end())
|
||||||
else if (pRet != 0)
|
{
|
||||||
success = (*pRet == *retMap[pDB]);
|
retMap.insert(CBMap::value_type(pDB, CBPair(callbackFn, pParam)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CBMapItPair retMapRange = retMap.equal_range(pDB);
|
||||||
|
for (CBMapIt it = retMapRange.first; it != retMapRange.second; ++it)
|
||||||
|
{
|
||||||
|
poco_assert (it->second.first != 0);
|
||||||
|
if ((callbackFn == 0) && (*pRet == *it->second.second))
|
||||||
|
{
|
||||||
|
retMap.erase(it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (success) retMap[pDB] = pParam;
|
if ((callbackFn == it->second.first) && (*pRet == *it->second.second))
|
||||||
return success;
|
{
|
||||||
|
it->second.second = pParam;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename CBT>
|
||||||
|
static bool registerUpdateHandler(const Session& session, CBT callbackFn, T* pParam)
|
||||||
|
/// Registers the callback by calling registerUpdateHandler(sqlite3*, CBT, T*).
|
||||||
|
{
|
||||||
|
return registerUpdateHandler(dbHandle(session), callbackFn, pParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -158,9 +207,9 @@ private:
|
|||||||
Utility(const Utility&);
|
Utility(const Utility&);
|
||||||
Utility& operator = (const Utility&);
|
Utility& operator = (const Utility&);
|
||||||
|
|
||||||
static void* eventHook(sqlite3* pDB, UpdateCallbackType callbackFn, void* pParam);
|
static void* eventHookRegister(sqlite3* pDB, UpdateCallbackType callbackFn, void* pParam);
|
||||||
static void* eventHook(sqlite3* pDB, CommitCallbackType callbackFn, void* pParam);
|
static void* eventHookRegister(sqlite3* pDB, CommitCallbackType callbackFn, void* pParam);
|
||||||
static void* eventHook(sqlite3* pDB, RollbackCallbackType callbackFn, void* pParam);
|
static void* eventHookRegister(sqlite3* pDB, RollbackCallbackType callbackFn, void* pParam);
|
||||||
|
|
||||||
static TypeMap _types;
|
static TypeMap _types;
|
||||||
static Poco::Mutex _mutex;
|
static Poco::Mutex _mutex;
|
||||||
@@ -168,7 +217,38 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// inlines
|
||||||
|
//
|
||||||
|
|
||||||
|
inline sqlite3* Utility::dbHandle(const Session& session)
|
||||||
|
{
|
||||||
|
return AnyCast<sqlite3*>(session.getProperty("handle"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline std::string Utility::lastError(const Session& session)
|
||||||
|
{
|
||||||
|
poco_assert_dbg ((0 == icompare(session.connector(), 0, 6, "sqlite")));
|
||||||
|
return lastError(dbHandle(session));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline bool Utility::memoryToFile(const std::string& fileName, const Session& session)
|
||||||
|
{
|
||||||
|
poco_assert_dbg ((0 == icompare(session.connector(), 0, 6, "sqlite")));
|
||||||
|
return memoryToFile(fileName, dbHandle(session));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline bool Utility::fileToMemory(const Session& session, const std::string& fileName)
|
||||||
|
{
|
||||||
|
poco_assert_dbg ((0 == icompare(session.connector(), 0, 6, "sqlite")));
|
||||||
|
return fileToMemory(dbHandle(session), fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} } } // namespace Poco::Data::SQLite
|
} } } // namespace Poco::Data::SQLite
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif // SQLite_Utility_INCLUDED
|
||||||
|
@@ -42,6 +42,7 @@
|
|||||||
#include "Poco/ActiveMethod.h"
|
#include "Poco/ActiveMethod.h"
|
||||||
#include "Poco/ActiveResult.h"
|
#include "Poco/ActiveResult.h"
|
||||||
#include "Poco/String.h"
|
#include "Poco/String.h"
|
||||||
|
#include "Poco/Mutex.h"
|
||||||
#include "Poco/Data/DataException.h"
|
#include "Poco/Data/DataException.h"
|
||||||
#include "sqlite3.h"
|
#include "sqlite3.h"
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@@ -67,6 +68,9 @@ SessionImpl::SessionImpl(const std::string& fileName, std::size_t loginTimeout):
|
|||||||
open();
|
open();
|
||||||
setConnectionTimeout(CONNECTION_TIMEOUT_DEFAULT);
|
setConnectionTimeout(CONNECTION_TIMEOUT_DEFAULT);
|
||||||
setProperty("handle", _pDB);
|
setProperty("handle", _pDB);
|
||||||
|
addFeature("autoCommit",
|
||||||
|
&SessionImpl::autoCommit,
|
||||||
|
&SessionImpl::isAutoCommit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -85,6 +89,7 @@ Poco::Data::StatementImpl* SessionImpl::createStatementImpl()
|
|||||||
|
|
||||||
void SessionImpl::begin()
|
void SessionImpl::begin()
|
||||||
{
|
{
|
||||||
|
Poco::Mutex::ScopedLock l(_mutex);
|
||||||
SQLiteStatementImpl tmp(*this, _pDB);
|
SQLiteStatementImpl tmp(*this, _pDB);
|
||||||
tmp.add(DEFERRED_BEGIN_TRANSACTION);
|
tmp.add(DEFERRED_BEGIN_TRANSACTION);
|
||||||
tmp.execute();
|
tmp.execute();
|
||||||
@@ -94,6 +99,7 @@ void SessionImpl::begin()
|
|||||||
|
|
||||||
void SessionImpl::commit()
|
void SessionImpl::commit()
|
||||||
{
|
{
|
||||||
|
Poco::Mutex::ScopedLock l(_mutex);
|
||||||
SQLiteStatementImpl tmp(*this, _pDB);
|
SQLiteStatementImpl tmp(*this, _pDB);
|
||||||
tmp.add(COMMIT_TRANSACTION);
|
tmp.add(COMMIT_TRANSACTION);
|
||||||
tmp.execute();
|
tmp.execute();
|
||||||
@@ -103,6 +109,7 @@ void SessionImpl::commit()
|
|||||||
|
|
||||||
void SessionImpl::rollback()
|
void SessionImpl::rollback()
|
||||||
{
|
{
|
||||||
|
Poco::Mutex::ScopedLock l(_mutex);
|
||||||
SQLiteStatementImpl tmp(*this, _pDB);
|
SQLiteStatementImpl tmp(*this, _pDB);
|
||||||
tmp.add(ABORT_TRANSACTION);
|
tmp.add(ABORT_TRANSACTION);
|
||||||
tmp.execute();
|
tmp.execute();
|
||||||
@@ -233,4 +240,21 @@ void SessionImpl::setConnectionTimeout(std::size_t timeout)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SessionImpl::autoCommit(const std::string&, bool)
|
||||||
|
{
|
||||||
|
// The problem here is to decide whether to call commit or rollback
|
||||||
|
// when autocommit is set to true. Hence, it is best not to implement
|
||||||
|
// this explicit call and only implicitly support autocommit setting.
|
||||||
|
throw NotImplementedException(
|
||||||
|
"SQLite autocommit is implicit with begin/commit/rollback.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SessionImpl::isAutoCommit(const std::string&)
|
||||||
|
{
|
||||||
|
Poco::Mutex::ScopedLock l(_mutex);
|
||||||
|
return (0 != sqlite3_get_autocommit(_pDB));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} } } // namespace Poco::Data::SQLite
|
} } } // namespace Poco::Data::SQLite
|
||||||
|
@@ -40,6 +40,7 @@
|
|||||||
#include "Poco/Data/SQLite/SQLiteException.h"
|
#include "Poco/Data/SQLite/SQLiteException.h"
|
||||||
#include "Poco/NumberFormatter.h"
|
#include "Poco/NumberFormatter.h"
|
||||||
#include "Poco/String.h"
|
#include "Poco/String.h"
|
||||||
|
#include "Poco/Any.h"
|
||||||
#include "Poco/Exception.h"
|
#include "Poco/Exception.h"
|
||||||
#if defined(POCO_UNBUNDLED)
|
#if defined(POCO_UNBUNDLED)
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
@@ -133,7 +134,7 @@ Utility::Utility()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string Utility::lastError(sqlite3 *pDB)
|
std::string Utility::lastError(sqlite3* pDB)
|
||||||
{
|
{
|
||||||
return std::string(sqlite3_errmsg(pDB));
|
return std::string(sqlite3_errmsg(pDB));
|
||||||
}
|
}
|
||||||
@@ -220,7 +221,7 @@ void Utility::throwException(int rc, const std::string& addErrMsg)
|
|||||||
case SQLITE_DONE:
|
case SQLITE_DONE:
|
||||||
break; // sqlite_step() has finished executing
|
break; // sqlite_step() has finished executing
|
||||||
default:
|
default:
|
||||||
throw SQLiteException(std::string("Unkown error code: ") + Poco::NumberFormatter::format(rc), addErrMsg);
|
throw SQLiteException(std::string("Unknown error code: ") + Poco::NumberFormatter::format(rc), addErrMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,20 +304,20 @@ bool Utility::setThreadMode(int mode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void* Utility::eventHook(sqlite3* pDB, UpdateCallbackType callbackFn, void* pParam)
|
void* Utility::eventHookRegister(sqlite3* pDB, UpdateCallbackType callbackFn, void* pParam)
|
||||||
{
|
{
|
||||||
typedef void(*pF)(void*, int, const char*, const char*, sqlite3_int64);
|
typedef void(*pF)(void*, int, const char*, const char*, sqlite3_int64);
|
||||||
return sqlite3_update_hook(pDB, reinterpret_cast<pF>(callbackFn), pParam);
|
return sqlite3_update_hook(pDB, reinterpret_cast<pF>(callbackFn), pParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void* Utility::eventHook(sqlite3* pDB, CommitCallbackType callbackFn, void* pParam)
|
void* Utility::eventHookRegister(sqlite3* pDB, CommitCallbackType callbackFn, void* pParam)
|
||||||
{
|
{
|
||||||
return sqlite3_commit_hook(pDB, callbackFn, pParam);
|
return sqlite3_commit_hook(pDB, callbackFn, pParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void* Utility::eventHook(sqlite3* pDB, RollbackCallbackType callbackFn, void* pParam)
|
void* Utility::eventHookRegister(sqlite3* pDB, RollbackCallbackType callbackFn, void* pParam)
|
||||||
{
|
{
|
||||||
return sqlite3_rollback_hook(pDB, callbackFn, pParam);
|
return sqlite3_rollback_hook(pDB, callbackFn, pParam);
|
||||||
}
|
}
|
||||||
|
@@ -45,6 +45,7 @@
|
|||||||
#include "Poco/Dynamic/Var.h"
|
#include "Poco/Dynamic/Var.h"
|
||||||
#include "Poco/Data/TypeHandler.h"
|
#include "Poco/Data/TypeHandler.h"
|
||||||
#include "Poco/Nullable.h"
|
#include "Poco/Nullable.h"
|
||||||
|
#include "Poco/Data/Transaction.h"
|
||||||
#include "Poco/Data/DataException.h"
|
#include "Poco/Data/DataException.h"
|
||||||
#include "Poco/Data/SQLite/SQLiteException.h"
|
#include "Poco/Data/SQLite/SQLiteException.h"
|
||||||
#include "Poco/Tuple.h"
|
#include "Poco/Tuple.h"
|
||||||
@@ -73,6 +74,7 @@ using Poco::Data::LimitException;
|
|||||||
using Poco::Data::CLOB;
|
using Poco::Data::CLOB;
|
||||||
using Poco::Data::Date;
|
using Poco::Data::Date;
|
||||||
using Poco::Data::Time;
|
using Poco::Data::Time;
|
||||||
|
using Poco::Data::Transaction;
|
||||||
using Poco::Data::AbstractExtractionVec;
|
using Poco::Data::AbstractExtractionVec;
|
||||||
using Poco::Data::AbstractExtractionVecVec;
|
using Poco::Data::AbstractExtractionVecVec;
|
||||||
using Poco::Data::AbstractBindingVec;
|
using Poco::Data::AbstractBindingVec;
|
||||||
@@ -87,11 +89,14 @@ using Poco::Logger;
|
|||||||
using Poco::Message;
|
using Poco::Message;
|
||||||
using Poco::AutoPtr;
|
using Poco::AutoPtr;
|
||||||
using Poco::Thread;
|
using Poco::Thread;
|
||||||
|
using Poco::format;
|
||||||
using Poco::InvalidAccessException;
|
using Poco::InvalidAccessException;
|
||||||
using Poco::RangeException;
|
using Poco::RangeException;
|
||||||
using Poco::BadCastException;
|
using Poco::BadCastException;
|
||||||
using Poco::NotFoundException;
|
using Poco::NotFoundException;
|
||||||
using Poco::NullPointerException;
|
using Poco::NullPointerException;
|
||||||
|
using Poco::TimeoutException;
|
||||||
|
using Poco::NotImplementedException;
|
||||||
using Poco::Data::SQLite::ConstraintViolationException;
|
using Poco::Data::SQLite::ConstraintViolationException;
|
||||||
using Poco::Data::SQLite::ParameterCountMismatchException;
|
using Poco::Data::SQLite::ParameterCountMismatchException;
|
||||||
using Poco::Int32;
|
using Poco::Int32;
|
||||||
@@ -362,9 +367,7 @@ void SQLiteTest::testInMemory()
|
|||||||
|
|
||||||
// load db from file to memory
|
// load db from file to memory
|
||||||
Session mem (Poco::Data::SQLite::Connector::KEY, ":memory:");
|
Session mem (Poco::Data::SQLite::Connector::KEY, ":memory:");
|
||||||
sqlite3* p = 0; Any a = p; // ??? clang generated code fails to AnyCast without these
|
assert (Poco::Data::SQLite::Utility::fileToMemory(mem, "dummy.db"));
|
||||||
sqlite3* pMemHandle = AnyCast<sqlite3*>(mem.getProperty("handle"));
|
|
||||||
assert (Poco::Data::SQLite::Utility::fileToMemory(pMemHandle, "dummy.db"));
|
|
||||||
|
|
||||||
mem << "SELECT COUNT(*) FROM PERSON", into(count), now;
|
mem << "SELECT COUNT(*) FROM PERSON", into(count), now;
|
||||||
assert (count == 1);
|
assert (count == 1);
|
||||||
@@ -378,7 +381,7 @@ void SQLiteTest::testInMemory()
|
|||||||
|
|
||||||
// save db from memory to file on the disk
|
// save db from memory to file on the disk
|
||||||
Session dsk (Poco::Data::SQLite::Connector::KEY, "dsk.db");
|
Session dsk (Poco::Data::SQLite::Connector::KEY, "dsk.db");
|
||||||
assert (Poco::Data::SQLite::Utility::memoryToFile("dsk.db", pMemHandle));
|
assert (Poco::Data::SQLite::Utility::memoryToFile("dsk.db", mem));
|
||||||
|
|
||||||
dsk << "SELECT COUNT(*) FROM PERSON", into(count), now;
|
dsk << "SELECT COUNT(*) FROM PERSON", into(count), now;
|
||||||
assert (count == 1);
|
assert (count == 1);
|
||||||
@@ -2661,29 +2664,29 @@ void SQLiteTest::testThreadModes()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SQLiteTest::sqliteUpdateCallbackFn(void* pVal, int opCode, const char* pDB, const char* pTable, Poco::Int64 val)
|
void SQLiteTest::sqliteUpdateCallbackFn(void* pVal, int opCode, const char* pDB, const char* pTable, Poco::Int64 row)
|
||||||
{
|
{
|
||||||
poco_check_ptr(pVal);
|
poco_check_ptr(pVal);
|
||||||
Poco::Int64* pV = reinterpret_cast<Poco::Int64*>(pVal);
|
Poco::Int64* pV = reinterpret_cast<Poco::Int64*>(pVal);
|
||||||
if (opCode == Utility::OPERATION_INSERT)
|
if (opCode == Utility::OPERATION_INSERT)
|
||||||
{
|
{
|
||||||
poco_assert (*pV == 2);
|
poco_assert (*pV == 2);
|
||||||
poco_assert (val == 1);
|
poco_assert (row == 1);
|
||||||
std::cout << "Inserted " << pDB << '.' << pTable << ", RowID=" << val << std::endl;
|
std::cout << "Inserted " << pDB << '.' << pTable << ", RowID=" << row << std::endl;
|
||||||
++_insertCounter;
|
++_insertCounter;
|
||||||
}
|
}
|
||||||
else if (opCode == Utility::OPERATION_UPDATE)
|
else if (opCode == Utility::OPERATION_UPDATE)
|
||||||
{
|
{
|
||||||
poco_assert (*pV == 3);
|
poco_assert (*pV == 3);
|
||||||
poco_assert (val == 1);
|
poco_assert (row == 1);
|
||||||
std::cout << "Updated " << pDB << '.' << pTable << ", RowID=" << val << std::endl;
|
std::cout << "Updated " << pDB << '.' << pTable << ", RowID=" << row << std::endl;
|
||||||
++_updateCounter;
|
++_updateCounter;
|
||||||
}
|
}
|
||||||
else if (opCode == Utility::OPERATION_DELETE)
|
else if (opCode == Utility::OPERATION_DELETE)
|
||||||
{
|
{
|
||||||
poco_assert (*pV == 4);
|
poco_assert (*pV == 4);
|
||||||
poco_assert (val == 1);
|
poco_assert (row == 1);
|
||||||
std::cout << "Deleted " << pDB << '.' << pTable << ", RowID=" << val << std::endl;
|
std::cout << "Deleted " << pDB << '.' << pTable << ", RowID=" << row << std::endl;
|
||||||
++_deleteCounter;
|
++_deleteCounter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2698,9 +2701,8 @@ void SQLiteTest::testUpdateCallback()
|
|||||||
|
|
||||||
Session tmp (Poco::Data::SQLite::Connector::KEY, "dummy.db");
|
Session tmp (Poco::Data::SQLite::Connector::KEY, "dummy.db");
|
||||||
assert (tmp.isConnected());
|
assert (tmp.isConnected());
|
||||||
sqlite3* pDB = AnyCast<sqlite3*>(tmp.getProperty("handle"));
|
|
||||||
Poco::Int64 val = 1;
|
Poco::Int64 val = 1;
|
||||||
assert (Utility::registerUpdateHandler(pDB, &sqliteUpdateCallbackFn, &val));
|
assert (Utility::registerUpdateHandler(tmp, &sqliteUpdateCallbackFn, &val));
|
||||||
|
|
||||||
std::string tableName("Person");
|
std::string tableName("Person");
|
||||||
std::string lastName("lastname");
|
std::string lastName("lastname");
|
||||||
@@ -2740,7 +2742,7 @@ void SQLiteTest::testUpdateCallback()
|
|||||||
assert (_deleteCounter == 1);
|
assert (_deleteCounter == 1);
|
||||||
|
|
||||||
// disarm callback and do the same drill
|
// disarm callback and do the same drill
|
||||||
assert (Utility::registerUpdateHandler(pDB, (Utility::UpdateCallbackType) 0, &val));
|
assert (Utility::registerUpdateHandler(tmp, (Utility::UpdateCallbackType) 0, &val));
|
||||||
|
|
||||||
tmp << "DROP TABLE IF EXISTS Person", now;
|
tmp << "DROP TABLE IF EXISTS Person", now;
|
||||||
tmp << "CREATE TABLE IF NOT EXISTS Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3))", now;
|
tmp << "CREATE TABLE IF NOT EXISTS Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3))", now;
|
||||||
@@ -2779,6 +2781,436 @@ void SQLiteTest::testUpdateCallback()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int SQLiteTest::sqliteCommitCallbackFn(void* pVal)
|
||||||
|
{
|
||||||
|
poco_check_ptr(pVal);
|
||||||
|
Poco::Int64* pV = reinterpret_cast<Poco::Int64*>(pVal);
|
||||||
|
poco_assert ((*pV) == 1);
|
||||||
|
++(*pV);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SQLiteTest::testCommitCallback()
|
||||||
|
{
|
||||||
|
Session tmp (Poco::Data::SQLite::Connector::KEY, "dummy.db");
|
||||||
|
assert (tmp.isConnected());
|
||||||
|
Poco::Int64 val = 1;
|
||||||
|
assert (Utility::registerUpdateHandler(tmp, &sqliteCommitCallbackFn, &val));
|
||||||
|
|
||||||
|
std::string tableName("Person");
|
||||||
|
std::string lastName("lastname");
|
||||||
|
std::string firstName("firstname");
|
||||||
|
std::string address("Address");
|
||||||
|
int age = 133132;
|
||||||
|
int count = 0;
|
||||||
|
std::string result;
|
||||||
|
tmp.begin();
|
||||||
|
tmp << "DROP TABLE IF EXISTS Person", now;
|
||||||
|
tmp << "CREATE TABLE IF NOT EXISTS Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3))", now;
|
||||||
|
tmp << "INSERT INTO PERSON VALUES(:ln, :fn, :ad, :age)", use(lastName), use(firstName), use(address), use(age), now;
|
||||||
|
tmp.commit();
|
||||||
|
assert (val == 2);
|
||||||
|
|
||||||
|
assert (Utility::registerUpdateHandler(tmp, (Utility::CommitCallbackType) 0, &val));
|
||||||
|
val = 0;
|
||||||
|
tmp.begin();
|
||||||
|
tmp << "DROP TABLE IF EXISTS Person", now;
|
||||||
|
tmp << "CREATE TABLE IF NOT EXISTS Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3))", now;
|
||||||
|
tmp << "INSERT INTO PERSON VALUES(:ln, :fn, :ad, :age)", use(lastName), use(firstName), use(address), use(age), now;
|
||||||
|
tmp.commit();
|
||||||
|
assert (val == 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SQLiteTest::sqliteRollbackCallbackFn(void* pVal)
|
||||||
|
{
|
||||||
|
poco_check_ptr(pVal);
|
||||||
|
Poco::Int64* pV = reinterpret_cast<Poco::Int64*>(pVal);
|
||||||
|
poco_assert ((*pV) == 1);
|
||||||
|
++(*pV);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SQLiteTest::testRollbackCallback()
|
||||||
|
{
|
||||||
|
Session tmp (Poco::Data::SQLite::Connector::KEY, "dummy.db");
|
||||||
|
assert (tmp.isConnected());
|
||||||
|
Poco::Int64 val = 1;
|
||||||
|
assert (Utility::registerUpdateHandler(tmp, &sqliteRollbackCallbackFn, &val));
|
||||||
|
|
||||||
|
std::string tableName("Person");
|
||||||
|
std::string lastName("lastname");
|
||||||
|
std::string firstName("firstname");
|
||||||
|
std::string address("Address");
|
||||||
|
int age = 133132;
|
||||||
|
int count = 0;
|
||||||
|
std::string result;
|
||||||
|
tmp.begin();
|
||||||
|
tmp << "DROP TABLE IF EXISTS Person", now;
|
||||||
|
tmp << "CREATE TABLE IF NOT EXISTS Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3))", now;
|
||||||
|
tmp << "INSERT INTO PERSON VALUES(:ln, :fn, :ad, :age)", use(lastName), use(firstName), use(address), use(age), now;
|
||||||
|
tmp.rollback();
|
||||||
|
assert (val == 2);
|
||||||
|
|
||||||
|
assert (Utility::registerUpdateHandler(tmp, (Utility::RollbackCallbackType) 0, &val));
|
||||||
|
val = 0;
|
||||||
|
tmp.begin();
|
||||||
|
tmp << "DROP TABLE IF EXISTS Person", now;
|
||||||
|
tmp << "CREATE TABLE IF NOT EXISTS Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3))", now;
|
||||||
|
tmp << "INSERT INTO PERSON VALUES(:ln, :fn, :ad, :age)", use(lastName), use(firstName), use(address), use(age), now;
|
||||||
|
tmp.rollback();
|
||||||
|
assert (val == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SQLiteTest::setTransactionIsolation(Session& session, Poco::UInt32 ti)
|
||||||
|
{
|
||||||
|
if (session.hasTransactionIsolation(ti))
|
||||||
|
{
|
||||||
|
std::string funct = "setTransactionIsolation()";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Transaction t(session, false);
|
||||||
|
t.setIsolation(ti);
|
||||||
|
|
||||||
|
assert (ti == t.getIsolation());
|
||||||
|
assert (t.isIsolation(ti));
|
||||||
|
|
||||||
|
assert (ti == session.getTransactionIsolation());
|
||||||
|
assert (session.isTransactionIsolation(ti));
|
||||||
|
}
|
||||||
|
catch(Poco::Exception& e){ std::cout << funct << ':' << e.displayText() << std::endl;}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << '[' << name() << ']' << " Warning, transaction isolation not supported: ";
|
||||||
|
switch (ti)
|
||||||
|
{
|
||||||
|
case Session::TRANSACTION_READ_COMMITTED:
|
||||||
|
std::cerr << "READ COMMITTED"; break;
|
||||||
|
case Session::TRANSACTION_READ_UNCOMMITTED:
|
||||||
|
std::cerr << "READ UNCOMMITTED"; break;
|
||||||
|
case Session::TRANSACTION_REPEATABLE_READ:
|
||||||
|
std::cerr << "REPEATABLE READ"; break;
|
||||||
|
case Session::TRANSACTION_SERIALIZABLE:
|
||||||
|
std::cerr << "SERIALIZABLE"; break;
|
||||||
|
default:
|
||||||
|
std::cerr << "UNKNOWN"; break;
|
||||||
|
}
|
||||||
|
std::cerr << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SQLiteTest::testSessionTransaction()
|
||||||
|
{
|
||||||
|
Session session (Poco::Data::SQLite::Connector::KEY, "dummy.db");
|
||||||
|
assert (session.isConnected());
|
||||||
|
|
||||||
|
session << "DROP TABLE IF EXISTS Person", now;
|
||||||
|
session << "CREATE TABLE IF NOT EXISTS Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3))", now;
|
||||||
|
|
||||||
|
if (!session.canTransact())
|
||||||
|
{
|
||||||
|
std::cout << "Session not capable of transactions." << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Session local (Poco::Data::SQLite::Connector::KEY, "dummy.db");
|
||||||
|
assert (local.isConnected());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
local.setFeature("autoCommit", true);
|
||||||
|
fail ("Setting SQLite auto-commit explicitly must fail!");
|
||||||
|
}
|
||||||
|
catch (NotImplementedException&) { }
|
||||||
|
assert (local.getFeature("autoCommit"));
|
||||||
|
|
||||||
|
std::string funct = "transaction()";
|
||||||
|
std::vector<std::string> lastNames;
|
||||||
|
std::vector<std::string> firstNames;
|
||||||
|
std::vector<std::string> addresses;
|
||||||
|
std::vector<int> ages;
|
||||||
|
std::string tableName("Person");
|
||||||
|
lastNames.push_back("LN1");
|
||||||
|
lastNames.push_back("LN2");
|
||||||
|
firstNames.push_back("FN1");
|
||||||
|
firstNames.push_back("FN2");
|
||||||
|
addresses.push_back("ADDR1");
|
||||||
|
addresses.push_back("ADDR2");
|
||||||
|
ages.push_back(1);
|
||||||
|
ages.push_back(2);
|
||||||
|
int count = 0, locCount = 0;
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
setTransactionIsolation(session, Session::TRANSACTION_READ_COMMITTED);
|
||||||
|
|
||||||
|
session.begin();
|
||||||
|
assert (!session.getFeature("autoCommit"));
|
||||||
|
assert (session.isTransaction());
|
||||||
|
session << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now;
|
||||||
|
assert (session.isTransaction());
|
||||||
|
|
||||||
|
Statement stmt = (local << "SELECT COUNT(*) FROM Person", into(locCount), async, now);
|
||||||
|
|
||||||
|
session << "SELECT COUNT(*) FROM Person", into(count), now;
|
||||||
|
assert (2 == count);
|
||||||
|
assert (session.isTransaction());
|
||||||
|
session.rollback();
|
||||||
|
assert (!session.isTransaction());
|
||||||
|
assert (session.getFeature("autoCommit"));
|
||||||
|
|
||||||
|
stmt.wait();
|
||||||
|
assert (0 == locCount);
|
||||||
|
|
||||||
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (0 == count);
|
||||||
|
assert (!session.isTransaction());
|
||||||
|
|
||||||
|
session.begin();
|
||||||
|
session << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now;
|
||||||
|
assert (session.isTransaction());
|
||||||
|
assert (!session.getFeature("autoCommit"));
|
||||||
|
|
||||||
|
Statement stmt1 = (local << "SELECT COUNT(*) FROM Person", into(locCount), now);
|
||||||
|
assert (0 == locCount);
|
||||||
|
|
||||||
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (2 == count);
|
||||||
|
|
||||||
|
session.commit();
|
||||||
|
assert (!session.isTransaction());
|
||||||
|
assert (session.getFeature("autoCommit"));
|
||||||
|
|
||||||
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (2 == count);
|
||||||
|
|
||||||
|
/* TODO: see http://www.sqlite.org/pragma.html#pragma_read_uncommitted
|
||||||
|
setTransactionIsolation(session, Session::TRANSACTION_READ_UNCOMMITTED);
|
||||||
|
*/
|
||||||
|
|
||||||
|
session.close();
|
||||||
|
assert (!session.isConnected());
|
||||||
|
|
||||||
|
local.close();
|
||||||
|
assert (!local.isConnected());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SQLiteTest::testTransaction()
|
||||||
|
{
|
||||||
|
Session session (Poco::Data::SQLite::Connector::KEY, "dummy.db");
|
||||||
|
assert (session.isConnected());
|
||||||
|
|
||||||
|
session << "DROP TABLE IF EXISTS Person", now;
|
||||||
|
session << "CREATE TABLE IF NOT EXISTS Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3))", now;
|
||||||
|
|
||||||
|
if (!session.canTransact())
|
||||||
|
{
|
||||||
|
std::cout << "Session not transaction-capable." << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Session local(Poco::Data::SQLite::Connector::KEY, "dummy.db");
|
||||||
|
|
||||||
|
setTransactionIsolation(session, Session::TRANSACTION_READ_COMMITTED);
|
||||||
|
|
||||||
|
std::string funct = "transaction()";
|
||||||
|
std::vector<std::string> lastNames;
|
||||||
|
std::vector<std::string> firstNames;
|
||||||
|
std::vector<std::string> addresses;
|
||||||
|
std::vector<int> ages;
|
||||||
|
std::string tableName("Person");
|
||||||
|
lastNames.push_back("LN1");
|
||||||
|
lastNames.push_back("LN2");
|
||||||
|
firstNames.push_back("FN1");
|
||||||
|
firstNames.push_back("FN2");
|
||||||
|
addresses.push_back("ADDR1");
|
||||||
|
addresses.push_back("ADDR2");
|
||||||
|
ages.push_back(1);
|
||||||
|
ages.push_back(2);
|
||||||
|
int count = 0, locCount = 0;
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
session.setTransactionIsolation(Session::TRANSACTION_READ_COMMITTED);
|
||||||
|
|
||||||
|
{
|
||||||
|
Transaction trans(session);
|
||||||
|
assert (trans.isActive());
|
||||||
|
assert (session.isTransaction());
|
||||||
|
|
||||||
|
session << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now;
|
||||||
|
|
||||||
|
assert (session.isTransaction());
|
||||||
|
assert (trans.isActive());
|
||||||
|
|
||||||
|
session << "SELECT COUNT(*) FROM Person", into(count), now;
|
||||||
|
assert (2 == count);
|
||||||
|
assert (session.isTransaction());
|
||||||
|
assert (trans.isActive());
|
||||||
|
// no explicit commit, so transaction RAII must roll back here
|
||||||
|
}
|
||||||
|
assert (!session.isTransaction());
|
||||||
|
|
||||||
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (0 == count);
|
||||||
|
assert (!session.isTransaction());
|
||||||
|
|
||||||
|
{
|
||||||
|
Transaction trans(session);
|
||||||
|
session << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now;
|
||||||
|
|
||||||
|
Statement stmt1 = (local << "SELECT COUNT(*) FROM Person", into(locCount), now);
|
||||||
|
|
||||||
|
assert (session.isTransaction());
|
||||||
|
assert (trans.isActive());
|
||||||
|
trans.commit();
|
||||||
|
assert (!session.isTransaction());
|
||||||
|
assert (!trans.isActive());
|
||||||
|
assert (0 == locCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (2 == count);
|
||||||
|
local << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (2 == count);
|
||||||
|
|
||||||
|
session << "DELETE FROM Person", now;
|
||||||
|
|
||||||
|
std::string sql1 = format("INSERT INTO Person VALUES ('%s','%s','%s',%d)", lastNames[0], firstNames[0], addresses[0], ages[0]);
|
||||||
|
std::string sql2 = format("INSERT INTO Person VALUES ('%s','%s','%s',%d)", lastNames[1], firstNames[1], addresses[1], ages[1]);
|
||||||
|
std::vector<std::string> sql;
|
||||||
|
sql.push_back(sql1);
|
||||||
|
sql.push_back(sql2);
|
||||||
|
|
||||||
|
Transaction trans(session);
|
||||||
|
|
||||||
|
trans.execute(sql1, false);
|
||||||
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (1 == count);
|
||||||
|
trans.execute(sql2, false);
|
||||||
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (2 == count);
|
||||||
|
|
||||||
|
Statement stmt2 = (local << "SELECT COUNT(*) FROM Person", into(locCount), now);
|
||||||
|
assert (0 == locCount);
|
||||||
|
|
||||||
|
trans.rollback();
|
||||||
|
|
||||||
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (0 == count);
|
||||||
|
|
||||||
|
trans.execute(sql);
|
||||||
|
|
||||||
|
Statement stmt3 = (local << "SELECT COUNT(*) FROM Person", into(locCount), now);
|
||||||
|
assert (2 == locCount);
|
||||||
|
|
||||||
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (2 == count);
|
||||||
|
|
||||||
|
session.close();
|
||||||
|
assert (!session.isConnected());
|
||||||
|
|
||||||
|
local.close();
|
||||||
|
assert (!local.isConnected());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct TestCommitTransactor
|
||||||
|
{
|
||||||
|
void operator () (Session& session) const
|
||||||
|
{
|
||||||
|
session << "INSERT INTO Person VALUES ('lastName','firstName','address',10)", now;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct TestRollbackTransactor
|
||||||
|
{
|
||||||
|
void operator () (Session& session) const
|
||||||
|
{
|
||||||
|
session << "INSERT INTO Person VALUES ('lastName','firstName','address',10)", now;
|
||||||
|
throw Poco::Exception("test");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void SQLiteTest::testTransactor()
|
||||||
|
{
|
||||||
|
Session session (Poco::Data::SQLite::Connector::KEY, "dummy.db");
|
||||||
|
assert (session.isConnected());
|
||||||
|
|
||||||
|
session << "DROP TABLE IF EXISTS Person", now;
|
||||||
|
session << "CREATE TABLE IF NOT EXISTS Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3))", now;
|
||||||
|
|
||||||
|
std::string funct = "transaction()";
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
assert (session.getFeature("autoCommit"));
|
||||||
|
session.setTransactionIsolation(Session::TRANSACTION_READ_COMMITTED);
|
||||||
|
|
||||||
|
TestCommitTransactor ct;
|
||||||
|
Transaction t1(session, ct);
|
||||||
|
|
||||||
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (1 == count);
|
||||||
|
|
||||||
|
session << "DELETE FROM Person", now;
|
||||||
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (0 == count);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TestRollbackTransactor rt;
|
||||||
|
Transaction t(session, rt);
|
||||||
|
fail ("must fail");
|
||||||
|
} catch (Poco::Exception&) { }
|
||||||
|
|
||||||
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (0 == count);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TestRollbackTransactor rt;
|
||||||
|
Transaction t(session);
|
||||||
|
t.transact(rt);
|
||||||
|
fail ("must fail");
|
||||||
|
} catch (Poco::Exception&) { }
|
||||||
|
|
||||||
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (0 == count);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TestRollbackTransactor rt;
|
||||||
|
Transaction t(session, false);
|
||||||
|
t.transact(rt);
|
||||||
|
fail ("must fail");
|
||||||
|
} catch (Poco::Exception&) { }
|
||||||
|
|
||||||
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (0 == count);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TestRollbackTransactor rt;
|
||||||
|
Transaction t(session, true);
|
||||||
|
t.transact(rt);
|
||||||
|
fail ("must fail");
|
||||||
|
} catch (Poco::Exception&) { }
|
||||||
|
|
||||||
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
|
assert (0 == count);
|
||||||
|
|
||||||
|
session.close();
|
||||||
|
assert (!session.isConnected());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SQLiteTest::setUp()
|
void SQLiteTest::setUp()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -2872,6 +3304,11 @@ CppUnit::Test* SQLiteTest::suite()
|
|||||||
CppUnit_addTest(pSuite, SQLiteTest, testSystemTable);
|
CppUnit_addTest(pSuite, SQLiteTest, testSystemTable);
|
||||||
CppUnit_addTest(pSuite, SQLiteTest, testThreadModes);
|
CppUnit_addTest(pSuite, SQLiteTest, testThreadModes);
|
||||||
CppUnit_addTest(pSuite, SQLiteTest, testUpdateCallback);
|
CppUnit_addTest(pSuite, SQLiteTest, testUpdateCallback);
|
||||||
|
CppUnit_addTest(pSuite, SQLiteTest, testCommitCallback);
|
||||||
|
CppUnit_addTest(pSuite, SQLiteTest, testRollbackCallback);
|
||||||
|
CppUnit_addTest(pSuite, SQLiteTest, testSessionTransaction);
|
||||||
|
CppUnit_addTest(pSuite, SQLiteTest, testTransaction);
|
||||||
|
CppUnit_addTest(pSuite, SQLiteTest, testTransactor);
|
||||||
|
|
||||||
return pSuite;
|
return pSuite;
|
||||||
}
|
}
|
||||||
|
@@ -40,6 +40,14 @@
|
|||||||
#include "CppUnit/TestCase.h"
|
#include "CppUnit/TestCase.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace Poco {
|
||||||
|
namespace Data {
|
||||||
|
|
||||||
|
class Session;
|
||||||
|
|
||||||
|
} }
|
||||||
|
|
||||||
|
|
||||||
class SQLiteTest: public CppUnit::TestCase
|
class SQLiteTest: public CppUnit::TestCase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -137,15 +145,25 @@ public:
|
|||||||
void testThreadModes();
|
void testThreadModes();
|
||||||
|
|
||||||
void testUpdateCallback();
|
void testUpdateCallback();
|
||||||
|
void testCommitCallback();
|
||||||
|
void testRollbackCallback();
|
||||||
|
|
||||||
|
void testSessionTransaction();
|
||||||
|
void testTransaction();
|
||||||
|
void testTransactor();
|
||||||
|
|
||||||
void setUp();
|
void setUp();
|
||||||
void tearDown();
|
void tearDown();
|
||||||
|
|
||||||
static void sqliteUpdateCallbackFn(void*, int, const char*, const char*, Poco::Int64);
|
static void sqliteUpdateCallbackFn(void*, int, const char*, const char*, Poco::Int64);
|
||||||
|
static int sqliteCommitCallbackFn(void*);
|
||||||
|
static void sqliteRollbackCallbackFn(void*);
|
||||||
|
|
||||||
static CppUnit::Test* suite();
|
static CppUnit::Test* suite();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void setTransactionIsolation(Poco::Data::Session& session, Poco::UInt32 ti);
|
||||||
|
|
||||||
static int _insertCounter;
|
static int _insertCounter;
|
||||||
static int _updateCounter;
|
static int _updateCounter;
|
||||||
static int _deleteCounter;
|
static int _deleteCounter;
|
||||||
|
@@ -81,14 +81,14 @@ public:
|
|||||||
Poco::UInt32 getTransactionIsolation();
|
Poco::UInt32 getTransactionIsolation();
|
||||||
bool hasTransactionIsolation(Poco::UInt32);
|
bool hasTransactionIsolation(Poco::UInt32);
|
||||||
bool isTransactionIsolation(Poco::UInt32);
|
bool isTransactionIsolation(Poco::UInt32);
|
||||||
const std::string& connectorName();
|
const std::string& connectorName() const;
|
||||||
void setFeature(const std::string& name, bool state);
|
void setFeature(const std::string& name, bool state);
|
||||||
bool getFeature(const std::string& name);
|
bool getFeature(const std::string& name);
|
||||||
void setProperty(const std::string& name, const Poco::Any& value);
|
void setProperty(const std::string& name, const Poco::Any& value);
|
||||||
Poco::Any getProperty(const std::string& name);
|
Poco::Any getProperty(const std::string& name);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SessionImpl* access();
|
SessionImpl* access() const;
|
||||||
/// Updates the last access timestamp,
|
/// Updates the last access timestamp,
|
||||||
/// verifies validity of the session
|
/// verifies validity of the session
|
||||||
/// and returns the session if it is valid.
|
/// and returns the session if it is valid.
|
||||||
@@ -96,18 +96,18 @@ protected:
|
|||||||
/// Throws an SessionUnavailableException if the
|
/// Throws an SessionUnavailableException if the
|
||||||
/// session is no longer valid.
|
/// session is no longer valid.
|
||||||
|
|
||||||
SessionImpl* impl();
|
SessionImpl* impl() const;
|
||||||
/// Returns a pointer to the SessionImpl.
|
/// Returns a pointer to the SessionImpl.
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Poco::AutoPtr<PooledSessionHolder> _pHolder;
|
mutable Poco::AutoPtr<PooledSessionHolder> _pHolder;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// inlines
|
// inlines
|
||||||
//
|
//
|
||||||
inline SessionImpl* PooledSessionImpl::impl()
|
inline SessionImpl* PooledSessionImpl::impl() const
|
||||||
{
|
{
|
||||||
return _pHolder->session();
|
return _pHolder->session();
|
||||||
}
|
}
|
||||||
|
@@ -274,10 +274,10 @@ public:
|
|||||||
/// Returns true iff the transaction isolation level corresponds
|
/// Returns true iff the transaction isolation level corresponds
|
||||||
/// to the supplied bitmask.
|
/// to the supplied bitmask.
|
||||||
|
|
||||||
std::string connector();
|
std::string connector() const;
|
||||||
/// Returns the connector name for this session.
|
/// Returns the connector name for this session.
|
||||||
|
|
||||||
std::string uri();
|
std::string uri() const;
|
||||||
/// Returns the URI for this session.
|
/// Returns the URI for this session.
|
||||||
|
|
||||||
static std::string uri(const std::string& connector,
|
static std::string uri(const std::string& connector,
|
||||||
@@ -443,7 +443,7 @@ inline bool Session::isTransactionIsolation(Poco::UInt32 ti)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline std::string Session::connector()
|
inline std::string Session::connector() const
|
||||||
{
|
{
|
||||||
return _pImpl->connectorName();
|
return _pImpl->connectorName();
|
||||||
}
|
}
|
||||||
@@ -456,7 +456,7 @@ inline std::string Session::uri(const std::string& connector,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline std::string Session::uri()
|
inline std::string Session::uri() const
|
||||||
{
|
{
|
||||||
return _pImpl->uri();
|
return _pImpl->uri();
|
||||||
}
|
}
|
||||||
|
@@ -139,16 +139,16 @@ public:
|
|||||||
/// Returns true iff the transaction isolation level corresponds
|
/// Returns true iff the transaction isolation level corresponds
|
||||||
/// to the supplied bitmask.
|
/// to the supplied bitmask.
|
||||||
|
|
||||||
virtual const std::string& connectorName() = 0;
|
virtual const std::string& connectorName() const = 0;
|
||||||
/// Returns the name of the connector.
|
/// Returns the name of the connector.
|
||||||
|
|
||||||
const std::string& connectionString();
|
const std::string& connectionString() const;
|
||||||
/// Returns the connection string.
|
/// Returns the connection string.
|
||||||
|
|
||||||
static std::string uri(const std::string& connector, const std::string& connectionString);
|
static std::string uri(const std::string& connector, const std::string& connectionString);
|
||||||
/// Returns formatted URI.
|
/// Returns formatted URI.
|
||||||
|
|
||||||
std::string uri();
|
std::string uri() const;
|
||||||
/// Returns the URI for this session.
|
/// Returns the URI for this session.
|
||||||
|
|
||||||
virtual void setFeature(const std::string& name, bool state) = 0;
|
virtual void setFeature(const std::string& name, bool state) = 0;
|
||||||
@@ -206,7 +206,7 @@ private:
|
|||||||
//
|
//
|
||||||
// inlines
|
// inlines
|
||||||
//
|
//
|
||||||
inline const std::string& SessionImpl::connectionString()
|
inline const std::string& SessionImpl::connectionString() const
|
||||||
{
|
{
|
||||||
return _connectionString;
|
return _connectionString;
|
||||||
}
|
}
|
||||||
@@ -231,7 +231,7 @@ inline std::string SessionImpl::uri(const std::string& connector,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline std::string SessionImpl::uri()
|
inline std::string SessionImpl::uri() const
|
||||||
{
|
{
|
||||||
return uri(connectorName(), connectionString());
|
return uri(connectorName(), connectionString());
|
||||||
}
|
}
|
||||||
|
@@ -163,7 +163,7 @@ void PooledSessionImpl::close()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const std::string& PooledSessionImpl::connectorName()
|
const std::string& PooledSessionImpl::connectorName() const
|
||||||
{
|
{
|
||||||
return access()->connectorName();
|
return access()->connectorName();
|
||||||
}
|
}
|
||||||
@@ -193,7 +193,7 @@ Poco::Any PooledSessionImpl::getProperty(const std::string& name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SessionImpl* PooledSessionImpl::access()
|
SessionImpl* PooledSessionImpl::access() const
|
||||||
{
|
{
|
||||||
if (_pHolder)
|
if (_pHolder)
|
||||||
{
|
{
|
||||||
|
@@ -476,7 +476,8 @@ void StatementImpl::formatSQL(std::vector<Any>& arguments)
|
|||||||
{
|
{
|
||||||
std::string sql;
|
std::string sql;
|
||||||
Poco::format(sql, _ostr.str(), arguments);
|
Poco::format(sql, _ostr.str(), arguments);
|
||||||
_ostr.str(""); _ostr << sql;
|
_ostr.str("");
|
||||||
|
_ostr << sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -145,7 +145,7 @@ bool SessionImpl::isTransactionIsolation(Poco::UInt32)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const std::string& SessionImpl::connectorName()
|
const std::string& SessionImpl::connectorName() const
|
||||||
{
|
{
|
||||||
return Connector::KEY;
|
return Connector::KEY;
|
||||||
}
|
}
|
||||||
|
@@ -105,7 +105,7 @@ public:
|
|||||||
/// Returns true iff the transaction isolation level corresponds
|
/// Returns true iff the transaction isolation level corresponds
|
||||||
/// to the supplied bitmask.
|
/// to the supplied bitmask.
|
||||||
|
|
||||||
const std::string& connectorName();
|
const std::string& connectorName() const;
|
||||||
/// Returns the name of the connector.
|
/// Returns the name of the connector.
|
||||||
|
|
||||||
void setConnected(const std::string& name, bool value);
|
void setConnected(const std::string& name, bool value);
|
||||||
|
Reference in New Issue
Block a user