1
mirror of https://invent.kde.org/network/falkon.git synced 2024-09-21 09:42:10 +02:00

Add SqlQueryJob to run queries on separate thread

This commit is contained in:
David Rosca 2018-04-11 16:50:18 +02:00
parent 0d2776a261
commit 8a5c69b8a1
No known key found for this signature in database
GPG Key ID: EBC3FC294452C6D8
7 changed files with 232 additions and 4 deletions

View File

@ -22,6 +22,7 @@ falkon_tests(
locationbartest locationbartest
webviewtest webviewtest
webtabtest webtabtest
sqldatabasetest
) )
set(falkon_autotests_SRCS ${CMAKE_SOURCE_DIR}/tests/modeltest/modeltest.cpp) set(falkon_autotests_SRCS ${CMAKE_SOURCE_DIR}/tests/modeltest/modeltest.cpp)

View File

@ -0,0 +1,90 @@
/* ============================================================
* Falkon - Qt web browser
* Copyright (C) 2018 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 "sqldatabasetest.h"
#include "sqldatabase.h"
#include <QtTest/QTest>
#include <QtTest/QSignalSpy>
#include <QSqlDatabase>
#include <QTemporaryFile>
void SqlDatabaseTest::initTestCase()
{
}
void SqlDatabaseTest::cleanupTestCase()
{
}
static bool waitForFinished(SqlQueryJob *job)
{
QSignalSpy spy(job, &SqlQueryJob::finished);
return spy.wait();
}
void SqlDatabaseTest::sqlQueryJobTest()
{
QTemporaryFile file;
file.open();
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(file.fileName());
db.open();
SqlDatabase::instance()->setDatabase(db);
QCOMPARE(db.tables().count(), 0);
SqlQueryJob *job = new SqlQueryJob();
job->setQuery("CREATE TABLE test1 (data TEXT, id INTEGER PRIMARY KEY)");
job->start();
QVERIFY(waitForFinished(job));
QVERIFY(!job->error().isValid());
QCOMPARE(db.tables(), QStringList{"test1"});
job = new SqlQueryJob();
job->setQuery("INSERT INTO test1 (data) VALUES (?)");
job->addBindValue("test-value");
job->start();
QVERIFY(waitForFinished(job));
QVERIFY(!job->error().isValid());
QCOMPARE(job->lastInsertId().toInt(), 1);
QSqlQuery query("SELECT data FROM test1", db);
query.next();
QCOMPARE(query.value(0).toString(), QString("test-value"));
QVERIFY(!query.next());
job = new SqlQueryJob();
job->setQuery("SELECT data FROM test1");
job->start();
QVERIFY(waitForFinished(job));
QVERIFY(!job->error().isValid());
QCOMPARE(job->records().size(), 1);
QCOMPARE(job->records().at(0).value(0).toString(), QString("test-value"));
job = new SqlQueryJob();
job->setQuery("SELECT invalid sql syntax; 1321sdsa from");
job->start();
QVERIFY(waitForFinished(job));
QVERIFY(job->error().isValid());
}
QTEST_GUILESS_MAIN(SqlDatabaseTest)

View File

@ -0,0 +1,31 @@
/* ============================================================
* Falkon - Qt web browser
* Copyright (C) 2018 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/>.
* ============================================================ */
#pragma once
#include <QObject>
class SqlDatabaseTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void sqlQueryJobTest();
};

View File

@ -19,13 +19,90 @@
#include <QApplication> #include <QApplication>
#include <QThreadStorage> #include <QThreadStorage>
#include <QFutureWatcher>
#include <QtConcurrent/QtConcurrentRun> #include <QtConcurrent/QtConcurrentRun>
QThreadStorage<QSqlDatabase> s_databases; QThreadStorage<QSqlDatabase> s_databases;
Q_GLOBAL_STATIC(SqlDatabase, qz_sql_database) Q_GLOBAL_STATIC(SqlDatabase, qz_sql_database)
// SqlQueryJob
SqlQueryJob::SqlQueryJob(QObject *parent)
: QObject(parent)
{
}
SqlQueryJob::SqlQueryJob(const QString &query, QObject *parent)
: QObject(parent)
{
setQuery(query);
}
void SqlQueryJob::setQuery(const QString &query)
{
m_query = query;
}
void SqlQueryJob::addBindValue(const QVariant &value)
{
m_boundValues.append(value);
}
QSqlError SqlQueryJob::error() const
{
return m_error;
}
QVariant SqlQueryJob::lastInsertId() const
{
return m_lastInsertId;
}
QVector<QSqlRecord> SqlQueryJob::records() const
{
return m_records;
}
void SqlQueryJob::start()
{
struct Result {
QSqlError error;
QVariant lastInsertId;
QVector<QSqlRecord> records;
};
const QString query = m_query;
m_query.clear();
const QVector<QVariant> boundValues = m_boundValues;
m_boundValues.clear();
auto watcher = new QFutureWatcher<Result>(this);
connect(watcher, &QFutureWatcher<Result>::finished, this, [=]() {
deleteLater();
const auto result = watcher->result();
m_error = result.error;
m_lastInsertId = result.lastInsertId;
m_records = result.records;
emit finished(this);
});
watcher->setFuture(QtConcurrent::run([=]() {
QSqlQuery q(SqlDatabase::instance()->database());
q.prepare(query);
for (const QVariant &value : boundValues) {
q.addBindValue(value);
}
q.exec();
Result res;
res.error = q.lastError();
res.lastInsertId = q.lastInsertId();
while (q.next()) {
res.records.append(q.record());
}
return res;
}));
}
// SqlDatabase // SqlDatabase
SqlDatabase::SqlDatabase(QObject* parent) SqlDatabase::SqlDatabase(QObject* parent)
: QObject(parent) : QObject(parent)

View File

@ -18,13 +18,40 @@
#ifndef SQLDATABASE_H #ifndef SQLDATABASE_H
#define SQLDATABASE_H #define SQLDATABASE_H
#include <QHash>
#include <QMutex>
#include <QFuture>
#include <QSqlQuery> #include <QSqlQuery>
#include <QSqlError>
#include <QSqlRecord>
#include "qzcommon.h" #include "qzcommon.h"
class FALKON_EXPORT SqlQueryJob : public QObject
{
Q_OBJECT
public:
explicit SqlQueryJob(QObject *parent = nullptr);
explicit SqlQueryJob(const QString &query, QObject *parent = nullptr);
void setQuery(const QString &query);
void addBindValue(const QVariant &value);
QSqlError error() const;
QVariant lastInsertId() const;
QVector<QSqlRecord> records() const;
void start();
Q_SIGNALS:
void finished(SqlQueryJob *job);
private:
QString m_query;
QVector<QVariant> m_boundValues;
QSqlError m_error;
QVariant m_lastInsertId;
QVector<QSqlRecord> m_records;
};
class FALKON_EXPORT SqlDatabase : public QObject class FALKON_EXPORT SqlDatabase : public QObject
{ {
Q_OBJECT Q_OBJECT

View File

@ -120,6 +120,7 @@ set(GENERATED_SOURCES
${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/delayedfilewatcher_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/delayedfilewatcher_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/iconprovider_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/iconprovider_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/qztools_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/qztools_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/sqlqueryjob_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/sqldatabase_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/sqldatabase_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/toolbutton_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/toolbutton_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/wheelhelper_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/wheelhelper_wrapper.cpp

View File

@ -222,6 +222,7 @@
</inject-code> </inject-code>
</modify-function> </modify-function>
</object-type> </object-type>
<object-type name="SqlQueryJob"/>
<object-type name="SqlDatabase"/> <object-type name="SqlDatabase"/>
<object-type name="ToolButton"/> <object-type name="ToolButton"/>
<value-type name="WheelHelper"> <value-type name="WheelHelper">