Switchable memory/file-based database.

This commit is contained in:
Martin Rotter 2014-01-21 07:55:10 +01:00
parent d21b5318a1
commit 46979667a4
11 changed files with 189 additions and 41 deletions

View file

@ -35,13 +35,13 @@ DatabaseFactory *DatabaseFactory::instance() {
void DatabaseFactory::assemblyDatabaseFilePath() { void DatabaseFactory::assemblyDatabaseFilePath() {
if (Settings::instance()->type() == Settings::Portable) { if (Settings::instance()->type() == Settings::Portable) {
m_databaseFilePath = qApp->applicationDirPath() + m_databaseFilePath = qApp->applicationDirPath() +
QDir::separator() + QDir::separator() +
QString(APP_DB_PATH); QString(APP_DB_PATH);
} }
else { else {
m_databaseFilePath = QDir::homePath() + QDir::separator() + m_databaseFilePath = QDir::homePath() + QDir::separator() +
QString(APP_LOW_H_NAME) + QDir::separator() + QString(APP_LOW_H_NAME) + QDir::separator() +
QString(APP_DB_PATH); QString(APP_DB_PATH);
} }
} }
@ -105,7 +105,7 @@ QSqlDatabase DatabaseFactory::initializeInMemoryDatabase() {
} }
// Loading messages from file-based database. // Loading messages from file-based database.
QSqlDatabase file_database = connection(objectName(), false); QSqlDatabase file_database = connection(objectName(), StrictlyFileBased);
QSqlQuery copy_contents(database); QSqlQuery copy_contents(database);
// Attach database. // Attach database.
@ -135,7 +135,7 @@ QSqlDatabase DatabaseFactory::initializeInMemoryDatabase() {
QSqlDatabase DatabaseFactory::initializeFileBasedDatabase(const QString &connection_name) { QSqlDatabase DatabaseFactory::initializeFileBasedDatabase(const QString &connection_name) {
// Prepare file paths. // Prepare file paths.
QDir db_path(getDatabasePath()); QDir db_path(databaseFilePath());
QFile db_file(db_path.absoluteFilePath(APP_DB_FILE)); QFile db_file(db_path.absoluteFilePath(APP_DB_FILE));
// Check if database directory exists. // Check if database directory exists.
@ -222,9 +222,11 @@ QSqlDatabase DatabaseFactory::initializeFileBasedDatabase(const QString &connect
} }
QSqlDatabase DatabaseFactory::connection(const QString &connection_name, QSqlDatabase DatabaseFactory::connection(const QString &connection_name,
bool in_memory) { DesiredType desired_type) {
if (in_memory) { if (desired_type == DatabaseFactory::StrictlyInMemory ||
// We request in-memory database. (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) { if (!m_inMemoryInitialized) {
// It is not initialized yet. // It is not initialized yet.
return initializeInMemoryDatabase(); return initializeInMemoryDatabase();
@ -267,7 +269,7 @@ QSqlDatabase DatabaseFactory::connection(const QString &connection_name,
// yet, add it and set it up. // yet, add it and set it up.
database = QSqlDatabase::addDatabase(DATABASE_DRIVER, connection_name); database = QSqlDatabase::addDatabase(DATABASE_DRIVER, connection_name);
QDir db_path(getDatabasePath()); QDir db_path(databaseFilePath());
QFile db_file(db_path.absoluteFilePath(APP_DB_FILE)); QFile db_file(db_path.absoluteFilePath(APP_DB_FILE));
// Setup database file path. // Setup database file path.
@ -296,8 +298,14 @@ void DatabaseFactory::removeConnection(const QString &connection_name) {
} }
void DatabaseFactory::saveMemoryDatabase() { void DatabaseFactory::saveMemoryDatabase() {
QSqlDatabase database = connection(); if (!m_inMemoryEnabled) {
QSqlDatabase file_database = connection(objectName(), false); 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); QSqlQuery copy_contents(database);
// Attach database. // Attach database.
@ -316,3 +324,10 @@ void DatabaseFactory::saveMemoryDatabase() {
copy_contents.exec("DETACH 'storage'"); copy_contents.exec("DETACH 'storage'");
copy_contents.finish(); 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");
}

View file

@ -10,19 +10,26 @@ class DatabaseFactory : public QObject {
Q_OBJECT Q_OBJECT
public: public:
// Describes what type of database user wants.
enum DesiredType {
StrictlyFileBased,
StrictlyInMemory,
FromSettings
};
// Destructor. // Destructor.
virtual ~DatabaseFactory(); virtual ~DatabaseFactory();
// Returns absolute file path to database file. // Returns absolute file path to database file.
inline QString getDatabasePath() { inline QString databaseFilePath() {
return m_databaseFilePath; return m_databaseFilePath;
} }
// If in-memory is true, then :memory: database is returned // If in-memory is true, then :memory: database is returned
// In-memory database is DEFAULT database. // In-memory database is DEFAULT database.
// NOTE: This always returns OPENED database. // NOTE: This always returns OPENED database.
QSqlDatabase connection(const QString &connection_name = QString(), QSqlDatabase connection(const QString &connection_name,
bool in_memory = true); DesiredType desired_type);
// Removes connection. // Removes connection.
void removeConnection(const QString &connection_name = QString()); void removeConnection(const QString &connection_name = QString());
@ -31,6 +38,9 @@ class DatabaseFactory : public QObject {
// to file-based database. // to file-based database.
void saveMemoryDatabase(); void saveMemoryDatabase();
// Sets m_inMemoryEnabled according to user settings.
void determineInMemoryDatabase();
// Singleton getter. // Singleton getter.
static DatabaseFactory *instance(); static DatabaseFactory *instance();
@ -53,6 +63,10 @@ class DatabaseFactory : public QObject {
bool m_fileBasedinitialized; bool m_fileBasedinitialized;
bool m_inMemoryInitialized; 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. // Private singleton value.
static QPointer<DatabaseFactory> s_instance; static QPointer<DatabaseFactory> s_instance;
}; };

View file

@ -176,7 +176,7 @@ QList<Message> FeedsModel::messagesForFeeds(const QList<FeedsModelFeed*> &feeds)
QList<Message> messages; QList<Message> messages;
QSqlDatabase database = DatabaseFactory::instance()->connection(objectName(), QSqlDatabase database = DatabaseFactory::instance()->connection(objectName(),
false); DatabaseFactory::FromSettings);
QSqlQuery query_read_msg(database); QSqlQuery query_read_msg(database);
query_read_msg.setForwardOnly(true); query_read_msg.setForwardOnly(true);
query_read_msg.prepare("SELECT title, url, author, date_created, contents " query_read_msg.prepare("SELECT title, url, author, date_created, contents "
@ -319,7 +319,7 @@ void FeedsModel::loadFromDatabase() {
m_rootItem->clearChildren(); m_rootItem->clearChildren();
QSqlDatabase database = DatabaseFactory::instance()->connection(objectName(), QSqlDatabase database = DatabaseFactory::instance()->connection(objectName(),
false); DatabaseFactory::FromSettings);
CategoryAssignment categories; CategoryAssignment categories;
FeedAssignment feeds; FeedAssignment feeds;
@ -432,7 +432,7 @@ QList<FeedsModelFeed*> FeedsModel::feedsForIndexes(const QModelIndexList &indexe
bool FeedsModel::markFeedsRead(const QList<FeedsModelFeed*> &feeds, bool FeedsModel::markFeedsRead(const QList<FeedsModelFeed*> &feeds,
int read) { int read) {
QSqlDatabase db_handle = DatabaseFactory::instance()->connection(objectName(), QSqlDatabase db_handle = DatabaseFactory::instance()->connection(objectName(),
false); DatabaseFactory::FromSettings);
if (!db_handle.transaction()) { if (!db_handle.transaction()) {
qWarning("Starting transaction for feeds read change."); qWarning("Starting transaction for feeds read change.");
@ -469,7 +469,7 @@ bool FeedsModel::markFeedsRead(const QList<FeedsModelFeed*> &feeds,
bool FeedsModel::markFeedsDeleted(const QList<FeedsModelFeed *> &feeds, bool FeedsModel::markFeedsDeleted(const QList<FeedsModelFeed *> &feeds,
int deleted) { int deleted) {
QSqlDatabase db_handle = DatabaseFactory::instance()->connection(objectName(), QSqlDatabase db_handle = DatabaseFactory::instance()->connection(objectName(),
false); DatabaseFactory::FromSettings);
if (!db_handle.transaction()) { if (!db_handle.transaction()) {
qWarning("Starting transaction for feeds clearing."); qWarning("Starting transaction for feeds clearing.");

View file

@ -48,7 +48,7 @@ QString FeedsModelFeed::typeToString(FeedsModelFeed::Type type) {
void FeedsModelFeed::updateCounts(bool including_total_count) { void FeedsModelFeed::updateCounts(bool including_total_count) {
QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelFeed", QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelFeed",
false); DatabaseFactory::FromSettings);
QSqlQuery query_all(database); QSqlQuery query_all(database);
query_all.setForwardOnly(true); query_all.setForwardOnly(true);

View file

@ -94,7 +94,7 @@ QVariant FeedsModelStandardCategory::data(int column, int role) const {
bool FeedsModelStandardCategory::addItself() { bool FeedsModelStandardCategory::addItself() {
// Children are removed, remove this standard category too. // Children are removed, remove this standard category too.
QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelStandardCategory", QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelStandardCategory",
false); DatabaseFactory::FromSettings);
QSqlQuery query_add(database); QSqlQuery query_add(database);
query_add.setForwardOnly(true); query_add.setForwardOnly(true);
@ -139,7 +139,7 @@ bool FeedsModelStandardCategory::removeItself() {
// Children are removed, remove this standard category too. // Children are removed, remove this standard category too.
QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelStandardCategory", QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelStandardCategory",
false); DatabaseFactory::FromSettings);
QSqlQuery query_remove(database); QSqlQuery query_remove(database);
query_remove.setForwardOnly(true); query_remove.setForwardOnly(true);

View file

@ -164,7 +164,7 @@ void FeedsModelStandardFeed::update() {
bool FeedsModelStandardFeed::removeItself() { bool FeedsModelStandardFeed::removeItself() {
QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelStandardFeed", QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelStandardFeed",
false); DatabaseFactory::FromSettings);
QSqlQuery query_remove(database); QSqlQuery query_remove(database);
query_remove.setForwardOnly(true); query_remove.setForwardOnly(true);
@ -188,7 +188,7 @@ void FeedsModelStandardFeed::updateMessages(const QList<Message> &messages) {
int feed_id = id(), message_id; int feed_id = id(), message_id;
qint64 message_creation_date; qint64 message_creation_date;
QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelStandardFeed", QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelStandardFeed",
false); DatabaseFactory::FromSettings);
// Prepare queries. // Prepare queries.
QSqlQuery query_select(database); QSqlQuery query_select(database);

View file

@ -14,7 +14,7 @@
MessagesModel::MessagesModel(QObject *parent) MessagesModel::MessagesModel(QObject *parent)
: QSqlTableModel(parent, : QSqlTableModel(parent,
DatabaseFactory::instance()->connection("MessagesModel", DatabaseFactory::instance()->connection("MessagesModel",
false)) { DatabaseFactory::FromSettings)) {
setObjectName("MessagesModel"); setObjectName("MessagesModel");
setupFonts(); setupFonts();

View file

@ -193,7 +193,7 @@ void FormMain::onAboutToQuit() {
m_ui->m_tabWidget->feedMessageViewer()->quitDownloader(); m_ui->m_tabWidget->feedMessageViewer()->quitDownloader();
//DatabaseFactory::instance()->saveMemoryDatabase(); DatabaseFactory::instance()->saveMemoryDatabase();
saveSize(); saveSize();
} }

View file

@ -437,6 +437,9 @@ void FormSettings::loadGeneral() {
tr(" (not supported on this platform)")); tr(" (not supported on this platform)"));
break; 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() { void FormSettings::saveGeneral() {
@ -448,6 +451,16 @@ void FormSettings::saveGeneral() {
else { else {
SystemFactory::getInstance()->setAutoStartStatus(SystemFactory::Disabled); 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() { void FormSettings::loadInterface() {

View file

@ -21,7 +21,16 @@
</property> </property>
<widget class="QWidget" name="m_pageGeneral"> <widget class="QWidget" name="m_pageGeneral">
<layout class="QFormLayout" name="formLayout_5"> <layout class="QFormLayout" name="formLayout_5">
<property name="margin"> <property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item row="0" column="0"> <item row="0" column="0">
@ -31,11 +40,53 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0">
<widget class="QCheckBox" name="m_cmbUseInMemoryDatabase">
<property name="text">
<string>Use in-memory database as the working database</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>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:
&lt;ul&gt;
&lt;li&gt;higher speed for feed/message manipulations (especially with thousands of messages displayed),&lt;/li&gt;
&lt;li&gt;whole database stored in RAM, thus your hard drive can rest more.&lt;/li&gt;
&lt;/ul&gt;
Disadvantages:
&lt;ul&gt;
&lt;li&gt;if application crashes, your changes from last session are lost,&lt;/li&gt;
&lt;li&gt;application startup and shutdown can take little longer (max. 2 seconds).&lt;/li&gt;
&lt;/ul&gt;
Authors of this application are NOT responsible for lost data.</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="indent">
<number>20</number>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="m_pageShortcuts"> <widget class="QWidget" name="m_pageShortcuts">
<layout class="QHBoxLayout" name="horizontalLayout_3"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="margin"> <property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
@ -51,12 +102,21 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>576</width> <width>564</width>
<height>373</height> <height>363</height>
</rect> </rect>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout_4"> <layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="margin"> <property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
@ -70,7 +130,16 @@
</widget> </widget>
<widget class="QWidget" name="m_pageUi"> <widget class="QWidget" name="m_pageUi">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<property name="margin"> <property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
@ -86,7 +155,16 @@
<string>Icons &amp;&amp; skins</string> <string>Icons &amp;&amp; skins</string>
</attribute> </attribute>
<layout class="QHBoxLayout" name="horizontalLayout_8"> <layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="margin"> <property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
@ -102,8 +180,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>568</width> <width>558</width>
<height>344</height> <height>337</height>
</rect> </rect>
</property> </property>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
@ -301,7 +379,16 @@
</widget> </widget>
<widget class="QWidget" name="m_pageLanguages"> <widget class="QWidget" name="m_pageLanguages">
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="margin"> <property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
@ -330,7 +417,16 @@
</widget> </widget>
<widget class="QWidget" name="m_pageProxy"> <widget class="QWidget" name="m_pageProxy">
<layout class="QHBoxLayout" name="horizontalLayout_5"> <layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="margin"> <property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
@ -387,7 +483,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="3" column="0" colspan="2">
<widget class="QLabel" name="m_lblMouseGestures"> <widget class="QLabel" name="m_lblMouseGestures">
<property name="text"> <property name="text">
<string>Mouse gestures work with middle mouse button. Possible gestures are: <string>Mouse gestures work with middle mouse button. Possible gestures are:
@ -559,7 +655,16 @@
</widget> </widget>
<widget class="QWidget" name="m_pageFeedsMessages"> <widget class="QWidget" name="m_pageFeedsMessages">
<layout class="QHBoxLayout" name="horizontalLayout_9"> <layout class="QHBoxLayout" name="horizontalLayout_9">
<property name="margin"> <property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
@ -655,8 +760,6 @@
</widget> </widget>
</item> </item>
</layout> </layout>
<zorder>groupBox_3</zorder>
<zorder>label</zorder>
</widget> </widget>
</widget> </widget>
</item> </item>

View file

@ -74,6 +74,9 @@ int main(int argc, char *argv[]) {
IconThemeFactory::instance()->loadCurrentIconTheme(); IconThemeFactory::instance()->loadCurrentIconTheme();
SkinFactory::instance()->loadCurrentSkin(); 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. // Load localization and setup locale before any widget is constructed.
LoadLocalization(); LoadLocalization();