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() {
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");
}

View file

@ -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<DatabaseFactory> s_instance;
};

View file

@ -176,7 +176,7 @@ QList<Message> FeedsModel::messagesForFeeds(const QList<FeedsModelFeed*> &feeds)
QList<Message> 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<FeedsModelFeed*> FeedsModel::feedsForIndexes(const QModelIndexList &indexe
bool FeedsModel::markFeedsRead(const QList<FeedsModelFeed*> &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<FeedsModelFeed*> &feeds,
bool FeedsModel::markFeedsDeleted(const QList<FeedsModelFeed *> &feeds,
int deleted) {
QSqlDatabase db_handle = DatabaseFactory::instance()->connection(objectName(),
false);
DatabaseFactory::FromSettings);
if (!db_handle.transaction()) {
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) {
QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelFeed",
false);
DatabaseFactory::FromSettings);
QSqlQuery query_all(database);
query_all.setForwardOnly(true);

View file

@ -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);

View file

@ -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<Message> &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);

View file

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

View file

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

View file

@ -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() {

View file

@ -21,7 +21,16 @@
</property>
<widget class="QWidget" name="m_pageGeneral">
<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>
</property>
<item row="0" column="0">
@ -31,11 +40,53 @@
</property>
</widget>
</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>
</widget>
<widget class="QWidget" name="m_pageShortcuts">
<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>
</property>
<item>
@ -51,12 +102,21 @@
<rect>
<x>0</x>
<y>0</y>
<width>576</width>
<height>373</height>
<width>564</width>
<height>363</height>
</rect>
</property>
<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>
</property>
<item>
@ -70,7 +130,16 @@
</widget>
<widget class="QWidget" name="m_pageUi">
<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>
</property>
<item>
@ -86,7 +155,16 @@
<string>Icons &amp;&amp; skins</string>
</attribute>
<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>
</property>
<item>
@ -102,8 +180,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>568</width>
<height>344</height>
<width>558</width>
<height>337</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
@ -301,7 +379,16 @@
</widget>
<widget class="QWidget" name="m_pageLanguages">
<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>
</property>
<item>
@ -330,7 +417,16 @@
</widget>
<widget class="QWidget" name="m_pageProxy">
<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>
</property>
<item>
@ -387,7 +483,7 @@
</property>
</widget>
</item>
<item row="3" column="0">
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="m_lblMouseGestures">
<property name="text">
<string>Mouse gestures work with middle mouse button. Possible gestures are:
@ -559,7 +655,16 @@
</widget>
<widget class="QWidget" name="m_pageFeedsMessages">
<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>
</property>
<item>
@ -655,8 +760,6 @@
</widget>
</item>
</layout>
<zorder>groupBox_3</zorder>
<zorder>label</zorder>
</widget>
</widget>
</item>

View file

@ -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();