docs(06-03): add SAFETY comments to all 12 hand-crafted SQL statements
- 7 SAFETY comments in search.go (FTS5 MATCH/INSERT/DELETE operations) - 3 SAFETY comments in library.go (FTS5 INSERT/DELETE in commitNewAudioFile, updateAudioFileMetadata) - 1 SAFETY comment in rescan.go (FTS5 DELETE in clearAllLibraryData) - 1 SAFETY comment in persistence.go (variable-count multi-row INSERT) - Cross-references link library.go/rescan.go back to search.go - Two-part format: why sqlc can't handle it + what makes it safe
This commit is contained in:
@@ -31,6 +31,7 @@ func (d *DB) SearchFTS(
|
|||||||
// special characters are treated as literals.
|
// special characters are treated as literals.
|
||||||
ftsQuery := buildFTSQuery(query)
|
ftsQuery := buildFTSQuery(query)
|
||||||
|
|
||||||
|
// SAFETY: FTS5 MATCH syntax unsupported by sqlc. Query is parameterized; no string interpolation.
|
||||||
rows, err := d.db.QueryContext(d.Ctx, `
|
rows, err := d.db.QueryContext(d.Ctx, `
|
||||||
SELECT
|
SELECT
|
||||||
tm.file_path,
|
tm.file_path,
|
||||||
@@ -77,6 +78,7 @@ func (d *DB) SearchFTSByFilename(
|
|||||||
ftsQuery := "file_path : " +
|
ftsQuery := "file_path : " +
|
||||||
strings.Join(tokens, " ")
|
strings.Join(tokens, " ")
|
||||||
|
|
||||||
|
// SAFETY: FTS5 MATCH syntax unsupported by sqlc. Query is parameterized; no string interpolation.
|
||||||
rows, err := d.db.QueryContext(d.Ctx, `
|
rows, err := d.db.QueryContext(d.Ctx, `
|
||||||
SELECT
|
SELECT
|
||||||
tm.file_path,
|
tm.file_path,
|
||||||
@@ -106,6 +108,7 @@ func (d *DB) InsertSearchIndex(
|
|||||||
rowid int64,
|
rowid int64,
|
||||||
filePath, title, artist, album string,
|
filePath, title, artist, album string,
|
||||||
) error {
|
) error {
|
||||||
|
// SAFETY: FTS5 virtual table INSERT unsupported by sqlc. All values are parameterized.
|
||||||
_, err := d.db.ExecContext(d.Ctx, `
|
_, err := d.db.ExecContext(d.Ctx, `
|
||||||
INSERT INTO search_index(rowid, file_path, title, artist, album)
|
INSERT INTO search_index(rowid, file_path, title, artist, album)
|
||||||
VALUES (?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?)
|
||||||
@@ -116,6 +119,7 @@ func (d *DB) InsertSearchIndex(
|
|||||||
|
|
||||||
// DeleteSearchIndex removes a row from the FTS5 search_index.
|
// DeleteSearchIndex removes a row from the FTS5 search_index.
|
||||||
func (d *DB) DeleteSearchIndex(rowid int64) error {
|
func (d *DB) DeleteSearchIndex(rowid int64) error {
|
||||||
|
// SAFETY: FTS5 virtual table DELETE unsupported by sqlc. Rowid is parameterized.
|
||||||
_, err := d.db.ExecContext(d.Ctx, `
|
_, err := d.db.ExecContext(d.Ctx, `
|
||||||
DELETE FROM search_index WHERE rowid = ?
|
DELETE FROM search_index WHERE rowid = ?
|
||||||
`, rowid)
|
`, rowid)
|
||||||
@@ -125,6 +129,7 @@ func (d *DB) DeleteSearchIndex(rowid int64) error {
|
|||||||
|
|
||||||
// ClearSearchIndex removes all rows from the FTS5 search_index.
|
// ClearSearchIndex removes all rows from the FTS5 search_index.
|
||||||
func (d *DB) ClearSearchIndex() error {
|
func (d *DB) ClearSearchIndex() error {
|
||||||
|
// SAFETY: FTS5 virtual table DELETE unsupported by sqlc. No parameters; unconditional delete.
|
||||||
_, err := d.db.ExecContext(d.Ctx, `
|
_, err := d.db.ExecContext(d.Ctx, `
|
||||||
DELETE FROM search_index
|
DELETE FROM search_index
|
||||||
`)
|
`)
|
||||||
@@ -141,6 +146,7 @@ func (d *DB) RebuildSearchIndex() error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: FTS5 virtual table INSERT unsupported by sqlc. All values sourced from track_metadata VIEW; no user input.
|
||||||
_, err := d.db.ExecContext(d.Ctx, `
|
_, err := d.db.ExecContext(d.Ctx, `
|
||||||
INSERT INTO search_index(rowid, file_path, title, artist, album)
|
INSERT INTO search_index(rowid, file_path, title, artist, album)
|
||||||
SELECT id, file_path, title, artist_name, album
|
SELECT id, file_path, title, artist_name, album
|
||||||
@@ -189,6 +195,7 @@ func (d *DB) SearchFTSTracks(
|
|||||||
|
|
||||||
ftsQuery := buildFTSQuery(query)
|
ftsQuery := buildFTSQuery(query)
|
||||||
|
|
||||||
|
// SAFETY: FTS5 MATCH syntax unsupported by sqlc. Query is parameterized; no string interpolation.
|
||||||
rows, err := d.db.QueryContext(d.Ctx, `
|
rows, err := d.db.QueryContext(d.Ctx, `
|
||||||
SELECT
|
SELECT
|
||||||
tm.file_path,
|
tm.file_path,
|
||||||
|
|||||||
@@ -793,6 +793,7 @@ func (l *Library) saveAudioFile(
|
|||||||
|
|
||||||
album := tags.Album
|
album := tags.Album
|
||||||
|
|
||||||
|
// SAFETY: FTS5 virtual table, see search.go:InsertSearchIndex. All values parameterized.
|
||||||
if _, err := tx.ExecContext(
|
if _, err := tx.ExecContext(
|
||||||
l.ctx,
|
l.ctx,
|
||||||
`INSERT INTO search_index(rowid, file_path, title, artist, album)
|
`INSERT INTO search_index(rowid, file_path, title, artist, album)
|
||||||
@@ -874,6 +875,7 @@ func (l *Library) updateAudioFileMetadata(
|
|||||||
|
|
||||||
album := tags.Album
|
album := tags.Album
|
||||||
|
|
||||||
|
// SAFETY: FTS5 virtual table, see search.go:DeleteSearchIndex. Rowid parameterized.
|
||||||
if _, err := tx.ExecContext(
|
if _, err := tx.ExecContext(
|
||||||
l.ctx,
|
l.ctx,
|
||||||
`DELETE FROM search_index WHERE rowid = ?`,
|
`DELETE FROM search_index WHERE rowid = ?`,
|
||||||
@@ -888,6 +890,7 @@ func (l *Library) updateAudioFileMetadata(
|
|||||||
metrics.addWarning(result.absolutePath, "commit", err)
|
metrics.addWarning(result.absolutePath, "commit", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: FTS5 virtual table, see search.go:InsertSearchIndex. All values parameterized.
|
||||||
if _, err := tx.ExecContext(
|
if _, err := tx.ExecContext(
|
||||||
l.ctx,
|
l.ctx,
|
||||||
`INSERT INTO search_index(rowid, file_path, title, artist, album)
|
`INSERT INTO search_index(rowid, file_path, title, artist, album)
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ func (l *Library) clearLibraryTables() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clear FTS5 search index.
|
// Clear FTS5 search index.
|
||||||
|
// SAFETY: FTS5 virtual table, see search.go:ClearSearchIndex. No parameters; unconditional delete.
|
||||||
if _, err := tx.ExecContext(
|
if _, err := tx.ExecContext(
|
||||||
l.ctx, `DELETE FROM search_index`,
|
l.ctx, `DELETE FROM search_index`,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ func (q *Queue) insertTrackBatch(tx *sql.Tx, batch []Track) error {
|
|||||||
args = append(args, track.AudioFileID, track.Position)
|
args = append(args, track.AudioFileID, track.Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: Multi-row INSERT with variable row count unsupported by sqlc. Placeholder count matches args length; no string interpolation.
|
||||||
query := "INSERT INTO queue_tracks (audio_file_id, position) VALUES " +
|
query := "INSERT INTO queue_tracks (audio_file_id, position) VALUES " +
|
||||||
strings.Join(valuePlaceholders, ",")
|
strings.Join(valuePlaceholders, ",")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user