10 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 06-sql-consolidation-code-quality | 01 | execute | 1 |
|
true |
|
|
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.
<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>
@.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
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 generatedirective at line 21
From backend/database/search.go:
func (d *DB) SearchFTS(query string, limit int) ([]SearchResult, error)— line 22func (d *DB) SearchFTSByFilename(query string, limit int) ([]SearchResult, error)— line 72func (d *DB) RebuildSearchIndex() error— line 161func (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.sqlwill sort after all table schemas (t > all existing prefixes)
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.
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.
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>