fix: drop+recreate contentless FTS5 index instead of DELETE

The search_index is a contentless FTS5 table (content=''), which
SQLite does not support DELETE on. ClearSearchIndex now drops and
recreates the virtual table. Single-row DeleteSearchIndex becomes
a no-op since contentless FTS5 also cannot delete individual rows;
stale entries are harmless (search JOINs filter them out) and the
index is fully rebuilt during FullRescan.
This commit is contained in:
2026-03-05 10:41:55 -05:00
parent 2439c2a728
commit 8e9a616037
4 changed files with 59 additions and 46 deletions
+30 -13
View File
@@ -117,24 +117,41 @@ func (d *DB) InsertSearchIndex(
return err
}
// DeleteSearchIndex removes a row from the FTS5 search_index.
func (d *DB) DeleteSearchIndex(rowid int64) error {
// SAFETY: FTS5 virtual table DELETE unsupported by sqlc. Rowid is parameterized.
_, err := d.db.ExecContext(d.Ctx, `
DELETE FROM search_index WHERE rowid = ?
`, rowid)
return err
// DeleteSearchIndex is a no-op for contentless FTS5 tables.
// Contentless FTS5 (content=”) does not support DELETE.
// Stale entries are harmless: they point to rowids that no longer
// match in track_metadata, so JOINs in search queries filter them
// out. The index is fully rebuilt during FullRescan.
func (d *DB) DeleteSearchIndex(_ int64) error {
return nil
}
// ClearSearchIndex removes all rows from the FTS5 search_index.
// The search_index is a contentless FTS5 table (content=”), which
// does not support DELETE. We drop and recreate it instead.
func (d *DB) ClearSearchIndex() error {
// SAFETY: FTS5 virtual table DELETE unsupported by sqlc. No parameters; unconditional delete.
_, err := d.db.ExecContext(d.Ctx, `
DELETE FROM search_index
`)
// SAFETY: FTS5 contentless table cannot be DELETEd from.
// Drop + recreate is the only way to clear it. No parameters.
if _, err := d.db.ExecContext(d.Ctx,
`DROP TABLE IF EXISTS search_index`,
); err != nil {
return fmt.Errorf("could not drop search_index: %w", err)
}
return err
if _, err := d.db.ExecContext(d.Ctx, `
CREATE VIRTUAL TABLE IF NOT EXISTS search_index USING fts5(
file_path,
title,
artist,
album,
content='',
tokenize='unicode61 remove_diacritics 2'
)
`); err != nil {
return fmt.Errorf("could not recreate search_index: %w", err)
}
return nil
}
// RebuildSearchIndex repopulates the FTS5 search_index from