Initial implementation of :memory: db handling.
This commit is contained in:
parent
7661992424
commit
8cffae07de
65
resources/misc/db_init_memory.sql
Normal file
65
resources/misc/db_init_memory.sql
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
DROP TABLE IF EXISTS Information;
|
||||||
|
-- !
|
||||||
|
CREATE TABLE IF NOT EXISTS Information (
|
||||||
|
key TEXT PRIMARY KEY,
|
||||||
|
value TEXT NOT NULL
|
||||||
|
);
|
||||||
|
-- !
|
||||||
|
INSERT INTO Information VALUES ('schema_version', '0.0.1');
|
||||||
|
-- !
|
||||||
|
DROP TABLE IF EXISTS Categories;
|
||||||
|
-- !
|
||||||
|
CREATE TABLE IF NOT EXISTS Categories (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
parent_id INTEGER NOT NULL,
|
||||||
|
title TEXT NOT NULL UNIQUE CHECK (title != ''),
|
||||||
|
description TEXT,
|
||||||
|
date_created INTEGER NOT NULL CHECK (date_created != 0),
|
||||||
|
icon BLOB,
|
||||||
|
type INTEGER NOT NULL,
|
||||||
|
|
||||||
|
FOREIGN KEY (parent_id) REFERENCES Categories (id)
|
||||||
|
);
|
||||||
|
-- !
|
||||||
|
DROP TABLE IF EXISTS Feeds;
|
||||||
|
-- !
|
||||||
|
CREATE TABLE IF NOT EXISTS Feeds (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
title TEXT NOT NULL CHECK (title != ''),
|
||||||
|
description TEXT,
|
||||||
|
date_created INTEGER NOT NULL CHECK (date_created != 0),
|
||||||
|
icon BLOB,
|
||||||
|
category INTEGER NOT NULL CHECK (category >= -1),
|
||||||
|
encoding TEXT NOT NULL CHECK (encoding != ''),
|
||||||
|
url TEXT NOT NULL UNIQUE CHECK (url != ''),
|
||||||
|
language TEXT,
|
||||||
|
type INTEGER NOT NULL CHECK (type >= 0)
|
||||||
|
);
|
||||||
|
-- !
|
||||||
|
DROP TABLE IF EXISTS FeedsData;
|
||||||
|
-- !
|
||||||
|
CREATE TABLE IF NOT EXISTS FeedsData (
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
key TEXT NOT NULL,
|
||||||
|
value TEXT,
|
||||||
|
|
||||||
|
PRIMARY KEY (id, key),
|
||||||
|
FOREIGN KEY (id) REFERENCES Feeds (id)
|
||||||
|
);
|
||||||
|
-- !
|
||||||
|
DROP TABLE IF EXISTS Messages;
|
||||||
|
-- !
|
||||||
|
CREATE TABLE IF NOT EXISTS Messages (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
read INTEGER(1) NOT NULL CHECK (read >= 0 AND read <= 1) DEFAULT (0),
|
||||||
|
deleted INTEGER(1) NOT NULL CHECK (deleted >= 0 AND deleted <= 1) DEFAULT (0),
|
||||||
|
important INTEGER(1) NOT NULL CHECK (important >= 0 AND important <= 1) DEFAULT (0),
|
||||||
|
feed INTEGER NOT NULL,
|
||||||
|
title TEXT NOT NULL CHECK (title != ''),
|
||||||
|
url TEXT,
|
||||||
|
author TEXT,
|
||||||
|
date_created INTEGER NOT NULL CHECK (date_created != 0),
|
||||||
|
contents TEXT,
|
||||||
|
|
||||||
|
FOREIGN KEY (feed) REFERENCES Feeds (id)
|
||||||
|
);
|
@ -13,7 +13,7 @@
|
|||||||
QPointer<DatabaseFactory> DatabaseFactory::s_instance;
|
QPointer<DatabaseFactory> DatabaseFactory::s_instance;
|
||||||
|
|
||||||
DatabaseFactory::DatabaseFactory(QObject *parent)
|
DatabaseFactory::DatabaseFactory(QObject *parent)
|
||||||
: QObject(parent), m_initialized(false) {
|
: QObject(parent), m_fileBasedinitialized(false), m_inMemoryInitialized(false) {
|
||||||
assemblyDatabaseFilePath();
|
assemblyDatabaseFilePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +42,96 @@ void DatabaseFactory::assemblyDatabaseFilePath() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSqlDatabase DatabaseFactory::initializeInMemory() {
|
||||||
|
QSqlDatabase database = QSqlDatabase::addDatabase(DATABASE_DRIVER);
|
||||||
|
|
||||||
|
database.setDatabaseName(":memory:");
|
||||||
|
|
||||||
|
if (!database.open()) {
|
||||||
|
qFatal("Database was NOT opened. Delivered error message: '%s'",
|
||||||
|
qPrintable(database.lastError().text()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
QSqlQuery query_db(database);
|
||||||
|
|
||||||
|
query_db.setForwardOnly(true);
|
||||||
|
query_db.exec("PRAGMA encoding = \"UTF-8\"");
|
||||||
|
query_db.exec("PRAGMA synchronous = OFF");
|
||||||
|
query_db.exec("PRAGMA journal_mode = MEMORY");
|
||||||
|
query_db.exec("PRAGMA page_size = 4096");
|
||||||
|
query_db.exec("PRAGMA cache_size = 16384");
|
||||||
|
query_db.exec("PRAGMA count_changes = OFF");
|
||||||
|
query_db.exec("PRAGMA temp_store = MEMORY");
|
||||||
|
|
||||||
|
// Sample query which checks for existence of tables.
|
||||||
|
query_db.exec("SELECT value FROM Information WHERE key = 'schema_version'");
|
||||||
|
|
||||||
|
if (query_db.lastError().isValid()) {
|
||||||
|
qWarning("Error occurred. Database is not initialized. Initializing now.");
|
||||||
|
|
||||||
|
QFile file_init(APP_MISC_PATH + QDir::separator() + APP_DB_INIT_MEMORY);
|
||||||
|
|
||||||
|
if (!file_init.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
// Database initialization file not opened. HUGE problem.
|
||||||
|
qFatal("Database initialization file '%s' from directory '%s' was not found. Database is uninitialized.",
|
||||||
|
APP_DB_INIT_FILE,
|
||||||
|
qPrintable(APP_MISC_PATH));
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList statements = QString(file_init.readAll()).split(APP_DB_INIT_SPLIT,
|
||||||
|
QString::SkipEmptyParts);
|
||||||
|
database.transaction();
|
||||||
|
|
||||||
|
foreach(const QString &statement, statements) {
|
||||||
|
query_db.exec(statement);
|
||||||
|
|
||||||
|
if (query_db.lastError().isValid()) {
|
||||||
|
qFatal("Database initialization failed. Initialization script '%s' is not correct.",
|
||||||
|
APP_DB_INIT_FILE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
database.commit();
|
||||||
|
qDebug("Database backend should be ready now.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
query_db.next();
|
||||||
|
|
||||||
|
qDebug("In-memory database connection seems to be established.");
|
||||||
|
qDebug("Database has version '%s'.", qPrintable(query_db.value(0).toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loading messages from file-based database.
|
||||||
|
QSqlDatabase file_database = connection("fdb", false);
|
||||||
|
|
||||||
|
QSqlQuery copy_msgs(database);
|
||||||
|
|
||||||
|
// Attach database.
|
||||||
|
copy_msgs.exec(QString("ATTACH DATABASE '%1' AS 'storage';").arg(file_database.databaseName()));
|
||||||
|
|
||||||
|
// Copy all stuff.
|
||||||
|
QStringList tables; tables << "Categories" << "Feeds" << "FeedsData" <<
|
||||||
|
"Messages";
|
||||||
|
|
||||||
|
foreach (const QString &table, tables) {
|
||||||
|
copy_msgs.exec(QString("INSERT INTO main.%1 SELECT * FROM storage.%1;").arg(table));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach database and finish.
|
||||||
|
copy_msgs.exec("DETACH 'storage'");
|
||||||
|
copy_msgs.finish();
|
||||||
|
|
||||||
|
// DB is attached.
|
||||||
|
|
||||||
|
query_db.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything is initialized now.
|
||||||
|
m_inMemoryInitialized = true;
|
||||||
|
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
|
||||||
QString DatabaseFactory::getDatabasePath() {
|
QString DatabaseFactory::getDatabasePath() {
|
||||||
return m_databasePath;
|
return m_databasePath;
|
||||||
}
|
}
|
||||||
@ -55,7 +145,8 @@ QString DatabaseFactory::getDatabasePath() {
|
|||||||
// a pri vypinani se zase :memory: presype do
|
// a pri vypinani se zase :memory: presype do
|
||||||
// souborove databaze
|
// souborove databaze
|
||||||
|
|
||||||
QSqlDatabase DatabaseFactory::initialize(const QString &connection_name) {
|
QSqlDatabase DatabaseFactory::initializeFileBased(const QString &connection_name,
|
||||||
|
bool in_memory) {
|
||||||
// Prepare file paths.
|
// Prepare file paths.
|
||||||
QDir db_path(getDatabasePath());
|
QDir db_path(getDatabasePath());
|
||||||
QFile db_file(db_path.absoluteFilePath(APP_DB_FILE));
|
QFile db_file(db_path.absoluteFilePath(APP_DB_FILE));
|
||||||
@ -72,10 +163,10 @@ QSqlDatabase DatabaseFactory::initialize(const QString &connection_name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Folders are created. Create new QSQLDatabase object.
|
// Folders are created. Create new QSQLDatabase object.
|
||||||
QSqlDatabase database = QSqlDatabase::addDatabase(DATABASE_DRIVER,
|
QSqlDatabase database;
|
||||||
connection_name);
|
|
||||||
|
|
||||||
// Setup database file path.
|
database = QSqlDatabase::addDatabase(DATABASE_DRIVER,
|
||||||
|
connection_name);
|
||||||
database.setDatabaseName(db_file.fileName());
|
database.setDatabaseName(db_file.fileName());
|
||||||
|
|
||||||
if (!database.open()) {
|
if (!database.open()) {
|
||||||
@ -139,14 +230,39 @@ QSqlDatabase DatabaseFactory::initialize(const QString &connection_name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Everything is initialized now.
|
// Everything is initialized now.
|
||||||
m_initialized = true;
|
m_fileBasedinitialized = true;
|
||||||
|
|
||||||
return database;
|
return database;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSqlDatabase DatabaseFactory::connection(const QString &connection_name) {
|
QSqlDatabase DatabaseFactory::connection(const QString &connection_name,
|
||||||
if (!m_initialized) {
|
bool in_memory) {
|
||||||
return initialize(connection_name);
|
if (in_memory) {
|
||||||
|
// We request in-memory database.
|
||||||
|
if (!m_inMemoryInitialized) {
|
||||||
|
// It is not initialized yet.
|
||||||
|
return initializeInMemory();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
QSqlDatabase database = QSqlDatabase::database();
|
||||||
|
|
||||||
|
database.setDatabaseName(":memory:");
|
||||||
|
|
||||||
|
if (!database.isOpen() && !database.open()) {
|
||||||
|
qFatal("In-memory database was NOT opened. Delivered error message: '%s'.",
|
||||||
|
qPrintable(database.lastError().text()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qDebug("In-memory database connection seems to be established.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_fileBasedinitialized) {
|
||||||
|
// File-based database is not yet initialised.
|
||||||
|
return initializeFileBased(connection_name, in_memory);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
QSqlDatabase database;
|
QSqlDatabase database;
|
||||||
@ -187,3 +303,26 @@ void DatabaseFactory::removeConnection(const QString &connection_name) {
|
|||||||
qDebug("Removing database connection '%s'.", qPrintable(connection_name));
|
qDebug("Removing database connection '%s'.", qPrintable(connection_name));
|
||||||
QSqlDatabase::removeDatabase(connection_name);
|
QSqlDatabase::removeDatabase(connection_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DatabaseFactory::saveMemoryDatabase() {
|
||||||
|
QSqlDatabase database = connection();
|
||||||
|
QSqlDatabase file_database = connection("fdb", false);
|
||||||
|
|
||||||
|
QSqlQuery copy_msgs(database);
|
||||||
|
|
||||||
|
// Attach database.
|
||||||
|
copy_msgs.exec(QString("ATTACH DATABASE '%1' AS 'storage';").arg(file_database.databaseName()));
|
||||||
|
|
||||||
|
// Copy all stuff.
|
||||||
|
QStringList tables; tables << "Categories" << "Feeds" << "FeedsData" <<
|
||||||
|
"Messages";
|
||||||
|
|
||||||
|
foreach (const QString &table, tables) {
|
||||||
|
copy_msgs.exec(QString("DELETE FROM storage.%1;").arg(table));
|
||||||
|
copy_msgs.exec(QString("INSERT INTO storage.%1 SELECT * FROM main.%1;").arg(table));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach database and finish.
|
||||||
|
copy_msgs.exec("DETACH 'storage'");
|
||||||
|
copy_msgs.finish();
|
||||||
|
}
|
||||||
|
@ -17,10 +17,15 @@ class DatabaseFactory : public QObject {
|
|||||||
QString getDatabasePath();
|
QString getDatabasePath();
|
||||||
|
|
||||||
// NOTE: This always returns OPENED database.
|
// NOTE: This always returns OPENED database.
|
||||||
QSqlDatabase connection(const QString &connection_name);
|
QSqlDatabase connection(const QString &connection_name = QString(),
|
||||||
|
bool in_memory = true);
|
||||||
|
|
||||||
// Removes connection.
|
// Removes connection.
|
||||||
void removeConnection(const QString &connection_name);
|
void removeConnection(const QString &connection_name = QString());
|
||||||
|
|
||||||
|
// Performs saving of items from in-memory database
|
||||||
|
// to file-based database.
|
||||||
|
void saveMemoryDatabase();
|
||||||
|
|
||||||
// Singleton getter.
|
// Singleton getter.
|
||||||
static DatabaseFactory *instance();
|
static DatabaseFactory *instance();
|
||||||
@ -34,13 +39,15 @@ class DatabaseFactory : public QObject {
|
|||||||
|
|
||||||
// Creates new connection, initializes database and
|
// Creates new connection, initializes database and
|
||||||
// returns opened connection.
|
// returns opened connection.
|
||||||
QSqlDatabase initialize(const QString &connection_name);
|
QSqlDatabase initializeInMemory();
|
||||||
|
QSqlDatabase initializeFileBased(const QString &connection_name, bool in_memory);
|
||||||
|
|
||||||
// Path to database file.
|
// Path to database file.
|
||||||
QString m_databasePath;
|
QString m_databasePath;
|
||||||
|
|
||||||
// Is database file initialized?
|
// Is database file initialized?
|
||||||
bool m_initialized;
|
bool m_fileBasedinitialized;
|
||||||
|
bool m_inMemoryInitialized;
|
||||||
|
|
||||||
// Private singleton value.
|
// Private singleton value.
|
||||||
static QPointer<DatabaseFactory> s_instance;
|
static QPointer<DatabaseFactory> s_instance;
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#define INTERNAL_URL_NEWSPAPER "@APP_LOW_NAME@:newspaper"
|
#define INTERNAL_URL_NEWSPAPER "@APP_LOW_NAME@:newspaper"
|
||||||
|
|
||||||
#define APP_DB_INIT_FILE "db_init.sql"
|
#define APP_DB_INIT_FILE "db_init.sql"
|
||||||
|
#define APP_DB_INIT_MEMORY "db_init_memory.sql"
|
||||||
#define APP_DB_INIT_SPLIT "-- !\n"
|
#define APP_DB_INIT_SPLIT "-- !\n"
|
||||||
#define APP_DB_PATH "data/database/local"
|
#define APP_DB_PATH "data/database/local"
|
||||||
#define APP_DB_FILE "database.db"
|
#define APP_DB_FILE "database.db"
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "core/defs.h"
|
#include "core/defs.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "core/systemfactory.h"
|
#include "core/systemfactory.h"
|
||||||
|
#include "core/databasefactory.h"
|
||||||
#include "gui/formabout.h"
|
#include "gui/formabout.h"
|
||||||
#include "gui/formsettings.h"
|
#include "gui/formsettings.h"
|
||||||
#include "gui/webbrowser.h"
|
#include "gui/webbrowser.h"
|
||||||
@ -193,6 +194,9 @@ void FormMain::onAboutToQuit() {
|
|||||||
qDebug("Cleaning up resources and saving application state.");
|
qDebug("Cleaning up resources and saving application state.");
|
||||||
|
|
||||||
m_ui->m_tabWidget->feedMessageViewer()->quitDownloader();
|
m_ui->m_tabWidget->feedMessageViewer()->quitDownloader();
|
||||||
|
|
||||||
|
DatabaseFactory::instance()->saveMemoryDatabase();
|
||||||
|
|
||||||
saveSize();
|
saveSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user