mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-04-26 19:34:04 +01:00
Use @signalapp/sqlcipher
This commit is contained in:
@@ -2,9 +2,10 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { noop } from 'lodash';
|
||||
import SQL from '@signalapp/better-sqlite3';
|
||||
import SQL from '@signalapp/sqlcipher';
|
||||
|
||||
import type { ReadableDB, WritableDB } from '../../sql/Interface';
|
||||
import type { QueryTemplate } from '../../sql/util';
|
||||
import { SCHEMA_VERSIONS } from '../../sql/migrations';
|
||||
import { consoleLogger } from '../../util/consoleLogger';
|
||||
|
||||
@@ -13,7 +14,7 @@ export function createDB(): WritableDB {
|
||||
}
|
||||
|
||||
export function updateToVersion(db: WritableDB, version: number): void {
|
||||
const startVersion = db.pragma('user_version', { simple: true });
|
||||
const startVersion = db.pragma('user_version', { simple: true }) as number;
|
||||
|
||||
const silentLogger = {
|
||||
...consoleLogger,
|
||||
@@ -68,7 +69,7 @@ export function getTableData(db: ReadableDB, table: string): TableRows {
|
||||
return db
|
||||
.prepare(`SELECT * FROM ${table}`)
|
||||
.all()
|
||||
.map((row: Record<string, string | number | Buffer | null>) => {
|
||||
.map(row => {
|
||||
const result: Record<
|
||||
string,
|
||||
string | number | null | Record<string, unknown>
|
||||
@@ -77,8 +78,8 @@ export function getTableData(db: ReadableDB, table: string): TableRows {
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
if (Buffer.isBuffer(value)) {
|
||||
result[key] = value.toString('hex');
|
||||
if (value instanceof Uint8Array) {
|
||||
result[key] = Buffer.from(value).toString('hex');
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
@@ -93,3 +94,14 @@ export function getTableData(db: ReadableDB, table: string): TableRows {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
export function explain(db: ReadableDB, template: QueryTemplate): string {
|
||||
const [query, params] = template;
|
||||
const details = db
|
||||
.prepare(`EXPLAIN QUERY PLAN ${query}`)
|
||||
.all<{ detail: string }>(params)
|
||||
.map(({ detail }) => detail)
|
||||
.join('\n');
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@ describe('SQL/updateToSchemaVersion1000', () => {
|
||||
${message.id},
|
||||
${message.conversationId},
|
||||
${message.type},
|
||||
${message.readStatus},
|
||||
${message.seenStatus},
|
||||
${message.readStatus ?? null},
|
||||
${message.seenStatus ?? null},
|
||||
${json}
|
||||
)
|
||||
`;
|
||||
@@ -95,7 +95,7 @@ describe('SQL/updateToSchemaVersion1000', () => {
|
||||
`;
|
||||
return db
|
||||
.prepare(query)
|
||||
.all()
|
||||
.all<{ json: string; readStatus: ReadStatus; seenStatus: SeenStatus }>()
|
||||
.map(row => {
|
||||
return {
|
||||
message: jsonToObject<MessageType>(row.json),
|
||||
|
||||
@@ -5,20 +5,22 @@ import { assert } from 'chai';
|
||||
|
||||
import type { ReadableDB, WritableDB } from '../../sql/Interface';
|
||||
import { jsonToObject, objectToJSON, sql, sqlJoin } from '../../sql/util';
|
||||
import { createDB, updateToVersion } from './helpers';
|
||||
import { createDB, updateToVersion, explain } from './helpers';
|
||||
import type { LegacyAttachmentDownloadJobType } from '../../sql/migrations/1040-undownloaded-backed-up-media';
|
||||
import type { AttachmentType } from '../../types/Attachment';
|
||||
import type { AttachmentDownloadJobType } from '../../types/AttachmentDownload';
|
||||
import { IMAGE_JPEG } from '../../types/MIME';
|
||||
|
||||
function getAttachmentDownloadJobs(db: ReadableDB) {
|
||||
function getAttachmentDownloadJobs(
|
||||
db: ReadableDB
|
||||
): Array<Record<string, unknown>> {
|
||||
const [query] = sql`
|
||||
SELECT * FROM attachment_downloads ORDER BY receivedAt DESC;
|
||||
`;
|
||||
|
||||
return db
|
||||
.prepare(query)
|
||||
.all()
|
||||
.all<{ attachmentJson: string }>()
|
||||
.map(job => ({
|
||||
...omit(job, 'attachmentJson'),
|
||||
attachment: jsonToObject(job.attachmentJson),
|
||||
@@ -64,7 +66,7 @@ function insertNewJob(
|
||||
${job.messageId},
|
||||
${job.attachmentType},
|
||||
${objectToJSON(job.attachment)},
|
||||
${job.attachment.digest},
|
||||
${job.attachment.digest ?? null},
|
||||
${job.attachment.contentType},
|
||||
${job.attachment.size},
|
||||
${job.receivedAt},
|
||||
@@ -238,7 +240,7 @@ describe('SQL/updateToSchemaVersion1040', () => {
|
||||
});
|
||||
|
||||
{
|
||||
const [query, params] = sql`
|
||||
const template = sql`
|
||||
SELECT * FROM attachment_downloads
|
||||
WHERE
|
||||
active = 0
|
||||
@@ -248,6 +250,7 @@ describe('SQL/updateToSchemaVersion1040', () => {
|
||||
LIMIT 5
|
||||
`;
|
||||
|
||||
const [query, params] = template;
|
||||
const result = db.prepare(query).all(params);
|
||||
assert.strictEqual(result.length, 2);
|
||||
assert.deepStrictEqual(
|
||||
@@ -255,11 +258,7 @@ describe('SQL/updateToSchemaVersion1040', () => {
|
||||
['message4', 'message1']
|
||||
);
|
||||
|
||||
const details = db
|
||||
.prepare(`EXPLAIN QUERY PLAN ${query}`)
|
||||
.all(params)
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
const details = explain(db, template);
|
||||
assert.include(
|
||||
details,
|
||||
'USING INDEX attachment_downloads_active_receivedAt'
|
||||
@@ -269,7 +268,7 @@ describe('SQL/updateToSchemaVersion1040', () => {
|
||||
}
|
||||
{
|
||||
const messageIds = ['message1', 'message2', 'message4'];
|
||||
const [query, params] = sql`
|
||||
const template = sql`
|
||||
SELECT * FROM attachment_downloads
|
||||
INDEXED BY attachment_downloads_active_messageId
|
||||
WHERE
|
||||
@@ -282,17 +281,15 @@ describe('SQL/updateToSchemaVersion1040', () => {
|
||||
LIMIT 5
|
||||
`;
|
||||
|
||||
const [query, params] = template;
|
||||
|
||||
const result = db.prepare(query).all(params);
|
||||
assert.strictEqual(result.length, 2);
|
||||
assert.deepStrictEqual(
|
||||
result.map(res => res.messageId),
|
||||
['message1', 'message4']
|
||||
);
|
||||
const details = db
|
||||
.prepare(`EXPLAIN QUERY PLAN ${query}`)
|
||||
.all(params)
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
const details = explain(db, template);
|
||||
|
||||
// This query _will_ use a temp b-tree for ordering, but the number of rows
|
||||
// should be quite low.
|
||||
@@ -466,16 +463,16 @@ function insertLegacyJob(
|
||||
job: Partial<LegacyAttachmentDownloadJobType>
|
||||
): void {
|
||||
db.prepare('INSERT OR REPLACE INTO messages (id) VALUES ($id)').run({
|
||||
id: job.messageId,
|
||||
id: job.messageId ?? null,
|
||||
});
|
||||
const [query, params] = sql`
|
||||
INSERT INTO attachment_downloads
|
||||
(id, timestamp, pending, json)
|
||||
VALUES
|
||||
(
|
||||
${job.id},
|
||||
${job.timestamp},
|
||||
${job.pending},
|
||||
${job.id ?? null},
|
||||
${job.timestamp ?? null},
|
||||
${job.pending ?? null},
|
||||
${objectToJSON(job)}
|
||||
);
|
||||
`;
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '../../sql/Server';
|
||||
import type { WritableDB, ReadableDB, MessageType } from '../../sql/Interface';
|
||||
import { sql, jsonToObject } from '../../sql/util';
|
||||
import { insertData, updateToVersion, createDB } from './helpers';
|
||||
import { insertData, updateToVersion, createDB, explain } from './helpers';
|
||||
import { MAX_SYNC_TASK_ATTEMPTS } from '../../util/syncTasks.types';
|
||||
import { WEEK } from '../../util/durations';
|
||||
|
||||
@@ -36,7 +36,7 @@ export function getMostRecentAddressableMessages(
|
||||
LIMIT ${limit};
|
||||
`;
|
||||
|
||||
const rows = db.prepare(query).all(parameters);
|
||||
const rows = db.prepare(query).all<{ json: string }>(parameters);
|
||||
|
||||
return rows.map(row => jsonToObject(row.json));
|
||||
}
|
||||
@@ -169,10 +169,9 @@ describe('SQL/updateToSchemaVersion1060', () => {
|
||||
});
|
||||
|
||||
it('ensures that index is used for getMostRecentAddressableMessages, with storyId', () => {
|
||||
const { detail } = db
|
||||
.prepare(
|
||||
`
|
||||
EXPLAIN QUERY PLAN
|
||||
const detail = explain(
|
||||
db,
|
||||
sql`
|
||||
SELECT json FROM messages
|
||||
INDEXED BY messages_by_date_addressable
|
||||
WHERE
|
||||
@@ -181,8 +180,7 @@ describe('SQL/updateToSchemaVersion1060', () => {
|
||||
ORDER BY received_at DESC, sent_at DESC
|
||||
LIMIT 5;
|
||||
`
|
||||
)
|
||||
.get();
|
||||
);
|
||||
|
||||
assert.notInclude(detail, 'B-TREE');
|
||||
assert.notInclude(detail, 'SCAN');
|
||||
|
||||
@@ -6,7 +6,7 @@ import { v4 as generateGuid } from 'uuid';
|
||||
|
||||
import type { WritableDB, ReadableDB, MessageType } from '../../sql/Interface';
|
||||
import { sql, jsonToObject } from '../../sql/util';
|
||||
import { createDB, insertData, updateToVersion } from './helpers';
|
||||
import { createDB, insertData, updateToVersion, explain } from './helpers';
|
||||
|
||||
import type { MessageAttributesType } from '../../model-types';
|
||||
import { DurationInSeconds } from '../../util/durations/duration-in-seconds';
|
||||
@@ -43,7 +43,7 @@ export function getMostRecentAddressableNondisappearingMessages(
|
||||
LIMIT ${limit};
|
||||
`;
|
||||
|
||||
const rows = db.prepare(query).all(parameters);
|
||||
const rows = db.prepare(query).all<{ json: string }>(parameters);
|
||||
|
||||
return rows.map(row => jsonToObject(row.json));
|
||||
}
|
||||
@@ -159,10 +159,9 @@ describe('SQL/updateToSchemaVersion1080', () => {
|
||||
});
|
||||
|
||||
it('ensures that index is used for getMostRecentAddressableNondisappearingMessagesSync, with storyId', () => {
|
||||
const { detail } = db
|
||||
.prepare(
|
||||
`
|
||||
EXPLAIN QUERY PLAN
|
||||
const detail = explain(
|
||||
db,
|
||||
sql`
|
||||
SELECT json FROM messages
|
||||
INDEXED BY messages_by_date_addressable_nondisappearing
|
||||
WHERE
|
||||
@@ -172,8 +171,7 @@ describe('SQL/updateToSchemaVersion1080', () => {
|
||||
ORDER BY received_at DESC, sent_at DESC
|
||||
LIMIT 5;
|
||||
`
|
||||
)
|
||||
.get();
|
||||
);
|
||||
|
||||
assert.notInclude(detail, 'B-TREE');
|
||||
assert.notInclude(detail, 'SCAN');
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import type { WritableDB } from '../../sql/Interface';
|
||||
import { createDB, updateToVersion } from './helpers';
|
||||
import { sql } from '../../sql/util';
|
||||
import { createDB, updateToVersion, explain } from './helpers';
|
||||
|
||||
describe('SQL/updateToSchemaVersion1090', () => {
|
||||
let db: WritableDB;
|
||||
@@ -18,16 +19,12 @@ describe('SQL/updateToSchemaVersion1090', () => {
|
||||
|
||||
describe('Additional messages_on_delete indexes', () => {
|
||||
it('uses index for selecting reactions by messageId', () => {
|
||||
const details = db
|
||||
.prepare(
|
||||
`EXPLAIN QUERY PLAN
|
||||
SELECT rowid FROM reactions
|
||||
const details = explain(
|
||||
db,
|
||||
sql`SELECT rowid FROM reactions
|
||||
WHERE messageId = '123';
|
||||
`
|
||||
)
|
||||
.all()
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
details,
|
||||
@@ -36,15 +33,10 @@ describe('SQL/updateToSchemaVersion1090', () => {
|
||||
});
|
||||
|
||||
it('uses index for selecting storyReads by storyId', () => {
|
||||
const details = db
|
||||
.prepare(
|
||||
`EXPLAIN QUERY PLAN
|
||||
DELETE FROM storyReads WHERE storyId = '123';
|
||||
`
|
||||
)
|
||||
.all()
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
const details = explain(
|
||||
db,
|
||||
sql`DELETE FROM storyReads WHERE storyId = '123';`
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
details,
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import type { WritableDB } from '../../sql/Interface';
|
||||
import { createDB, updateToVersion } from './helpers';
|
||||
import { sql } from '../../sql/util';
|
||||
import { createDB, updateToVersion, explain } from './helpers';
|
||||
|
||||
describe('SQL/updateToSchemaVersion1120', () => {
|
||||
let db: WritableDB;
|
||||
@@ -17,15 +18,10 @@ describe('SQL/updateToSchemaVersion1120', () => {
|
||||
});
|
||||
|
||||
it('uses index for deleting edited messages', () => {
|
||||
const details = db
|
||||
.prepare(
|
||||
`EXPLAIN QUERY PLAN
|
||||
DELETE FROM edited_messages WHERE messageId = 'messageId';
|
||||
`
|
||||
)
|
||||
.all()
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
const details = explain(
|
||||
db,
|
||||
sql`DELETE FROM edited_messages WHERE messageId = 'messageId';`
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
details,
|
||||
@@ -34,15 +30,10 @@ describe('SQL/updateToSchemaVersion1120', () => {
|
||||
});
|
||||
|
||||
it('uses index for deleting mentions', () => {
|
||||
const details = db
|
||||
.prepare(
|
||||
`EXPLAIN QUERY PLAN
|
||||
DELETE FROM mentions WHERE messageId = 'messageId';
|
||||
`
|
||||
)
|
||||
.all()
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
const details = explain(
|
||||
db,
|
||||
sql`DELETE FROM mentions WHERE messageId = 'messageId';`
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
details,
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import type { WritableDB } from '../../sql/Interface';
|
||||
import { createDB, updateToVersion } from './helpers';
|
||||
import { sql } from '../../sql/util';
|
||||
import { createDB, updateToVersion, explain } from './helpers';
|
||||
|
||||
describe('SQL/updateToSchemaVersion1130', () => {
|
||||
let db: WritableDB;
|
||||
@@ -17,10 +18,9 @@ describe('SQL/updateToSchemaVersion1130', () => {
|
||||
});
|
||||
|
||||
it('uses new index for getAllStories query and no params', () => {
|
||||
const details = db
|
||||
.prepare(
|
||||
`
|
||||
EXPLAIN QUERY PLAN
|
||||
const details = explain(
|
||||
db,
|
||||
sql`
|
||||
SELECT json, id
|
||||
FROM messages
|
||||
WHERE
|
||||
@@ -29,19 +29,15 @@ describe('SQL/updateToSchemaVersion1130', () => {
|
||||
(NULL IS NULL OR sourceServiceId IS NULL)
|
||||
ORDER BY received_at ASC, sent_at ASC;
|
||||
`
|
||||
)
|
||||
.all()
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
);
|
||||
|
||||
assert.strictEqual(details, 'SCAN messages USING INDEX messages_isStory');
|
||||
});
|
||||
|
||||
it('uses new index for getAllStories query and with conversationId', () => {
|
||||
const details = db
|
||||
.prepare(
|
||||
`
|
||||
EXPLAIN QUERY PLAN
|
||||
const details = explain(
|
||||
db,
|
||||
sql`
|
||||
SELECT json, id
|
||||
FROM messages
|
||||
WHERE
|
||||
@@ -50,19 +46,15 @@ describe('SQL/updateToSchemaVersion1130', () => {
|
||||
(NULL IS NULL OR sourceServiceId IS NULL)
|
||||
ORDER BY received_at ASC, sent_at ASC;
|
||||
`
|
||||
)
|
||||
.all()
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
);
|
||||
|
||||
assert.strictEqual(details, 'SCAN messages USING INDEX messages_isStory');
|
||||
});
|
||||
|
||||
it('uses new index for getAllStories query and with sourceServiceId', () => {
|
||||
const details = db
|
||||
.prepare(
|
||||
`
|
||||
EXPLAIN QUERY PLAN
|
||||
const details = explain(
|
||||
db,
|
||||
sql`
|
||||
SELECT json, id
|
||||
FROM messages
|
||||
WHERE
|
||||
@@ -71,19 +63,15 @@ describe('SQL/updateToSchemaVersion1130', () => {
|
||||
('something' IS NULL OR sourceServiceId IS 'something')
|
||||
ORDER BY received_at ASC, sent_at ASC;
|
||||
`
|
||||
)
|
||||
.all()
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
);
|
||||
|
||||
assert.strictEqual(details, 'SCAN messages USING INDEX messages_isStory');
|
||||
});
|
||||
|
||||
it('uses new index for getAllStories query and both params', () => {
|
||||
const details = db
|
||||
.prepare(
|
||||
`
|
||||
EXPLAIN QUERY PLAN
|
||||
const details = explain(
|
||||
db,
|
||||
sql`
|
||||
SELECT json, id
|
||||
FROM messages
|
||||
WHERE
|
||||
@@ -92,27 +80,20 @@ describe('SQL/updateToSchemaVersion1130', () => {
|
||||
('something' IS NULL OR sourceServiceId IS 'something')
|
||||
ORDER BY received_at ASC, sent_at ASC;
|
||||
`
|
||||
)
|
||||
.all()
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
);
|
||||
|
||||
assert.strictEqual(details, 'SCAN messages USING INDEX messages_isStory');
|
||||
});
|
||||
|
||||
it('uses previous index for getAllStories get replies query', () => {
|
||||
const details = db
|
||||
.prepare(
|
||||
`
|
||||
EXPLAIN QUERY PLAN
|
||||
const details = explain(
|
||||
db,
|
||||
sql`
|
||||
SELECT DISTINCT storyId
|
||||
FROM messages
|
||||
WHERE storyId IS NOT NULL
|
||||
`
|
||||
)
|
||||
.all()
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
details,
|
||||
@@ -121,10 +102,9 @@ describe('SQL/updateToSchemaVersion1130', () => {
|
||||
});
|
||||
|
||||
it('uses previous index for getAllStories get replies from self query', () => {
|
||||
const details = db
|
||||
.prepare(
|
||||
`
|
||||
EXPLAIN QUERY PLAN
|
||||
const details = explain(
|
||||
db,
|
||||
sql`
|
||||
SELECT DISTINCT storyId
|
||||
FROM messages
|
||||
WHERE (
|
||||
@@ -132,10 +112,7 @@ describe('SQL/updateToSchemaVersion1130', () => {
|
||||
type IS 'outgoing'
|
||||
)
|
||||
`
|
||||
)
|
||||
.all()
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
details,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { assert } from 'chai';
|
||||
import { omit } from 'lodash';
|
||||
import type { WritableDB } from '../../sql/Interface';
|
||||
import { createDB, updateToVersion } from './helpers';
|
||||
import { createDB, updateToVersion, explain } from './helpers';
|
||||
import type { AttachmentDownloadJobType } from '../../types/AttachmentDownload';
|
||||
import { jsonToObject, objectToJSON, sql } from '../../sql/util';
|
||||
import { IMAGE_BMP } from '../../types/MIME';
|
||||
@@ -59,14 +59,14 @@ function insertOldJob(
|
||||
db.prepare(query).run(params);
|
||||
}
|
||||
|
||||
function getAttachmentDownloadJobs(db: WritableDB) {
|
||||
function getAttachmentDownloadJobs(db: WritableDB): unknown {
|
||||
const [query] = sql`
|
||||
SELECT * FROM attachment_downloads ORDER BY receivedAt DESC;
|
||||
`;
|
||||
|
||||
return db
|
||||
.prepare(query)
|
||||
.all()
|
||||
.all<{ active: number; attachmentJson: string }>()
|
||||
.map(job => ({
|
||||
...omit(job, 'attachmentJson'),
|
||||
active: job.active === 1,
|
||||
@@ -108,17 +108,13 @@ describe('SQL/updateToSchemaVersion1180', () => {
|
||||
});
|
||||
it('uses convering index for summing all pending backup jobs', async () => {
|
||||
updateToVersion(db, 1180);
|
||||
const details = db
|
||||
.prepare(
|
||||
const details = explain(
|
||||
db,
|
||||
sql`
|
||||
SELECT SUM(ciphertextSize) FROM attachment_downloads
|
||||
WHERE source = 'backup_import';
|
||||
`
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT SUM(ciphertextSize) FROM attachment_downloads
|
||||
WHERE source = 'backup_import';
|
||||
`
|
||||
)
|
||||
.all()
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
details,
|
||||
@@ -127,17 +123,13 @@ describe('SQL/updateToSchemaVersion1180', () => {
|
||||
});
|
||||
it('uses index for deleting all backup jobs', async () => {
|
||||
updateToVersion(db, 1180);
|
||||
const details = db
|
||||
.prepare(
|
||||
const details = explain(
|
||||
db,
|
||||
sql`
|
||||
DELETE FROM attachment_downloads
|
||||
WHERE source = 'backup_import';
|
||||
`
|
||||
EXPLAIN QUERY PLAN
|
||||
DELETE FROM attachment_downloads
|
||||
WHERE source = 'backup_import';
|
||||
`
|
||||
)
|
||||
.all()
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
details,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { assert } from 'chai';
|
||||
|
||||
import { AttachmentDownloadSource, type WritableDB } from '../../sql/Interface';
|
||||
import { objectToJSON, sql } from '../../sql/util';
|
||||
import { createDB, updateToVersion } from './helpers';
|
||||
import { createDB, updateToVersion, explain } from './helpers';
|
||||
import type { AttachmentDownloadJobType } from '../../types/AttachmentDownload';
|
||||
import { IMAGE_JPEG } from '../../types/MIME';
|
||||
|
||||
@@ -71,7 +71,7 @@ function insertJob(
|
||||
${job.messageId},
|
||||
${job.attachmentType},
|
||||
${objectToJSON(job.attachment)},
|
||||
${job.attachment.digest},
|
||||
${job.attachment.digest ?? null},
|
||||
${job.attachment.contentType},
|
||||
${job.attachment.size},
|
||||
${job.receivedAt},
|
||||
@@ -113,7 +113,7 @@ describe('SQL/updateToSchemaVersion1200', () => {
|
||||
|
||||
it('uses correct index for standard query', () => {
|
||||
const now = Date.now();
|
||||
const [query, params] = sql`
|
||||
const template = sql`
|
||||
SELECT * FROM attachment_downloads
|
||||
WHERE
|
||||
active = 0
|
||||
@@ -122,11 +122,7 @@ describe('SQL/updateToSchemaVersion1200', () => {
|
||||
ORDER BY receivedAt DESC
|
||||
LIMIT 3
|
||||
`;
|
||||
const details = db
|
||||
.prepare(`EXPLAIN QUERY PLAN ${query}`)
|
||||
.all(params)
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
const details = explain(db, template);
|
||||
assert.equal(
|
||||
details,
|
||||
'SEARCH attachment_downloads USING INDEX attachment_downloads_active_receivedAt (active=?)'
|
||||
@@ -136,7 +132,7 @@ describe('SQL/updateToSchemaVersion1200', () => {
|
||||
it('uses correct index for standard query with sources', () => {
|
||||
const now = Date.now();
|
||||
// query with sources (e.g. when backup-import is paused)
|
||||
const [query, params] = sql`
|
||||
const template = sql`
|
||||
SELECT * FROM attachment_downloads
|
||||
WHERE
|
||||
active IS 0
|
||||
@@ -147,11 +143,7 @@ describe('SQL/updateToSchemaVersion1200', () => {
|
||||
ORDER BY receivedAt DESC
|
||||
LIMIT 3
|
||||
`;
|
||||
const details = db
|
||||
.prepare(`EXPLAIN QUERY PLAN ${query}`)
|
||||
.all(params)
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
const details = explain(db, template);
|
||||
assert.equal(
|
||||
details,
|
||||
'SEARCH attachment_downloads USING INDEX attachment_downloads_active_source_receivedAt (active=? AND source=?)'
|
||||
@@ -160,7 +152,7 @@ describe('SQL/updateToSchemaVersion1200', () => {
|
||||
|
||||
it('uses provided index for prioritized query with sources', () => {
|
||||
// prioritize visible messages with sources (e.g. when backup-import is paused)
|
||||
const [query, params] = sql`
|
||||
const template = sql`
|
||||
SELECT * FROM attachment_downloads
|
||||
INDEXED BY attachment_downloads_active_messageId
|
||||
WHERE
|
||||
@@ -174,36 +166,30 @@ describe('SQL/updateToSchemaVersion1200', () => {
|
||||
ORDER BY receivedAt ASC
|
||||
LIMIT 3
|
||||
`;
|
||||
const [query, params] = template;
|
||||
const result = db.prepare(query).all(params);
|
||||
assert.strictEqual(result.length, 1);
|
||||
assert.deepStrictEqual(result[0].messageId, 'message12');
|
||||
const details = db
|
||||
.prepare(`EXPLAIN QUERY PLAN ${query}`)
|
||||
.all(params)
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
const details = explain(db, template);
|
||||
assert.equal(
|
||||
details,
|
||||
'SEARCH attachment_downloads USING INDEX attachment_downloads_active_messageId (active=? AND messageId=?), USE TEMP B-TREE FOR ORDER BY'
|
||||
'SEARCH attachment_downloads USING INDEX attachment_downloads_active_messageId (active=? AND messageId=?)\nUSE TEMP B-TREE FOR ORDER BY'
|
||||
);
|
||||
});
|
||||
|
||||
it('uses existing index to remove all backup jobs ', () => {
|
||||
// prioritize visible messages with sources (e.g. when backup-import is paused)
|
||||
const [query, params] = sql`
|
||||
const template = sql`
|
||||
DELETE FROM attachment_downloads
|
||||
WHERE source = 'backup_import';
|
||||
`;
|
||||
|
||||
const details = db
|
||||
.prepare(`EXPLAIN QUERY PLAN ${query}`)
|
||||
.all(params)
|
||||
.map(step => step.detail)
|
||||
.join(', ');
|
||||
const details = explain(db, template);
|
||||
assert.equal(
|
||||
details,
|
||||
'SEARCH attachment_downloads USING COVERING INDEX attachment_downloads_source_ciphertextSize (source=?)'
|
||||
);
|
||||
const [query, params] = template;
|
||||
db.prepare(query).run(params);
|
||||
assert.equal(
|
||||
db.prepare('SELECT * FROM attachment_downloads').all().length,
|
||||
|
||||
@@ -9,8 +9,9 @@ import {
|
||||
saveSyncTasks,
|
||||
incrementAllSyncTaskAttempts,
|
||||
} from '../../sql/Server';
|
||||
import { sql } from '../../sql/util';
|
||||
import type { WritableDB } from '../../sql/Interface';
|
||||
import { updateToVersion, createDB } from './helpers';
|
||||
import { updateToVersion, createDB, explain } from './helpers';
|
||||
|
||||
import type { SyncTaskType } from '../../util/syncTasks';
|
||||
|
||||
@@ -27,17 +28,15 @@ describe('SQL/updateToSchemaVersion1330', () => {
|
||||
|
||||
describe('Sync Tasks task index', () => {
|
||||
it('uses the task index for queries', () => {
|
||||
const { detail } = db
|
||||
.prepare(
|
||||
`
|
||||
EXPLAIN QUERY PLAN
|
||||
const detail = explain(
|
||||
db,
|
||||
sql`
|
||||
SELECT rowid, * FROM syncTasks
|
||||
WHERE rowid > 0 AND type IN ('delete-converation', 'delete-local-conversation')
|
||||
ORDER BY rowid ASC
|
||||
LIMIT 10000
|
||||
`
|
||||
)
|
||||
.get();
|
||||
);
|
||||
assert.include(detail, 'USING INDEX syncTasks_type');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -82,17 +82,37 @@ describe('SQL/updateToSchemaVersion87(cleanup)', () => {
|
||||
}
|
||||
|
||||
function getCountOfKyberKeys(): number {
|
||||
return db.prepare('SELECT count(*) FROM kyberPreKeys;').pluck(true).get();
|
||||
return (
|
||||
db
|
||||
.prepare('SELECT count(*) FROM kyberPreKeys;', {
|
||||
pluck: true,
|
||||
})
|
||||
.get<number>() ?? 0
|
||||
);
|
||||
}
|
||||
function getCountOfPreKeys(): number {
|
||||
return db.prepare('SELECT count(*) FROM preKeys;').pluck(true).get();
|
||||
return (
|
||||
db
|
||||
.prepare('SELECT count(*) FROM preKeys;', {
|
||||
pluck: true,
|
||||
})
|
||||
.get<number>() ?? 0
|
||||
);
|
||||
}
|
||||
function getCountOfSignedKeys(): number {
|
||||
return db.prepare('SELECT count(*) FROM signedPreKeys;').pluck(true).get();
|
||||
return (
|
||||
db
|
||||
.prepare('SELECT count(*) FROM signedPreKeys;', {
|
||||
pluck: true,
|
||||
})
|
||||
.get<number>() ?? 0
|
||||
);
|
||||
}
|
||||
|
||||
function getPragma(): number {
|
||||
return db.prepare('PRAGMA user_version;').pluck(true).get();
|
||||
return db.pragma('user_version', {
|
||||
simple: true,
|
||||
}) as number;
|
||||
}
|
||||
|
||||
function generateKyberKey(
|
||||
|
||||
@@ -435,7 +435,7 @@ describe('SQL/updateToSchemaVersion89', () => {
|
||||
`;
|
||||
return db
|
||||
.prepare(query)
|
||||
.all()
|
||||
.all<{ json: string }>()
|
||||
.map(row => {
|
||||
return jsonToObject<MessageType>(row.json);
|
||||
});
|
||||
|
||||
@@ -4,7 +4,14 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import type { WritableDB } from '../../sql/Interface';
|
||||
import { createDB, updateToVersion, insertData, getTableData } from './helpers';
|
||||
import { sql } from '../../sql/util';
|
||||
import {
|
||||
createDB,
|
||||
updateToVersion,
|
||||
insertData,
|
||||
getTableData,
|
||||
explain,
|
||||
} from './helpers';
|
||||
|
||||
describe('SQL/updateToSchemaVersion90', () => {
|
||||
let db: WritableDB;
|
||||
@@ -85,20 +92,16 @@ describe('SQL/updateToSchemaVersion90', () => {
|
||||
it('should use storyId index', () => {
|
||||
updateToVersion(db, 90);
|
||||
|
||||
const details = db
|
||||
.prepare(
|
||||
`
|
||||
EXPLAIN QUERY PLAN
|
||||
const details = explain(
|
||||
db,
|
||||
sql`
|
||||
UPDATE messages
|
||||
SET json = json_remove(json, '$.storyReplyContext.attachment.screenshotData')
|
||||
WHERE isStory = 0
|
||||
AND storyId > '0'
|
||||
AND json->'storyReplyContext.attachment.screenshotData' IS NOT NULL;
|
||||
`
|
||||
)
|
||||
.all()
|
||||
.map(({ detail }) => detail)
|
||||
.join('\n');
|
||||
);
|
||||
|
||||
assert.include(details, 'USING INDEX messages_by_storyId');
|
||||
assert.notInclude(details, 'SCAN');
|
||||
|
||||
@@ -50,11 +50,19 @@ describe('SQL/updateToSchemaVersion91', () => {
|
||||
}
|
||||
|
||||
function getCountOfKeys(): number {
|
||||
return db.prepare('SELECT count(*) FROM preKeys;').pluck(true).get();
|
||||
return (
|
||||
db
|
||||
.prepare('SELECT count(*) FROM preKeys;', {
|
||||
pluck: true,
|
||||
})
|
||||
.get<number>() ?? 0
|
||||
);
|
||||
}
|
||||
|
||||
function getPragma(): number {
|
||||
return db.prepare('PRAGMA user_version;').pluck(true).get();
|
||||
return db.pragma('user_version', {
|
||||
simple: true,
|
||||
}) as number;
|
||||
}
|
||||
|
||||
function generateKey(
|
||||
|
||||
@@ -60,14 +60,28 @@ describe('SQL/updateToSchemaVersion92', () => {
|
||||
}
|
||||
|
||||
function getCountOfKyberKeys(): number {
|
||||
return db.prepare('SELECT count(*) FROM kyberPreKeys;').pluck(true).get();
|
||||
return (
|
||||
db
|
||||
.prepare('SELECT count(*) FROM kyberPreKeys;', {
|
||||
pluck: true,
|
||||
})
|
||||
.get<number>() ?? 0
|
||||
);
|
||||
}
|
||||
function getCountOfSignedKeys(): number {
|
||||
return db.prepare('SELECT count(*) FROM signedPreKeys;').pluck(true).get();
|
||||
return (
|
||||
db
|
||||
.prepare('SELECT count(*) FROM signedPreKeys;', {
|
||||
pluck: true,
|
||||
})
|
||||
.get<number>() ?? 0
|
||||
);
|
||||
}
|
||||
|
||||
function getPragma(): number {
|
||||
return db.prepare('PRAGMA user_version;').pluck(true).get();
|
||||
return db.pragma('user_version', {
|
||||
simple: true,
|
||||
}) as number;
|
||||
}
|
||||
|
||||
function generateKyberKey(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user