Page MenuHomePhorge

D14974.1765072111.diff
No OneTemporary

Size
10 KB
Referenced Files
None
Subscribers
None

D14974.1765072111.diff

diff --git a/lib/reducers/db-ops-reducer.js b/lib/reducers/db-ops-reducer.js
--- a/lib/reducers/db-ops-reducer.js
+++ b/lib/reducers/db-ops-reducer.js
@@ -22,6 +22,9 @@
return store;
}
+// There is a similar code for creating a search index after restore.
+// Before making any changes here, please reference
+// `copyContentFromDatabase` in `SQLiteQueryExecutor.cpp`.
function getMessageSearchStoreOps(
messageStoreOps: ?$ReadOnlyArray<MessageStoreOperation>,
): $ReadOnlyArray<MessageSearchStoreOperation> {
diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp
--- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp
+++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp
@@ -1714,6 +1714,49 @@
sql << "INSERT OR IGNORE INTO " << tableName << " SELECT *"
<< " FROM sourceDB." << tableName << ";" << std::endl;
}
+
+ // There is a similar code for "live" index processing. Before making any
+ // changes here, please reference `getMessageSearchStoreOps` in
+ // `lib/reducers/db-ops-reducer.js`.
+ int textTypeInt = static_cast<int>(MessageType::TEXT);
+ int editTypeInt = static_cast<int>(MessageType::EDIT_MESSAGE);
+ int deleteTypeInt = static_cast<int>(MessageType::DELETE_MESSAGE);
+
+ // Populate message_search table for copied TEXT messages
+ sql << "INSERT OR IGNORE INTO message_search ("
+ << " original_message_id, message_id, processed_content) "
+ << "SELECT id, id, content "
+ << "FROM sourceDB.backup_messages "
+ << "WHERE type = " << textTypeInt << ";" << std::endl;
+
+ // Update message_search table for EDIT_MESSAGE entries
+ sql << "UPDATE message_search "
+ << "SET "
+ << " message_id = b.id,"
+ << " processed_content = IIF("
+ << " JSON_VALID(b.content),"
+ << " JSON_EXTRACT(b.content, '$.text'),"
+ << " NULL"
+ << " )"
+ << "FROM sourceDB.backup_messages AS b "
+ << "WHERE message_search.original_message_id = IIF("
+ << " JSON_VALID(b.content),"
+ << " JSON_EXTRACT(b.content, '$.targetMessageID'),"
+ << " NULL"
+ << ") "
+ << " AND b.type = " << editTypeInt << ";" << std::endl;
+
+ // Delete message_search entries for DELETE_MESSAGE
+ sql << "DELETE FROM message_search "
+ << "WHERE original_message_id IN ("
+ << " SELECT IIF("
+ << " JSON_VALID(content),"
+ << " JSON_EXTRACT(content, '$.targetMessageID'),"
+ << " NULL"
+ << " ) "
+ << " FROM sourceDB.backup_messages "
+ << " WHERE type = " << deleteTypeInt << ");" << std::endl;
+
sql << "DETACH DATABASE sourceDB;";
executeQuery(this->getConnection(), sql.str());
}
diff --git a/web/shared-worker/_generated/comm_query_executor.wasm b/web/shared-worker/_generated/comm_query_executor.wasm
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
literal 0
Hc$@<O00001
diff --git a/web/shared-worker/queries/multiple-databases.test.js b/web/shared-worker/queries/multiple-databases.test.js
--- a/web/shared-worker/queries/multiple-databases.test.js
+++ b/web/shared-worker/queries/multiple-databases.test.js
@@ -1,6 +1,10 @@
// @flow
+import { getProtocolByThreadID } from 'lib/shared/threads/protocols/thread-protocols.js';
+import { messageTypes } from 'lib/types/message-types-enum.js';
+
import { getDatabaseModule } from '../db-module.js';
+import type { WebMessage } from '../types/sqlite-query-executor.js';
import { clearSensitiveData } from '../utils/db-utils.js';
const MAIN_FILE_PATH = 'main.sqlite';
@@ -135,4 +139,243 @@
expect(notMigratedQueryExecutor.getDatabaseVersion()).toBe(0);
clearSensitiveData(dbModule, notMigratedFilePath, notMigratedQueryExecutor);
});
+
+ it('populates message_search table for TEXT messages during copy', () => {
+ const threadID = '40db5619-feb2-4e5f-bd0c-1f9a709d366e';
+ const messageID = 'test-message-id';
+ const messageContent = 'Hello world test message';
+
+ const message: WebMessage = {
+ id: messageID,
+ localID: null,
+ thread: threadID,
+ user: '111',
+ type: messageTypes.TEXT,
+ futureType: null,
+ content: messageContent,
+ time: BigInt(123),
+ };
+
+ // Add message to the backup database
+ backupQueryExecutor.replaceMessage(
+ message,
+ !!getProtocolByThreadID(threadID)?.dataIsBackedUp,
+ );
+
+ // Verify message is not searchable in main database before copy
+ let searchResults = mainQueryExecutor.searchMessages(
+ 'Hello',
+ threadID,
+ null,
+ null,
+ );
+ expect(searchResults.length).toBe(0);
+
+ // Copy content from backup to main
+ mainQueryExecutor.copyContentFromDatabase(BACKUP_FILE_PATH, null);
+
+ // Verify message is now searchable in main database
+ searchResults = mainQueryExecutor.searchMessages(
+ 'Hello',
+ threadID,
+ null,
+ null,
+ );
+ expect(searchResults.length).toBe(1);
+ expect(searchResults[0].message.id).toBe(messageID);
+ expect(searchResults[0].message.content).toBe(messageContent);
+ });
+
+ it('populates message_search table for EDIT_MESSAGE messages during copy', () => {
+ const threadID = '40db5619-feb2-4e5f-bd0c-1f9a709d366e';
+ const originalMessageID = 'original-message-id';
+ const editMessageID = 'edit-message-id';
+ const editContent = JSON.stringify({
+ targetMessageID: originalMessageID,
+ text: 'edited text',
+ });
+
+ const originalMessage: WebMessage = {
+ id: originalMessageID,
+ localID: null,
+ thread: threadID,
+ user: '111',
+ type: messageTypes.TEXT,
+ futureType: null,
+ content: 'Original content',
+ time: BigInt(100),
+ };
+
+ const editMessage: WebMessage = {
+ id: editMessageID,
+ localID: null,
+ thread: threadID,
+ user: '111',
+ type: messageTypes.EDIT_MESSAGE,
+ futureType: null,
+ content: editContent,
+ time: BigInt(200),
+ };
+
+ // Add messages to backup database
+ backupQueryExecutor.replaceMessage(
+ originalMessage,
+ !!getProtocolByThreadID(threadID)?.dataIsBackedUp,
+ );
+ backupQueryExecutor.replaceMessage(
+ editMessage,
+ !!getProtocolByThreadID(threadID)?.dataIsBackedUp,
+ );
+
+ // Copy content from backup to main
+ mainQueryExecutor.copyContentFromDatabase(BACKUP_FILE_PATH, null);
+
+ // Verify edit message is searchable
+ // Should find the edit and original message content
+ const searchResults = mainQueryExecutor.searchMessages(
+ 'edited',
+ threadID,
+ null,
+ null,
+ );
+ expect(searchResults.length).toBe(2);
+ expect(searchResults[0].message.id).toBe(originalMessageID);
+ expect(searchResults[1].message.id).toBe(editMessageID);
+ });
+
+ it('removes DELETE_MESSAGE entries from existing message_search during copy', () => {
+ const threadID = '40db5619-feb2-4e5f-bd0c-1f9a709d366e';
+ const messageID = 'message-to-delete';
+
+ // First, add a TEXT message that gets indexed
+ const textMessage: WebMessage = {
+ id: messageID,
+ localID: null,
+ thread: threadID,
+ user: '111',
+ type: messageTypes.TEXT,
+ futureType: null,
+ content: 'This message will be deleted',
+ time: BigInt(100),
+ };
+
+ mainQueryExecutor.replaceMessage(
+ textMessage,
+ !!getProtocolByThreadID(threadID)?.dataIsBackedUp,
+ );
+ mainQueryExecutor.updateMessageSearchIndex(
+ messageID,
+ messageID,
+ 'This message will be deleted',
+ );
+
+ // Verify message is searchable
+ let searchResults = mainQueryExecutor.searchMessages(
+ 'deleted',
+ threadID,
+ null,
+ null,
+ );
+ expect(searchResults.length).toBe(1);
+
+ const deleteMessage: WebMessage = {
+ id: 'delete-message-id',
+ localID: null,
+ thread: threadID,
+ user: '111',
+ type: messageTypes.DELETE_MESSAGE,
+ futureType: null,
+ content: JSON.stringify({ targetMessageID: messageID }),
+ time: BigInt(300),
+ };
+
+ backupQueryExecutor.replaceMessage(
+ deleteMessage,
+ !!getProtocolByThreadID(threadID)?.dataIsBackedUp,
+ );
+
+ // Copy content from backup to main
+ // This should remove the search index entry
+ mainQueryExecutor.copyContentFromDatabase(BACKUP_FILE_PATH, null);
+
+ // Verify message is no longer searchable
+ searchResults = mainQueryExecutor.searchMessages(
+ 'deleted',
+ threadID,
+ null,
+ null,
+ );
+ expect(searchResults.length).toBe(0);
+ });
+
+ it('handles EDIT_MESSAGE and DELETE_MESSAGE with invalid JSON gracefully', () => {
+ const threadID = '40db5619-feb2-4e5f-bd0c-1f9a709d366e';
+ const originalMessageID = 'original-message-id';
+
+ // Add original TEXT message that gets indexed
+ const originalMessage: WebMessage = {
+ id: originalMessageID,
+ localID: null,
+ thread: threadID,
+ user: '111',
+ type: messageTypes.TEXT,
+ futureType: null,
+ content: 'Original message to edit',
+ time: BigInt(100),
+ };
+
+ backupQueryExecutor.replaceMessage(
+ originalMessage,
+ !!getProtocolByThreadID(threadID)?.dataIsBackedUp,
+ );
+
+ // Add EDIT_MESSAGE with invalid JSON
+ const invalidEditMessage: WebMessage = {
+ id: 'invalid-edit-id',
+ localID: null,
+ thread: threadID,
+ user: '111',
+ type: messageTypes.EDIT_MESSAGE,
+ futureType: null,
+ content: 'invalid json { broken',
+ time: BigInt(200),
+ };
+
+ // Add DELETE_MESSAGE with invalid JSON
+ const invalidDeleteMessage: WebMessage = {
+ id: 'invalid-delete-id',
+ localID: null,
+ thread: threadID,
+ user: '111',
+ type: messageTypes.DELETE_MESSAGE,
+ futureType: null,
+ content: 'also invalid json } broken',
+ time: BigInt(300),
+ };
+
+ backupQueryExecutor.replaceMessage(
+ invalidEditMessage,
+ !!getProtocolByThreadID(threadID)?.dataIsBackedUp,
+ );
+ backupQueryExecutor.replaceMessage(
+ invalidDeleteMessage,
+ !!getProtocolByThreadID(threadID)?.dataIsBackedUp,
+ );
+
+ // Copy content - should not fail despite invalid
+ // JSON in EDIT/DELETE messages
+ expect(() => {
+ mainQueryExecutor.copyContentFromDatabase(BACKUP_FILE_PATH, null);
+ }).not.toThrow();
+
+ // Original message should still be searchable (invalid EDIT/DELETE ignored)
+ const searchResults = mainQueryExecutor.searchMessages(
+ 'Original',
+ threadID,
+ null,
+ null,
+ );
+ expect(searchResults.length).toBe(1);
+ expect(searchResults[0].message.id).toBe(originalMessageID);
+ });
});

File Metadata

Mime Type
text/plain
Expires
Sun, Dec 7, 1:48 AM (10 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5842027
Default Alt Text
D14974.1765072111.diff (10 KB)

Event Timeline