From 46979667a42406cc73091bf4de3151e24d833ef0 Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Tue, 21 Jan 2014 07:55:10 +0100 Subject: [PATCH] Switchable memory/file-based database. --- src/core/databasefactory.cpp | 39 ++++--- src/core/databasefactory.h | 20 +++- src/core/feedsmodel.cpp | 8 +- src/core/feedsmodelfeed.cpp | 2 +- src/core/feedsmodelstandardcategory.cpp | 4 +- src/core/feedsmodelstandardfeed.cpp | 4 +- src/core/messagesmodel.cpp | 2 +- src/gui/formmain.cpp | 2 +- src/gui/formsettings.cpp | 13 +++ src/gui/formsettings.ui | 133 +++++++++++++++++++++--- src/main.cpp | 3 + 11 files changed, 189 insertions(+), 41 deletions(-) diff --git a/src/core/databasefactory.cpp b/src/core/databasefactory.cpp index 7106fcb21..c3c70891c 100644 --- a/src/core/databasefactory.cpp +++ b/src/core/databasefactory.cpp @@ -35,13 +35,13 @@ DatabaseFactory *DatabaseFactory::instance() { void DatabaseFactory::assemblyDatabaseFilePath() { if (Settings::instance()->type() == Settings::Portable) { m_databaseFilePath = qApp->applicationDirPath() + - QDir::separator() + - QString(APP_DB_PATH); + QDir::separator() + + QString(APP_DB_PATH); } else { m_databaseFilePath = QDir::homePath() + QDir::separator() + - QString(APP_LOW_H_NAME) + QDir::separator() + - QString(APP_DB_PATH); + QString(APP_LOW_H_NAME) + QDir::separator() + + QString(APP_DB_PATH); } } @@ -105,7 +105,7 @@ QSqlDatabase DatabaseFactory::initializeInMemoryDatabase() { } // Loading messages from file-based database. - QSqlDatabase file_database = connection(objectName(), false); + QSqlDatabase file_database = connection(objectName(), StrictlyFileBased); QSqlQuery copy_contents(database); // Attach database. @@ -135,7 +135,7 @@ QSqlDatabase DatabaseFactory::initializeInMemoryDatabase() { QSqlDatabase DatabaseFactory::initializeFileBasedDatabase(const QString &connection_name) { // Prepare file paths. - QDir db_path(getDatabasePath()); + QDir db_path(databaseFilePath()); QFile db_file(db_path.absoluteFilePath(APP_DB_FILE)); // Check if database directory exists. @@ -222,9 +222,11 @@ QSqlDatabase DatabaseFactory::initializeFileBasedDatabase(const QString &connect } QSqlDatabase DatabaseFactory::connection(const QString &connection_name, - bool in_memory) { - if (in_memory) { - // We request in-memory database. + DesiredType desired_type) { + if (desired_type == DatabaseFactory::StrictlyInMemory || + (desired_type == DatabaseFactory::FromSettings && m_inMemoryEnabled)) { + // We request in-memory database (either user don't care + // about the type or user overrided it in the settings). if (!m_inMemoryInitialized) { // It is not initialized yet. return initializeInMemoryDatabase(); @@ -267,7 +269,7 @@ QSqlDatabase DatabaseFactory::connection(const QString &connection_name, // yet, add it and set it up. database = QSqlDatabase::addDatabase(DATABASE_DRIVER, connection_name); - QDir db_path(getDatabasePath()); + QDir db_path(databaseFilePath()); QFile db_file(db_path.absoluteFilePath(APP_DB_FILE)); // Setup database file path. @@ -296,8 +298,14 @@ void DatabaseFactory::removeConnection(const QString &connection_name) { } void DatabaseFactory::saveMemoryDatabase() { - QSqlDatabase database = connection(); - QSqlDatabase file_database = connection(objectName(), false); + if (!m_inMemoryEnabled) { + return; + } + + qDebug("Saving in-memory working database back to persistent file-based storage."); + + QSqlDatabase database = connection(objectName(), StrictlyInMemory); + QSqlDatabase file_database = connection(objectName(), StrictlyFileBased); QSqlQuery copy_contents(database); // Attach database. @@ -316,3 +324,10 @@ void DatabaseFactory::saveMemoryDatabase() { copy_contents.exec("DETACH 'storage'"); copy_contents.finish(); } + +void DatabaseFactory::determineInMemoryDatabase() { + m_inMemoryEnabled = Settings::instance()->value(APP_CFG_GEN, "use_in_memory_db", false).toBool(); + + qDebug("Working database source was determined as %s.", + m_inMemoryEnabled ? "in-memory database" : "file-based database"); +} diff --git a/src/core/databasefactory.h b/src/core/databasefactory.h index 9d8cb05fe..8d8d87ccc 100644 --- a/src/core/databasefactory.h +++ b/src/core/databasefactory.h @@ -10,19 +10,26 @@ class DatabaseFactory : public QObject { Q_OBJECT public: + // Describes what type of database user wants. + enum DesiredType { + StrictlyFileBased, + StrictlyInMemory, + FromSettings + }; + // Destructor. virtual ~DatabaseFactory(); // Returns absolute file path to database file. - inline QString getDatabasePath() { + inline QString databaseFilePath() { return m_databaseFilePath; } // If in-memory is true, then :memory: database is returned // In-memory database is DEFAULT database. // NOTE: This always returns OPENED database. - QSqlDatabase connection(const QString &connection_name = QString(), - bool in_memory = true); + QSqlDatabase connection(const QString &connection_name, + DesiredType desired_type); // Removes connection. void removeConnection(const QString &connection_name = QString()); @@ -31,6 +38,9 @@ class DatabaseFactory : public QObject { // to file-based database. void saveMemoryDatabase(); + // Sets m_inMemoryEnabled according to user settings. + void determineInMemoryDatabase(); + // Singleton getter. static DatabaseFactory *instance(); @@ -53,6 +63,10 @@ class DatabaseFactory : public QObject { bool m_fileBasedinitialized; bool m_inMemoryInitialized; + // Is true when user selected in-memory database. + // NOTE: This is set only on application startup. + bool m_inMemoryEnabled; + // Private singleton value. static QPointer s_instance; }; diff --git a/src/core/feedsmodel.cpp b/src/core/feedsmodel.cpp index d519b6130..5541b967b 100644 --- a/src/core/feedsmodel.cpp +++ b/src/core/feedsmodel.cpp @@ -176,7 +176,7 @@ QList FeedsModel::messagesForFeeds(const QList &feeds) QList messages; QSqlDatabase database = DatabaseFactory::instance()->connection(objectName(), - false); + DatabaseFactory::FromSettings); QSqlQuery query_read_msg(database); query_read_msg.setForwardOnly(true); query_read_msg.prepare("SELECT title, url, author, date_created, contents " @@ -319,7 +319,7 @@ void FeedsModel::loadFromDatabase() { m_rootItem->clearChildren(); QSqlDatabase database = DatabaseFactory::instance()->connection(objectName(), - false); + DatabaseFactory::FromSettings); CategoryAssignment categories; FeedAssignment feeds; @@ -432,7 +432,7 @@ QList FeedsModel::feedsForIndexes(const QModelIndexList &indexe bool FeedsModel::markFeedsRead(const QList &feeds, int read) { QSqlDatabase db_handle = DatabaseFactory::instance()->connection(objectName(), - false); + DatabaseFactory::FromSettings); if (!db_handle.transaction()) { qWarning("Starting transaction for feeds read change."); @@ -469,7 +469,7 @@ bool FeedsModel::markFeedsRead(const QList &feeds, bool FeedsModel::markFeedsDeleted(const QList &feeds, int deleted) { QSqlDatabase db_handle = DatabaseFactory::instance()->connection(objectName(), - false); + DatabaseFactory::FromSettings); if (!db_handle.transaction()) { qWarning("Starting transaction for feeds clearing."); diff --git a/src/core/feedsmodelfeed.cpp b/src/core/feedsmodelfeed.cpp index e7f5a22df..887dd8c13 100755 --- a/src/core/feedsmodelfeed.cpp +++ b/src/core/feedsmodelfeed.cpp @@ -48,7 +48,7 @@ QString FeedsModelFeed::typeToString(FeedsModelFeed::Type type) { void FeedsModelFeed::updateCounts(bool including_total_count) { QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelFeed", - false); + DatabaseFactory::FromSettings); QSqlQuery query_all(database); query_all.setForwardOnly(true); diff --git a/src/core/feedsmodelstandardcategory.cpp b/src/core/feedsmodelstandardcategory.cpp index 921ff4313..cb4bde4a6 100755 --- a/src/core/feedsmodelstandardcategory.cpp +++ b/src/core/feedsmodelstandardcategory.cpp @@ -94,7 +94,7 @@ QVariant FeedsModelStandardCategory::data(int column, int role) const { bool FeedsModelStandardCategory::addItself() { // Children are removed, remove this standard category too. QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelStandardCategory", - false); + DatabaseFactory::FromSettings); QSqlQuery query_add(database); query_add.setForwardOnly(true); @@ -139,7 +139,7 @@ bool FeedsModelStandardCategory::removeItself() { // Children are removed, remove this standard category too. QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelStandardCategory", - false); + DatabaseFactory::FromSettings); QSqlQuery query_remove(database); query_remove.setForwardOnly(true); diff --git a/src/core/feedsmodelstandardfeed.cpp b/src/core/feedsmodelstandardfeed.cpp index c8d26e0fe..9a77c344c 100755 --- a/src/core/feedsmodelstandardfeed.cpp +++ b/src/core/feedsmodelstandardfeed.cpp @@ -164,7 +164,7 @@ void FeedsModelStandardFeed::update() { bool FeedsModelStandardFeed::removeItself() { QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelStandardFeed", - false); + DatabaseFactory::FromSettings); QSqlQuery query_remove(database); query_remove.setForwardOnly(true); @@ -188,7 +188,7 @@ void FeedsModelStandardFeed::updateMessages(const QList &messages) { int feed_id = id(), message_id; qint64 message_creation_date; QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelStandardFeed", - false); + DatabaseFactory::FromSettings); // Prepare queries. QSqlQuery query_select(database); diff --git a/src/core/messagesmodel.cpp b/src/core/messagesmodel.cpp index 8f85e6acf..087cf2975 100644 --- a/src/core/messagesmodel.cpp +++ b/src/core/messagesmodel.cpp @@ -14,7 +14,7 @@ MessagesModel::MessagesModel(QObject *parent) : QSqlTableModel(parent, DatabaseFactory::instance()->connection("MessagesModel", - false)) { + DatabaseFactory::FromSettings)) { setObjectName("MessagesModel"); setupFonts(); diff --git a/src/gui/formmain.cpp b/src/gui/formmain.cpp index f3b2afae1..5256e9c49 100755 --- a/src/gui/formmain.cpp +++ b/src/gui/formmain.cpp @@ -193,7 +193,7 @@ void FormMain::onAboutToQuit() { m_ui->m_tabWidget->feedMessageViewer()->quitDownloader(); - //DatabaseFactory::instance()->saveMemoryDatabase(); + DatabaseFactory::instance()->saveMemoryDatabase(); saveSize(); } diff --git a/src/gui/formsettings.cpp b/src/gui/formsettings.cpp index 129baef31..f5a8d695e 100755 --- a/src/gui/formsettings.cpp +++ b/src/gui/formsettings.cpp @@ -437,6 +437,9 @@ void FormSettings::loadGeneral() { tr(" (not supported on this platform)")); break; } + + // Load in-memory database status. + m_ui->m_cmbUseInMemoryDatabase->setChecked(Settings::instance()->value(APP_CFG_GEN, "use_in_memory_db", false).toBool()); } void FormSettings::saveGeneral() { @@ -448,6 +451,16 @@ void FormSettings::saveGeneral() { else { SystemFactory::getInstance()->setAutoStartStatus(SystemFactory::Disabled); } + + // Setup in-memory database status. + bool original_inmemory = Settings::instance()->value(APP_CFG_GEN, "use_in_memory_db", false).toBool(); + bool new_inmemory = m_ui->m_cmbUseInMemoryDatabase->isChecked(); + + if (original_inmemory != new_inmemory) { + m_changedDataTexts.append(tr("in-memory database switched")); + } + + Settings::instance()->setValue(APP_CFG_GEN, "use_in_memory_db", new_inmemory); } void FormSettings::loadInterface() { diff --git a/src/gui/formsettings.ui b/src/gui/formsettings.ui index 2dd9f2c5e..4e3d8775a 100644 --- a/src/gui/formsettings.ui +++ b/src/gui/formsettings.ui @@ -21,7 +21,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -31,11 +40,53 @@ + + + + Use in-memory database as the working database + + + + + + + Usage of in-memory working database has several advantages and pitfalls. Make sure that you are familiar with these before you turn this feature on. Advantages: +<ul> +<li>higher speed for feed/message manipulations (especially with thousands of messages displayed),</li> +<li>whole database stored in RAM, thus your hard drive can rest more.</li> +</ul> +Disadvantages: +<ul> +<li>if application crashes, your changes from last session are lost,</li> +<li>application startup and shutdown can take little longer (max. 2 seconds).</li> +</ul> +Authors of this application are NOT responsible for lost data. + + + Qt::RichText + + + true + + + 20 + + + - + + 0 + + + 0 + + + 0 + + 0 @@ -51,12 +102,21 @@ 0 0 - 576 - 373 + 564 + 363 - + + 0 + + + 0 + + + 0 + + 0 @@ -70,7 +130,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -86,7 +155,16 @@ Icons && skins - + + 0 + + + 0 + + + 0 + + 0 @@ -102,8 +180,8 @@ 0 0 - 568 - 344 + 558 + 337 @@ -301,7 +379,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -330,7 +417,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -387,7 +483,7 @@ - + Mouse gestures work with middle mouse button. Possible gestures are: @@ -559,7 +655,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -655,8 +760,6 @@ - groupBox_3 - label diff --git a/src/main.cpp b/src/main.cpp index 91129131c..999426372 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -74,6 +74,9 @@ int main(int argc, char *argv[]) { IconThemeFactory::instance()->loadCurrentIconTheme(); SkinFactory::instance()->loadCurrentSkin(); + // Decide whether user decided to use in-memory database or not. + DatabaseFactory::instance()->determineInMemoryDatabase(); + // Load localization and setup locale before any widget is constructed. LoadLocalization();