Merge origin/master into fix/electron-playlist-cleared-columns

Brings in the journey baseline lowered by #1895, which the PR's
baseline direction check compares against.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5.5 committed 2026-10-12 08:52:48 +02:00
commit 8c1fc21e3f
26 files changed
+969 -36

No files matched your search

@@ -358,7 +358,12 @@ describe('content title FTS tokenizer upgrade', () => {
expect.stringContaining("tokenize='trigram remove_diacritics 1'")
);
expect(rebuild.run).toHaveBeenCalled();
expect(marker.run).toHaveBeenCalled();
// The rebuild also covers the plain index migration; without its
// record the next launch rebuilds the whole index again.
expect(marker.run.mock.calls).toEqual([
['migration:content-title-fts-remove-diacritics:v1'],
['migration:content-title-fts-trigram:v1'],
]);
// One transaction, so a rejected CREATE rolls the drop back and the
// working index survives.
expect(transaction).toHaveBeenCalled();
+13 -9
View File
@@ -755,15 +755,19 @@ function upgradeContentTitleFtsTokenizer(sqliteDb: Database.Database): boolean {
)
.run();
sqliteDb
.prepare(
`INSERT INTO app_state (key, value, updated_at)
VALUES (?, 'done', datetime('now'))
ON CONFLICT(key) DO UPDATE SET
value = excluded.value,
updated_at = excluded.updated_at`
)
.run(CONTENT_TITLE_FTS_DIACRITICS_MIGRATION_KEY);
// The rebuild is also all that `ensureContentTitleFts` does, so
// its record is written too. Without it the next launch rebuilds
// the whole index again, on the main thread, on the first launch
// after a fresh install has imported its catalog.
const markDone = sqliteDb.prepare(
`INSERT INTO app_state (key, value, updated_at)
VALUES (?, 'done', datetime('now'))
ON CONFLICT(key) DO UPDATE SET
value = excluded.value,
updated_at = excluded.updated_at`
);
markDone.run(CONTENT_TITLE_FTS_DIACRITICS_MIGRATION_KEY);
markDone.run(CONTENT_TITLE_FTS_MIGRATION_KEY);
});
executeMigration();
@@ -71,6 +71,30 @@ function seed(sqlite: Database.Database) {
}
}
const titleIndexRebuilds: string[] = [];
/**
* Every full rebuild of the title FTS index goes through `prepare`. A profile
* needs one; another on a later launch re-indexes the whole catalog on the
* main thread before the app can read anything.
*/
function countTitleIndexRebuilds(connection: Database.Database): void {
const prepare = connection.prepare;
connection.prepare = function countingPrepare(
this: Database.Database,
sql: string
) {
if (
/content_title_fts\(content_title_fts\)\s+VALUES\s+\('rebuild'\)/.test(
sql
)
) {
titleIndexRebuilds.push(sql);
}
return prepare.call(this, sql);
} as typeof connection.prepare;
}
/**
* The performance capture counts one `exec` call as one SQL statement
* (apps/electron-backend/src/app/workers/database-worker-sql-statement-count.ts),
@@ -81,6 +105,7 @@ function seed(sqlite: Database.Database) {
function requireSingleStatementExec(): string[] {
const batches: string[] = [];
setDatabaseConnectionObserver((connection) => {
countTitleIndexRebuilds(connection);
const exec = connection.exec;
connection.exec = function singleStatementExec(sql: string) {
try {
@@ -230,6 +255,11 @@ async function main() {
[],
'Initialization must pass one SQL statement per exec call'
);
assert.equal(
titleIndexRebuilds.length,
1,
'The title index must be rebuilt once per profile, not on every launch'
);
process.stdout.write('upgrade verified');
}