[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