]> Some of my projects - localmylist.git/commitdiff
Make SqlAsyncQuery fast. Add functions to fill database classes directly from an...
authorAPTX <marek321@gmail.com>
Mon, 25 Mar 2013 15:29:52 +0000 (16:29 +0100)
committerAPTX <marek321@gmail.com>
Mon, 25 Mar 2013 15:29:52 +0000 (16:29 +0100)
localmylist/asyncquerytask.cpp
localmylist/database.cpp
localmylist/database.h
localmylist/mylist.cpp
localmylist/sqlasyncquery.cpp
localmylist/sqlasyncquery.h
localmylist/sqlasyncqueryinternal.cpp
localmylist/sqlasyncqueryinternal.h
localmylist/sqlquery.h
localmylist/sqlresultiteratorinterface.h [new file with mode: 0644]

index 78f3a25126f1bbfb559b963763b7c66368fe7e51..655fd627fad60202bfd6fe19b9457b782e02840b 100644 (file)
@@ -5,7 +5,6 @@
 #include <QSqlRecord>
 #include <QVector>
 #include "database.h"
-
 namespace LocalMyList {
 
 AsyncQueryTask::AsyncQueryTask(QObject *parent) :
@@ -44,29 +43,47 @@ void AsyncQueryTask::start()
 {
        QSqlQuery q(db->connection());
 
-       q.prepare(m_query->query);
+       m_result = new Internal::Result;
+
+       if (!q.prepare(m_query->query))
+       {
+               m_result->error = q.lastError().text();
+               emit finished();
+               return;
+       }
 
        foreach(const BoundValue &v, m_query->boundValues)
        {
                q.bindValue(v.name, v.value, v.paramType);
        }
 
-       m_result = new Internal::Result;
        if (!q.exec())
        {
                m_result->error = q.lastError().text();
        }
        else
        {
-               while (q.next())
+               m_result->rowCount = q.size();
+
+               if (!q.next())
                {
-                       Internal::Row row(q.record().count());
-                       for (int i = 0; i < row.count(); ++i)
+                       m_result->rowCount = 0;
+               }
+               else
+               {
+                       QSqlRecord r = q.record();
+                       for (int i = 0; i < r.count(); ++i)
                        {
-                               row[i].name = q.record().fieldName(i);
-                               row[i].value = q.value(i);
+                               m_result->fieldNames << r.fieldName(i);
                        }
-                       m_result->rows << row;
+                       m_result->data.reserve(m_result->rowCount * m_result->columnCount());
+
+                       do {
+                               for (int i = 0; i < m_result->columnCount(); ++i)
+                               {
+                                       m_result->data << q.value(i);
+                               }
+                       } while(q.next());
                }
        }
        emit finished();
index 20f9a2e1acaea46755e916f068b87dbbf05734f9..275d8d3a532ae1d429e3ab6ac2e22917371082ec 100644 (file)
@@ -1313,6 +1313,154 @@ void Database::disconnect()
        emit disconnected();
 }
 
+void Database::readAnimeData(SqlResultIteratorInterface &result, Anime &data, int offset)
+{
+       data.aid = result.value(offset++).toInt();
+       data.entryAdded = result.value(offset++).toDateTime();
+       data.anidbUpdate = result.value(offset++).toDateTime();
+       data.entryUpdate = result.value(offset++).toDateTime();
+       data.myUpdate = result.value(offset++).toDateTime();
+       data.titleEnglish = result.value(offset++).toString();
+       data.titleRomaji = result.value(offset++).toString();
+       data.titleKanji = result.value(offset++).toString();
+       data.description = result.value(offset++).toString();
+       data.year = result.value(offset++).toString();
+       data.startDate = result.value(offset++).toDateTime();
+       data.endDate = result.value(offset++).toDateTime();
+       data.type = result.value(offset++).toString();
+       data.totalEpisodeCount = result.value(offset++).toInt();
+       data.highestEpno = result.value(offset++).toInt();
+       data.rating = result.value(offset++).toDouble();
+       data.votes = result.value(offset++).toInt();
+       data.tempRating = result.value(offset++).toDouble();
+       data.tempVotes = result.value(offset++).toInt();
+       data.myVote = result.value(offset++).toDouble();
+       data.myVoteDate = result.value(offset++).toDateTime();
+       data.myTempVote = result.value(offset++).toDouble();
+       data.myTempVoteDate = result.value(offset++).toDateTime();
+}
+
+void Database::readEpisodeData(SqlResultIteratorInterface &result, Episode &data, int offset)
+{
+       data.eid = result.value(offset++).toInt();
+       data.aid = result.value(offset++).toInt();
+       data.entryAdded = result.value(offset++).toDateTime();
+       data.anidbUpdate = result.value(offset++).toDateTime();
+       data.entryUpdate = result.value(offset++).toDateTime();
+       data.myUpdate = result.value(offset++).toDateTime();
+       data.epno = result.value(offset++).toInt();
+       data.titleEnglish = result.value(offset++).toString();
+       data.titleRomaji = result.value(offset++).toString();
+       data.titleKanji = result.value(offset++).toString();
+       data.length = result.value(offset++).toInt();
+       data.airdate = result.value(offset++).toDateTime();
+       data.state = result.value(offset++).toInt();
+       data.type = result.value(offset++).toString();
+       data.recap = result.value(offset++).toBool();
+       data.rating = result.value(offset++).toDouble();
+       data.votes = result.value(offset++).toInt();
+       data.myVote = result.value(offset++).toDouble();
+       data.myVoteDate = result.value(offset++).toDateTime();
+}
+
+void Database::readFileData(SqlResultIteratorInterface &result, File &data, int offset)
+{
+       data.fid = result.value(offset++).toInt();
+       data.eid = result.value(offset++).toInt();
+       data.aid = result.value(offset++).toInt();
+       data.gid = result.value(offset++).toInt();
+       data.entryAdded = result.value(offset++).toDateTime();
+       data.anidbUpdate = result.value(offset++).toDateTime();
+       data.entryUpdate = result.value(offset++).toDateTime();
+       data.myUpdate = result.value(offset++).toDateTime();
+       data.ed2k = result.value(offset++).toByteArray();
+       data.size = result.value(offset++).toLongLong();
+       data.length = result.value(offset++).toInt();
+       data.extension = result.value(offset++).toString();
+       data.groupName = result.value(offset++).toString();
+       data.groupNameShort = result.value(offset++).toString();
+       data.crc = result.value(offset++).toString();
+       data.releaseDate = result.value(offset++).toDateTime();
+       data.version = result.value(offset++).toInt();
+       data.censored = result.value(offset++).toBool();
+       data.source = result.value(offset++).toString();
+       data.qualityId = result.value(offset++).toInt();
+       data.quality = result.value(offset++).toString();
+       data.resolution = result.value(offset++).toString();
+       data.videoCodec = result.value(offset++).toString();
+       data.audioCodec = result.value(offset++).toString();
+       data.audioLanguage = result.value(offset++).toString();
+       data.subtitleLanguage = result.value(offset++).toString();
+       data.aspectRatio = result.value(offset++).toString();
+       data.myWatched = result.value(offset++).toDateTime();
+       data.myState = result.value(offset++).toInt();
+       data.myFileState = result.value(offset++).toInt();
+       data.myStorage = result.value(offset++).toString();
+       data.mySource = result.value(offset++).toString();
+       data.myOther = result.value(offset++).toString();
+}
+
+void Database::readFileLocationData(SqlResultIteratorInterface &result, FileLocation &data, int offset)
+{
+       data.locationId = result.value(offset++).toInt();
+       data.fid = result.value(offset++).toInt();
+       data.hostId = result.value(offset++).toInt();
+       data.path = result.value(offset++).toString();
+       data.renamed = result.value(offset++).toDateTime();
+       data.failedRename = result.value(offset++).toBool();
+}
+
+void Database::readOpenFileData(SqlResultIteratorInterface &result, OpenFileData &data, int offset)
+{
+       data.fid = result.value(offset++).toInt();
+       data.animeTitle = result.value(offset++).toString();
+       data.episodeTitle = result.value(offset++).toString();
+       data.epno = result.value(offset++).toInt();
+       data.path = result.value(offset++).toString();
+}
+
+void Database::readUnknownFileData(SqlResultIteratorInterface &result, UnknownFile &data, int offset)
+{
+       data.ed2k = result.value(offset++).toByteArray();
+       data.size = result.value(offset++).toLongLong();
+       data.hostId = result.value(offset++).toInt();
+       data.path = result.value(offset++).toString();
+}
+
+QString Database::animeFields()
+{
+       return
+       "a.aid, a.entry_added, a.anidb_update, a.entry_update, a.my_update, a.title_english, "
+       "a.title_romaji, a.title_kanji, a.description, a.year, a.start_date, a.end_date, "
+       "a.type, a.total_episode_count, a.highest_epno, a.rating, a.votes, "
+       "a.temp_rating, a.temp_votes, a.my_vote, a.my_vote_date, "
+       "a.my_temp_vote, a.my_temp_vote_date ";
+}
+
+QString Database::episodeFields()
+{
+       return
+       "e.eid, e.aid, e.entry_added, e.anidb_update, e.entry_update, e.my_update, e.epno, "
+       "e.title_english, e.title_romaji, e.title_kanji, e.length, e.airdate, e.state, "
+       "e.type, e.recap, e.rating, e.votes, e.my_vote, e.my_vote_date ";
+}
+
+QString Database::fileFields()
+{
+       return
+       "f.fid, f.eid, f.aid, f.gid, f.entry_added, f.anidb_update, f.entry_update, f.my_update, "
+       "f.ed2k, f.size, f.length, f.extension, f.group_name, f.group_name_short, f.crc, "
+       "f.release_date, f.version, f.censored, f.source, f.quality_id, f.quality, f.resolution, "
+       "f.video_codec, f.audio_codec, f.audio_language, f.subtitle_language, f.aspect_ratio, "
+       "f.my_watched, f.my_state, f.my_file_state, f.my_storage, f.my_source, f.my_other ";
+}
+
+QString Database::fileLocationFields()
+{
+       return
+       "fl.location_id, fl.fid, fl.host_id, fl.path, fl.renamed, fl.failed_rename ";
+}
+
 void Database::subscribeToNotifications()
 {
        d->db.driver()->subscribeToNotification("new_pending_request");
index 5d70ed168dfff4212542d23618dcd706526faf47..196d726c6379df86192e7c1105805a867b951db3 100644 (file)
@@ -5,13 +5,27 @@
 #include <QVariant>
 #include <QSqlDatabase>
 #include <QSqlQuery>
+#include <QSqlRecord>
 #include <QDateTime>
 #include <QStringList>
 
 #include "databaseclasses.h"
+#include "sqlresultiteratorinterface.h"
 
 namespace LocalMyList {
 
+class LOCALMYLISTSHARED_EXPORT QSqlResultIterator : public SqlResultIteratorInterface
+{
+public:
+       QSqlResultIterator(QSqlQuery &query) : q(query) {}
+       bool next() { return q.next(); }
+       QVariant value(int index) const { return q.value(index); }
+       QVariant value(const QString &name) const { return q.value(name); }
+       int indexOf(const QString &name ) const { return q.record().indexOf(name); }
+private:
+       QSqlQuery &q;
+};
+
 struct DatabaseInternal;
 
 class LOCALMYLISTSHARED_EXPORT Database : public QObject
@@ -116,6 +130,18 @@ public slots:
        bool connect();
        void disconnect();
 
+       static void readAnimeData(SqlResultIteratorInterface &result, Anime &data, int offset = 0);
+       static void readEpisodeData(SqlResultIteratorInterface &result, Episode &data, int offset = 0);
+       static void readFileData(SqlResultIteratorInterface &result, File &data, int offset = 0);
+       static void readFileLocationData(SqlResultIteratorInterface &result, FileLocation &data, int offset = 0);
+       static void readOpenFileData(SqlResultIteratorInterface &result, OpenFileData &data, int offset = 0);
+       static void readUnknownFileData(SqlResultIteratorInterface &result, UnknownFile &data, int offset = 0);
+
+       static QString animeFields();
+       static QString episodeFields();
+       static QString fileFields();
+       static QString fileLocationFields();
+
 signals:
        void connected();
        void disconnected();
index 3feb3d97b4fa75e7cf208e45fbbe12304b3b9592..21f6b5a8be7fdfa36286662925107f49c5eb5b24 100644 (file)
@@ -10,6 +10,7 @@
 #include "addfiletask.h"
 #include "animetitleparsetask.h"
 #include "mylistexportparsetask.h"
+#include "asyncquerytask.h"
 #include "workthread.h"
 #ifndef LOCALMYLIST_NO_ANIDBUDPCLIENT
 #      include "requesthandler.h"
@@ -274,7 +275,9 @@ void MyList::executeTask(AbstractTask *task)
 
        connect(task, SIGNAL(finished()), this, SLOT(taskFinished()), Qt::QueuedConnection);
        tasks.insert(task);
-       db->log(tr("Starting task %1 on %2").arg(task->taskName(), task->taskSubject()));
+
+       if (!dynamic_cast<AsyncQueryTask *>(task))
+               db->log(tr("Starting task %1 on %2").arg(task->taskName(), task->taskSubject()));
 
        QMetaObject::invokeMethod(task, "start", Qt::QueuedConnection);
 
@@ -286,7 +289,8 @@ void MyList::taskFinished()
        AbstractTask *task = qobject_cast<AbstractTask *>(sender());
        Q_ASSERT(task);
        tasks.remove(task);
-       db->log(tr("Task %1 on %2 finished").arg(task->taskName(), task->taskSubject()));
+       if (!dynamic_cast<AsyncQueryTask *>(task))
+               db->log(tr("Task %1 on %2 finished").arg(task->taskName(), task->taskSubject()));
        task->deleteLater();
 
        emit taskCountChanged();
index e1fa802f1e5b09e3e01113c428742dc8986bf9e8..55eec353c09dde141d54dbd259d1cdf05d1d2570 100644 (file)
@@ -62,6 +62,11 @@ void SqlAsyncQuery::finish()
        return d->finish();
 }
 
+bool SqlAsyncQuery::isWorking() const
+{
+       return d->working;
+}
+
 QString SqlAsyncQuery::executedQuery() const
 {
        return d->executedQuery();
index d9d2af2c930c845cee0c9dd2dbdc9b0c1c863521..0012089cbc37e9128f5219d86dcd9fce01e28adf 100644 (file)
@@ -5,12 +5,14 @@
 #include <QObject>
 #include <QSql>
 
+#include "sqlresultiteratorinterface.h"
+
 namespace LocalMyList {
 
 namespace Internal {
        class SqlAsyncQueryInternal;
 }
-class LOCALMYLISTSHARED_EXPORT SqlAsyncQuery : public QObject
+class LOCALMYLISTSHARED_EXPORT SqlAsyncQuery : public QObject, public SqlResultIteratorInterface
 {
        Q_OBJECT
 public:
@@ -29,6 +31,8 @@ public:
        int indexOf(const QString &name ) const;
        void finish();
 
+       bool isWorking() const;
+
        QString executedQuery() const;
        QString lastError() const;
 
index 0753f41d8c4155da9e45c060d92db130449671a4..5a4405bcf78959a597ec117cc9292d7b0e5de485 100644 (file)
@@ -85,7 +85,7 @@ bool SqlAsyncQueryInternal::next()
                return false;
 
        ++currentRow;
-       bool ret = currentRow < result->rows.count();
+       bool ret = currentRow < result->rowCount;
 
        if (!ret)
                currentRow = -2;
@@ -99,10 +99,10 @@ QVariant SqlAsyncQueryInternal::value(int index) const
                return QVariant();
        if (currentRow < 0)
                return QVariant();
-       if (index < 0 || index >= result->rows.at(currentRow).count())
+       if (index < 0 || index >= result->columnCount())
                return QVariant();
 
-       return result->rows.at(currentRow).at(index).value;
+       return result->data[currentRow * result->columnCount() + index];
 }
 
 QVariant SqlAsyncQueryInternal::value(const QString &name) const
@@ -117,16 +117,7 @@ int SqlAsyncQueryInternal::indexOf(const QString &name) const
        if (currentRow < 0)
                return -1;
 
-       int idx = -1;
-       for (int i = 0; i < result->rows.at(currentRow).count(); ++i)
-       {
-               if (result->rows.at(currentRow).at(i).name == name)
-               {
-                       idx = i;
-                       break;
-               }
-       }
-       return idx;
+       return result->fieldNames.indexOf(name);
 }
 
 void SqlAsyncQueryInternal::finish()
@@ -159,6 +150,7 @@ void SqlAsyncQueryInternal::resultReady(Result *newResult)
        if (result)
                delete result;
        result = newResult;
+       m_lastError = result->error;
        currentRow = -1;
        working = false;
 }
index 596cfdc7f6672ffe02e07321233e0db3ceb90f6e..57e969ce1e4fa3990d2681d4687d61bda8b07f0f 100644 (file)
@@ -1,10 +1,9 @@
 #ifndef SQLASYNCQUERYINTERNAL_H
 #define SQLASYNCQUERYINTERNAL_H
 
-#include <QString>
+#include <QStringList>
 #include <QVariant>
 #include <QSql>
-#include <QVector>
 
 namespace LocalMyList {
 
@@ -27,17 +26,12 @@ struct AsyncQuery
        BoundValues boundValues;
 };
 
-struct Field
-{
-       QString name;
-       QVariant value;
-};
-
-typedef QVector<Field> Row;
-
 struct Result
 {
-       QList<Row> rows;
+       QStringList fieldNames;
+       QVariantList data;
+       int rowCount;
+       int columnCount() const { return fieldNames.count(); }
        QString error;
 };
 
index 4812b2233c5ddd0be057a580031aa0a5dedf3bd1..4c1c68cfede3ddfed5b6876f5d52e429aab76d7e 100644 (file)
@@ -5,11 +5,13 @@
 #include <QObject>
 #include <QSqlQuery>
 
+#include "sqlresultiteratorinterface.h"
+
 namespace LocalMyList {
 
 class SqlQueryInternal;
 
-class LOCALMYLISTSHARED_EXPORT SqlQuery : public QObject
+class LOCALMYLISTSHARED_EXPORT SqlQuery : public QObject, public SqlResultIteratorInterface
 {
        Q_OBJECT
 public:
@@ -24,7 +26,7 @@ public:
        bool next();
        QVariant value(int index) const;
        QVariant value(const QString &name) const;
-       int indexOf(const QString &name ) const;
+       int indexOf(const QString &name) const;
        void finish();
 
        QString executedQuery() const;
diff --git a/localmylist/sqlresultiteratorinterface.h b/localmylist/sqlresultiteratorinterface.h
new file mode 100644 (file)
index 0000000..e9dea89
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef SQLRESULTITERATORINTERFACE_H
+#define SQLRESULTITERATORINTERFACE_H
+
+#include "localmylist_global.h"
+
+namespace LocalMyList {
+
+class LOCALMYLISTSHARED_EXPORT SqlResultIteratorInterface
+{
+public:
+       virtual bool next() = 0;
+       virtual QVariant value(int index) const = 0;
+       virtual QVariant value(const QString &name) const = 0;
+       virtual int indexOf(const QString &name ) const = 0;
+};
+
+} // namespace LocalMyList
+
+#endif // SQLRESULTITERATORINTERFACE_H