From 8a5c69b8a1d7b35c0d806322e4507ad61da43186 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Wed, 11 Apr 2018 16:50:18 +0200 Subject: [PATCH] Add SqlQueryJob to run queries on separate thread --- autotests/CMakeLists.txt | 1 + autotests/sqldatabasetest.cpp | 90 ++++++++++++++++++++ autotests/sqldatabasetest.h | 31 +++++++ src/lib/tools/sqldatabase.cpp | 79 ++++++++++++++++- src/lib/tools/sqldatabase.h | 33 ++++++- src/plugins/PyFalkon/CMakeLists.txt | 1 + src/plugins/PyFalkon/typesystem_pyfalkon.xml | 1 + 7 files changed, 232 insertions(+), 4 deletions(-) create mode 100644 autotests/sqldatabasetest.cpp create mode 100644 autotests/sqldatabasetest.h diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index 0d86e9ebf..d2fad04b2 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -22,6 +22,7 @@ falkon_tests( locationbartest webviewtest webtabtest + sqldatabasetest ) set(falkon_autotests_SRCS ${CMAKE_SOURCE_DIR}/tests/modeltest/modeltest.cpp) diff --git a/autotests/sqldatabasetest.cpp b/autotests/sqldatabasetest.cpp new file mode 100644 index 000000000..7bf61fdb0 --- /dev/null +++ b/autotests/sqldatabasetest.cpp @@ -0,0 +1,90 @@ +/* ============================================================ +* Falkon - Qt web browser +* Copyright (C) 2018 David Rosca +* +* 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 . +* ============================================================ */ +#include "sqldatabasetest.h" +#include "sqldatabase.h" + +#include +#include +#include +#include + +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) diff --git a/autotests/sqldatabasetest.h b/autotests/sqldatabasetest.h new file mode 100644 index 000000000..67a4d74a6 --- /dev/null +++ b/autotests/sqldatabasetest.h @@ -0,0 +1,31 @@ +/* ============================================================ +* Falkon - Qt web browser +* Copyright (C) 2018 David Rosca +* +* 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 . +* ============================================================ */ +#pragma once + +#include + +class SqlDatabaseTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + + void sqlQueryJobTest(); +}; diff --git a/src/lib/tools/sqldatabase.cpp b/src/lib/tools/sqldatabase.cpp index 85ffda36d..1542b3de7 100644 --- a/src/lib/tools/sqldatabase.cpp +++ b/src/lib/tools/sqldatabase.cpp @@ -19,13 +19,90 @@ #include #include - +#include #include QThreadStorage s_databases; 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 SqlQueryJob::records() const +{ + return m_records; +} + +void SqlQueryJob::start() +{ + struct Result { + QSqlError error; + QVariant lastInsertId; + QVector records; + }; + + const QString query = m_query; + m_query.clear(); + const QVector boundValues = m_boundValues; + m_boundValues.clear(); + + auto watcher = new QFutureWatcher(this); + connect(watcher, &QFutureWatcher::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(QObject* parent) : QObject(parent) diff --git a/src/lib/tools/sqldatabase.h b/src/lib/tools/sqldatabase.h index 7045b0c1e..c2ce86504 100644 --- a/src/lib/tools/sqldatabase.h +++ b/src/lib/tools/sqldatabase.h @@ -18,13 +18,40 @@ #ifndef SQLDATABASE_H #define SQLDATABASE_H -#include -#include -#include #include +#include +#include #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 records() const; + + void start(); + +Q_SIGNALS: + void finished(SqlQueryJob *job); + +private: + QString m_query; + QVector m_boundValues; + QSqlError m_error; + QVariant m_lastInsertId; + QVector m_records; +}; + class FALKON_EXPORT SqlDatabase : public QObject { Q_OBJECT diff --git a/src/plugins/PyFalkon/CMakeLists.txt b/src/plugins/PyFalkon/CMakeLists.txt index 05b1b2bb6..dd44cae4c 100644 --- a/src/plugins/PyFalkon/CMakeLists.txt +++ b/src/plugins/PyFalkon/CMakeLists.txt @@ -120,6 +120,7 @@ set(GENERATED_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/delayedfilewatcher_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/iconprovider_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/toolbutton_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/PyFalkon/wheelhelper_wrapper.cpp diff --git a/src/plugins/PyFalkon/typesystem_pyfalkon.xml b/src/plugins/PyFalkon/typesystem_pyfalkon.xml index 279d84c45..449f84941 100644 --- a/src/plugins/PyFalkon/typesystem_pyfalkon.xml +++ b/src/plugins/PyFalkon/typesystem_pyfalkon.xml @@ -222,6 +222,7 @@ +