Clean up transaction handling in sql.js

This commit is contained in:
Scott Nonnenberg 2019-06-24 11:43:45 -07:00 committed by Ken Powers
parent 3feb0037e5
commit 48691a2558

View file

@ -216,8 +216,9 @@ async function updateToSchemaVersion1(currentVersion, instance) {
await instance.run('BEGIN TRANSACTION;'); await instance.run('BEGIN TRANSACTION;');
await instance.run( try {
`CREATE TABLE messages( await instance.run(
`CREATE TABLE messages(
id STRING PRIMARY KEY ASC, id STRING PRIMARY KEY ASC,
json TEXT, json TEXT,
@ -233,63 +234,67 @@ async function updateToSchemaVersion1(currentVersion, instance) {
hasFileAttachments INTEGER, hasFileAttachments INTEGER,
hasVisualMediaAttachments INTEGER hasVisualMediaAttachments INTEGER
);` );`
); );
await instance.run(`CREATE INDEX messages_unread ON messages ( await instance.run(`CREATE INDEX messages_unread ON messages (
unread unread
);`); );`);
await instance.run(`CREATE INDEX messages_expires_at ON messages ( await instance.run(`CREATE INDEX messages_expires_at ON messages (
expires_at expires_at
);`); );`);
await instance.run(`CREATE INDEX messages_receipt ON messages ( await instance.run(`CREATE INDEX messages_receipt ON messages (
sent_at sent_at
);`); );`);
await instance.run(`CREATE INDEX messages_schemaVersion ON messages ( await instance.run(`CREATE INDEX messages_schemaVersion ON messages (
schemaVersion schemaVersion
);`); );`);
await instance.run(`CREATE INDEX messages_conversation ON messages ( await instance.run(`CREATE INDEX messages_conversation ON messages (
conversationId, conversationId,
received_at received_at
);`); );`);
await instance.run(`CREATE INDEX messages_duplicate_check ON messages ( await instance.run(`CREATE INDEX messages_duplicate_check ON messages (
source, source,
sourceDevice, sourceDevice,
sent_at sent_at
);`); );`);
await instance.run(`CREATE INDEX messages_hasAttachments ON messages ( await instance.run(`CREATE INDEX messages_hasAttachments ON messages (
conversationId, conversationId,
hasAttachments, hasAttachments,
received_at received_at
);`); );`);
await instance.run(`CREATE INDEX messages_hasFileAttachments ON messages ( await instance.run(`CREATE INDEX messages_hasFileAttachments ON messages (
conversationId, conversationId,
hasFileAttachments, hasFileAttachments,
received_at received_at
);`); );`);
await instance.run(`CREATE INDEX messages_hasVisualMediaAttachments ON messages ( await instance.run(`CREATE INDEX messages_hasVisualMediaAttachments ON messages (
conversationId, conversationId,
hasVisualMediaAttachments, hasVisualMediaAttachments,
received_at received_at
);`); );`);
await instance.run(`CREATE TABLE unprocessed( await instance.run(`CREATE TABLE unprocessed(
id STRING, id STRING,
timestamp INTEGER, timestamp INTEGER,
json TEXT json TEXT
);`); );`);
await instance.run(`CREATE INDEX unprocessed_id ON unprocessed ( await instance.run(`CREATE INDEX unprocessed_id ON unprocessed (
id id
);`); );`);
await instance.run(`CREATE INDEX unprocessed_timestamp ON unprocessed ( await instance.run(`CREATE INDEX unprocessed_timestamp ON unprocessed (
timestamp timestamp
);`); );`);
await instance.run('PRAGMA schema_version = 1;'); await instance.run('PRAGMA schema_version = 1;');
await instance.run('COMMIT TRANSACTION;'); await instance.run('COMMIT TRANSACTION;');
console.log('updateToSchemaVersion1: success!'); console.log('updateToSchemaVersion1: success!');
} catch (error) {
await instance.run('ROLLBACK;');
throw error;
}
} }
async function updateToSchemaVersion2(currentVersion, instance) { async function updateToSchemaVersion2(currentVersion, instance) {
@ -301,38 +306,43 @@ async function updateToSchemaVersion2(currentVersion, instance) {
await instance.run('BEGIN TRANSACTION;'); await instance.run('BEGIN TRANSACTION;');
await instance.run( try {
`ALTER TABLE messages await instance.run(
`ALTER TABLE messages
ADD COLUMN expireTimer INTEGER;` ADD COLUMN expireTimer INTEGER;`
); );
await instance.run( await instance.run(
`ALTER TABLE messages `ALTER TABLE messages
ADD COLUMN expirationStartTimestamp INTEGER;` ADD COLUMN expirationStartTimestamp INTEGER;`
); );
await instance.run( await instance.run(
`ALTER TABLE messages `ALTER TABLE messages
ADD COLUMN type STRING;` ADD COLUMN type STRING;`
); );
await instance.run(`CREATE INDEX messages_expiring ON messages ( await instance.run(`CREATE INDEX messages_expiring ON messages (
expireTimer, expireTimer,
expirationStartTimestamp, expirationStartTimestamp,
expires_at expires_at
);`); );`);
await instance.run( await instance.run(
`UPDATE messages SET `UPDATE messages SET
expirationStartTimestamp = json_extract(json, '$.expirationStartTimestamp'), expirationStartTimestamp = json_extract(json, '$.expirationStartTimestamp'),
expireTimer = json_extract(json, '$.expireTimer'), expireTimer = json_extract(json, '$.expireTimer'),
type = json_extract(json, '$.type');` type = json_extract(json, '$.type');`
); );
await instance.run('PRAGMA schema_version = 2;'); await instance.run('PRAGMA schema_version = 2;');
await instance.run('COMMIT TRANSACTION;'); await instance.run('COMMIT TRANSACTION;');
console.log('updateToSchemaVersion2: success!'); console.log('updateToSchemaVersion2: success!');
} catch (error) {
await instance.run('ROLLBACK;');
throw error;
}
} }
async function updateToSchemaVersion3(currentVersion, instance) { async function updateToSchemaVersion3(currentVersion, instance) {
@ -344,25 +354,30 @@ async function updateToSchemaVersion3(currentVersion, instance) {
await instance.run('BEGIN TRANSACTION;'); await instance.run('BEGIN TRANSACTION;');
await instance.run('DROP INDEX messages_expiring;'); try {
await instance.run('DROP INDEX messages_unread;'); await instance.run('DROP INDEX messages_expiring;');
await instance.run('DROP INDEX messages_unread;');
await instance.run(`CREATE INDEX messages_without_timer ON messages ( await instance.run(`CREATE INDEX messages_without_timer ON messages (
expireTimer, expireTimer,
expires_at, expires_at,
type type
) WHERE expires_at IS NULL AND expireTimer IS NOT NULL;`); ) WHERE expires_at IS NULL AND expireTimer IS NOT NULL;`);
await instance.run(`CREATE INDEX messages_unread ON messages ( await instance.run(`CREATE INDEX messages_unread ON messages (
conversationId, conversationId,
unread unread
) WHERE unread IS NOT NULL;`); ) WHERE unread IS NOT NULL;`);
await instance.run('ANALYZE;'); await instance.run('ANALYZE;');
await instance.run('PRAGMA schema_version = 3;'); await instance.run('PRAGMA schema_version = 3;');
await instance.run('COMMIT TRANSACTION;'); await instance.run('COMMIT TRANSACTION;');
console.log('updateToSchemaVersion3: success!'); console.log('updateToSchemaVersion3: success!');
} catch (error) {
await instance.run('ROLLBACK;');
throw error;
}
} }
async function updateToSchemaVersion4(currentVersion, instance) { async function updateToSchemaVersion4(currentVersion, instance) {
@ -374,8 +389,9 @@ async function updateToSchemaVersion4(currentVersion, instance) {
await instance.run('BEGIN TRANSACTION;'); await instance.run('BEGIN TRANSACTION;');
await instance.run( try {
`CREATE TABLE conversations( await instance.run(
`CREATE TABLE conversations(
id STRING PRIMARY KEY ASC, id STRING PRIMARY KEY ASC,
json TEXT, json TEXT,
@ -385,20 +401,24 @@ async function updateToSchemaVersion4(currentVersion, instance) {
name TEXT, name TEXT,
profileName TEXT profileName TEXT
);` );`
); );
await instance.run(`CREATE INDEX conversations_active ON conversations ( await instance.run(`CREATE INDEX conversations_active ON conversations (
active_at active_at
) WHERE active_at IS NOT NULL;`); ) WHERE active_at IS NOT NULL;`);
await instance.run(`CREATE INDEX conversations_type ON conversations ( await instance.run(`CREATE INDEX conversations_type ON conversations (
type type
) WHERE type IS NOT NULL;`); ) WHERE type IS NOT NULL;`);
await instance.run('PRAGMA schema_version = 4;'); await instance.run('PRAGMA schema_version = 4;');
await instance.run('COMMIT TRANSACTION;'); await instance.run('COMMIT TRANSACTION;');
console.log('updateToSchemaVersion4: success!'); console.log('updateToSchemaVersion4: success!');
} catch (error) {
await instance.run('ROLLBACK;');
throw error;
}
} }
async function updateToSchemaVersion6(currentVersion, instance) { async function updateToSchemaVersion6(currentVersion, instance) {
@ -408,56 +428,61 @@ async function updateToSchemaVersion6(currentVersion, instance) {
console.log('updateToSchemaVersion6: starting...'); console.log('updateToSchemaVersion6: starting...');
await instance.run('BEGIN TRANSACTION;'); await instance.run('BEGIN TRANSACTION;');
// key-value, ids are strings, one extra column try {
await instance.run( // key-value, ids are strings, one extra column
`CREATE TABLE sessions( await instance.run(
`CREATE TABLE sessions(
id STRING PRIMARY KEY ASC, id STRING PRIMARY KEY ASC,
number STRING, number STRING,
json TEXT json TEXT
);` );`
); );
await instance.run(`CREATE INDEX sessions_number ON sessions ( await instance.run(`CREATE INDEX sessions_number ON sessions (
number number
) WHERE number IS NOT NULL;`); ) WHERE number IS NOT NULL;`);
// key-value, ids are strings // key-value, ids are strings
await instance.run( await instance.run(
`CREATE TABLE groups( `CREATE TABLE groups(
id STRING PRIMARY KEY ASC, id STRING PRIMARY KEY ASC,
json TEXT json TEXT
);` );`
); );
await instance.run( await instance.run(
`CREATE TABLE identityKeys( `CREATE TABLE identityKeys(
id STRING PRIMARY KEY ASC, id STRING PRIMARY KEY ASC,
json TEXT json TEXT
);` );`
); );
await instance.run( await instance.run(
`CREATE TABLE items( `CREATE TABLE items(
id STRING PRIMARY KEY ASC, id STRING PRIMARY KEY ASC,
json TEXT json TEXT
);` );`
); );
// key-value, ids are integers // key-value, ids are integers
await instance.run( await instance.run(
`CREATE TABLE preKeys( `CREATE TABLE preKeys(
id INTEGER PRIMARY KEY ASC, id INTEGER PRIMARY KEY ASC,
json TEXT json TEXT
);` );`
); );
await instance.run( await instance.run(
`CREATE TABLE signedPreKeys( `CREATE TABLE signedPreKeys(
id INTEGER PRIMARY KEY ASC, id INTEGER PRIMARY KEY ASC,
json TEXT json TEXT
);` );`
); );
await instance.run('PRAGMA schema_version = 6;'); await instance.run('PRAGMA schema_version = 6;');
await instance.run('COMMIT TRANSACTION;'); await instance.run('COMMIT TRANSACTION;');
console.log('updateToSchemaVersion6: success!'); console.log('updateToSchemaVersion6: success!');
} catch (error) {
await instance.run('ROLLBACK;');
throw error;
}
} }
async function updateToSchemaVersion7(currentVersion, instance) { async function updateToSchemaVersion7(currentVersion, instance) {
@ -467,33 +492,38 @@ async function updateToSchemaVersion7(currentVersion, instance) {
console.log('updateToSchemaVersion7: starting...'); console.log('updateToSchemaVersion7: starting...');
await instance.run('BEGIN TRANSACTION;'); await instance.run('BEGIN TRANSACTION;');
// SQLite has been coercing our STRINGs into numbers, so we force it with TEXT try {
// We create a new table then copy the data into it, since we can't modify columns // SQLite has been coercing our STRINGs into numbers, so we force it with TEXT
// We create a new table then copy the data into it, since we can't modify columns
await instance.run('DROP INDEX sessions_number;'); await instance.run('DROP INDEX sessions_number;');
await instance.run('ALTER TABLE sessions RENAME TO sessions_old;'); await instance.run('ALTER TABLE sessions RENAME TO sessions_old;');
await instance.run( await instance.run(
`CREATE TABLE sessions( `CREATE TABLE sessions(
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
number TEXT, number TEXT,
json TEXT json TEXT
);` );`
); );
await instance.run(`CREATE INDEX sessions_number ON sessions ( await instance.run(`CREATE INDEX sessions_number ON sessions (
number number
) WHERE number IS NOT NULL;`); ) WHERE number IS NOT NULL;`);
await instance.run(`INSERT INTO sessions(id, number, json) await instance.run(`INSERT INTO sessions(id, number, json)
SELECT "+" || id, number, json FROM sessions_old; SELECT "+" || id, number, json FROM sessions_old;
`); `);
await instance.run('DROP TABLE sessions_old;'); await instance.run('DROP TABLE sessions_old;');
await instance.run('PRAGMA schema_version = 7;'); await instance.run('PRAGMA schema_version = 7;');
await instance.run('COMMIT TRANSACTION;'); await instance.run('COMMIT TRANSACTION;');
console.log('updateToSchemaVersion7: success!'); console.log('updateToSchemaVersion7: success!');
} catch (error) {
await instance.run('ROLLBACK;');
throw error;
}
} }
async function updateToSchemaVersion8(currentVersion, instance) { async function updateToSchemaVersion8(currentVersion, instance) {
@ -503,25 +533,28 @@ async function updateToSchemaVersion8(currentVersion, instance) {
console.log('updateToSchemaVersion8: starting...'); console.log('updateToSchemaVersion8: starting...');
await instance.run('BEGIN TRANSACTION;'); await instance.run('BEGIN TRANSACTION;');
// First, we pull a new body field out of the message table's json blob try {
await instance.run( // First, we pull a new body field out of the message table's json blob
`ALTER TABLE messages await instance.run(
`ALTER TABLE messages
ADD COLUMN body TEXT;` ADD COLUMN body TEXT;`
); );
await instance.run("UPDATE messages SET body = json_extract(json, '$.body')"); await instance.run(
"UPDATE messages SET body = json_extract(json, '$.body')"
);
// Then we create our full-text search table and populate it // Then we create our full-text search table and populate it
await instance.run(` await instance.run(`
CREATE VIRTUAL TABLE messages_fts CREATE VIRTUAL TABLE messages_fts
USING fts5(id UNINDEXED, body); USING fts5(id UNINDEXED, body);
`); `);
await instance.run(` await instance.run(`
INSERT INTO messages_fts(id, body) INSERT INTO messages_fts(id, body)
SELECT id, body FROM messages; SELECT id, body FROM messages;
`); `);
// Then we set up triggers to keep the full-text search table up to date // Then we set up triggers to keep the full-text search table up to date
await instance.run(` await instance.run(`
CREATE TRIGGER messages_on_insert AFTER INSERT ON messages BEGIN CREATE TRIGGER messages_on_insert AFTER INSERT ON messages BEGIN
INSERT INTO messages_fts ( INSERT INTO messages_fts (
id, id,
@ -532,12 +565,12 @@ async function updateToSchemaVersion8(currentVersion, instance) {
); );
END; END;
`); `);
await instance.run(` await instance.run(`
CREATE TRIGGER messages_on_delete AFTER DELETE ON messages BEGIN CREATE TRIGGER messages_on_delete AFTER DELETE ON messages BEGIN
DELETE FROM messages_fts WHERE id = old.id; DELETE FROM messages_fts WHERE id = old.id;
END; END;
`); `);
await instance.run(` await instance.run(`
CREATE TRIGGER messages_on_update AFTER UPDATE ON messages BEGIN CREATE TRIGGER messages_on_update AFTER UPDATE ON messages BEGIN
DELETE FROM messages_fts WHERE id = old.id; DELETE FROM messages_fts WHERE id = old.id;
INSERT INTO messages_fts( INSERT INTO messages_fts(
@ -550,13 +583,17 @@ async function updateToSchemaVersion8(currentVersion, instance) {
END; END;
`); `);
// For formatting search results: // For formatting search results:
// https://sqlite.org/fts5.html#the_highlight_function // https://sqlite.org/fts5.html#the_highlight_function
// https://sqlite.org/fts5.html#the_snippet_function // https://sqlite.org/fts5.html#the_snippet_function
await instance.run('PRAGMA schema_version = 8;'); await instance.run('PRAGMA schema_version = 8;');
await instance.run('COMMIT TRANSACTION;'); await instance.run('COMMIT TRANSACTION;');
console.log('updateToSchemaVersion8: success!'); console.log('updateToSchemaVersion8: success!');
} catch (error) {
await instance.run('ROLLBACK;');
throw error;
}
} }
async function updateToSchemaVersion9(currentVersion, instance) { async function updateToSchemaVersion9(currentVersion, instance) {
@ -566,25 +603,30 @@ async function updateToSchemaVersion9(currentVersion, instance) {
console.log('updateToSchemaVersion9: starting...'); console.log('updateToSchemaVersion9: starting...');
await instance.run('BEGIN TRANSACTION;'); await instance.run('BEGIN TRANSACTION;');
await instance.run(`CREATE TABLE attachment_downloads( try {
await instance.run(`CREATE TABLE attachment_downloads(
id STRING primary key, id STRING primary key,
timestamp INTEGER, timestamp INTEGER,
pending INTEGER, pending INTEGER,
json TEXT json TEXT
);`); );`);
await instance.run(`CREATE INDEX attachment_downloads_timestamp await instance.run(`CREATE INDEX attachment_downloads_timestamp
ON attachment_downloads ( ON attachment_downloads (
timestamp timestamp
) WHERE pending = 0;`); ) WHERE pending = 0;`);
await instance.run(`CREATE INDEX attachment_downloads_pending await instance.run(`CREATE INDEX attachment_downloads_pending
ON attachment_downloads ( ON attachment_downloads (
pending pending
) WHERE pending != 0;`); ) WHERE pending != 0;`);
await instance.run('PRAGMA schema_version = 9;'); await instance.run('PRAGMA schema_version = 9;');
await instance.run('COMMIT TRANSACTION;'); await instance.run('COMMIT TRANSACTION;');
console.log('updateToSchemaVersion9: success!'); console.log('updateToSchemaVersion9: success!');
} catch (error) {
await instance.run('ROLLBACK;');
throw error;
}
} }
async function updateToSchemaVersion10(currentVersion, instance) { async function updateToSchemaVersion10(currentVersion, instance) {
@ -594,11 +636,12 @@ async function updateToSchemaVersion10(currentVersion, instance) {
console.log('updateToSchemaVersion10: starting...'); console.log('updateToSchemaVersion10: starting...');
await instance.run('BEGIN TRANSACTION;'); await instance.run('BEGIN TRANSACTION;');
await instance.run('DROP INDEX unprocessed_id;'); try {
await instance.run('DROP INDEX unprocessed_timestamp;'); await instance.run('DROP INDEX unprocessed_id;');
await instance.run('ALTER TABLE unprocessed RENAME TO unprocessed_old;'); await instance.run('DROP INDEX unprocessed_timestamp;');
await instance.run('ALTER TABLE unprocessed RENAME TO unprocessed_old;');
await instance.run(`CREATE TABLE unprocessed( await instance.run(`CREATE TABLE unprocessed(
id STRING, id STRING,
timestamp INTEGER, timestamp INTEGER,
version INTEGER, version INTEGER,
@ -610,14 +653,14 @@ async function updateToSchemaVersion10(currentVersion, instance) {
serverTimestamp INTEGER serverTimestamp INTEGER
);`); );`);
await instance.run(`CREATE INDEX unprocessed_id ON unprocessed ( await instance.run(`CREATE INDEX unprocessed_id ON unprocessed (
id id
);`); );`);
await instance.run(`CREATE INDEX unprocessed_timestamp ON unprocessed ( await instance.run(`CREATE INDEX unprocessed_timestamp ON unprocessed (
timestamp timestamp
);`); );`);
await instance.run(`INSERT INTO unprocessed ( await instance.run(`INSERT INTO unprocessed (
id, id,
timestamp, timestamp,
version, version,
@ -640,11 +683,15 @@ async function updateToSchemaVersion10(currentVersion, instance) {
FROM unprocessed_old; FROM unprocessed_old;
`); `);
await instance.run('DROP TABLE unprocessed_old;'); await instance.run('DROP TABLE unprocessed_old;');
await instance.run('PRAGMA schema_version = 10;'); await instance.run('PRAGMA schema_version = 10;');
await instance.run('COMMIT TRANSACTION;'); await instance.run('COMMIT TRANSACTION;');
console.log('updateToSchemaVersion10: success!'); console.log('updateToSchemaVersion10: success!');
} catch (error) {
await instance.run('ROLLBACK;');
throw error;
}
} }
async function updateToSchemaVersion11(currentVersion, instance) { async function updateToSchemaVersion11(currentVersion, instance) {
@ -654,11 +701,16 @@ async function updateToSchemaVersion11(currentVersion, instance) {
console.log('updateToSchemaVersion11: starting...'); console.log('updateToSchemaVersion11: starting...');
await instance.run('BEGIN TRANSACTION;'); await instance.run('BEGIN TRANSACTION;');
await instance.run('DROP TABLE groups;'); try {
await instance.run('DROP TABLE groups;');
await instance.run('PRAGMA schema_version = 11;'); await instance.run('PRAGMA schema_version = 11;');
await instance.run('COMMIT TRANSACTION;'); await instance.run('COMMIT TRANSACTION;');
console.log('updateToSchemaVersion11: success!'); console.log('updateToSchemaVersion11: success!');
} catch (error) {
await instance.run('ROLLBACK;');
throw error;
}
} }
async function updateToSchemaVersion12(currentVersion, instance) { async function updateToSchemaVersion12(currentVersion, instance) {
@ -669,7 +721,8 @@ async function updateToSchemaVersion12(currentVersion, instance) {
console.log('updateToSchemaVersion12: starting...'); console.log('updateToSchemaVersion12: starting...');
await instance.run('BEGIN TRANSACTION;'); await instance.run('BEGIN TRANSACTION;');
await instance.run(`CREATE TABLE sticker_packs( try {
await instance.run(`CREATE TABLE sticker_packs(
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
key TEXT NOT NULL, key TEXT NOT NULL,
@ -684,7 +737,7 @@ async function updateToSchemaVersion12(currentVersion, instance) {
title STRING title STRING
);`); );`);
await instance.run(`CREATE TABLE stickers( await instance.run(`CREATE TABLE stickers(
id INTEGER NOT NULL, id INTEGER NOT NULL,
packId TEXT NOT NULL, packId TEXT NOT NULL,
@ -702,12 +755,12 @@ async function updateToSchemaVersion12(currentVersion, instance) {
ON DELETE CASCADE ON DELETE CASCADE
);`); );`);
await instance.run(`CREATE INDEX stickers_recents await instance.run(`CREATE INDEX stickers_recents
ON stickers ( ON stickers (
lastUsed lastUsed
) WHERE lastUsed IS NOT NULL;`); ) WHERE lastUsed IS NOT NULL;`);
await instance.run(`CREATE TABLE sticker_references( await instance.run(`CREATE TABLE sticker_references(
messageId STRING, messageId STRING,
packId TEXT, packId TEXT,
CONSTRAINT sticker_references_fk CONSTRAINT sticker_references_fk
@ -716,9 +769,13 @@ async function updateToSchemaVersion12(currentVersion, instance) {
ON DELETE CASCADE ON DELETE CASCADE
);`); );`);
await instance.run('PRAGMA schema_version = 12;'); await instance.run('PRAGMA schema_version = 12;');
await instance.run('COMMIT TRANSACTION;'); await instance.run('COMMIT TRANSACTION;');
console.log('updateToSchemaVersion12: success!'); console.log('updateToSchemaVersion12: success!');
} catch (error) {
await instance.run('ROLLBACK;');
throw error;
}
} }
async function updateToSchemaVersion13(currentVersion, instance) { async function updateToSchemaVersion13(currentVersion, instance) {
@ -729,13 +786,18 @@ async function updateToSchemaVersion13(currentVersion, instance) {
console.log('updateToSchemaVersion13: starting...'); console.log('updateToSchemaVersion13: starting...');
await instance.run('BEGIN TRANSACTION;'); await instance.run('BEGIN TRANSACTION;');
await instance.run( try {
'ALTER TABLE sticker_packs ADD COLUMN attemptedStatus STRING;' await instance.run(
); 'ALTER TABLE sticker_packs ADD COLUMN attemptedStatus STRING;'
);
await instance.run('PRAGMA schema_version = 13;'); await instance.run('PRAGMA schema_version = 13;');
await instance.run('COMMIT TRANSACTION;'); await instance.run('COMMIT TRANSACTION;');
console.log('updateToSchemaVersion13: success!'); console.log('updateToSchemaVersion13: success!');
} catch (error) {
await instance.run('ROLLBACK;');
throw error;
}
} }
async function updateToSchemaVersion14(currentVersion, instance) { async function updateToSchemaVersion14(currentVersion, instance) {
@ -746,19 +808,24 @@ async function updateToSchemaVersion14(currentVersion, instance) {
console.log('updateToSchemaVersion14: starting...'); console.log('updateToSchemaVersion14: starting...');
await instance.run('BEGIN TRANSACTION;'); await instance.run('BEGIN TRANSACTION;');
await instance.run(`CREATE TABLE emojis( try {
await instance.run(`CREATE TABLE emojis(
shortName STRING PRIMARY KEY, shortName STRING PRIMARY KEY,
lastUsage INTEGER lastUsage INTEGER
);`); );`);
await instance.run(`CREATE INDEX emojis_lastUsage await instance.run(`CREATE INDEX emojis_lastUsage
ON emojis ( ON emojis (
lastUsage lastUsage
);`); );`);
await instance.run('PRAGMA schema_version = 14;'); await instance.run('PRAGMA schema_version = 14;');
await instance.run('COMMIT TRANSACTION;'); await instance.run('COMMIT TRANSACTION;');
console.log('updateToSchemaVersion14: success!'); console.log('updateToSchemaVersion14: success!');
} catch (error) {
await instance.run('ROLLBACK;');
throw error;
}
} }
async function updateToSchemaVersion15(currentVersion, instance) { async function updateToSchemaVersion15(currentVersion, instance) {
@ -769,31 +836,36 @@ async function updateToSchemaVersion15(currentVersion, instance) {
console.log('updateToSchemaVersion15: starting...'); console.log('updateToSchemaVersion15: starting...');
await instance.run('BEGIN TRANSACTION;'); await instance.run('BEGIN TRANSACTION;');
// SQLite has again coerced our STRINGs into numbers, so we force it with TEXT try {
// We create a new table then copy the data into it, since we can't modify columns // SQLite has again coerced our STRINGs into numbers, so we force it with TEXT
// We create a new table then copy the data into it, since we can't modify columns
await instance.run('DROP INDEX emojis_lastUsage;'); await instance.run('DROP INDEX emojis_lastUsage;');
await instance.run('ALTER TABLE emojis RENAME TO emojis_old;'); await instance.run('ALTER TABLE emojis RENAME TO emojis_old;');
await instance.run(`CREATE TABLE emojis( await instance.run(`CREATE TABLE emojis(
shortName TEXT PRIMARY KEY, shortName TEXT PRIMARY KEY,
lastUsage INTEGER lastUsage INTEGER
);`); );`);
await instance.run(`CREATE INDEX emojis_lastUsage await instance.run(`CREATE INDEX emojis_lastUsage
ON emojis ( ON emojis (
lastUsage lastUsage
);`); );`);
await instance.run('DELETE FROM emojis WHERE shortName = 1'); await instance.run('DELETE FROM emojis WHERE shortName = 1');
await instance.run(`INSERT INTO emojis(shortName, lastUsage) await instance.run(`INSERT INTO emojis(shortName, lastUsage)
SELECT shortName, lastUsage FROM emojis_old; SELECT shortName, lastUsage FROM emojis_old;
`); `);
await instance.run('DROP TABLE emojis_old;'); await instance.run('DROP TABLE emojis_old;');
await instance.run('PRAGMA schema_version = 15;'); await instance.run('PRAGMA schema_version = 15;');
await instance.run('COMMIT TRANSACTION;'); await instance.run('COMMIT TRANSACTION;');
console.log('updateToSchemaVersion15: success!'); console.log('updateToSchemaVersion15: success!');
} catch (error) {
await instance.run('ROLLBACK;');
throw error;
}
} }
const SCHEMA_VERSIONS = [ const SCHEMA_VERSIONS = [
@ -1105,17 +1177,16 @@ async function createOrUpdate(table, data) {
} }
async function bulkAdd(table, array) { async function bulkAdd(table, array) {
let promise; await db.run('BEGIN TRANSACTION;');
db.serialize(() => { try {
promise = Promise.all([ await Promise.all([...map(array, data => createOrUpdate(table, data))]);
db.run('BEGIN TRANSACTION;'),
...map(array, data => createOrUpdate(table, data)),
db.run('COMMIT TRANSACTION;'),
]);
});
await promise; await db.run('COMMIT TRANSACTION;');
} catch (error) {
await db.run('ROLLBACK;');
throw error;
}
} }
async function getById(table, id) { async function getById(table, id) {
@ -1208,19 +1279,20 @@ async function saveConversation(data) {
} }
async function saveConversations(arrayOfConversations) { async function saveConversations(arrayOfConversations) {
let promise; await db.run('BEGIN TRANSACTION;');
db.serialize(() => { try {
promise = Promise.all([ await Promise.all([
db.run('BEGIN TRANSACTION;'),
...map(arrayOfConversations, conversation => ...map(arrayOfConversations, conversation =>
saveConversation(conversation) saveConversation(conversation)
), ),
db.run('COMMIT TRANSACTION;'),
]); ]);
});
await promise; await db.run('COMMIT TRANSACTION;');
} catch (error) {
await db.run('ROLLBACK;');
throw error;
}
} }
async function updateConversation(data) { async function updateConversation(data) {
@ -1525,17 +1597,18 @@ async function saveMessage(data, { forceSave } = {}) {
} }
async function saveMessages(arrayOfMessages, { forceSave } = {}) { async function saveMessages(arrayOfMessages, { forceSave } = {}) {
let promise; await db.run('BEGIN TRANSACTION;');
db.serialize(() => { try {
promise = Promise.all([ await Promise.all([
db.run('BEGIN TRANSACTION;'),
...map(arrayOfMessages, message => saveMessage(message, { forceSave })), ...map(arrayOfMessages, message => saveMessage(message, { forceSave })),
db.run('COMMIT TRANSACTION;'),
]); ]);
});
await promise; await db.run('COMMIT TRANSACTION;');
} catch (error) {
await db.run('ROLLBACK;');
throw error;
}
} }
async function removeMessage(id) { async function removeMessage(id) {
@ -1736,19 +1809,20 @@ async function saveUnprocessed(data, { forceSave } = {}) {
} }
async function saveUnprocesseds(arrayOfUnprocessed, { forceSave } = {}) { async function saveUnprocesseds(arrayOfUnprocessed, { forceSave } = {}) {
let promise; await db.run('BEGIN TRANSACTION;');
db.serialize(() => { try {
promise = Promise.all([ await Promise.all([
db.run('BEGIN TRANSACTION;'),
...map(arrayOfUnprocessed, unprocessed => ...map(arrayOfUnprocessed, unprocessed =>
saveUnprocessed(unprocessed, { forceSave }) saveUnprocessed(unprocessed, { forceSave })
), ),
db.run('COMMIT TRANSACTION;'),
]); ]);
});
await promise; await db.run('COMMIT TRANSACTION;');
} catch (error) {
await db.run('ROLLBACK;');
throw error;
}
} }
async function updateUnprocessedAttempts(id, attempts) { async function updateUnprocessedAttempts(id, attempts) {
@ -2149,7 +2223,7 @@ async function deleteStickerPackReference(messageId, packId) {
} }
const count = countRow['count(*)']; const count = countRow['count(*)'];
if (count > 0) { if (count > 0) {
await db.run('COMMIT TRANSACTION'); await db.run('COMMIT TRANSACTION;');
return null; return null;
} }
@ -2160,13 +2234,13 @@ async function deleteStickerPackReference(messageId, packId) {
); );
if (!packRow) { if (!packRow) {
console.log('deleteStickerPackReference: did not find referenced pack'); console.log('deleteStickerPackReference: did not find referenced pack');
await db.run('COMMIT TRANSACTION'); await db.run('COMMIT TRANSACTION;');
return null; return null;
} }
const { status } = packRow; const { status } = packRow;
if (status === 'installed') { if (status === 'installed') {
await db.run('COMMIT TRANSACTION'); await db.run('COMMIT TRANSACTION;');
return null; return null;
} }
@ -2274,26 +2348,31 @@ async function getRecentStickers({ limit } = {}) {
async function updateEmojiUsage(shortName, timeUsed = Date.now()) { async function updateEmojiUsage(shortName, timeUsed = Date.now()) {
await db.run('BEGIN TRANSACTION;'); await db.run('BEGIN TRANSACTION;');
const rows = await db.get( try {
'SELECT * FROM emojis WHERE shortName = $shortName;', const rows = await db.get(
{ 'SELECT * FROM emojis WHERE shortName = $shortName;',
$shortName: shortName, {
$shortName: shortName,
}
);
if (rows) {
await db.run(
'UPDATE emojis SET lastUsage = $timeUsed WHERE shortName = $shortName;',
{ $shortName: shortName, $timeUsed: timeUsed }
);
} else {
await db.run(
'INSERT INTO emojis(shortName, lastUsage) VALUES ($shortName, $timeUsed);',
{ $shortName: shortName, $timeUsed: timeUsed }
);
} }
);
if (rows) { await db.run('COMMIT TRANSACTION;');
await db.run( } catch (error) {
'UPDATE emojis SET lastUsage = $timeUsed WHERE shortName = $shortName;', await db.run('ROLLBACK;');
{ $shortName: shortName, $timeUsed: timeUsed } throw error;
);
} else {
await db.run(
'INSERT INTO emojis(shortName, lastUsage) VALUES ($shortName, $timeUsed);',
{ $shortName: shortName, $timeUsed: timeUsed }
);
} }
await db.run('COMMIT TRANSACTION;');
} }
async function getRecentEmojis(limit = 32) { async function getRecentEmojis(limit = 32) {
@ -2309,11 +2388,10 @@ async function getRecentEmojis(limit = 32) {
// All data in database // All data in database
async function removeAll() { async function removeAll() {
let promise; await db.run('BEGIN TRANSACTION;');
db.serialize(() => { try {
promise = Promise.all([ await Promise.all([
db.run('BEGIN TRANSACTION;'),
db.run('DELETE FROM conversations;'), db.run('DELETE FROM conversations;'),
db.run('DELETE FROM identityKeys;'), db.run('DELETE FROM identityKeys;'),
db.run('DELETE FROM items;'), db.run('DELETE FROM items;'),
@ -2327,19 +2405,21 @@ async function removeAll() {
db.run('DELETE FROM stickers;'), db.run('DELETE FROM stickers;'),
db.run('DELETE FROM sticker_packs;'), db.run('DELETE FROM sticker_packs;'),
db.run('DELETE FROM sticker_references;'), db.run('DELETE FROM sticker_references;'),
db.run('COMMIT TRANSACTION;'),
]); ]);
});
await promise; await db.run('COMMIT TRANSACTION;');
} catch (error) {
await db.run('ROLLBACK;');
throw error;
}
} }
// Anything that isn't user-visible data // Anything that isn't user-visible data
async function removeAllConfiguration() { async function removeAllConfiguration() {
let promise; await db.run('BEGIN TRANSACTION;');
db.serialize(() => { try {
promise = Promise.all([ await Promise.all([
db.run('BEGIN TRANSACTION;'), db.run('BEGIN TRANSACTION;'),
db.run('DELETE FROM identityKeys;'), db.run('DELETE FROM identityKeys;'),
db.run('DELETE FROM items;'), db.run('DELETE FROM items;'),
@ -2347,11 +2427,13 @@ async function removeAllConfiguration() {
db.run('DELETE FROM sessions;'), db.run('DELETE FROM sessions;'),
db.run('DELETE FROM signedPreKeys;'), db.run('DELETE FROM signedPreKeys;'),
db.run('DELETE FROM unprocessed;'), db.run('DELETE FROM unprocessed;'),
db.run('COMMIT TRANSACTION;'),
]); ]);
});
await promise; await db.run('COMMIT TRANSACTION;');
} catch (error) {
await db.run('ROLLBACK;');
throw error;
}
} }
async function getMessagesNeedingUpgrade(limit, { maxVersion }) { async function getMessagesNeedingUpgrade(limit, { maxVersion }) {