more callbacks and transaction tests

- added callbacks for commit and rollback with tests
- added transaction tests
This commit is contained in:
aleks-f
2013-02-17 15:12:07 -06:00
parent 59087c7cf1
commit 9ea546d981
15 changed files with 635 additions and 65 deletions

View File

@@ -141,7 +141,7 @@ public:
SessionHandle& handle();
// Get handle
const std::string& connectorName();
const std::string& connectorName() const;
/// Returns the name of the connector.
private:
@@ -208,7 +208,7 @@ inline SessionHandle& SessionImpl::handle()
}
inline const std::string& SessionImpl::connectorName()
inline const std::string& SessionImpl::connectorName() const
{
return _connector;
}

View File

@@ -122,7 +122,7 @@ public:
bool isTransaction();
/// Returns true iff a transaction is in progress.
const std::string& connectorName();
const std::string& connectorName() const;
/// Returns the name of the connector.
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;
}

View File

@@ -52,6 +52,9 @@ struct sqlite3_stmt;
namespace Poco {
class Mutex;
namespace Data {
namespace SQLite {
@@ -125,7 +128,13 @@ public:
/// Returns true iff the transaction isolation level corresponds
/// 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.
private:
@@ -134,7 +143,7 @@ private:
bool _connected;
bool _isTransaction;
int _timeout;
Mutex _mutex;
static const std::string DEFERRED_BEGIN_TRANSACTION;
static const std::string COMMIT_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;
}

View File

@@ -42,6 +42,7 @@
#include "Poco/Data/SQLite/SQLite.h"
#include "Poco/Data/MetaColumn.h"
#include "Poco/Data/Session.h"
#include "Poco/Mutex.h"
#include "Poco/Types.h"
#include <map>
@@ -57,7 +58,7 @@ namespace SQLite {
class SQLite_API Utility
/// Various utility functions for SQLite, mostly return code handling
/// Various utility functions for SQLite.
{
public:
static const std::string SQLITE_DATE_FORMAT;
@@ -72,8 +73,14 @@ public:
static const int OPERATION_DELETE;
static const int OPERATION_UPDATE;
static sqlite3* dbHandle(const Session& session);
/// Returns native DB handle.
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());
/// 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
/// 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);
/// Saves the contents of an opened database in memory to the
/// 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();
/// 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)
/// Registers the callback for (1)(insert, delete, update), (2)(commit) or
/// 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
/// callbackFn set to zero disables notifications.
///
/// See http://www.sqlite.org/c3ref/update_hook.html and
/// 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 RetMap retMap;
T* pRet = reinterpret_cast<T*>(eventHook(pDB, callbackFn, pParam));
static CBMap retMap;
T* pRet = reinterpret_cast<T*>(eventHookRegister(pDB, callbackFn, pParam));
bool success = false;
if (pRet == 0)
{
if (retMap.find(pDB) == retMap.end())
success = (pRet == 0);
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;
return success;
if ((callbackFn == it->second.first) && (*pRet == *it->second.second))
{
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:
@@ -158,9 +207,9 @@ private:
Utility(const Utility&);
Utility& operator = (const Utility&);
static void* eventHook(sqlite3* pDB, UpdateCallbackType callbackFn, void* pParam);
static void* eventHook(sqlite3* pDB, CommitCallbackType callbackFn, void* pParam);
static void* eventHook(sqlite3* pDB, RollbackCallbackType callbackFn, void* pParam);
static void* eventHookRegister(sqlite3* pDB, UpdateCallbackType callbackFn, void* pParam);
static void* eventHookRegister(sqlite3* pDB, CommitCallbackType callbackFn, void* pParam);
static void* eventHookRegister(sqlite3* pDB, RollbackCallbackType callbackFn, void* pParam);
static TypeMap _types;
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
#endif
#endif // SQLite_Utility_INCLUDED

View File

@@ -42,6 +42,7 @@
#include "Poco/ActiveMethod.h"
#include "Poco/ActiveResult.h"
#include "Poco/String.h"
#include "Poco/Mutex.h"
#include "Poco/Data/DataException.h"
#include "sqlite3.h"
#include <cstdlib>
@@ -67,6 +68,9 @@ SessionImpl::SessionImpl(const std::string& fileName, std::size_t loginTimeout):
open();
setConnectionTimeout(CONNECTION_TIMEOUT_DEFAULT);
setProperty("handle", _pDB);
addFeature("autoCommit",
&SessionImpl::autoCommit,
&SessionImpl::isAutoCommit);
}
@@ -85,6 +89,7 @@ Poco::Data::StatementImpl* SessionImpl::createStatementImpl()
void SessionImpl::begin()
{
Poco::Mutex::ScopedLock l(_mutex);
SQLiteStatementImpl tmp(*this, _pDB);
tmp.add(DEFERRED_BEGIN_TRANSACTION);
tmp.execute();
@@ -94,6 +99,7 @@ void SessionImpl::begin()
void SessionImpl::commit()
{
Poco::Mutex::ScopedLock l(_mutex);
SQLiteStatementImpl tmp(*this, _pDB);
tmp.add(COMMIT_TRANSACTION);
tmp.execute();
@@ -103,6 +109,7 @@ void SessionImpl::commit()
void SessionImpl::rollback()
{
Poco::Mutex::ScopedLock l(_mutex);
SQLiteStatementImpl tmp(*this, _pDB);
tmp.add(ABORT_TRANSACTION);
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

View File

@@ -40,6 +40,7 @@
#include "Poco/Data/SQLite/SQLiteException.h"
#include "Poco/NumberFormatter.h"
#include "Poco/String.h"
#include "Poco/Any.h"
#include "Poco/Exception.h"
#if defined(POCO_UNBUNDLED)
#include <sqlite3.h>
@@ -220,7 +221,7 @@ void Utility::throwException(int rc, const std::string& addErrMsg)
case SQLITE_DONE:
break; // sqlite_step() has finished executing
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);
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);
}
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);
}

View File

@@ -45,6 +45,7 @@
#include "Poco/Dynamic/Var.h"
#include "Poco/Data/TypeHandler.h"
#include "Poco/Nullable.h"
#include "Poco/Data/Transaction.h"
#include "Poco/Data/DataException.h"
#include "Poco/Data/SQLite/SQLiteException.h"
#include "Poco/Tuple.h"
@@ -73,6 +74,7 @@ using Poco::Data::LimitException;
using Poco::Data::CLOB;
using Poco::Data::Date;
using Poco::Data::Time;
using Poco::Data::Transaction;
using Poco::Data::AbstractExtractionVec;
using Poco::Data::AbstractExtractionVecVec;
using Poco::Data::AbstractBindingVec;
@@ -87,11 +89,14 @@ using Poco::Logger;
using Poco::Message;
using Poco::AutoPtr;
using Poco::Thread;
using Poco::format;
using Poco::InvalidAccessException;
using Poco::RangeException;
using Poco::BadCastException;
using Poco::NotFoundException;
using Poco::NullPointerException;
using Poco::TimeoutException;
using Poco::NotImplementedException;
using Poco::Data::SQLite::ConstraintViolationException;
using Poco::Data::SQLite::ParameterCountMismatchException;
using Poco::Int32;
@@ -362,9 +367,7 @@ void SQLiteTest::testInMemory()
// load db from file to memory
Session mem (Poco::Data::SQLite::Connector::KEY, ":memory:");
sqlite3* p = 0; Any a = p; // ??? clang generated code fails to AnyCast without these
sqlite3* pMemHandle = AnyCast<sqlite3*>(mem.getProperty("handle"));
assert (Poco::Data::SQLite::Utility::fileToMemory(pMemHandle, "dummy.db"));
assert (Poco::Data::SQLite::Utility::fileToMemory(mem, "dummy.db"));
mem << "SELECT COUNT(*) FROM PERSON", into(count), now;
assert (count == 1);
@@ -378,7 +381,7 @@ void SQLiteTest::testInMemory()
// save db from memory to file on the disk
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;
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::Int64* pV = reinterpret_cast<Poco::Int64*>(pVal);
if (opCode == Utility::OPERATION_INSERT)
{
poco_assert (*pV == 2);
poco_assert (val == 1);
std::cout << "Inserted " << pDB << '.' << pTable << ", RowID=" << val << std::endl;
poco_assert (row == 1);
std::cout << "Inserted " << pDB << '.' << pTable << ", RowID=" << row << std::endl;
++_insertCounter;
}
else if (opCode == Utility::OPERATION_UPDATE)
{
poco_assert (*pV == 3);
poco_assert (val == 1);
std::cout << "Updated " << pDB << '.' << pTable << ", RowID=" << val << std::endl;
poco_assert (row == 1);
std::cout << "Updated " << pDB << '.' << pTable << ", RowID=" << row << std::endl;
++_updateCounter;
}
else if (opCode == Utility::OPERATION_DELETE)
{
poco_assert (*pV == 4);
poco_assert (val == 1);
std::cout << "Deleted " << pDB << '.' << pTable << ", RowID=" << val << std::endl;
poco_assert (row == 1);
std::cout << "Deleted " << pDB << '.' << pTable << ", RowID=" << row << std::endl;
++_deleteCounter;
}
}
@@ -2698,9 +2701,8 @@ void SQLiteTest::testUpdateCallback()
Session tmp (Poco::Data::SQLite::Connector::KEY, "dummy.db");
assert (tmp.isConnected());
sqlite3* pDB = AnyCast<sqlite3*>(tmp.getProperty("handle"));
Poco::Int64 val = 1;
assert (Utility::registerUpdateHandler(pDB, &sqliteUpdateCallbackFn, &val));
assert (Utility::registerUpdateHandler(tmp, &sqliteUpdateCallbackFn, &val));
std::string tableName("Person");
std::string lastName("lastname");
@@ -2740,7 +2742,7 @@ void SQLiteTest::testUpdateCallback()
assert (_deleteCounter == 1);
// 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 << "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()
{
}
@@ -2872,6 +3304,11 @@ CppUnit::Test* SQLiteTest::suite()
CppUnit_addTest(pSuite, SQLiteTest, testSystemTable);
CppUnit_addTest(pSuite, SQLiteTest, testThreadModes);
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;
}

View File

@@ -40,6 +40,14 @@
#include "CppUnit/TestCase.h"
namespace Poco {
namespace Data {
class Session;
} }
class SQLiteTest: public CppUnit::TestCase
{
public:
@@ -137,15 +145,25 @@ public:
void testThreadModes();
void testUpdateCallback();
void testCommitCallback();
void testRollbackCallback();
void testSessionTransaction();
void testTransaction();
void testTransactor();
void setUp();
void tearDown();
static void sqliteUpdateCallbackFn(void*, int, const char*, const char*, Poco::Int64);
static int sqliteCommitCallbackFn(void*);
static void sqliteRollbackCallbackFn(void*);
static CppUnit::Test* suite();
private:
void setTransactionIsolation(Poco::Data::Session& session, Poco::UInt32 ti);
static int _insertCounter;
static int _updateCounter;
static int _deleteCounter;

View File

@@ -81,14 +81,14 @@ public:
Poco::UInt32 getTransactionIsolation();
bool hasTransactionIsolation(Poco::UInt32);
bool isTransactionIsolation(Poco::UInt32);
const std::string& connectorName();
const std::string& connectorName() const;
void setFeature(const std::string& name, bool state);
bool getFeature(const std::string& name);
void setProperty(const std::string& name, const Poco::Any& value);
Poco::Any getProperty(const std::string& name);
protected:
SessionImpl* access();
SessionImpl* access() const;
/// Updates the last access timestamp,
/// verifies validity of the session
/// and returns the session if it is valid.
@@ -96,18 +96,18 @@ protected:
/// Throws an SessionUnavailableException if the
/// session is no longer valid.
SessionImpl* impl();
SessionImpl* impl() const;
/// Returns a pointer to the SessionImpl.
private:
Poco::AutoPtr<PooledSessionHolder> _pHolder;
mutable Poco::AutoPtr<PooledSessionHolder> _pHolder;
};
//
// inlines
//
inline SessionImpl* PooledSessionImpl::impl()
inline SessionImpl* PooledSessionImpl::impl() const
{
return _pHolder->session();
}

View File

@@ -274,10 +274,10 @@ public:
/// Returns true iff the transaction isolation level corresponds
/// to the supplied bitmask.
std::string connector();
std::string connector() const;
/// Returns the connector name for this session.
std::string uri();
std::string uri() const;
/// Returns the URI for this session.
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();
}
@@ -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();
}

View File

@@ -139,16 +139,16 @@ public:
/// Returns true iff the transaction isolation level corresponds
/// to the supplied bitmask.
virtual const std::string& connectorName() = 0;
virtual const std::string& connectorName() const = 0;
/// Returns the name of the connector.
const std::string& connectionString();
const std::string& connectionString() const;
/// Returns the connection string.
static std::string uri(const std::string& connector, const std::string& connectionString);
/// Returns formatted URI.
std::string uri();
std::string uri() const;
/// Returns the URI for this session.
virtual void setFeature(const std::string& name, bool state) = 0;
@@ -206,7 +206,7 @@ private:
//
// inlines
//
inline const std::string& SessionImpl::connectionString()
inline const std::string& SessionImpl::connectionString() const
{
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());
}

View File

@@ -163,7 +163,7 @@ void PooledSessionImpl::close()
}
const std::string& PooledSessionImpl::connectorName()
const std::string& PooledSessionImpl::connectorName() const
{
return access()->connectorName();
}
@@ -193,7 +193,7 @@ Poco::Any PooledSessionImpl::getProperty(const std::string& name)
}
SessionImpl* PooledSessionImpl::access()
SessionImpl* PooledSessionImpl::access() const
{
if (_pHolder)
{

View File

@@ -476,7 +476,8 @@ void StatementImpl::formatSQL(std::vector<Any>& arguments)
{
std::string sql;
Poco::format(sql, _ostr.str(), arguments);
_ostr.str(""); _ostr << sql;
_ostr.str("");
_ostr << sql;
}

View File

@@ -145,7 +145,7 @@ bool SessionImpl::isTransactionIsolation(Poco::UInt32)
}
const std::string& SessionImpl::connectorName()
const std::string& SessionImpl::connectorName() const
{
return Connector::KEY;
}

View File

@@ -105,7 +105,7 @@ public:
/// Returns true iff the transaction isolation level corresponds
/// to the supplied bitmask.
const std::string& connectorName();
const std::string& connectorName() const;
/// Returns the name of the connector.
void setConnected(const std::string& name, bool value);