From: APTX Date: Mon, 25 Mar 2013 15:29:52 +0000 (+0100) Subject: Make SqlAsyncQuery fast. Add functions to fill database classes directly from an... X-Git-Url: https://gitweb.tyo.aptx.org/?a=commitdiff_plain;h=e7ed2947a443de4e68aec0adc7b44c640174d866;p=localmylist.git Make SqlAsyncQuery fast. Add functions to fill database classes directly from an SQL result. --- diff --git a/localmylist/asyncquerytask.cpp b/localmylist/asyncquerytask.cpp index 78f3a25..655fd62 100644 --- a/localmylist/asyncquerytask.cpp +++ b/localmylist/asyncquerytask.cpp @@ -5,7 +5,6 @@ #include #include #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(); diff --git a/localmylist/database.cpp b/localmylist/database.cpp index 20f9a2e..275d8d3 100644 --- a/localmylist/database.cpp +++ b/localmylist/database.cpp @@ -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"); diff --git a/localmylist/database.h b/localmylist/database.h index 5d70ed1..196d726 100644 --- a/localmylist/database.h +++ b/localmylist/database.h @@ -5,13 +5,27 @@ #include #include #include +#include #include #include #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(); diff --git a/localmylist/mylist.cpp b/localmylist/mylist.cpp index 3feb3d9..21f6b5a 100644 --- a/localmylist/mylist.cpp +++ b/localmylist/mylist.cpp @@ -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(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(sender()); Q_ASSERT(task); tasks.remove(task); - db->log(tr("Task %1 on %2 finished").arg(task->taskName(), task->taskSubject())); + if (!dynamic_cast(task)) + db->log(tr("Task %1 on %2 finished").arg(task->taskName(), task->taskSubject())); task->deleteLater(); emit taskCountChanged(); diff --git a/localmylist/sqlasyncquery.cpp b/localmylist/sqlasyncquery.cpp index e1fa802..55eec35 100644 --- a/localmylist/sqlasyncquery.cpp +++ b/localmylist/sqlasyncquery.cpp @@ -62,6 +62,11 @@ void SqlAsyncQuery::finish() return d->finish(); } +bool SqlAsyncQuery::isWorking() const +{ + return d->working; +} + QString SqlAsyncQuery::executedQuery() const { return d->executedQuery(); diff --git a/localmylist/sqlasyncquery.h b/localmylist/sqlasyncquery.h index d9d2af2..0012089 100644 --- a/localmylist/sqlasyncquery.h +++ b/localmylist/sqlasyncquery.h @@ -5,12 +5,14 @@ #include #include +#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; diff --git a/localmylist/sqlasyncqueryinternal.cpp b/localmylist/sqlasyncqueryinternal.cpp index 0753f41..5a4405b 100644 --- a/localmylist/sqlasyncqueryinternal.cpp +++ b/localmylist/sqlasyncqueryinternal.cpp @@ -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; } diff --git a/localmylist/sqlasyncqueryinternal.h b/localmylist/sqlasyncqueryinternal.h index 596cfdc..57e969c 100644 --- a/localmylist/sqlasyncqueryinternal.h +++ b/localmylist/sqlasyncqueryinternal.h @@ -1,10 +1,9 @@ #ifndef SQLASYNCQUERYINTERNAL_H #define SQLASYNCQUERYINTERNAL_H -#include +#include #include #include -#include namespace LocalMyList { @@ -27,17 +26,12 @@ struct AsyncQuery BoundValues boundValues; }; -struct Field -{ - QString name; - QVariant value; -}; - -typedef QVector Row; - struct Result { - QList rows; + QStringList fieldNames; + QVariantList data; + int rowCount; + int columnCount() const { return fieldNames.count(); } QString error; }; diff --git a/localmylist/sqlquery.h b/localmylist/sqlquery.h index 4812b22..4c1c68c 100644 --- a/localmylist/sqlquery.h +++ b/localmylist/sqlquery.h @@ -5,11 +5,13 @@ #include #include +#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 index 0000000..e9dea89 --- /dev/null +++ b/localmylist/sqlresultiteratorinterface.h @@ -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