Separate calls in sql channel
This commit is contained in:
parent
85b130a12d
commit
9a98ae0a4e
7 changed files with 90 additions and 45 deletions
|
@ -10,7 +10,11 @@ import { remove as removeEphemeralConfig } from './ephemeral_config';
|
|||
let sql:
|
||||
| Pick<
|
||||
MainSQL,
|
||||
'sqlRead' | 'sqlWrite' | 'pauseWriteAccess' | 'resumeWriteAccess'
|
||||
| 'sqlRead'
|
||||
| 'sqlWrite'
|
||||
| 'pauseWriteAccess'
|
||||
| 'resumeWriteAccess'
|
||||
| 'removeDB'
|
||||
>
|
||||
| undefined;
|
||||
|
||||
|
@ -18,6 +22,7 @@ let initialized = false;
|
|||
|
||||
const SQL_READ_KEY = 'sql-channel:read';
|
||||
const SQL_WRITE_KEY = 'sql-channel:write';
|
||||
const SQL_REMOVE_DB_KEY = 'sql-channel:remove-db';
|
||||
const ERASE_SQL_KEY = 'erase-sql-key';
|
||||
const PAUSE_WRITE_ACCESS = 'pause-sql-writes';
|
||||
const RESUME_WRITE_ACCESS = 'resume-sql-writes';
|
||||
|
@ -44,6 +49,13 @@ export function initialize(mainSQL: typeof sql): void {
|
|||
return sql.sqlWrite(callName, ...args);
|
||||
});
|
||||
|
||||
ipcMain.handle(SQL_REMOVE_DB_KEY, () => {
|
||||
if (!sql) {
|
||||
throw new Error(`${SQL_REMOVE_DB_KEY}: Not yet initialized!`);
|
||||
}
|
||||
return sql.removeDB();
|
||||
});
|
||||
|
||||
ipcMain.handle(ERASE_SQL_KEY, () => {
|
||||
removeUserConfig();
|
||||
removeEphemeralConfig();
|
||||
|
|
|
@ -24,7 +24,7 @@ import * as Errors from '../types/errors';
|
|||
import type { StoredJob } from '../jobs/types';
|
||||
import { formatJobForInsert } from '../jobs/formatJobForInsert';
|
||||
import { cleanupMessages } from '../util/cleanup';
|
||||
import { AccessType, ipcInvoke, doShutdown } from './channels';
|
||||
import { AccessType, ipcInvoke, doShutdown, removeDB } from './channels';
|
||||
|
||||
import type {
|
||||
ClientInterfaceWrap,
|
||||
|
@ -124,6 +124,7 @@ const clientOnlyWritable: ClientOnlyWritableInterface = {
|
|||
|
||||
flushUpdateConversationBatcher,
|
||||
|
||||
removeDB,
|
||||
shutdown,
|
||||
removeMessagesInConversation,
|
||||
|
||||
|
|
|
@ -646,8 +646,6 @@ type ReadableInterface = {
|
|||
type WritableInterface = {
|
||||
close: () => void;
|
||||
|
||||
removeDB: () => void;
|
||||
|
||||
removeIndexedDBFiles: () => void;
|
||||
|
||||
removeIdentityKeyById: (id: IdentityKeyIdType) => number;
|
||||
|
@ -1118,6 +1116,7 @@ export type ClientOnlyWritableInterface = ClientInterfaceWrap<{
|
|||
// Client-side only
|
||||
|
||||
shutdown: () => void;
|
||||
removeDB: () => void;
|
||||
removeMessagesInConversation: (
|
||||
conversationId: string,
|
||||
options: {
|
||||
|
|
|
@ -350,7 +350,6 @@ export const DataReader: ServerReadableInterface = {
|
|||
|
||||
export const DataWriter: ServerWritableInterface = {
|
||||
close: closeWritable,
|
||||
removeDB,
|
||||
removeIndexedDBFiles,
|
||||
|
||||
createOrUpdateIdentityKey,
|
||||
|
@ -629,20 +628,29 @@ function openAndMigrateDatabase(
|
|||
// If that fails, we try to open the database with 3.x compatibility to extract the
|
||||
// user_version (previously stored in schema_version, blown away by cipher_migrate).
|
||||
db = new SQL(filePath) as WritableDB;
|
||||
keyDatabase(db, key);
|
||||
try {
|
||||
keyDatabase(db, key);
|
||||
|
||||
// https://www.zetetic.net/blog/2018/11/30/sqlcipher-400-release/#compatability-sqlcipher-4-0-0
|
||||
db.pragma('cipher_compatibility = 3');
|
||||
migrateSchemaVersion(db);
|
||||
db.close();
|
||||
// https://www.zetetic.net/blog/2018/11/30/sqlcipher-400-release/#compatability-sqlcipher-4-0-0
|
||||
db.pragma('cipher_compatibility = 3');
|
||||
migrateSchemaVersion(db);
|
||||
db.close();
|
||||
|
||||
// After migrating user_version -> schema_version, we reopen database, because we can't
|
||||
// migrate to the latest ciphers after we've modified the defaults.
|
||||
db = new SQL(filePath) as WritableDB;
|
||||
keyDatabase(db, key);
|
||||
// After migrating user_version -> schema_version, we reopen database, because
|
||||
// we can't migrate to the latest ciphers after we've modified the defaults.
|
||||
db = new SQL(filePath) as WritableDB;
|
||||
keyDatabase(db, key);
|
||||
|
||||
db.pragma('cipher_migrate');
|
||||
switchToWAL(db);
|
||||
db.pragma('cipher_migrate');
|
||||
switchToWAL(db);
|
||||
} catch (error) {
|
||||
try {
|
||||
db.close();
|
||||
} catch {
|
||||
// Best effort
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
|
@ -659,8 +667,17 @@ function openAndSetUpSQLCipher(
|
|||
|
||||
const db = openAndMigrateDatabase(filePath, key, readonly);
|
||||
|
||||
// Because foreign key support is not enabled by default!
|
||||
db.pragma('foreign_keys = ON');
|
||||
try {
|
||||
// Because foreign key support is not enabled by default!
|
||||
db.pragma('foreign_keys = ON');
|
||||
} catch (error) {
|
||||
try {
|
||||
db.close();
|
||||
} catch {
|
||||
// Best effort
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
|
@ -750,13 +767,7 @@ function closeWritable(db: WritableDB): void {
|
|||
db.close();
|
||||
}
|
||||
|
||||
function removeDB(db: WritableDB): void {
|
||||
try {
|
||||
db.close();
|
||||
} catch (error) {
|
||||
logger.error('removeDB: Failed to close database:', error.stack);
|
||||
}
|
||||
|
||||
export function removeDB(): void {
|
||||
if (!databaseFilePath) {
|
||||
throw new Error(
|
||||
'removeDB: Cannot erase database without a databaseFilePath!'
|
||||
|
|
|
@ -9,6 +9,7 @@ import { missingCaseError } from '../util/missingCaseError';
|
|||
|
||||
const SQL_READ_KEY = 'sql-channel:read';
|
||||
const SQL_WRITE_KEY = 'sql-channel:write';
|
||||
const SQL_REMOVE_DB_KEY = 'sql-channel:remove-db';
|
||||
let activeJobCount = 0;
|
||||
let resolveShutdown: (() => void) | undefined;
|
||||
let shutdownPromise: Promise<void> | null = null;
|
||||
|
@ -77,3 +78,7 @@ export async function doShutdown(): Promise<void> {
|
|||
log.info('data.shutdown: process complete');
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeDB(): Promise<void> {
|
||||
return ipcRenderer.invoke(SQL_REMOVE_DB_KEY);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import { app } from 'electron';
|
|||
import { strictAssert } from '../util/assert';
|
||||
import { explodePromise } from '../util/explodePromise';
|
||||
import type { LoggerType } from '../types/Logging';
|
||||
import * as Errors from '../types/errors';
|
||||
import { SqliteErrorKind } from './errors';
|
||||
import type {
|
||||
ServerReadableDirectInterface,
|
||||
|
@ -200,6 +201,16 @@ export class MainSQL {
|
|||
}
|
||||
|
||||
public async close(): Promise<void> {
|
||||
if (this.onReady) {
|
||||
try {
|
||||
await this.onReady;
|
||||
} catch (err) {
|
||||
this.logger?.error(`MainSQL close, failed: ${Errors.toLogFormat(err)}`);
|
||||
// Init failed
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.isReady) {
|
||||
throw new Error('Not initialized');
|
||||
}
|
||||
|
@ -256,11 +267,6 @@ export class MainSQL {
|
|||
await promise;
|
||||
}
|
||||
|
||||
// Special case since we need to broadcast this to every pool entry.
|
||||
if (method === 'removeDB') {
|
||||
return (await this.removeDB()) as Result;
|
||||
}
|
||||
|
||||
const primary = this.pool[0];
|
||||
|
||||
const { result, duration } = await this.send<SqlCallResult>(primary, {
|
||||
|
|
|
@ -11,7 +11,7 @@ import type {
|
|||
WrappedWorkerLogEntry,
|
||||
} from './main';
|
||||
import type { WritableDB } from './Interface';
|
||||
import { initialize, DataReader, DataWriter } from './Server';
|
||||
import { initialize, DataReader, DataWriter, removeDB } from './Server';
|
||||
import { SqliteErrorKind, parseSqliteError } from './errors';
|
||||
|
||||
if (!parentPort) {
|
||||
|
@ -102,6 +102,31 @@ port.on('message', ({ seq, request }: WrappedWorkerRequest) => {
|
|||
return;
|
||||
}
|
||||
|
||||
// Removing database does not require active connection.
|
||||
if (request.type === 'removeDB') {
|
||||
try {
|
||||
if (db) {
|
||||
if (isPrimary) {
|
||||
DataWriter.close(db);
|
||||
} else {
|
||||
DataReader.close(db);
|
||||
}
|
||||
db = undefined;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to close database before removal');
|
||||
}
|
||||
|
||||
if (isPrimary) {
|
||||
removeDB();
|
||||
}
|
||||
|
||||
isRemoved = true;
|
||||
|
||||
respond(seq, undefined, undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!db) {
|
||||
throw new Error('Not initialized');
|
||||
}
|
||||
|
@ -119,20 +144,6 @@ port.on('message', ({ seq, request }: WrappedWorkerRequest) => {
|
|||
return;
|
||||
}
|
||||
|
||||
if (request.type === 'removeDB') {
|
||||
if (isPrimary) {
|
||||
DataWriter.removeDB(db);
|
||||
} else {
|
||||
DataReader.close(db);
|
||||
}
|
||||
|
||||
isRemoved = true;
|
||||
db = undefined;
|
||||
|
||||
respond(seq, undefined, undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.type === 'sqlCall:read' || request.type === 'sqlCall:write') {
|
||||
const DataInterface =
|
||||
request.type === 'sqlCall:read' ? DataReader : DataWriter;
|
||||
|
|
Loading…
Reference in a new issue