[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: playground/utils/charm/trunk
From: Mirko Boehm <mirko () kde ! org>
Date: 2010-03-25 21:30:32
Message-ID: 20100325213032.99606AC87F () svn ! kde ! org
[Download RAW message or body]
SVN commit 1107519 by mirko:
- add a test to reproduce the somewhat erratic behaviour on nested transactions
M +1 -0 Core/CMakeLists.txt
A Core/CharmExceptions.cpp [License: Trivial file.]
M +10 -3 Core/CharmExceptions.h
M +42 -0 Core/MySqlStorage.cpp
M +21 -10 Core/MySqlStorage.h
M +21 -21 Core/SqlStorage.cpp
M +3 -3 Core/SqlStorage.h
M +90 -3 Tests/SqlTransactionTests.cpp
M +6 -0 Tests/SqlTransactionTests.h
M +23 -45 Tools/TimesheetProcessor/Database.cpp
--- trunk/playground/utils/charm/trunk/Core/CMakeLists.txt #1107518:1107519
@@ -10,6 +10,7 @@
SET( CharmCore_SRCS
CharmConstants.cpp
+ CharmExceptions.cpp
Controller.cpp
SqLiteStorage.cpp
MySqlStorage.cpp
--- trunk/playground/utils/charm/trunk/Core/CharmExceptions.h #1107518:1107519
@@ -16,10 +16,17 @@
QString m_message;
};
+class ParseError : public CharmException {
+public:
+ explicit ParseError( const QString& text = QString() )
+ : CharmException( text )
+ {}
+};
+
class XmlSerializationException : public CharmException
{
public:
- XmlSerializationException( const QString& message )
+ explicit XmlSerializationException( const QString& message )
: CharmException( message )
{}
};
@@ -27,7 +34,7 @@
class UnsupportedDatabaseVersionException : public CharmException
{
public:
- UnsupportedDatabaseVersionException( const QString& message )
+ explicit UnsupportedDatabaseVersionException( const QString& message )
: CharmException( message )
{}
};
@@ -35,7 +42,7 @@
class InvalidTaskListException : public CharmException
{
public:
- InvalidTaskListException( const QString& message )
+ explicit InvalidTaskListException( const QString& message )
: CharmException( message )
{}
};
--- trunk/playground/utils/charm/trunk/Core/MySqlStorage.cpp #1107518:1107519
@@ -10,6 +10,7 @@
#include "MySqlStorage.h"
#include "CharmConstants.h"
+#include "CharmExceptions.h"
// DATABASE STRUCTURE DEFINITION FOR MYSQL
static const QString Tables[] =
@@ -164,3 +165,44 @@
{
return createDatabaseTables();
}
+
+MySqlStorage::Parameters MySqlStorage::parseParameterEnvironmentVariable()
+{
+ // read configuration from the environment:
+ char* const databaseConfigurationString = getenv( "CHARM_DATABASE_CONFIGURATION" \
); + if ( databaseConfigurationString != 0 ) {
+ Parameters p;
+ // the string is supposed to be of the format \
"hostname;port;username;password" + // if port is 0 or empty, the default port \
is used + QStringList elements = QString::fromLocal8Bit( \
databaseConfigurationString ).split( ';' ); + if ( elements.count() != 4 ) {
+ throw ParseError( QObject::tr( "Bad database configuration string \
format" ) ); + } else {
+ p.host = elements.at( 0 );
+ p.name = elements.at( 2 );
+ p.password = elements.at( 3 );
+ bool ok;
+ if( ! elements.at( 1 ).isEmpty() ) {
+ p.port = elements.at( 1 ).toInt( &ok );
+ if ( ok != true || p.port < 0 ) {
+ throw ParseError( QObject::tr(
+ "The port must be a non-negative integer number in the \
database configuration string format" ) ); + }
+ }
+ }
+ return p;
+ } else {
+ throw ParseError( QObject::tr( "Timesheet processor configuration not found \
(CHARM_DATABASE_CONFIGURATION). Aborting." ) ); + }
+}
+
+void MySqlStorage::configure( const Parameters& parameters )
+{
+ database().setHostName( parameters.host );
+ database().setDatabaseName( parameters.database );
+ database().setUserName( parameters.name );
+ database().setPassword( parameters.password );
+ if ( parameters.port != 0 ) {
+ database().setPort( parameters.port );
+ }
+}
--- trunk/playground/utils/charm/trunk/Core/MySqlStorage.h #1107518:1107519
@@ -13,20 +13,31 @@
class MySqlStorage: public SqlStorage
{
public:
- MySqlStorage();
- virtual ~MySqlStorage();
+ struct Parameters {
+ Parameters() : port( 3309), database( "Charm" ) {}
+ unsigned int port;
+ QString database;
+ QString name;
+ QString password;
+ QString host;
+ };
- QSqlDatabase& database();
-
- QString description() const;
- bool connect(Configuration&);
- bool disconnect();
- int installationId() const;
- bool createDatabase(Configuration&);
+ MySqlStorage();
+ virtual ~MySqlStorage();
+
+ QSqlDatabase& database();
+
+ QString description() const;
+ bool connect(Configuration&);
+ bool disconnect();
+ int installationId() const;
+ bool createDatabase(Configuration&);
bool createDatabaseTables();
+ static Parameters parseParameterEnvironmentVariable();
+ void configure( const Parameters& );
protected:
- QString lastInsertRowFunction() const;
+ QString lastInsertRowFunction() const;
private:
QSqlDatabase m_database;
--- trunk/playground/utils/charm/trunk/Core/SqlStorage.cpp #1107518:1107519
@@ -336,28 +336,28 @@
bool SqlStorage::runQuery(QSqlQuery& query)
{
- static const bool DoChitChat = false;
- if (DoChitChat)
- qDebug() << MARKER << endl << "SqlStorage::runQuery: executing query:"
- << endl << query.executedQuery();
- bool result = query.exec();
- if ( DoChitChat )
- {
- if ( result )
- {
+ static const bool DoChitChat = false;
+ if (DoChitChat)
+ qDebug() << MARKER << endl << "SqlStorage::runQuery: executing query:"
+ << endl << query.executedQuery();
+ bool result = query.exec();
+ if ( DoChitChat )
+ {
+ if ( result )
+ {
- qDebug() << "SqlStorage::runQuery: SUCCESS" << endl << MARKER;
- }
- else
- {
- qDebug() << "SqlStorage::runQuery: FAILURE" << endl
- << "Database says: " << query.lastError().databaseText() << endl
- << "Driver says: " << query.lastError().driverText() << endl
- << MARKER;
- }
- }
- return result;
- }
+ qDebug() << "SqlStorage::runQuery: SUCCESS" << endl << MARKER;
+ }
+ else
+ {
+ qDebug() << "SqlStorage::runQuery: FAILURE" << endl
+ << "Database says: " << query.lastError().databaseText() << endl
+ << "Driver says: " << query.lastError().driverText() << endl
+ << MARKER;
+ }
+ }
+ return result;
+}
void SqlStorage::stateChanged(State previous)
{
--- trunk/playground/utils/charm/trunk/Core/SqlStorage.h #1107518:1107519
@@ -61,12 +61,12 @@
bool verifyDatabase() throw ( UnsupportedDatabaseVersionException );
virtual bool createDatabaseTables() = 0;
+ // run the query and process possible errors
+ static bool runQuery( QSqlQuery& );
+
protected:
virtual QString lastInsertRowFunction() const = 0;
- // run the query and process possible errors
- bool runQuery( QSqlQuery& );
-
private:
// a debug helper that populates the database with canned values:
void populateDatabase();
--- trunk/playground/utils/charm/trunk/Tests/SqlTransactionTests.cpp #1107518:1107519
@@ -1,7 +1,12 @@
#include <QtTest/QtTest>
#include <QSqlDatabase>
+#include <QSqlQuery>
#include <QSqlDriver>
+#include <QSqlError>
+#include "Core/MySqlStorage.h"
+#include "Core/SqlRaiiTransactor.h"
+
#include "SqlTransactionTests.h"
SqlTransactionTests::SqlTransactionTests()
@@ -14,9 +19,6 @@
const char DriverName[] = "QMYSQL";
QVERIFY( QSqlDatabase::isDriverAvailable( DriverName ) );
QSqlDatabase db = QSqlDatabase::addDatabase( DriverName, \
"test-mysql.charm.kdab.net" );
- QSqlDriver* driver = db.driver();
- // cannot check that yet, it can only be determined after login
- // QVERIFY( driver->hasFeature( QSqlDriver::Transactions ) );
}
void SqlTransactionTests::testSqLiteDriverRequirements()
@@ -28,6 +30,91 @@
QVERIFY( driver->hasFeature( QSqlDriver::Transactions ) );
}
+MySqlStorage SqlTransactionTests::prepareMySqlStorage()
+{
+ MySqlStorage storage;
+ MySqlStorage::Parameters parameters = \
MySqlStorage::parseParameterEnvironmentVariable(); + storage.configure( parameters \
); + return storage;
+}
+
+void SqlTransactionTests::testMySqlTransactionRollback()
+{
+ MySqlStorage storage = prepareMySqlStorage();
+ QVERIFY( storage.database().open() );
+
+ QSqlDriver* driver = storage.database().driver();
+ QVERIFY( driver->hasFeature( QSqlDriver::Transactions ) );
+
+ QList<Task> tasksBefore = storage.getAllTasks();
+ QVERIFY( ! tasksBefore.isEmpty() );
+ Task first = tasksBefore.first();
+ // test a simple transaction that is completed and committed:
+ {
+ SqlRaiiTransactor transactor( storage.database() );
+ QSqlQuery query( storage.database() );
+ query.prepare("DELETE from Tasks where id=:id");
+ query.bindValue( "id", first.id() );
+ QVERIFY( storage.runQuery( query ) );
+ } // this transaction was NOT commited
+ QList<Task> tasksAfter = storage.getAllTasks();
+ QVERIFY( ! tasksAfter.isEmpty() );
+ QVERIFY( tasksBefore == tasksAfter );
+}
+
+void SqlTransactionTests::testMySqlTransactionCommit()
+{
+ MySqlStorage storage = prepareMySqlStorage();
+ QVERIFY( storage.database().open() );
+
+ QList<Task> tasksBefore = storage.getAllTasks();
+ QVERIFY( ! tasksBefore.isEmpty() );
+ Task first = tasksBefore.takeFirst();
+ // test a simple transaction that is completed and committed:
+ {
+ SqlRaiiTransactor transactor( storage.database() );
+ QSqlQuery query( storage.database() );
+ query.prepare("DELETE from Tasks where id=:id");
+ query.bindValue( "id", first.id() );
+ QVERIFY( storage.runQuery( query ) );
+ transactor.commit();
+ } // this transaction WAS commited
+ QList<Task> tasksAfter = storage.getAllTasks();
+ QVERIFY( ! tasksAfter.isEmpty() );
+ QVERIFY( tasksBefore == tasksAfter );
+}
+
+// this test more or less documents the behaviour of the mysql driver which allows \
nested transactions, which fail, without +// reporting an error
+void SqlTransactionTests::testMySqlNestedTransactions()
+{
+ MySqlStorage storage = prepareMySqlStorage();
+ QVERIFY( storage.database().open() );
+
+ QList<Task> tasksBefore = storage.getAllTasks();
+ QVERIFY( ! tasksBefore.isEmpty() );
+ Task first = tasksBefore.takeFirst();
+ // test a simple transaction that is completed and committed:
+ {
+ SqlRaiiTransactor transactor( storage.database() );
+ QSqlQuery query( storage.database() );
+ query.prepare("DELETE from Tasks where id=:id");
+ query.bindValue( "id", first.id() );
+ QVERIFY( storage.runQuery( query ) );
+ { // now before the first transaction is commited and done, a second one \
is started + // this should throw an exception
+ SqlRaiiTransactor transactor2( storage.database() );
+ QSqlError error = storage.database().lastError();
+ // QFAIL( "I should not get here." );
+ transactor2.commit();
+ }
+ QSqlError error1 = storage.database().lastError();
+ transactor.commit();
+ QSqlError error2 = storage.database().lastError();
+ QFAIL( "I should not get here." );
+ }
+}
+
QTEST_MAIN( SqlTransactionTests )
#include "SqlTransactionTests.moc"
--- trunk/playground/utils/charm/trunk/Tests/SqlTransactionTests.h #1107518:1107519
@@ -13,6 +13,12 @@
private slots:
void testMySqlDriverRequirements();
void testSqLiteDriverRequirements();
+ void testMySqlTransactionRollback();
+ void testMySqlTransactionCommit();
+ void testMySqlNestedTransactions();
+
+private:
+ MySqlStorage prepareMySqlStorage();
};
--- trunk/playground/utils/charm/trunk/Tools/TimesheetProcessor/Database.cpp \
#1107518:1107519 @@ -74,54 +74,32 @@
{
return m_storage.database();
}
+
void Database::login() throw (TimesheetProcessorException )
{
- QString name( "timecheater" );
- QString pass( "polkadots" );
- QString host( "127.0.0.1" );
- QString database( "Charm" );
- unsigned int port = 8889;
+ MySqlStorage::Parameters parameters;
+ try {
+ parameters = MySqlStorage::parseParameterEnvironmentVariable();
+ } catch( ParseError& e ) {
+ throw TimesheetProcessorException( e.what() );
+ }
+ m_storage.configure( parameters );
+ bool ok = m_storage.database().open();
+ if ( !ok ) {
+ QSqlError error = m_storage.database().lastError();
- // read configuration from the environment:
- char* databaseConfigurationString = getenv( "CHARM_DATABASE_CONFIGURATION" );
- if ( databaseConfigurationString != 0 ) {
- // the string is supposed to be of the format "hostname;port;username;password"
- // of port is 0; the default port is used
- QStringList elements = QString::fromLocal8Bit( databaseConfigurationString \
).split( ';' );
- if ( elements.count() != 4 ) {
- throw TimesheetProcessorException( QObject::tr( "Bad database configuration \
string format" ) );
- } else {
- bool ok;
- host = elements.at( 0 );
- port = elements.at( 1 ).toInt( &ok );
- name = elements.at( 2 );
- pass = elements.at( 3 );
- if ( ok != true || port < 0 ) {
- throw TimesheetProcessorException( QObject::tr(
- "The port must be a non-negative integer number in the database configuration \
string format" ) );
- }
- }
- }
- m_storage.database().setHostName( host );
- m_storage.database().setDatabaseName( database );
- m_storage.database().setUserName( name );
- m_storage.database().setPassword( pass );
- if ( port != 0 ) {
- m_storage.database().setPort( port );
- }
- bool ok = m_storage.database().open();
- if ( !ok ) {
- QSqlError error = m_storage.database().lastError();
-
- QString msg = QObject::tr( "Cannot connect to database %1 on host %2, database \
said "
- "\"%3\", driver said \"%4\"" ) .arg( database ) .arg( host ) .arg( \
error.driverText() ) .arg( error.databaseText() );
- throw TimesheetProcessorException( msg );
- }
- // check if the driver has transaction support
- if( ! m_storage.database().driver()->hasFeature( QSqlDriver::Transactions ) \
) {
- QString msg = QObject::tr( "The database driver in use does not support \
transactions. Transactions are required." );
- throw TimesheetProcessorException( msg );
- }
+ QString msg = QObject::tr( "Cannot connect to database %1 on host %2, \
database said " + "\"%3\", driver said \"%4\"" )
+ .arg( parameters.database ) .arg( parameters.host )
+ .arg( error.driverText() )
+ .arg( error.databaseText() );
+ throw TimesheetProcessorException( msg );
+ }
+ // check if the driver has transaction support
+ if( ! m_storage.database().driver()->hasFeature( QSqlDriver::Transactions ) ) {
+ QString msg = QObject::tr( "The database driver in use does not support \
transactions. Transactions are required." ); + throw \
TimesheetProcessorException( msg ); + }
}
void Database::initializeDatabase() throw (TimesheetProcessorException )
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic