Work on bug/enhancement #95: rolling DB schema update.

This commit is contained in:
Martin Rotter 2014-10-27 16:27:19 +01:00
parent 504950f631
commit a1e415c518
15 changed files with 110 additions and 31 deletions

View File

@ -68,7 +68,7 @@ CREATE TABLE IF NOT EXISTS Messages (
author TEXT NOT NULL,
date_created BIGINT NOT NULL CHECK (date_created != 0),
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)
);

View File

@ -62,7 +62,7 @@ CREATE TABLE IF NOT EXISTS Messages (
author TEXT NOT NULL,
date_created INTEGER NOT NULL CHECK (date_created != 0),
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)
);

View File

@ -62,7 +62,7 @@ CREATE TABLE IF NOT EXISTS Messages (
author TEXT NOT NULL,
date_created INTEGER NOT NULL CHECK (date_created != 0),
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)
);

View File

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

View File

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

View File

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

View File

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

View File

@ -4,6 +4,7 @@
Fixed:
<ul>
<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>
Added:

View File

@ -120,7 +120,7 @@ bool FeedsModelRecycleBin::empty() {
QSqlQuery query_empty_bin(db_handle);
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.");
db_handle.rollback();
@ -148,7 +148,7 @@ bool FeedsModelRecycleBin::restore() {
QSqlQuery query_empty_bin(db_handle);
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.");
db_handle.rollback();
@ -170,7 +170,7 @@ void FeedsModelRecycleBin::updateCounts(bool update_total_count) {
QSqlQuery query_all(database);
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();
}
else {
@ -178,7 +178,7 @@ void FeedsModelRecycleBin::updateCounts(bool 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();
}
else {

View File

@ -72,7 +72,7 @@ void MessagesModel::loadMessages(const QList<int> feed_ids) {
if (feed_ids.size() == 1 && feed_ids[0] == ID_RECYCLE_BIN) {
m_messageMode = MessagesFromRecycleBin;
setFilter("is_deleted = 1");
setFilter("is_deleted = 1 AND is_pdeleted = 0");
}
else {
m_messageMode = MessagesFromFeeds;
@ -389,7 +389,8 @@ bool MessagesModel::setBatchMessagesDeleted(const QModelIndexList &messages, int
QString::number(deleted));
}
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)) {

View File

@ -92,8 +92,8 @@
#define APP_DB_SQLITE_FILE "database.db"
// Keep this in sync with schema versions declared in SQL initialization code.
#define APP_DB_SCHEMA_VERSION "0.0.2"
#define APP_DB_UPDATE_FILE_PATTERN "data_update_%1_%2_3.sql"
#define APP_DB_SCHEMA_VERSION "2"
#define APP_DB_UPDATE_FILE_PATTERN "db_update_%1_%2_%3.sql"
#define APP_DB_COMMENT_SPLIT "-- !\n"
#define APP_DB_WEB_PATH "data/database/web"

View File

@ -77,11 +77,6 @@ void FormCategoryDetails::setEditableCategory(FeedsModelCategory *editable_categ
}
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.
loadCategories(m_feedsModel->allCategories().values(), m_feedsModel->rootItem(), input_category);

View File

@ -223,7 +223,6 @@ void FormSettings::saveFeedsMessages() {
settings->setValue(APP_CFG_MESSAGES, "custom_date_format",
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()->sourceModel()->reloadWholeLayout();
qApp->mainForm()->tabWidget()->feedMessageViewer()->messagesView()->sourceModel()->updateDateFormat();

View File

@ -233,8 +233,7 @@ QSqlDatabase DatabaseFactory::sqliteInitializeFileBasedDatabase(const QString &c
// Folders are created. Create new QSQLDatabase object.
QSqlDatabase database;
database = QSqlDatabase::addDatabase(APP_DB_SQLITE_DRIVER,
connection_name);
database = QSqlDatabase::addDatabase(APP_DB_SQLITE_DRIVER, connection_name);
database.setDatabaseName(db_file.fileName());
if (!database.open()) {
@ -289,15 +288,15 @@ QSqlDatabase DatabaseFactory::sqliteInitializeFileBasedDatabase(const QString &c
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.",
qPrintable(installed_db_schema),
qPrintable(APP_DB_SCHEMA_VERSION));
APP_DB_SCHEMA_VERSION);
}
else {
qDebug("Database schema was updated from '%s' to '%s' successully.",
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.",
@ -319,20 +318,93 @@ QString DatabaseFactory::sqliteDatabaseFilePath() const {
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) {
case SQLITE:
case SQLITE_MEMORY:
break;
return sqliteUpdateDatabaseSchema(database, source_db_schema_version);
case MYSQL:
break;
return mysqlUpdateDatabaseSchema(database, source_db_schema_version);
default:
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;
}

View File

@ -86,7 +86,6 @@ class DatabaseFactory : public QObject {
// SQLITE stuff.
//
QString sqliteDatabaseFilePath() const;
bool updateDatabaseSchema(const QString &source_db_schema_version);
//
// MySQL stuff.
@ -108,6 +107,8 @@ class DatabaseFactory : public QObject {
// application session.
void determineDriver();
bool updateDatabaseSchema(QSqlDatabase database, const QString &source_db_schema_version);
// Holds the type of currently activated database backend.
UsedDriver m_activeDatabaseDriver;
@ -121,6 +122,9 @@ class DatabaseFactory : public QObject {
// Initializes MySQL database.
QSqlDatabase mysqlInitializeDatabase(const QString &connection_name);
// Updates database schema.
bool mysqlUpdateDatabaseSchema(QSqlDatabase database, const QString &source_db_schema_version);
// Runs "VACUUM" on the database.
bool mysqlVacuumDatabase();
@ -144,6 +148,9 @@ class DatabaseFactory : public QObject {
// Assemblies database file path.
void sqliteAssemblyDatabaseFilePath();
// Updates database schema.
bool sqliteUpdateDatabaseSchema(QSqlDatabase database, const QString &source_db_schema_version);
// Creates new connection, initializes database and
// returns opened connections.
QSqlDatabase sqliteInitializeInMemoryDatabase();