Rework the database system

This commit is contained in:
Tobias Fella 2020-03-16 22:37:04 +01:00
parent 5168e06147
commit a6f6969912
No known key found for this signature in database
GPG Key ID: E55EDAB3CA5D9925
7 changed files with 160 additions and 36 deletions

View File

@ -5,6 +5,7 @@ set(alligator_SRCS
fetcher.cpp
feed.cpp
entry.cpp
database.cpp
resources.qrc
)

89
src/database.cpp Normal file
View File

@ -0,0 +1,89 @@
/**
* Copyright 2020 Tobias Fella <fella@posteo.de>
*
* 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 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include <QDir>
#include <QStandardPaths>
#include <QSqlError>
#include <QSqlDatabase>
#include "database.h"
#include "alligator-debug.h"
#define TRUE_OR_RETURN(x) if(!x) return false;
Database::Database()
{
QSqlDatabase db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"));
QString databasePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QDir(databasePath).mkpath(databasePath);
db.setDatabaseName(databasePath + QStringLiteral("/database.db3"));
qCDebug(ALLIGATOR) << "Opening database " << databasePath << "/database.db3";
db.open();
if(!migrate()) {
qCCritical(ALLIGATOR) << "Failed to migrate the database";
}
}
bool Database::migrate() {
qCDebug(ALLIGATOR) << "Migrating database";
if(version() < 1) TRUE_OR_RETURN(migrateTo1());
return true;
}
bool Database::migrateTo1() {
QSqlQuery query(QSqlDatabase::database());
TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Feeds (name TEXT, url TEXT);")));
TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Entries (feed TEXT, id TEXT, title TEXT, content TEXT);")));
TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Authors (id TEXT, name TEXT, uri TEXT, email TEXT);")));
TRUE_OR_RETURN(execute(QStringLiteral("PRAGMA user_version = 1;")));
return true;
}
bool Database::execute(QString query) {
QSqlQuery q;
q.prepare(query);
return execute(q);
}
bool Database::execute(QSqlQuery &query) {
if(!query.exec()) {
qCWarning(ALLIGATOR) << "Failed to execute SQL Query";
qCWarning(ALLIGATOR) << query.lastQuery();
qCWarning(ALLIGATOR) << query.lastError();
return false;
}
return true;
}
int Database::version() {
QSqlQuery query;
query.prepare(QStringLiteral("PRAGMA user_version;"));
execute(query);
if(query.next()) {
bool ok;
int value = query.value(0).toInt(&ok);
qCDebug(ALLIGATOR) << "Database version " << value;
if(ok) return value;
} else {
qCCritical(ALLIGATOR) << "Failed to check database version";
}
return -1;
}

42
src/database.h Normal file
View File

@ -0,0 +1,42 @@
/**
* Copyright 2020 Tobias Fella <fella@posteo.de>
*
* 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 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <QSqlQuery>
class Database
{
public:
static Database &instance()
{
static Database _instance;
return _instance;
}
bool execute(QSqlQuery &query);
bool execute(QString query);
private:
Database();
int version();
bool migrate();
bool migrateTo1();
};

View File

@ -18,12 +18,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QVector>
#include "entryListModel.h"
#include "fetcher.h"
#include "database.h"
EntryListModel::EntryListModel(QObject *parent)
: QAbstractListModel(parent)
@ -70,10 +69,10 @@ void EntryListModel::fetch()
{
connect(&Fetcher::instance(), &Fetcher::finished, this, [this]() {
beginResetModel();
QSqlQuery query(QSqlDatabase::database());
QSqlQuery query;
query.prepare(QStringLiteral("SELECT id, title, content FROM Entries WHERE feed=:feed;"));
query.bindValue(QStringLiteral(":feed"), m_feed);
query.exec();
Database::instance().execute(query);
while (query.next()) {
m_entries.append(Entry(query.value(1).toString(), query.value(2).toString(), false, false));
}

View File

@ -18,21 +18,22 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QUrl>
#include <QSqlError>
#include <Syndication/Syndication>
#include "feedListModel.h"
#include "fetcher.h"
#include "database.h"
#include "alligator-debug.h"
FeedListModel::FeedListModel(QObject *parent)
: QAbstractListModel(parent)
{
QSqlQuery query(QSqlDatabase::database());
query.exec(QStringLiteral("SELECT name, url FROM Feeds"));
QSqlQuery query;
query.prepare(QStringLiteral("SELECT name, url FROM Feeds"));
Database::instance().execute(query);
beginInsertRows(QModelIndex(), 0, query.size());
while (query.next()) {
feeds += Feed(query.value(1).toString(), query.value(0).toString());
@ -63,11 +64,17 @@ int FeedListModel::rowCount(const QModelIndex &index) const
void FeedListModel::addFeed(QString url)
{
QSqlQuery query;
query.prepare(QStringLiteral("SELECT COUNT (url) FROM Feeds WHERE url=:url;"));
query.bindValue(QStringLiteral(":url"), url);
Database::instance().execute(query);
query.next();
if(query.value(0).toInt() != 0) return;
connect(&Fetcher::instance(), &Fetcher::finished, this, [this, url]() {
QSqlQuery query(QSqlDatabase::database());
QSqlQuery query;
query.prepare(QStringLiteral("SELECT name FROM Feeds WHERE url=:url;"));
query.bindValue(QStringLiteral(":url"), url);
query.exec();
Database::instance().execute(query);
query.next();
for(int i = 0; i < feeds.length(); i++) {
if(feeds[i].url() == url) {
@ -82,21 +89,21 @@ void FeedListModel::addFeed(QString url)
beginInsertRows(QModelIndex(), feeds.size(), feeds.size());
feeds.append(Feed(url));
endInsertRows();
QSqlQuery query(QSqlDatabase::database());
query.prepare(QStringLiteral("INSERT INTO Feeds VALUES (:url, :name);"));
query.bindValue(QStringLiteral(":url"), url);
query.bindValue(QStringLiteral(":name"), url);
query.exec();
Database::instance().execute(query);
}
void FeedListModel::remove_feed(int index)
{
Feed toRemove = feeds[index];
QSqlQuery query(QSqlDatabase::database());
QSqlQuery query;
query.prepare(QStringLiteral("DELETE FROM Feeds WHERE name=:name AND url=url;"));
query.bindValue(QStringLiteral(":url"), toRemove.url());
query.bindValue(QStringLiteral(":name"), toRemove.name());
query.exec();
Database::instance().execute(query);
beginRemoveRows(QModelIndex(), index, index);
feeds.remove(index);
endRemoveRows();

View File

@ -20,13 +20,13 @@
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <Syndication/Syndication>
#include "fetcher.h"
#include "database.h"
Fetcher::Fetcher() {
}
@ -47,7 +47,6 @@ void Fetcher::fetch(QUrl url)
QSqlQuery query(db);
for (const auto &entry : feed->items()) {
query = QSqlQuery(db);
query.prepare(QStringLiteral("INSERT INTO Entries VALUES (:feed, :id, :title, :content);"));
query.bindValue(QStringLiteral(":feed"), url.toString());
query.bindValue(QStringLiteral(":id"), entry->id());
@ -56,7 +55,7 @@ void Fetcher::fetch(QUrl url)
query.bindValue(QStringLiteral(":content"), entry->content());
else
query.bindValue(QStringLiteral(":content"), entry->description());
query.exec();
Database::instance().execute(query);
for (const auto &author : entry->authors()) {
query = QSqlQuery(db);
query.prepare(QStringLiteral("INSERT INTO Authors VALUES(:id, :name, :uri, :email);"));
@ -64,12 +63,12 @@ void Fetcher::fetch(QUrl url)
query.bindValue(QStringLiteral(":name"), author->name());
query.bindValue(QStringLiteral(":uri"), author->uri());
query.bindValue(QStringLiteral(":email"), author->email());
query.exec();
Database::instance().execute(query);
}
query.prepare(QStringLiteral("UPDATE Feeds SET name=:name WHERE url=:url;"));
query.bindValue(QStringLiteral(":name"), feed->title());
query.bindValue(QStringLiteral(":url"), url.toString());
query.exec();
Database::instance().execute(query);
}
delete reply;
emit finished();

View File

@ -21,14 +21,10 @@
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QUrl>
#include <QStandardPaths>
#include <QDir>
#include "entryListModel.h"
#include "feedListModel.h"
#include "database.h"
#include "alligator-debug.h"
@ -48,16 +44,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
QQmlApplicationEngine engine;
QSqlDatabase db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"));
QString databasePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QDir(databasePath).mkpath(databasePath);
db.setDatabaseName(databasePath + "/database.db3");
db.open();
QSqlQuery query(db);
query.exec(QStringLiteral("CREATE TABLE IF NOT EXISTS Feeds (name TEXT, url TEXT);"));
query.exec(QStringLiteral("CREATE TABLE IF NOT EXISTS Entries (feed TEXT, id TEXT UNIQUE, title TEXT, content TEXT);"));
query.exec(QStringLiteral("CREATE TABLE IF NOT EXISTS Authors (id TEXT, name TEXT, uri TEXT, email TEXT);"));
Database::instance();
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));