diff --git a/ts/sql/migrations/1561-cleanup-polls.std.ts b/ts/sql/migrations/1561-cleanup-polls.std.ts new file mode 100644 index 0000000000..fe8aa5f625 --- /dev/null +++ b/ts/sql/migrations/1561-cleanup-polls.std.ts @@ -0,0 +1,25 @@ +// Copyright 2025 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import type { LoggerType } from '../../types/Logging.std.js'; +import type { WritableDB } from '../Interface.std.js'; +import { sql } from '../util.std.js'; + +// This migration was backported to earlier branches as 1541 and 1551 +export default function updateToSchemaVersion1561( + db: WritableDB, + logger: LoggerType +): void { + const [query, params] = sql` + UPDATE messages + SET + json = json_remove(json, '$.poll'), + hasUnreadPollVotes = 0 + WHERE isErased = 1 AND ( + json->'poll' IS NOT NULL OR + hasUnreadPollVotes IS NOT 0 + ) + `; + const result = db.prepare(query).run(params); + logger.info(`Updated ${result.changes} poll messages`); +} diff --git a/ts/sql/migrations/index.node.ts b/ts/sql/migrations/index.node.ts index 8d229da536..3b8120cca6 100644 --- a/ts/sql/migrations/index.node.ts +++ b/ts/sql/migrations/index.node.ts @@ -132,6 +132,7 @@ import updateToSchemaVersion1530 from './1530-update-expiring-index.std.js'; import updateToSchemaVersion1540 from './1540-partial-expiring-index.std.js'; import updateToSchemaVersion1550 from './1550-has-link-preview.std.js'; import updateToSchemaVersion1560 from './1560-pinned-messages.std.js'; +import updateToSchemaVersion1561 from './1561-cleanup-polls.std.js'; import { DataWriter } from '../Server.node.js'; @@ -1622,6 +1623,8 @@ export const SCHEMA_VERSIONS: ReadonlyArray = [ { version: 1540, update: updateToSchemaVersion1540 }, { version: 1550, update: updateToSchemaVersion1550 }, { version: 1560, update: updateToSchemaVersion1560 }, + // 1561, 1551, and 1541 all refer to the same migration + { version: 1561, update: updateToSchemaVersion1561 }, ]; export class DBVersionFromFutureError extends Error { diff --git a/ts/test-node/sql/migration_1561_test.node.ts b/ts/test-node/sql/migration_1561_test.node.ts new file mode 100644 index 0000000000..8661dc1ac4 --- /dev/null +++ b/ts/test-node/sql/migration_1561_test.node.ts @@ -0,0 +1,80 @@ +// Copyright 2025 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import { assert } from 'chai'; + +import { type WritableDB } from '../../sql/Interface.std.js'; +import { + createDB, + getTableData, + insertData, + updateToVersion, +} from './helpers.node.js'; + +describe('SQL/updateToSchemaVersion1561', () => { + let db: WritableDB; + + beforeEach(() => { + db = createDB(); + updateToVersion(db, 1560); + }); + afterEach(() => { + db.close(); + }); + + it('removes json.poll and hasUnreadPollVotes', () => { + insertData(db, 'messages', [ + { + id: 'message-id', + isErased: 0, + json: { + id: 'message-id', + poll: { + question: 'poll question', + }, + }, + hasUnreadPollVotes: 1, + }, + { + id: 'message-id-2', + isErased: 1, + json: { + id: 'message-id-2', + poll: { + question: 'poll question', + }, + }, + hasUnreadPollVotes: 1, + }, + ]); + updateToVersion(db, 1561); + assert.deepStrictEqual( + getTableData(db, 'messages').map(row => ({ + id: row.id, + json: row.json, + isErased: row.isErased, + hasUnreadPollVotes: row.hasUnreadPollVotes, + })), + [ + { + id: 'message-id', + isErased: 0, + json: { + id: 'message-id', + poll: { + question: 'poll question', + }, + }, + hasUnreadPollVotes: 1, + }, + { + id: 'message-id-2', + isErased: 1, + json: { + id: 'message-id-2', + }, + hasUnreadPollVotes: 0, + }, + ] + ); + }); +}); diff --git a/ts/util/cleanup.preload.ts b/ts/util/cleanup.preload.ts index b7e4b0da64..732c470615 100644 --- a/ts/util/cleanup.preload.ts +++ b/ts/util/cleanup.preload.ts @@ -65,8 +65,10 @@ export async function eraseMessageContents( bodyRanges: undefined, contact: [], editHistory: undefined, + hasUnreadPollVotes: undefined, isErased: true, preview: [], + poll: undefined, quote: undefined, sticker: undefined, ...additionalProperties,