Skip to content

Commit

Permalink
Merge branch 'v1.17-dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
anthonybilinski committed Nov 26, 2019
2 parents 7475ba4 + 0506477 commit e9570ea
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 24 deletions.
6 changes: 5 additions & 1 deletion src/chatlog/chatlog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,10 @@ void ChatLog::checkVisibility(bool causedWheelEvent)
auto upperBound = std::lower_bound(lowerBound, lines.cend(), getVisibleRect().bottom(),
ChatLine::lessThanBSRectTop);

const ChatLine::Ptr lastLineBeforeVisible = lowerBound == lines.cbegin()
? ChatLine::Ptr()
: *std::prev(lowerBound);

// set visibilty
QList<ChatLine::Ptr> newVisibleLines;
for (auto itr = lowerBound; itr != upperBound; ++itr) {
Expand All @@ -807,7 +811,7 @@ void ChatLog::checkVisibility(bool causedWheelEvent)
// visibleLines.last()->getRow() << " total " << visibleLines.size();

if (!visibleLines.isEmpty()) {
emit firstVisibleLineChanged(visibleLines.at(0));
emit firstVisibleLineChanged(lastLineBeforeVisible, visibleLines.at(0));
}

if (causedWheelEvent) {
Expand Down
2 changes: 1 addition & 1 deletion src/chatlog/chatlog.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class ChatLog : public QGraphicsView
signals:
void selectionChanged();
void workerTimeoutFinished();
void firstVisibleLineChanged(const ChatLine::Ptr&);
void firstVisibleLineChanged(const ChatLine::Ptr& prevLine, const ChatLine::Ptr& firstLine);
void loadHistoryLower();
void loadHistoryUpper();

Expand Down
24 changes: 23 additions & 1 deletion src/persistence/history.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
#include "db/rawdatabase.h"

namespace {
static constexpr int SCHEMA_VERSION = 3;
static constexpr int SCHEMA_VERSION = 4;

bool createCurrentSchema(RawDatabase& db)
{
Expand Down Expand Up @@ -63,6 +63,9 @@ bool createCurrentSchema(RawDatabase& db)
"file_state INTEGER NOT NULL);"
"CREATE TABLE faux_offline_pending (id INTEGER PRIMARY KEY);"
"CREATE TABLE broken_messages (id INTEGER PRIMARY KEY);"));
// sqlite doesn't support including the index as part of the CREATE TABLE statement, so add a second query
queries += RawDatabase::Query(
"CREATE INDEX chat_id_idx on history (chat_id);");
queries += RawDatabase::Query(QStringLiteral("PRAGMA user_version = %1;").arg(SCHEMA_VERSION));
return db.execNow(queries);
}
Expand Down Expand Up @@ -178,6 +181,18 @@ bool dbSchema2to3(RawDatabase& db)
return db.execNow(upgradeQueries);
}

bool dbSchema3to4(RawDatabase& db)
{
QVector<RawDatabase::Query> upgradeQueries;
upgradeQueries += RawDatabase::Query{QString(
"CREATE INDEX chat_id_idx on history (chat_id);")};

upgradeQueries += RawDatabase::Query(QStringLiteral("PRAGMA user_version = 4;"));

return db.execNow(upgradeQueries);
}


/**
* @brief Upgrade the db schema
* @note On future alterations of the database all you have to do is bump the SCHEMA_VERSION
Expand Down Expand Up @@ -252,6 +267,13 @@ void dbSchemaUpgrade(std::shared_ptr<RawDatabase>& db)
return;
}
qDebug() << "Database upgraded incrementally to schema version 3";
case 3:
if (!dbSchema3to4(*db)) {
qCritical() << "Failed to upgrade db to schema version 4, aborting";
db.reset();
return;
}
qDebug() << "Database upgraded incrementally to schema version 4";
// etc.
default:
qInfo() << "Database upgrade finished (databaseSchemaVersion" << databaseSchemaVersion
Expand Down
4 changes: 2 additions & 2 deletions src/widget/chatformheader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,14 @@ const QString VIDEO_TOOL_TIP[] = {

const QString VOL_TOOL_TIP[] = {
ChatFormHeader::tr("Sound can be disabled only during a call"),
ChatFormHeader::tr("Unmute call"),
ChatFormHeader::tr("Mute call"),
ChatFormHeader::tr("Unmute call"),
};

const QString MIC_TOOL_TIP[] = {
ChatFormHeader::tr("Microphone can be muted only during a call"),
ChatFormHeader::tr("Unmute microphone"),
ChatFormHeader::tr("Mute microphone"),
ChatFormHeader::tr("Unmute microphone"),
};

template <class T, class Fun>
Expand Down
11 changes: 8 additions & 3 deletions src/widget/form/genericchatform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ QDateTime GenericChatForm::getTime(const ChatLine::Ptr &chatLine) const
if (timestamp) {
return timestamp->getTime();
} else {
return QDateTime::currentDateTime();
return QDateTime();
}
}

Expand Down Expand Up @@ -1190,9 +1190,14 @@ void GenericChatForm::loadHistoryUpper()
}
}

void GenericChatForm::updateShowDateInfo(const ChatLine::Ptr& line)
void GenericChatForm::updateShowDateInfo(const ChatLine::Ptr& prevLine, const ChatLine::Ptr& topLine)
{
const auto date = getTime(line);
// If the dateInfo is visible we need to pretend the top line is the one
// covered by the date to prevent oscillations
const auto effectiveTopLine = (dateInfo->isVisible() && prevLine)
? prevLine : topLine;

const auto date = getTime(effectiveTopLine);

if (date.isValid() && date.date() != QDate::currentDate()) {
const auto dateText = QStringLiteral("<b>%1<\b>").arg(date.toString(Settings::getInstance().getDateFormat()));
Expand Down
2 changes: 1 addition & 1 deletion src/widget/form/genericchatform.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ protected slots:
void onExportChat();
void searchFormShow();
void onSearchTriggered();
void updateShowDateInfo(const ChatLine::Ptr& line);
void updateShowDateInfo(const ChatLine::Ptr& prevLine, const ChatLine::Ptr& topLine);

void searchInBegin(const QString& phrase, const ParameterSearch& parameter);
void onSearchUp(const QString& phrase, const ParameterSearch& parameter);
Expand Down
69 changes: 54 additions & 15 deletions test/persistence/dbschema_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,20 @@
#include <QtTest/QtTest>
#include <QString>

#include <algorithm>
#include <memory>

struct SqliteMasterEntry {
QString name;
QString sql;
};

bool operator==(const SqliteMasterEntry& lhs, const SqliteMasterEntry& rhs)
{
return lhs.name == rhs.name &&
lhs.sql == rhs.sql;
}

class TestDbSchema : public QObject
{
Q_OBJECT
Expand All @@ -37,11 +49,12 @@ private slots:
void test0to1();
void test1to2();
void test2to3();
void test3to4();
void cleanupTestCase();
private:
bool initSucess{false};
void createSchemaAtVersion(std::shared_ptr<RawDatabase>, const QMap<QString, QString>& schema);
void verifyDb(std::shared_ptr<RawDatabase> db, const QMap<QString, QString>& expectedSql);
void createSchemaAtVersion(std::shared_ptr<RawDatabase>, const std::vector<SqliteMasterEntry>& schema);
void verifyDb(std::shared_ptr<RawDatabase> db, const std::vector<SqliteMasterEntry>& expectedSql);
};

const QString testFileList[] = {
Expand All @@ -50,18 +63,21 @@ const QString testFileList[] = {
"testIsNewDbFalse.db",
"test0to1.db",
"test1to2.db",
"test2to3.db"
"test2to3.db",
"test3to4.db"
};

const QMap<QString, QString> schema0 {
// db schemas can be select with "SELECT name, sql FROM sqlite_master;" on the database.

const std::vector<SqliteMasterEntry> schema0 {
{"aliases", "CREATE TABLE aliases (id INTEGER PRIMARY KEY, owner INTEGER, display_name BLOB NOT NULL, UNIQUE(owner, display_name))"},
{"faux_offline_pending", "CREATE TABLE faux_offline_pending (id INTEGER PRIMARY KEY)"},
{"history", "CREATE TABLE history (id INTEGER PRIMARY KEY, timestamp INTEGER NOT NULL, chat_id INTEGER NOT NULL, sender_alias INTEGER NOT NULL, message BLOB NOT NULL)"},
{"peers", "CREATE TABLE peers (id INTEGER PRIMARY KEY, public_key TEXT NOT NULL UNIQUE)"}
};

// added file transfer history
const QMap<QString, QString> schema1 {
const std::vector<SqliteMasterEntry> schema1 {
{"aliases", "CREATE TABLE aliases (id INTEGER PRIMARY KEY, owner INTEGER, display_name BLOB NOT NULL, UNIQUE(owner, display_name))"},
{"faux_offline_pending", "CREATE TABLE faux_offline_pending (id INTEGER PRIMARY KEY)"},
{"file_transfers", "CREATE TABLE file_transfers (id INTEGER PRIMARY KEY, chat_id INTEGER NOT NULL, file_restart_id BLOB NOT NULL, file_name BLOB NOT NULL, file_path BLOB NOT NULL, file_hash BLOB NOT NULL, file_size INTEGER NOT NULL, direction INTEGER NOT NULL, file_state INTEGER NOT NULL)"},
Expand All @@ -70,7 +86,7 @@ const QMap<QString, QString> schema1 {
};

// move stuck faux offline messages do a table of "broken" messages
const QMap<QString, QString> schema2 {
const std::vector<SqliteMasterEntry> schema2 {
{"aliases", "CREATE TABLE aliases (id INTEGER PRIMARY KEY, owner INTEGER, display_name BLOB NOT NULL, UNIQUE(owner, display_name))"},
{"faux_offline_pending", "CREATE TABLE faux_offline_pending (id INTEGER PRIMARY KEY)"},
{"file_transfers", "CREATE TABLE file_transfers (id INTEGER PRIMARY KEY, chat_id INTEGER NOT NULL, file_restart_id BLOB NOT NULL, file_name BLOB NOT NULL, file_path BLOB NOT NULL, file_hash BLOB NOT NULL, file_size INTEGER NOT NULL, direction INTEGER NOT NULL, file_state INTEGER NOT NULL)"},
Expand All @@ -82,6 +98,17 @@ const QMap<QString, QString> schema2 {
// move stuck 0-length action messages to the existing "broken_messages" table. Not a real schema upgrade.
const auto schema3 = schema2;

// create index in history table on chat_id to improve query speed. Not a real schema upgrade.
const std::vector<SqliteMasterEntry> schema4 {
{"aliases", "CREATE TABLE aliases (id INTEGER PRIMARY KEY, owner INTEGER, display_name BLOB NOT NULL, UNIQUE(owner, display_name))"},
{"faux_offline_pending", "CREATE TABLE faux_offline_pending (id INTEGER PRIMARY KEY)"},
{"file_transfers", "CREATE TABLE file_transfers (id INTEGER PRIMARY KEY, chat_id INTEGER NOT NULL, file_restart_id BLOB NOT NULL, file_name BLOB NOT NULL, file_path BLOB NOT NULL, file_hash BLOB NOT NULL, file_size INTEGER NOT NULL, direction INTEGER NOT NULL, file_state INTEGER NOT NULL)"},
{"history", "CREATE TABLE history (id INTEGER PRIMARY KEY, timestamp INTEGER NOT NULL, chat_id INTEGER NOT NULL, sender_alias INTEGER NOT NULL, message BLOB NOT NULL, file_id INTEGER)"},
{"peers", "CREATE TABLE peers (id INTEGER PRIMARY KEY, public_key TEXT NOT NULL UNIQUE)"},
{"broken_messages", "CREATE TABLE broken_messages (id INTEGER PRIMARY KEY)"},
{"chat_id_idx", "CREATE INDEX chat_id_idx on history (chat_id)"}
};

void TestDbSchema::initTestCase()
{
for (const auto& path : testFileList) {
Expand All @@ -101,28 +128,32 @@ void TestDbSchema::cleanupTestCase()
}
}

void TestDbSchema::verifyDb(std::shared_ptr<RawDatabase> db, const QMap<QString, QString>& expectedSql)
void TestDbSchema::verifyDb(std::shared_ptr<RawDatabase> db, const std::vector<SqliteMasterEntry>& expectedSql)
{
QVERIFY(db->execNow(RawDatabase::Query(QStringLiteral(
"SELECT name, sql FROM sqlite_master "
"WHERE type='table';"),
"SELECT name, sql FROM sqlite_master;"),
[&](const QVector<QVariant>& row) {
const QString tableName = row[0].toString();
if (row[1].isNull()) {
// implicit indexes are automatically created for primary key constraints and unique constraints
// so their existence is already covered by the table creation SQL
return;
}
QString tableSql = row[1].toString();
QVERIFY(expectedSql.contains(tableName));
// table and column names can be quoted. UPDATE TEABLE automatically quotes the new names, but this
// has no functional impact on the schema. Strip quotes for comparison so that our created schema
// matches schema made from UPDATE TABLEs.
const QString unquotedTableSql = tableSql.remove("\"");
QVERIFY(expectedSql.value(tableName) == unquotedTableSql);
SqliteMasterEntry entry{tableName, unquotedTableSql};
QVERIFY(std::find(expectedSql.begin(), expectedSql.end(), entry) != expectedSql.end());
})));
}

void TestDbSchema::createSchemaAtVersion(std::shared_ptr<RawDatabase> db, const QMap<QString, QString>& schema)
void TestDbSchema::createSchemaAtVersion(std::shared_ptr<RawDatabase> db, const std::vector<SqliteMasterEntry>& schema)
{
QVector<RawDatabase::Query> queries;
for (auto const& tableCreation : schema.values()) {
queries += tableCreation;
for (auto const& entry : schema) {
queries += entry.sql;
}
QVERIFY(db->execNow(queries));
}
Expand All @@ -132,7 +163,7 @@ void TestDbSchema::testCreation()
QVector<RawDatabase::Query> queries;
auto db = std::shared_ptr<RawDatabase>{new RawDatabase{"testCreation.db", {}, {}}};
QVERIFY(createCurrentSchema(*db));
verifyDb(db, schema3);
verifyDb(db, schema4);
}

void TestDbSchema::testIsNewDb()
Expand Down Expand Up @@ -314,5 +345,13 @@ void TestDbSchema::test2to3()
verifyDb(db, schema3);
}

void TestDbSchema::test3to4()
{
auto db = std::shared_ptr<RawDatabase>{new RawDatabase{"test3to4.db", {}, {}}};
createSchemaAtVersion(db, schema3);
QVERIFY(dbSchema3to4(*db));
verifyDb(db, schema4);
}

QTEST_GUILESS_MAIN(TestDbSchema)
#include "dbschema_test.moc"

0 comments on commit e9570ea

Please sign in to comment.