struct DatabaseInternal
{
- DatabaseInternal() : transactionLevel(0) {}
+ DatabaseInternal() : transactionLevel(0), thread(0) {}
QSqlDatabase db;
}
};
-Database::Database(const QString &connectionName) : d(0)
+Database::Database(const QString &connectionName) : d(0), pingTimer(0)
{
this->connectionName = connectionName;
reconnectAttempt = false;
+
+ if (connectionName != defaultConnectionName)
+ return;
+
+ qDebug() << "Default connection. Setting up pingTimer";
+
+ pingTimer = new QTimer(this);
+ QObject::connect(this, SIGNAL(connected()), this, SLOT(startPingTimer()));
+ QObject::connect(this, SIGNAL(disconnected()), this, SLOT(stopPingTimer()));
+ QObject::connect(pingTimer, SIGNAL(timeout()), this, SLOT(pingTimeout()));
}
Database::~Database()
emit disconnected();
}
+bool Database::reconnect()
+{
+ disconnect();
+ bool ret = connect();
+ if (ret)
+ {
+ emit reconnected();
+ qDebug() << "reconnected";
+ }
+ return ret;
+}
+
void Database::readAnimeTitleData(const SqlResultIteratorInterface &result, AnimeTitle &data, int offset)
{
data.titleId = result.value(offset++).toInt();
{
if (!isConnected())
{
- disconnect();
- if (!connect())
+ if (!reconnect())
{
auto ite = d->preparedQueries.insert("___invalid", QSqlQuery(d->db));
return ite.value();
if (!isConnected())
{
- disconnect();
- if (!connect())
+ if (!reconnect())
return query;
}
if (!isConnected())
{
- disconnect();
- connect();
+ reconnect();
// TODO can more be done?
return false;
}
+ lastQuery = QDateTime::currentDateTime();
+
if (!query.exec())
return checkError(query);
return true;
if (!isConnected())
{
- disconnect();
- if (!connect())
+ if (!reconnect())
return false;
}
QSqlQuery query = QSqlQuery(d->db);
+ lastQuery = QDateTime::currentDateTime();
if (!query.exec(sql))
return checkError(query);
if (query.lastError().type() == QSqlError::ConnectionError)
{
- disconnect();
-
- if (!connect())
+ if (!reconnect())
return false;
return retryExec(query, prepared);
#endif
}
+void Database::startPingTimer()
+{
+ qDebug() << "Starting pingtimer";
+ lastQuery = QDateTime::currentDateTime();
+
+ pingTimer->setSingleShot(false);
+ pingTimer->start(1000 * 60);
+}
+
+void Database::stopPingTimer()
+{
+ qDebug() << "Stoppting pingtimer";
+ pingTimer->stop();
+}
+
+void Database::pingTimeout()
+{
+ if (lastQuery.addSecs(60) > QDateTime::currentDateTime())
+ return;
+
+ exec("SELECT 1");
+}
+
RaiiTransaction::RaiiTransaction(Database *db) : d(db), c(false)
{
#include <QStringList>
#include <QSqlDriver>
#include <QVariant>
+#include <QTimer>
#include "databaseclasses.h"
#include "sqlresultiteratorinterface.h"
struct DatabaseInternal;
+static const QString defaultConnectionName = [](){return QString("default");}();
+
class LOCALMYLISTSHARED_EXPORT Database : public QObject
{
Q_OBJECT
public:
- Database(const QString &connectionName = "default");
+ Database(const QString &connectionName = defaultConnectionName);
~Database();
public slots:
bool connect();
void disconnect();
+ bool reconnect();
static void readAnimeTitleData(const SqlResultIteratorInterface &result, AnimeTitle &data, int offset = 0);
static void readAnimeData(const SqlResultIteratorInterface &result, Anime &data, int offset = 0);
signals:
void connected();
void disconnected();
+ void reconnected();
void newPendingRequest();
void newPendingMyListUpdate();
void handleNotification(const QString &name);
#endif
+ void startPingTimer();
+ void stopPingTimer();
+ void pingTimeout();
+
private:
void subscribeToNotifications();
QString connectionName;
bool reconnectAttempt;
+
+ QTimer *pingTimer;
+ QDateTime lastQuery;
};
class LOCALMYLISTSHARED_EXPORT RaiiTransaction
m_directoryWatcher = 0;
workThread = 0;
m_defaultLocalQSettings = new QSettings(QSettings::IniFormat, QSettings::UserScope, organizationName, libraryName, this);
-
- db = new Database("main");
+ db = new Database();
connect(db, SIGNAL(connected()), this, SLOT(setupHostInfo()));
m_settings = new Settings(db, this);
return;
workThread = new WorkThread("workThread", dbs, this);
connect(workThread, SIGNAL(finished()), workThread, SLOT(deleteLater()));
+ connect(database(), SIGNAL(reconnected()), workThread->database(), SLOT(reconnect()), Qt::QueuedConnection);
workThread->start();
}
void MyList::signalDebugMessage(const QString &message)
{
- emit instance()->debugMessage(message);
+ // Do not create a mylist instance here.
+ // Can cause a loop if message handler is called from MyList constructor.
+ if (m_instance)
+ emit instance()->debugMessage(message);
}
const char *MyList::revision()
connect(db, SIGNAL(renameDataChanged()), this, SLOT(handleRename()), Qt::QueuedConnection);
connect(this, SIGNAL(renameBatchFinished()), this, SLOT(handleRename()), Qt::QueuedConnection);
connect(settings, SIGNAL(settingsChanged()), this, SLOT(clearSetup()));
+ connect(db, SIGNAL(reconnected()), this, SLOT(handleRename()), Qt::QueuedConnection);
m_setup = false;
}
this->db = db;
connect(this, SIGNAL(batchFinished()), this, SLOT(handleRequests()), Qt::QueuedConnection);
connect(this, SIGNAL(myListUpdateBatchFinished()), this, SLOT(handleMyListUpdates()), Qt::QueuedConnection);
+ connect(db, SIGNAL(reconnected()), this, SLOT(handleRequests()), Qt::QueuedConnection);
+ connect(db, SIGNAL(reconnected()), this, SLOT(handleMyListUpdates()), Qt::QueuedConnection);
db->failPendingRequestsFromOldClients();
}