Work on bug/enhancement #95: rolling DB schema update.
This commit is contained in:
parent
504950f631
commit
a1e415c518
|
@ -68,7 +68,7 @@ CREATE TABLE IF NOT EXISTS Messages (
|
||||||
author TEXT NOT NULL,
|
author TEXT NOT NULL,
|
||||||
date_created BIGINT NOT NULL CHECK (date_created != 0),
|
date_created BIGINT NOT NULL CHECK (date_created != 0),
|
||||||
contents TEXT,
|
contents TEXT,
|
||||||
is_hidden INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_hidden >= 0 AND is_hidden <= 1),
|
is_pdeleted INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_pdeleted >= 0 AND is_pdeleted <= 1),
|
||||||
|
|
||||||
FOREIGN KEY (feed) REFERENCES Feeds (id)
|
FOREIGN KEY (feed) REFERENCES Feeds (id)
|
||||||
);
|
);
|
||||||
|
|
|
@ -62,7 +62,7 @@ CREATE TABLE IF NOT EXISTS Messages (
|
||||||
author TEXT NOT NULL,
|
author TEXT NOT NULL,
|
||||||
date_created INTEGER NOT NULL CHECK (date_created != 0),
|
date_created INTEGER NOT NULL CHECK (date_created != 0),
|
||||||
contents TEXT,
|
contents TEXT,
|
||||||
is_hidden INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_hidden >= 0 AND is_hidden <= 1),
|
is_pdeleted INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_pdeleted >= 0 AND is_pdeleted <= 1),
|
||||||
|
|
||||||
FOREIGN KEY (feed) REFERENCES Feeds (id)
|
FOREIGN KEY (feed) REFERENCES Feeds (id)
|
||||||
);
|
);
|
||||||
|
|
|
@ -62,7 +62,7 @@ CREATE TABLE IF NOT EXISTS Messages (
|
||||||
author TEXT NOT NULL,
|
author TEXT NOT NULL,
|
||||||
date_created INTEGER NOT NULL CHECK (date_created != 0),
|
date_created INTEGER NOT NULL CHECK (date_created != 0),
|
||||||
contents TEXT,
|
contents TEXT,
|
||||||
is_hidden INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_hidden >= 0 AND is_hidden <= 1),
|
is_pdeleted INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_pdeleted >= 0 AND is_pdeleted <= 1),
|
||||||
|
|
||||||
FOREIGN KEY (feed) REFERENCES Feeds (id)
|
FOREIGN KEY (feed) REFERENCES Feeds (id)
|
||||||
);
|
);
|
|
@ -1,2 +0,0 @@
|
||||||
ALTER TABLE Messages
|
|
||||||
ADD COLUMN is_pdeleted INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_pdeleted >= 0 AND is_pdeleted <= 1);
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
ALTER TABLE Messages
|
||||||
|
ADD COLUMN is_pdeleted INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_pdeleted >= 0 AND is_pdeleted <= 1);
|
||||||
|
-- !
|
||||||
|
UPDATE Information SET inf_value = '2' WHERE inf_key = 'schema_version';
|
|
@ -1,2 +0,0 @@
|
||||||
ALTER TABLE Messages
|
|
||||||
ADD COLUMN is_pdeleted INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_pdeleted >= 0 AND is_pdeleted <= 1);
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
ALTER TABLE Messages
|
||||||
|
ADD COLUMN is_pdeleted INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_pdeleted >= 0 AND is_pdeleted <= 1);
|
||||||
|
-- !
|
||||||
|
UPDATE Information SET inf_value = '2' WHERE inf_key = 'schema_version';
|
|
@ -4,6 +4,7 @@
|
||||||
Fixed:
|
Fixed:
|
||||||
<ul>
|
<ul>
|
||||||
<li>Database is now correctly restored when using SQLite memory databases.</li>
|
<li>Database is now correctly restored when using SQLite memory databases.</li>
|
||||||
|
<li>When items are deleted from recycle bin then they are kept in DB and marked as "permanently deleted" (bug # 95).'
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
Added:
|
Added:
|
||||||
|
|
|
@ -120,7 +120,7 @@ bool FeedsModelRecycleBin::empty() {
|
||||||
QSqlQuery query_empty_bin(db_handle);
|
QSqlQuery query_empty_bin(db_handle);
|
||||||
query_empty_bin.setForwardOnly(true);
|
query_empty_bin.setForwardOnly(true);
|
||||||
|
|
||||||
if (!query_empty_bin.exec("DELETE FROM Messages WHERE is_deleted = 1;")) {
|
if (!query_empty_bin.exec("UPDATE Messages SET is_pdeleted = 1 WHERE is_deleted = 1;")) {
|
||||||
qWarning("Query execution failed for recycle bin emptying.");
|
qWarning("Query execution failed for recycle bin emptying.");
|
||||||
|
|
||||||
db_handle.rollback();
|
db_handle.rollback();
|
||||||
|
@ -148,7 +148,7 @@ bool FeedsModelRecycleBin::restore() {
|
||||||
QSqlQuery query_empty_bin(db_handle);
|
QSqlQuery query_empty_bin(db_handle);
|
||||||
query_empty_bin.setForwardOnly(true);
|
query_empty_bin.setForwardOnly(true);
|
||||||
|
|
||||||
if (!query_empty_bin.exec("UPDATE Messages SET is_deleted = 0 WHERE is_deleted = 1;")) {
|
if (!query_empty_bin.exec("UPDATE Messages SET is_deleted = 0 WHERE is_deleted = 1 AND is_pdeleted = 0;")) {
|
||||||
qWarning("Query execution failed for recycle bin restoring.");
|
qWarning("Query execution failed for recycle bin restoring.");
|
||||||
|
|
||||||
db_handle.rollback();
|
db_handle.rollback();
|
||||||
|
@ -170,7 +170,7 @@ void FeedsModelRecycleBin::updateCounts(bool update_total_count) {
|
||||||
QSqlQuery query_all(database);
|
QSqlQuery query_all(database);
|
||||||
query_all.setForwardOnly(true);
|
query_all.setForwardOnly(true);
|
||||||
|
|
||||||
if (query_all.exec("SELECT count(*) FROM Messages WHERE is_read = 0 AND is_deleted = 1;") && query_all.next()) {
|
if (query_all.exec("SELECT count(*) FROM Messages WHERE is_read = 0 AND is_deleted = 1 AND is_pdeleted = 0;") && query_all.next()) {
|
||||||
m_unreadCount = query_all.value(0).toInt();
|
m_unreadCount = query_all.value(0).toInt();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -178,7 +178,7 @@ void FeedsModelRecycleBin::updateCounts(bool update_total_count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (update_total_count) {
|
if (update_total_count) {
|
||||||
if (query_all.exec("SELECT count(*) FROM Messages WHERE is_deleted = 1;") && query_all.next()) {
|
if (query_all.exec("SELECT count(*) FROM Messages WHERE is_deleted = 1 AND is_pdeleted = 0;") && query_all.next()) {
|
||||||
m_totalCount = query_all.value(0).toInt();
|
m_totalCount = query_all.value(0).toInt();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -72,7 +72,7 @@ void MessagesModel::loadMessages(const QList<int> feed_ids) {
|
||||||
|
|
||||||
if (feed_ids.size() == 1 && feed_ids[0] == ID_RECYCLE_BIN) {
|
if (feed_ids.size() == 1 && feed_ids[0] == ID_RECYCLE_BIN) {
|
||||||
m_messageMode = MessagesFromRecycleBin;
|
m_messageMode = MessagesFromRecycleBin;
|
||||||
setFilter("is_deleted = 1");
|
setFilter("is_deleted = 1 AND is_pdeleted = 0");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_messageMode = MessagesFromFeeds;
|
m_messageMode = MessagesFromFeeds;
|
||||||
|
@ -389,7 +389,8 @@ bool MessagesModel::setBatchMessagesDeleted(const QModelIndexList &messages, int
|
||||||
QString::number(deleted));
|
QString::number(deleted));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sql_delete_query = QString("DELETE FROM Messages WHERE id in (%1);").arg(message_ids.join(", "));
|
sql_delete_query = QString("UPDATE Messages SET is_pdeleted = %2 WHERE id IN (%1);").arg(message_ids.join(", "),
|
||||||
|
QString::number(deleted));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query_read_msg.exec(sql_delete_query)) {
|
if (query_read_msg.exec(sql_delete_query)) {
|
||||||
|
|
|
@ -92,8 +92,8 @@
|
||||||
#define APP_DB_SQLITE_FILE "database.db"
|
#define APP_DB_SQLITE_FILE "database.db"
|
||||||
|
|
||||||
// Keep this in sync with schema versions declared in SQL initialization code.
|
// Keep this in sync with schema versions declared in SQL initialization code.
|
||||||
#define APP_DB_SCHEMA_VERSION "0.0.2"
|
#define APP_DB_SCHEMA_VERSION "2"
|
||||||
#define APP_DB_UPDATE_FILE_PATTERN "data_update_%1_%2_3.sql"
|
#define APP_DB_UPDATE_FILE_PATTERN "db_update_%1_%2_%3.sql"
|
||||||
#define APP_DB_COMMENT_SPLIT "-- !\n"
|
#define APP_DB_COMMENT_SPLIT "-- !\n"
|
||||||
#define APP_DB_WEB_PATH "data/database/web"
|
#define APP_DB_WEB_PATH "data/database/web"
|
||||||
|
|
||||||
|
|
|
@ -77,11 +77,6 @@ void FormCategoryDetails::setEditableCategory(FeedsModelCategory *editable_categ
|
||||||
}
|
}
|
||||||
|
|
||||||
int FormCategoryDetails::exec(FeedsModelCategory *input_category, FeedsModelRootItem *parent_to_select) {
|
int FormCategoryDetails::exec(FeedsModelCategory *input_category, FeedsModelRootItem *parent_to_select) {
|
||||||
// TODO: řeseni bugu #92. pridal sem parametr parent_to_select
|
|
||||||
// kde volajici preda pointer na doporuceny nadrazeny prvek,
|
|
||||||
// nebo null pokud nic nedoporucuje.
|
|
||||||
// vybereme
|
|
||||||
|
|
||||||
// Load categories.
|
// Load categories.
|
||||||
loadCategories(m_feedsModel->allCategories().values(), m_feedsModel->rootItem(), input_category);
|
loadCategories(m_feedsModel->allCategories().values(), m_feedsModel->rootItem(), input_category);
|
||||||
|
|
||||||
|
|
|
@ -223,7 +223,6 @@ void FormSettings::saveFeedsMessages() {
|
||||||
settings->setValue(APP_CFG_MESSAGES, "custom_date_format",
|
settings->setValue(APP_CFG_MESSAGES, "custom_date_format",
|
||||||
m_ui->m_cmbMessagesDateTimeFormat->itemData(m_ui->m_cmbMessagesDateTimeFormat->currentIndex()).toString());
|
m_ui->m_cmbMessagesDateTimeFormat->itemData(m_ui->m_cmbMessagesDateTimeFormat->currentIndex()).toString());
|
||||||
|
|
||||||
// TODO: aktualizovat messageview s novym formatem
|
|
||||||
qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->updateAutoUpdateStatus();
|
qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->updateAutoUpdateStatus();
|
||||||
qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->sourceModel()->reloadWholeLayout();
|
qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->sourceModel()->reloadWholeLayout();
|
||||||
qApp->mainForm()->tabWidget()->feedMessageViewer()->messagesView()->sourceModel()->updateDateFormat();
|
qApp->mainForm()->tabWidget()->feedMessageViewer()->messagesView()->sourceModel()->updateDateFormat();
|
||||||
|
|
|
@ -233,8 +233,7 @@ QSqlDatabase DatabaseFactory::sqliteInitializeFileBasedDatabase(const QString &c
|
||||||
// Folders are created. Create new QSQLDatabase object.
|
// Folders are created. Create new QSQLDatabase object.
|
||||||
QSqlDatabase database;
|
QSqlDatabase database;
|
||||||
|
|
||||||
database = QSqlDatabase::addDatabase(APP_DB_SQLITE_DRIVER,
|
database = QSqlDatabase::addDatabase(APP_DB_SQLITE_DRIVER, connection_name);
|
||||||
connection_name);
|
|
||||||
database.setDatabaseName(db_file.fileName());
|
database.setDatabaseName(db_file.fileName());
|
||||||
|
|
||||||
if (!database.open()) {
|
if (!database.open()) {
|
||||||
|
@ -289,15 +288,15 @@ QSqlDatabase DatabaseFactory::sqliteInitializeFileBasedDatabase(const QString &c
|
||||||
|
|
||||||
QString installed_db_schema = query_db.value(0).toString();
|
QString installed_db_schema = query_db.value(0).toString();
|
||||||
|
|
||||||
if (!updateDatabaseSchema(installed_db_schema)) {
|
if (!updateDatabaseSchema(database, installed_db_schema)) {
|
||||||
qFatal("Database schema was not updated from '%s' to '%s' successully.",
|
qFatal("Database schema was not updated from '%s' to '%s' successully.",
|
||||||
qPrintable(installed_db_schema),
|
qPrintable(installed_db_schema),
|
||||||
qPrintable(APP_DB_SCHEMA_VERSION));
|
APP_DB_SCHEMA_VERSION);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
qDebug("Database schema was updated from '%s' to '%s' successully.",
|
qDebug("Database schema was updated from '%s' to '%s' successully.",
|
||||||
qPrintable(installed_db_schema),
|
qPrintable(installed_db_schema),
|
||||||
qPrintable(APP_DB_SCHEMA_VERSION));
|
APP_DB_SCHEMA_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug("File-based SQLite database connection '%s' to file '%s' seems to be established.",
|
qDebug("File-based SQLite database connection '%s' to file '%s' seems to be established.",
|
||||||
|
@ -319,20 +318,93 @@ QString DatabaseFactory::sqliteDatabaseFilePath() const {
|
||||||
return m_sqliteDatabaseFilePath + QDir::separator() + APP_DB_SQLITE_FILE;
|
return m_sqliteDatabaseFilePath + QDir::separator() + APP_DB_SQLITE_FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatabaseFactory::updateDatabaseSchema(const QString &source_db_schema_version) {
|
bool DatabaseFactory::updateDatabaseSchema(QSqlDatabase database, const QString &source_db_schema_version) {
|
||||||
switch (m_activeDatabaseDriver) {
|
switch (m_activeDatabaseDriver) {
|
||||||
case SQLITE:
|
case SQLITE:
|
||||||
case SQLITE_MEMORY:
|
case SQLITE_MEMORY:
|
||||||
break;
|
return sqliteUpdateDatabaseSchema(database, source_db_schema_version);
|
||||||
|
|
||||||
case MYSQL:
|
case MYSQL:
|
||||||
break;
|
return mysqlUpdateDatabaseSchema(database, source_db_schema_version);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseFactory::sqliteUpdateDatabaseSchema(QSqlDatabase database, const QString &source_db_schema_version) {
|
||||||
|
int working_version = QString(source_db_schema_version).remove('.').toInt();
|
||||||
|
int current_version = QString(APP_DB_SCHEMA_VERSION).remove('.').toInt();
|
||||||
|
|
||||||
|
while (working_version != current_version) {
|
||||||
|
QString update_file_name = QString(APP_MISC_PATH) + QDir::separator() +
|
||||||
|
QString(APP_DB_UPDATE_FILE_PATTERN).arg("sqlite",
|
||||||
|
QString::number(working_version),
|
||||||
|
QString::number(working_version + 1));
|
||||||
|
|
||||||
|
if (!QFile::exists(update_file_name)) {
|
||||||
|
qFatal("Updating of database schema failed. File '%s' does not exist.", qPrintable(QDir::toNativeSeparators(update_file_name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile update_file_handle(update_file_name);
|
||||||
|
|
||||||
|
if (!update_file_handle.open(QIODevice::Text | QIODevice::ReadOnly | QIODevice::Unbuffered)) {
|
||||||
|
qFatal("Updating of database schema failed. File '%s' cannot be opened.", qPrintable(QDir::toNativeSeparators(update_file_name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList statements = QString(update_file_handle.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts);
|
||||||
|
|
||||||
|
foreach (const QString &statement, statements) {
|
||||||
|
QSqlQuery query = database.exec(statement);
|
||||||
|
|
||||||
|
if (query.lastError().isValid()) {
|
||||||
|
qFatal("Query for updating database schema failed: '%s'.", qPrintable(query.lastError().text()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the version.
|
||||||
|
qDebug("Updating database schema: '%d' -> '%d'.", working_version, working_version + 1);
|
||||||
|
working_version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseFactory::mysqlUpdateDatabaseSchema(QSqlDatabase database, const QString &source_db_schema_version) {
|
||||||
|
int working_version = QString(source_db_schema_version).remove('.').toInt();
|
||||||
|
int current_version = QString(APP_DB_SCHEMA_VERSION).remove('.').toInt();
|
||||||
|
|
||||||
|
while (working_version != current_version) {
|
||||||
|
QString update_file_name = QString(APP_MISC_PATH) + QDir::separator() +
|
||||||
|
QString(APP_DB_UPDATE_FILE_PATTERN).arg("mysql",
|
||||||
|
QString::number(working_version),
|
||||||
|
QString::number(working_version + 1));
|
||||||
|
|
||||||
|
if (!QFile::exists(update_file_name)) {
|
||||||
|
qFatal("Updating of database schema failed. File '%s' does not exist.", qPrintable(QDir::toNativeSeparators(update_file_name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile update_file_handle(update_file_name);
|
||||||
|
|
||||||
|
if (!update_file_handle.open(QIODevice::Text | QIODevice::ReadOnly | QIODevice::Unbuffered)) {
|
||||||
|
qFatal("Updating of database schema failed. File '%s' cannot be opened.", qPrintable(QDir::toNativeSeparators(update_file_name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList statements = QString(update_file_handle.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts);
|
||||||
|
|
||||||
|
foreach (const QString &statement, statements) {
|
||||||
|
QSqlQuery query = database.exec(statement);
|
||||||
|
|
||||||
|
if (query.lastError().isValid()) {
|
||||||
|
qFatal("Query for updating database schema failed: '%s'.", qPrintable(query.lastError().text()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the version.
|
||||||
|
qDebug("Updating database schema: '%d' -> '%d'.", working_version, working_version + 1);
|
||||||
|
working_version++;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: tady udělat update databázového schématu na novou verzi.
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,6 @@ class DatabaseFactory : public QObject {
|
||||||
// SQLITE stuff.
|
// SQLITE stuff.
|
||||||
//
|
//
|
||||||
QString sqliteDatabaseFilePath() const;
|
QString sqliteDatabaseFilePath() const;
|
||||||
bool updateDatabaseSchema(const QString &source_db_schema_version);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// MySQL stuff.
|
// MySQL stuff.
|
||||||
|
@ -108,6 +107,8 @@ class DatabaseFactory : public QObject {
|
||||||
// application session.
|
// application session.
|
||||||
void determineDriver();
|
void determineDriver();
|
||||||
|
|
||||||
|
bool updateDatabaseSchema(QSqlDatabase database, const QString &source_db_schema_version);
|
||||||
|
|
||||||
// Holds the type of currently activated database backend.
|
// Holds the type of currently activated database backend.
|
||||||
UsedDriver m_activeDatabaseDriver;
|
UsedDriver m_activeDatabaseDriver;
|
||||||
|
|
||||||
|
@ -121,6 +122,9 @@ class DatabaseFactory : public QObject {
|
||||||
// Initializes MySQL database.
|
// Initializes MySQL database.
|
||||||
QSqlDatabase mysqlInitializeDatabase(const QString &connection_name);
|
QSqlDatabase mysqlInitializeDatabase(const QString &connection_name);
|
||||||
|
|
||||||
|
// Updates database schema.
|
||||||
|
bool mysqlUpdateDatabaseSchema(QSqlDatabase database, const QString &source_db_schema_version);
|
||||||
|
|
||||||
// Runs "VACUUM" on the database.
|
// Runs "VACUUM" on the database.
|
||||||
bool mysqlVacuumDatabase();
|
bool mysqlVacuumDatabase();
|
||||||
|
|
||||||
|
@ -144,6 +148,9 @@ class DatabaseFactory : public QObject {
|
||||||
// Assemblies database file path.
|
// Assemblies database file path.
|
||||||
void sqliteAssemblyDatabaseFilePath();
|
void sqliteAssemblyDatabaseFilePath();
|
||||||
|
|
||||||
|
// Updates database schema.
|
||||||
|
bool sqliteUpdateDatabaseSchema(QSqlDatabase database, const QString &source_db_schema_version);
|
||||||
|
|
||||||
// Creates new connection, initializes database and
|
// Creates new connection, initializes database and
|
||||||
// returns opened connections.
|
// returns opened connections.
|
||||||
QSqlDatabase sqliteInitializeInMemoryDatabase();
|
QSqlDatabase sqliteInitializeInMemoryDatabase();
|
||||||
|
|
Loading…
Reference in New Issue