UI: ConfManager: Add "app_fts" table for full-text search

This commit is contained in:
Nodir Temirkhodjaev 2023-08-14 13:13:33 +05:00
parent 787718a07e
commit db3a2b4aa4
6 changed files with 127 additions and 18 deletions

View File

@ -31,12 +31,12 @@
#define HAVE_MALLOC_USABLE_SIZE 1
#define HAVE_ISNAN 1
//#define SQLITE_ENABLE_FTS5 1
#define SQLITE_ENABLE_FTS5 1
//#define SQLITE_ENABLE_JSON1 1
#define SQLITE_ENABLE_MEMORY_MANAGEMENT 1
//#define SQLITE_ENABLE_NULL_TRIM 1
//#define SQLITE_ENABLE_PREUPDATE_HOOK 1
//#define SQLITE_ENABLE_SESSION 1
#define SQLITE_ENABLE_SESSION 1
#define SQLITE_ENABLE_STAT4 1
//#define SQLITE_ENABLE_UNLOCK_NOTIFY 1
@ -62,7 +62,7 @@
#define SQLITE_OMIT_AUTOINIT 1
#define SQLITE_OMIT_AUTOMATIC_INDEX 1
#define SQLITE_OMIT_AUTORESET 1
#define SQLITE_OMIT_BLOB_LITERAL 1
//#define SQLITE_OMIT_BLOB_LITERAL 1
#define SQLITE_OMIT_CAST 1
#define SQLITE_OMIT_CHECK 1
#define SQLITE_OMIT_COMPLETE 1
@ -71,7 +71,7 @@
#define SQLITE_OMIT_EXPLAIN 1
#define SQLITE_OMIT_FOREIGN_KEY 1
#define SQLITE_OMIT_GET_TABLE 1
#define SQLITE_OMIT_INCRBLOB 1
//#define SQLITE_OMIT_INCRBLOB 1
#define SQLITE_OMIT_INTEGRITY_CHECK 1
#define SQLITE_OMIT_JSON 1
#define SQLITE_OMIT_LOAD_EXTENSION 1

View File

@ -19,6 +19,8 @@ const char *const defaultSqlPragmas = "PRAGMA journal_mode = WAL;"
"PRAGMA synchronous = NORMAL;"
"PRAGMA encoding = 'UTF-8';";
const QString ftsTableSuffix = "_fts";
QAtomicInt g_sqliteInitCount;
bool removeDbFile(const QString &filePath)
@ -45,6 +47,12 @@ bool renameDbFile(const QString &filePath, const QString &newFilePath)
return true;
}
QString makeTriggerColumnNames(
const QString &rowIdName, const QStringList &columnNames, const QString &prefix)
{
return (prefix + rowIdName) + (',' + prefix) + columnNames.join(',' + prefix);
}
}
SqliteDb::SqliteDb(const QString &filePath, quint32 openFlags) :
@ -248,6 +256,11 @@ bool SqliteDb::setBusyTimeoutMs(int v)
return sqlite3_busy_timeout(m_db, v) == SQLITE_OK;
}
QString SqliteDb::getFtsTableName(const QString &tableName)
{
return tableName + ftsTableSuffix;
}
QString SqliteDb::migrationOldSchemaName()
{
return QLatin1String("old");
@ -269,8 +282,10 @@ QStringList SqliteDb::tableNames(const QString &schemaName)
const auto masterTable = entityName(schemaName, "sqlite_master");
const auto sql = QString("SELECT name FROM %1"
" WHERE type = 'table' AND name NOT LIKE 'sqlite_%';")
.arg(masterTable);
" WHERE type = 'table'"
" AND name NOT LIKE 'sqlite_%'"
" AND name NOT LIKE '%%2_%';")
.arg(masterTable, ftsTableSuffix);
SqliteStmt stmt;
if (stmt.prepare(db(), sql.toLatin1())) {
@ -353,14 +368,14 @@ bool SqliteDb::migrateDb(const MigrateOptions &opt, int userVersion, bool isNewD
}
// Run migration SQL scripts
bool success = migrateSqlScripts(opt, userVersion, isNewDb);
if (!migrateSqlScripts(opt, userVersion, isNewDb))
return false;
// Re-create the DB: End
if (success && opt.recreate) {
success = importBackup(opt);
}
if (opt.recreate && !(createFtsTables(opt) && importBackup(opt)))
return false;
return success;
return true;
}
bool SqliteDb::migrateSqlScripts(const MigrateOptions &opt, int userVersion, bool isNewDb)
@ -415,6 +430,74 @@ bool SqliteDb::migrateSqlScripts(const MigrateOptions &opt, int userVersion, boo
return success;
}
bool SqliteDb::createFtsTables(const MigrateOptions &opt)
{
if (opt.ftsTables.isEmpty())
return true;
bool success = true;
beginTransaction();
for (const FtsTable &ftsTable : opt.ftsTables) {
beginSavepoint();
success = createFtsTable(ftsTable);
if (success) {
releaseSavepoint();
} else {
qCCritical(LC) << "FTS error:" << ftsTable.contentTable << errorMessage();
rollbackSavepoint();
break;
}
}
commitTransaction();
return success;
}
bool SqliteDb::createFtsTable(const FtsTable &ftsTable)
{
/*
* %1: content table name
* %2: fts table name
* %3: content rowid column name
* %4: content column names list
* %5: triggered new column names list (new.*)
* %6: triggered old column names list (old.*)
*/
static const char *const ftsCreateSql =
"CREATE VIRTUAL TABLE %2 USING fts5(%4, content='%1', content_rowid='%3');"
"CREATE TRIGGER %2_ai AFTER INSERT ON %1 BEGIN"
" INSERT INTO %2(rowid, %4) VALUES (%5);"
"END;"
"CREATE TRIGGER %2_ad AFTER DELETE ON %1 BEGIN"
" INSERT INTO %2(%2, rowid, %4) VALUES('delete', %6);"
"END;"
"CREATE TRIGGER %2_au AFTER UPDATE ON %1 BEGIN"
" INSERT INTO %2(%2, rowid, %4) VALUES('delete', %6);"
" INSERT INTO %2(rowid, %4) VALUES (%5);"
"END;";
const auto contentTableName = ftsTable.contentTable;
const auto ftsTableName = getFtsTableName(contentTableName);
const auto contentRowidName = ftsTable.contentRowid;
const auto contentColumnNames = ftsTable.columns.join(',');
const auto newTriggerColumnNames =
makeTriggerColumnNames(contentRowidName, ftsTable.columns, "new.");
const auto oldTriggerColumnNames =
makeTriggerColumnNames(contentRowidName, ftsTable.columns, "old.");
const auto sql =
QString(ftsCreateSql)
.arg(contentTableName, ftsTableName, contentRowidName, contentColumnNames,
newTriggerColumnNames, oldTriggerColumnNames);
return executeStr(sql);
}
bool SqliteDb::clearWithBackup(const char *sqlPragmas)
{
const QString oldEncoding = this->encoding();

View File

@ -33,6 +33,13 @@ public:
OpenDefaultReadWrite = (OpenReadWrite | OpenCreate | OpenNoMutex)
};
struct FtsTable
{
const QString contentTable;
const QString contentRowid;
const QStringList columns;
};
struct MigrateOptions
{
const QString sqlDir;
@ -43,6 +50,7 @@ public:
bool autoCopyTables = true;
SQLITEDB_MIGRATE_FUNC migrateFunc = nullptr;
void *migrateContext = nullptr;
QVector<FtsTable> ftsTables;
};
explicit SqliteDb(
@ -99,6 +107,8 @@ public:
bool setBusyTimeoutMs(int v);
static QString getFtsTableName(const QString &tableName);
static QString migrationOldSchemaName();
static QString migrationNewSchemaName();
static QString entityName(const QString &schemaName, const QString &objectName);
@ -114,6 +124,9 @@ private:
bool migrateDb(const MigrateOptions &opt, int userVersion, bool isNewDb);
bool migrateSqlScripts(const MigrateOptions &opt, int userVersion, bool isNewDb);
bool createFtsTables(const MigrateOptions &opt);
bool createFtsTable(const FtsTable &ftsTable);
bool clearWithBackup(const char *sqlPragmas);
bool importBackup(const MigrateOptions &opt);

View File

@ -71,10 +71,12 @@ void AppInfoManager::setUp()
return;
}
SqliteDb::MigrateOptions opt = { .sqlDir = ":/appinfo/migrations",
SqliteDb::MigrateOptions opt = {
.sqlDir = ":/appinfo/migrations",
.version = DATABASE_USER_VERSION,
.recreate = true,
.importOldData = false };
.importOldData = false,
};
if (!sqliteDb()->migrate(opt)) {
qCCritical(LC) << "Migration error" << sqliteDb()->filePath();

View File

@ -37,7 +37,7 @@ namespace {
const QLoggingCategory LC("conf");
constexpr int DATABASE_USER_VERSION = 21;
constexpr int DATABASE_USER_VERSION = 22;
constexpr int APP_END_TIMER_INTERVAL_MIN = 100;
constexpr int APP_END_TIMER_INTERVAL_MAX = 24 * 60 * 60 * 1000; // 1 day
@ -536,10 +536,19 @@ void ConfManager::setUp()
return;
}
SqliteDb::MigrateOptions opt = { .sqlDir = ":/conf/migrations",
SqliteDb::MigrateOptions opt = {
.sqlDir = ":/conf/migrations",
.version = DATABASE_USER_VERSION,
.recreate = true,
.migrateFunc = &migrateFunc };
.migrateFunc = &migrateFunc,
.ftsTables = {
{
.contentTable = "app",
.contentRowid = "app_id",
.columns = { "path", "name" }
},
},
};
if (!sqliteDb()->migrate(opt)) {
qCCritical(LC) << "Migration error" << sqliteDb()->filePath();

View File

@ -81,10 +81,12 @@ void StatManager::setUp()
return;
}
SqliteDb::MigrateOptions opt = { .sqlDir = ":/stat/migrations/traf",
SqliteDb::MigrateOptions opt = {
.sqlDir = ":/stat/migrations/traf",
.version = DATABASE_USER_VERSION,
.recreate = true,
.migrateFunc = &migrateFunc };
.migrateFunc = &migrateFunc,
};
if (!sqliteDb()->migrate(opt)) {
qCCritical(LC) << "Migration error" << sqliteDb()->filePath();