]> Some of my projects - localmylist.git/commitdiff
Add support for nested database transactions.
authorAPTX <marek321@gmail.com>
Wed, 5 Jun 2013 12:32:50 +0000 (14:32 +0200)
committerAPTX <marek321@gmail.com>
Wed, 5 Jun 2013 12:32:50 +0000 (14:32 +0200)
localmylist/database.cpp

index c98070f129317e4a8a0a8ccbd0a08d7e40e02eaf..20c16e15824efa5e194b3ae012ab9a3d779b632f 100644 (file)
@@ -9,15 +9,41 @@
 #include <QThread>
 #include <QDebug>
 
+#include <functional>
+
 namespace LocalMyList {
 
 struct DatabaseInternal
 {
+       DatabaseInternal() : transactionLevel(0) {}
+
        QSqlDatabase db;
 
        QHash<const char *const, QSqlQuery> preparedQueries;
+       int transactionLevel;
 
        QThread *thread;
+
+       bool transactionInternal(std::function<bool()> transaction, std::function<bool()> savepoint)
+       {
+               if (transactionLevel)
+               {
+                       if (!savepoint())
+                       {
+                               qDebug() << "Savepoint Transaction Error:" << db.lastError();
+                               return false;
+                       }
+               }
+               else
+               {
+                       if (!transaction())
+                       {
+                               qDebug() << "Transaction Error:" << db.lastError();
+                               return false;
+                       }
+               }
+               return true;
+       }
 };
 
 Database::Database(const QString &connectionName) : d(0)
@@ -56,33 +82,56 @@ bool Database::transaction()
 {
        Q_ASSERT_X(d->thread == QThread::currentThread(), "threads", "DB used from different thread");
 
-       bool success = d->db.transaction();
-       if (success)
-               return true;
-       qDebug() << "Transaction Error:" << d->db.lastError();
-       return false;
+
+       if (!d->transactionInternal(
+                       [this]{ return d->db.transaction(); },
+                       [this]{ return exec("SAVEPOINT transaction_level_" + QString::number(d->transactionLevel)); }))
+       {
+               return false;
+       }
+
+       ++d->transactionLevel;
+       return true;
 }
 
 bool Database::commit()
 {
        Q_ASSERT_X(d->thread == QThread::currentThread(), "threads", "DB used from different thread");
 
-       bool success = d->db.commit();
-       if (success)
-               return true;
-       qDebug() << "Commit Error:" << d->db.lastError();
-       return false;
+       if (!d->transactionLevel)
+               return false;
+
+       --d->transactionLevel;
+
+       if (!d->transactionInternal(
+                       [this]{ return d->db.commit(); },
+                       [this]{ return exec("RELEASE SAVEPOINT transaction_level_" + QString::number(d->transactionLevel)); }))
+       {
+               d->transactionLevel = 0;
+               return false;
+       }
+
+       return true;
 }
 
 bool Database::rollback()
 {
        Q_ASSERT_X(d->thread == QThread::currentThread(), "threads", "DB used from different thread");
 
-       bool success = d->db.rollback();
-       if (success)
-               return true;
-qDebug() << "Commit Error:" << d->db.lastError();
-return false;
+       if (!d->transactionLevel)
+               return false;
+
+       --d->transactionLevel;
+
+       if (!d->transactionInternal(
+                       [this]{ return d->db.rollback(); },
+                       [this]{ return exec("ROLLBACK TO SAVEPOINT transaction_level_" + QString::number(d->transactionLevel)); }))
+       {
+               d->transactionLevel = 0;
+               return false;
+       }
+
+       return true;
 }
 
 OpenFileData Database::firstUnwatchedByExactTitle(const QString &title)
@@ -1584,6 +1633,7 @@ void Database::disconnect()
                return;
 
        d->preparedQueries.clear();
+       d->transactionLevel = 0;
 
        auto subscribedNotifications = d->db.driver()->subscribedToNotifications();
        foreach (const QString &notification, subscribedNotifications)