mirror of
https://invent.kde.org/network/falkon.git
synced 2024-09-23 02:32:10 +02:00
188 lines
4.8 KiB
C++
188 lines
4.8 KiB
C++
|
/* ============================================================
|
||
|
* 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();
|
||
|
}
|
||
|
}
|