diff --git a/internal/storage/sqlite/migrations.go b/internal/storage/sqlite/migrations.go index 778ce754..cb2fb061 100644 --- a/internal/storage/sqlite/migrations.go +++ b/internal/storage/sqlite/migrations.go @@ -42,6 +42,7 @@ var migrationsList = []Migration{ {"pinned_column", migrations.MigratePinnedColumn}, {"is_template_column", migrations.MigrateIsTemplateColumn}, {"remove_depends_on_fk", migrations.MigrateRemoveDependsOnFK}, + {"additional_indexes", migrations.MigrateAdditionalIndexes}, } // MigrationInfo contains metadata about a migration for inspection @@ -91,6 +92,7 @@ func getMigrationDescription(name string) string { "pinned_column": "Adds pinned column for persistent context markers (bd-7h5)", "is_template_column": "Adds is_template column for template molecules (beads-1ra)", "remove_depends_on_fk": "Removes FK constraint on depends_on_id to allow external references (bd-zmmy)", + "additional_indexes": "Adds performance optimization indexes for common query patterns (bd-h0we)", } if desc, ok := descriptions[name]; ok { diff --git a/internal/storage/sqlite/migrations/026_additional_indexes.go b/internal/storage/sqlite/migrations/026_additional_indexes.go new file mode 100644 index 00000000..c729d4a8 --- /dev/null +++ b/internal/storage/sqlite/migrations/026_additional_indexes.go @@ -0,0 +1,58 @@ +package migrations + +import ( + "database/sql" + "fmt" +) + +// MigrateAdditionalIndexes adds performance optimization indexes identified +// during schema review (bd-h0we). +// +// Indexes added: +// - idx_issues_updated_at: For GetStaleIssues date filtering (bd-bha9) +// - idx_issues_status_priority: For common list query patterns (bd-a9y3) +// - idx_labels_label_issue: Covering index for label lookups (bd-jke6) +// - idx_dependencies_issue_type: For blocked issues queries (bd-8x3w) +// - idx_events_issue_type: For close reason queries (bd-lk39) +func MigrateAdditionalIndexes(db *sql.DB) error { + indexes := []struct { + name string + sql string + }{ + // Issues table indexes + { + name: "idx_issues_updated_at", + sql: `CREATE INDEX IF NOT EXISTS idx_issues_updated_at ON issues(updated_at)`, + }, + { + name: "idx_issues_status_priority", + sql: `CREATE INDEX IF NOT EXISTS idx_issues_status_priority ON issues(status, priority)`, + }, + + // Labels table covering index + { + name: "idx_labels_label_issue", + sql: `CREATE INDEX IF NOT EXISTS idx_labels_label_issue ON labels(label, issue_id)`, + }, + + // Dependencies table composite index + { + name: "idx_dependencies_issue_type", + sql: `CREATE INDEX IF NOT EXISTS idx_dependencies_issue_type ON dependencies(issue_id, type)`, + }, + + // Events table composite index + { + name: "idx_events_issue_type", + sql: `CREATE INDEX IF NOT EXISTS idx_events_issue_type ON events(issue_id, event_type)`, + }, + } + + for _, idx := range indexes { + if _, err := db.Exec(idx.sql); err != nil { + return fmt.Errorf("failed to create index %s: %w", idx.name, err) + } + } + + return nil +}