diff --git a/Makefile b/Makefile index 18e903b4..4de4ebbd 100644 --- a/Makefile +++ b/Makefile @@ -191,6 +191,7 @@ SQLITE_SOURCES := deps/sqlite/sqlite3.c SQLITE_OBJS := $(call get_objs,SQLITE_SOURCES) $(SQLITE_OBJS): CFLAGS += \ -DSQLITE_DBCONFIG_DEFAULT_DEFENSIVE \ + -DSQLITE_ENABLE_FTS5 \ -DSQLITE_ENABLE_JSON1 \ -DSQLITE_MAX_LENGTH=5242880 \ -DSQLITE_MAX_SQL_LENGTH=100000 \ diff --git a/src/ssb.db.c b/src/ssb.db.c index 54479f12..c1d588b9 100644 --- a/src/ssb.db.c +++ b/src/ssb.db.c @@ -26,6 +26,32 @@ static void _tf_ssb_db_exec(sqlite3* db, const char* statement) } } +static bool _tf_ssb_db_has_rows(sqlite3* db, const char* query) +{ + bool found = false; + sqlite3_stmt* statement = NULL; + if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK) + { + int result = SQLITE_OK; + while ((result = sqlite3_step(statement)) == SQLITE_ROW) + { + found = true; + } + if (result != SQLITE_DONE) + { + printf("%s\n", sqlite3_errmsg(db)); + abort(); + } + sqlite3_finalize(statement); + } + else + { + printf("%s\n", sqlite3_errmsg(db)); + abort(); + } + return found; +} + void tf_ssb_db_init(tf_ssb_t* ssb) { sqlite3* db = tf_ssb_get_db(ssb); @@ -81,6 +107,18 @@ void tf_ssb_db_init(tf_ssb_t* ssb) ")"); _tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS identities_user ON identities (user, public_key)"); + if (!_tf_ssb_db_has_rows(db, "PRAGMA table_list('messages_fts')") || + sqlite3_exec(db, "INSERT INTO messages_fts(messages_fts, rank) VALUES ('integrity-check', 1)", NULL, NULL, NULL) == SQLITE_CORRUPT_VTAB) + { + _tf_ssb_db_exec(db, "CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(content, content=messages, content_rowid=rowid)"); + printf("Populating full-text search...\n"); + _tf_ssb_db_exec(db, "INSERT INTO messages_fts (rowid, content) SELECT rowid, content FROM messages"); + printf("Done.\n"); + } + + _tf_ssb_db_exec(db, "CREATE TRIGGER IF NOT EXISTS messages_ai AFTER INSERT ON messages BEGIN INSERT INTO messages_fts(rowid, content) VALUES (new.rowid, new.content); END"); + _tf_ssb_db_exec(db, "CREATE TRIGGER IF NOT EXISTS messages_ad AFTER DELETE ON messages BEGIN INSERT INTO messages_fts(messages_fts, rowid, content) VALUES ('delete', old.rowid, old.content); END"); + bool need_add_sequence_before_author = true; bool need_convert_timestamp_to_real = false; @@ -536,12 +574,14 @@ static int _tf_ssb_sqlite_authorizer(void* user_data, int action_code, const cha strcmp(arg0, "blob_wants") == 0 || strcmp(arg0, "json_each") == 0 || strcmp(arg0, "messages") == 0 || + strcmp(arg0, "messages_fts") == 0 || + strcmp(arg0, "messages_fts_idx") == 0 || strcmp(arg0, "sqlite_master") == 0 || false) ? SQLITE_OK : SQLITE_DENY; break; - case SQLITE_UPDATE: - return SQLITE_OK; + case SQLITE_PRAGMA: + return strcmp(arg0, "data_version") == 0 ? SQLITE_OK : SQLITE_DENY; } return SQLITE_DENY; }