1
mirror of https://invent.kde.org/network/falkon.git synced 2024-12-20 10:46:35 +01:00

[SqlDatabase] New class allowing to exec Sql queries in separate thread

It uses its own QSqlDatabase connection, which is supported according to
docs.
This commit is contained in:
nowrep 2014-03-15 01:03:06 +01:00
parent 16ff847a8b
commit 167ae2af50
5 changed files with 295 additions and 3 deletions

View File

@ -32,6 +32,7 @@
#include "rssmanager.h"
#include "proxystyle.h"
#include "pluginproxy.h"
#include "sqldatabase.h"
#include "iconprovider.h"
#include "browserwindow.h"
#include "cookiemanager.h"
@ -271,6 +272,7 @@ MainApplication::~MainApplication()
delete m_bookmarks;
delete m_cookieJar;
SqlDatabase::destroy();
IconProvider::instance()->saveIconsToDatabase();
}

View File

@ -250,7 +250,7 @@ void ProfileManager::connectDatabase()
// Reconnect
if (m_databaseConnected) {
QSqlDatabase::removeDatabase(QLatin1String("qt_sql_default_connection"));
QSqlDatabase::removeDatabase(QSqlDatabase::database().connectionName());
}
QSqlDatabase db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"));

View File

@ -260,7 +260,8 @@ SOURCES += \
history/historymenu.cpp \
app/datapaths.cpp \
app/profilemanager.cpp \
app/mainmenu.cpp
app/mainmenu.cpp \
tools/sqldatabase.cpp
HEADERS += \
@ -461,7 +462,8 @@ HEADERS += \
history/historymenu.h \
app/datapaths.h \
app/profilemanager.h \
app/mainmenu.h
app/mainmenu.h \
tools/sqldatabase.h
FORMS += \
preferences/autofillmanager.ui \

View File

@ -0,0 +1,187 @@
/* ============================================================
* QupZilla - WebKit based browser
* Copyright (C) 2014 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "sqldatabase.h"
#include <QMetaMethod>
#define CONNECTION_NAME QSL("QupZilla::DatabaseWorker")
SqlDatabase* SqlDatabase::s_instance = 0;
// SqlDatabase
SqlDatabase::SqlDatabase(QObject* parent)
: QObject(parent)
{
qRegisterMetaType<QSqlQuery>("QSqlQuery");
m_worker = new DatabaseWorker;
m_thread = new DatabaseWorkerThread(m_worker);
m_worker->moveToThread(m_thread);
m_thread->start();
connect(m_thread, SIGNAL(started()), m_worker, SLOT(threadStarted()));
}
SqlDatabase::~SqlDatabase()
{
m_thread->exit();
m_thread->wait();
delete m_thread;
delete m_worker;
}
void SqlDatabase::execAsync(const QSqlQuery &query, QObject* receiver, const char* slot)
{
m_worker->execQueryAsync(query, receiver, slot);
}
void SqlDatabase::transactionAsync(const QList<QSqlQuery> &queries)
{
m_worker->transactionAsync(queries);
}
SqlDatabase* SqlDatabase::instance()
{
if (!s_instance) {
s_instance = new SqlDatabase;
}
return s_instance;
}
void SqlDatabase::destroy()
{
delete s_instance;
s_instance = 0;
}
// DatabaseWorker
DatabaseWorker::DatabaseWorker()
: QObject()
, m_started(false)
{
}
void DatabaseWorker::execQueryAsync(const QSqlQuery &query, QObject* receiver, const char* slot)
{
QueryData data;
data.queries.append(query);
data.receiver = receiver;
data.slot = slot;
m_queries.enqueue(data);
if (m_started) {
QMetaObject::invokeMethod(this, "execPendingQueries", Qt::QueuedConnection);
}
}
void DatabaseWorker::transactionAsync(const QList<QSqlQuery> &queries)
{
QueryData data;
data.queries = queries;
data.receiver = 0;
data.slot = 0;
m_queries.enqueue(data);
if (m_started) {
QMetaObject::invokeMethod(this, "execPendingQueries", Qt::QueuedConnection);
}
}
bool DatabaseWorker::hasPendingQueries() const
{
return !m_queries.isEmpty();
}
void DatabaseWorker::threadStarted()
{
m_started = true;
QSqlDatabase::cloneDatabase(QSqlDatabase::database(), CONNECTION_NAME);
m_db = QSqlDatabase::database(CONNECTION_NAME);
// Execute queries that got queued before starting thread
if (hasPendingQueries()) {
QMetaObject::invokeMethod(this, "execPendingQueries", Qt::QueuedConnection);
}
}
static QSqlQuery copyQueryToDatabase(const QSqlQuery &query, const QSqlDatabase &db)
{
QSqlQuery out(db);
out.prepare(query.lastQuery());
const QList<QVariant> boundValues = query.boundValues().values();
foreach (const QVariant &variant, boundValues) {
out.addBindValue(variant);
}
return out;
}
void DatabaseWorker::execPendingQueries()
{
while (hasPendingQueries()) {
QueryData data = m_queries.dequeue();
Q_ASSERT(!data.queries.isEmpty());
// Transaction
if (data.queries.size() > 1) {
m_db.transaction();
foreach (const QSqlQuery &q, data.queries) {
QSqlQuery query = copyQueryToDatabase(q, m_db);
query.exec();
}
m_db.commit();
}
// Single query
else {
QSqlQuery query = copyQueryToDatabase(data.queries.takeFirst(), m_db);
query.exec();
// Invoke connected slot
if (data.receiver) {
// SLOT() macro is prepending a "1" to the slot signature
int index = data.receiver->metaObject()->indexOfMethod(QMetaObject::normalizedSignature(data.slot + 1));
Q_ASSERT(index >= 0);
QMetaMethod method = data.receiver->metaObject()->method(index);
method.invoke(data.receiver, Qt::QueuedConnection, Q_ARG(QSqlQuery, query));
}
}
}
}
// DatabaseWorkerThread
DatabaseWorkerThread::DatabaseWorkerThread(DatabaseWorker* worker)
: QThread()
, m_worker(worker)
{
}
void DatabaseWorkerThread::run()
{
exec();
if (m_worker->hasPendingQueries()) {
m_worker->execPendingQueries();
}
}

101
src/lib/tools/sqldatabase.h Normal file
View File

@ -0,0 +1,101 @@
/* ============================================================
* QupZilla - WebKit based browser
* Copyright (C) 2014 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#ifndef SQLDATABASE_H
#define SQLDATABASE_H
#include <QQueue>
#include <QThread>
#include <QSqlQuery>
#include <QSqlDatabase>
#include "qzcommon.h"
class QThread;
class QSqlQuery;
class DatabaseWorker;
class DatabaseWorkerThread;
// Queries are executed in FIFO order
class QUPZILLA_EXPORT SqlDatabase : public QObject
{
Q_OBJECT
public:
explicit SqlDatabase(QObject* parent = 0);
~SqlDatabase();
// Executes query async and send result if receiver is not null.
// Slot must have "name(QSqlQuery)" signature
void execAsync(const QSqlQuery &query, QObject* receiver = 0, const char* slot = 0);
// Executes transaction async without sending result
void transactionAsync(const QList<QSqlQuery> &queries);
// May be called only after creating QSqlDatabase connection in main thread
static SqlDatabase* instance();
// Must be called before closing main thread QSqlDatabase connection
static void destroy();
private:
DatabaseWorker* m_worker;
DatabaseWorkerThread* m_thread;
static SqlDatabase* s_instance;
};
class DatabaseWorker : public QObject
{
Q_OBJECT
public:
explicit DatabaseWorker();
void execQueryAsync(const QSqlQuery &query, QObject* receiver = 0, const char* slot = 0);
void transactionAsync(const QList<QSqlQuery> &queries);
bool hasPendingQueries() const;
public slots:
void threadStarted();
void execPendingQueries();
private:
struct QueryData {
QList<QSqlQuery> queries;
QObject* receiver;
const char* slot;
};
QQueue<QueryData> m_queries;
QSqlDatabase m_db;
bool m_started;
};
class DatabaseWorkerThread : public QThread
{
public:
explicit DatabaseWorkerThread(DatabaseWorker* worker);
private:
void run();
DatabaseWorker* m_worker;
};
#endif // SQLDATABASE_H