#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)
{
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)
return;
d->preparedQueries.clear();
+ d->transactionLevel = 0;
auto subscribedNotifications = d->db.driver()->subscribedToNotifications();
foreach (const QString ¬ification, subscribedNotifications)