242 lines
10 KiB
Markdown
242 lines
10 KiB
Markdown
---
|
|
phase: 06-sql-consolidation-code-quality
|
|
plan: 01
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- backend/database/database.go
|
|
- backend/database/search.go
|
|
- backend/database/sql/schemas/track_metadata_view.sql
|
|
- backend/database/sql/sqlcgen/models.go
|
|
autonomous: true
|
|
requirements: [QUAL-01]
|
|
|
|
must_haves:
|
|
truths:
|
|
- "All FTS5 search queries (SearchFTS, SearchFTSByFilename, SearchFTSTracks) use the track_metadata VIEW instead of inline 5-table JOINs"
|
|
- "RebuildSearchIndex SELECTs from track_metadata VIEW instead of duplicating the JOIN"
|
|
- "Migration 4 creates the track_metadata VIEW for existing databases"
|
|
- "sqlc generate succeeds with the VIEW schema file and produces updated models"
|
|
- "Existing FTS5 search tests (15 tests) pass unchanged after VIEW consolidation"
|
|
artifacts:
|
|
- path: "backend/database/sql/schemas/track_metadata_view.sql"
|
|
provides: "VIEW definition for sqlc schema awareness"
|
|
contains: "CREATE VIEW IF NOT EXISTS track_metadata"
|
|
- path: "backend/database/database.go"
|
|
provides: "Migration 4 creating VIEW for existing databases"
|
|
contains: "migration4TrackMetadataView"
|
|
- path: "backend/database/search.go"
|
|
provides: "Consolidated search queries using VIEW"
|
|
contains: "track_metadata"
|
|
key_links:
|
|
- from: "backend/database/search.go"
|
|
to: "track_metadata VIEW"
|
|
via: "JOIN track_metadata tm ON tm.id = si.rowid"
|
|
pattern: "JOIN track_metadata"
|
|
- from: "backend/database/database.go"
|
|
to: "track_metadata VIEW"
|
|
via: "migration 4 CREATE VIEW"
|
|
pattern: "CREATE VIEW IF NOT EXISTS track_metadata"
|
|
---
|
|
|
|
<objective>
|
|
Consolidate the duplicated 5-table FTS5 JOIN pattern into a single SQLite VIEW named `track_metadata`, and update all search queries to use it.
|
|
|
|
Purpose: Eliminate 4+ copies of the same complex JOIN across search.go and database.go. A single VIEW is the source of truth for audio file metadata JOINs — changes to the schema only need updating in one place.
|
|
|
|
Output: Migration 4 (VIEW creation), sqlc schema file, consolidated search.go queries, updated sqlc-generated code.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@/home/caleb/.config/opencode/get-shit-done/workflows/execute-plan.md
|
|
@/home/caleb/.config/opencode/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/06-sql-consolidation-code-quality/06-RESEARCH.md
|
|
|
|
@backend/database/database.go
|
|
@backend/database/search.go
|
|
@backend/database/sql/schemas/
|
|
@backend/database/sqlc.yaml
|
|
|
|
<interfaces>
|
|
<!-- Key types and contracts the executor needs. -->
|
|
|
|
From backend/database/database.go:
|
|
- Migrations are Go functions registered in a slice, applied sequentially by PRAGMA user_version
|
|
- Pattern: `migration2BasenameAndFTS`, `migration3UniqueArtistCreditArtist` — each bumps user_version
|
|
- Current highest migration: 3 (user_version=3)
|
|
- `//go:generate go tool sqlc generate` directive at line 21
|
|
|
|
From backend/database/search.go:
|
|
- `func (d *DB) SearchFTS(query string, limit int) ([]SearchResult, error)` — line 22
|
|
- `func (d *DB) SearchFTSByFilename(query string, limit int) ([]SearchResult, error)` — line 72
|
|
- `func (d *DB) RebuildSearchIndex() error` — line 161
|
|
- `func (d *DB) SearchFTSTracks(query string, limit int) ([]SearchTrackResult, error)` — line 222
|
|
- All 4 functions contain inline 5-table JOINs (audio_files → recordings → artist_credit → release_group_recordings subquery → release_groups)
|
|
|
|
From backend/database/sql/schemas/ directory:
|
|
- Schema files sorted alphabetically; sqlc processes them in filesystem order
|
|
- Tables: artist_credit.sql, artists.sql, audio_files.sql, cover_art.sql, file_types.sql, genres.sql, recordings.sql, release_group_recordings.sql, release_groups.sql, etc.
|
|
- `track_metadata_view.sql` will sort after all table schemas (t > all existing prefixes)
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Create track_metadata VIEW schema and migration</name>
|
|
<files>
|
|
backend/database/sql/schemas/track_metadata_view.sql
|
|
backend/database/database.go
|
|
</files>
|
|
<action>
|
|
1. Create `backend/database/sql/schemas/track_metadata_view.sql` with the VIEW definition:
|
|
```sql
|
|
CREATE VIEW IF NOT EXISTS track_metadata AS
|
|
SELECT
|
|
af.id,
|
|
af.file_path,
|
|
af.length_milliseconds,
|
|
COALESCE(r.name, '') AS title,
|
|
COALESCE(ac.text, '') AS artist_name,
|
|
r.track_number,
|
|
r.disc_number,
|
|
COALESCE(rg.name, '') AS album,
|
|
CAST(COALESCE(
|
|
(SELECT GROUP_CONCAT(g.name, '||')
|
|
FROM recording_genres rg_sub
|
|
JOIN genres g ON rg_sub.genre_id = g.id
|
|
WHERE rg_sub.recording_id = r.id),
|
|
''
|
|
) AS TEXT) AS genre,
|
|
COALESCE(r.year, 0) AS year,
|
|
COALESCE(r.composer, '') AS composer,
|
|
COALESCE(ft.extension, '') AS file_type,
|
|
af.sample_rate,
|
|
af.bit_depth,
|
|
af.channels,
|
|
af.bitrate,
|
|
af.file_size
|
|
FROM audio_files af
|
|
LEFT JOIN recordings r ON af.recording_id = r.id
|
|
LEFT JOIN artist_credit ac ON r.artist_credit_id = ac.id
|
|
LEFT JOIN (
|
|
SELECT recording_id,
|
|
MIN(release_group_id) AS release_group_id
|
|
FROM release_group_recordings
|
|
GROUP BY recording_id
|
|
) rgr ON r.id = rgr.recording_id
|
|
LEFT JOIN release_groups rg ON rgr.release_group_id = rg.id
|
|
LEFT JOIN file_types ft ON af.file_type_id = ft.id;
|
|
```
|
|
|
|
2. In `backend/database/database.go`, add migration 4 (`migration4TrackMetadataView`):
|
|
- The migration function should execute `CREATE VIEW IF NOT EXISTS track_metadata AS ...` (same SQL as the schema file)
|
|
- Register it in the migrations slice after migration 3
|
|
- Follow the existing migration function pattern (takes `*sql.DB` and `context.Context`, returns `error`)
|
|
|
|
3. Run `go tool sqlc generate` from `backend/database/` to regenerate code with VIEW awareness.
|
|
|
|
4. **CRITICAL:** Do NOT change `migration2BasenameAndFTS` to use the VIEW — migration 2 runs before migration 4 for databases upgrading from version 1. The inline JOIN in migration 2 must stay as-is.
|
|
|
|
5. Verify sqlc generate succeeds without errors.
|
|
</action>
|
|
<verify>
|
|
<automated>cd backend/database && go tool sqlc generate && echo "sqlc OK"</automated>
|
|
</verify>
|
|
<done>
|
|
- `track_metadata_view.sql` exists in schemas directory with the VIEW definition
|
|
- Migration 4 registered in database.go, creates the VIEW for existing databases
|
|
- `sqlc generate` succeeds and recognizes the VIEW
|
|
- migration2 code is unchanged (still uses inline JOIN)
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Consolidate search queries to use track_metadata VIEW</name>
|
|
<files>
|
|
backend/database/search.go
|
|
</files>
|
|
<action>
|
|
Update all 4 search functions in `search.go` to use the `track_metadata` VIEW instead of inline JOINs:
|
|
|
|
1. **SearchFTS** (line ~22): Replace the inline 5-table JOIN with:
|
|
```sql
|
|
SELECT tm.file_path, tm.length_milliseconds, tm.title, tm.artist_name, tm.album
|
|
FROM search_index si
|
|
JOIN track_metadata tm ON tm.id = si.rowid
|
|
WHERE search_index MATCH ?
|
|
ORDER BY rank
|
|
LIMIT ?
|
|
```
|
|
Only select the 5 columns the function actually uses — SQLite optimizes away unused VIEW columns.
|
|
|
|
2. **SearchFTSByFilename** (line ~72): Same pattern as SearchFTS but with the filename-specific FTS query logic. Replace the inline JOIN with `JOIN track_metadata tm ON tm.id = si.rowid`. Keep the same column selection.
|
|
|
|
3. **SearchFTSTracks** (line ~222): Replace the inline 6-table JOIN (includes file_types) with the VIEW. The VIEW already includes `file_type` (from the file_types JOIN), so this becomes simpler. Select the columns needed by `SearchTrackResult`: file_path, length_milliseconds, title, artist_name, album, track_number, disc_number, genre, year, composer, file_type, sample_rate, bit_depth, channels, bitrate, file_size.
|
|
|
|
4. **RebuildSearchIndex** (line ~161): Replace the inline JOIN with:
|
|
```sql
|
|
INSERT INTO search_index(rowid, file_path, title, artist, album)
|
|
SELECT id, file_path, title, artist_name, album
|
|
FROM track_metadata
|
|
```
|
|
|
|
**Preserve:** All FTS5 MATCH syntax, ORDER BY rank, LIMIT clauses, error handling, row scanning, and function signatures remain identical. Only the FROM/JOIN clauses change.
|
|
|
|
**Do NOT touch:** `InsertSearchIndex`, `DeleteSearchIndex`, `ClearSearchIndex` — these are single-row FTS5 operations that don't use JOINs.
|
|
</action>
|
|
<verify>
|
|
<automated>cd backend && go test -tags webkit2_41 -race -count=1 -timeout 60s ./database/...</automated>
|
|
</verify>
|
|
<done>
|
|
- SearchFTS, SearchFTSByFilename, SearchFTSTracks, and RebuildSearchIndex all use `track_metadata` VIEW
|
|
- No inline 5-table JOIN patterns remain in search.go (except in comments)
|
|
- All 15 existing FTS5 search tests pass with -race
|
|
- Function signatures unchanged — callers are unaffected
|
|
</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
```bash
|
|
# 1. Verify sqlc generates cleanly
|
|
cd backend/database && go tool sqlc generate
|
|
|
|
# 2. Verify all database tests pass (15 search tests + migrations)
|
|
cd backend && go test -tags webkit2_41 -race -count=1 -timeout 60s ./database/...
|
|
|
|
# 3. Verify no inline JOIN duplication remains in search.go
|
|
grep -c "LEFT JOIN recordings" backend/database/search.go # Should be 0
|
|
|
|
# 4. Verify VIEW is referenced
|
|
grep -c "track_metadata" backend/database/search.go # Should be 4+
|
|
|
|
# 5. Verify migration2 is unchanged
|
|
grep "LEFT JOIN recordings" backend/database/database.go # Should still exist (migration2 only)
|
|
|
|
# 6. Full build check
|
|
go build -tags webkit2_41 ./...
|
|
```
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- The duplicated 5-table JOIN pattern is eliminated from search.go (0 copies remain)
|
|
- All search queries use the `track_metadata` VIEW
|
|
- Migration 4 creates the VIEW for existing databases
|
|
- sqlc schema file enables future sqlc queries against the VIEW
|
|
- All 15 existing database tests pass with -race
|
|
- Full project builds without errors
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/06-sql-consolidation-code-quality/06-01-SUMMARY.md`
|
|
</output>
|