From f3a61a64582316f517ce448c5ed2e86e72154595 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Tue, 14 Oct 2025 00:19:10 -0700 Subject: [PATCH] Add auto-migration for dirty_issues table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement automatic database migration to add the dirty_issues table for existing databases that were created before the incremental export feature (bd-39) was implemented. Changes: - Add migrateDirtyIssuesTable() function in sqlite.go - Check for dirty_issues table existence on database initialization - Create table and index if missing (silent migration) - Call migration after schema initialization in New() The migration: - Queries sqlite_master to check if dirty_issues table exists - If missing, creates the table with proper schema and index - Happens automatically on first database access after upgrade - No user intervention required - Fails safely if table already exists (no-op) Testing: - Created test database without dirty_issues table - Verified table was auto-created on first command - Verified issue was properly marked dirty - All existing tests pass This makes the incremental export feature (bd-39) work seamlessly with existing databases without requiring manual migration steps. Closes bd-51 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- internal/storage/sqlite/sqlite.go | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/internal/storage/sqlite/sqlite.go b/internal/storage/sqlite/sqlite.go index 1a1cf27d..af999a05 100644 --- a/internal/storage/sqlite/sqlite.go +++ b/internal/storage/sqlite/sqlite.go @@ -48,6 +48,11 @@ func New(path string) (*SQLiteStorage, error) { return nil, fmt.Errorf("failed to initialize schema: %w", err) } + // Migrate existing databases to add dirty_issues table if missing + if err := migrateDirtyIssuesTable(db); err != nil { + return nil, fmt.Errorf("failed to migrate dirty_issues table: %w", err) + } + // Get next ID nextID := getNextID(db) @@ -57,6 +62,41 @@ func New(path string) (*SQLiteStorage, error) { }, nil } +// migrateDirtyIssuesTable checks if the dirty_issues table exists and creates it if missing. +// This ensures existing databases created before the incremental export feature get migrated automatically. +func migrateDirtyIssuesTable(db *sql.DB) error { + // Check if dirty_issues table exists + var tableName string + err := db.QueryRow(` + SELECT name FROM sqlite_master + WHERE type='table' AND name='dirty_issues' + `).Scan(&tableName) + + if err == sql.ErrNoRows { + // Table doesn't exist, create it + _, err := db.Exec(` + CREATE TABLE dirty_issues ( + issue_id TEXT PRIMARY KEY, + marked_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE + ); + CREATE INDEX idx_dirty_issues_marked_at ON dirty_issues(marked_at); + `) + if err != nil { + return fmt.Errorf("failed to create dirty_issues table: %w", err) + } + // Table created successfully - no need to log, happens silently + return nil + } + + if err != nil { + return fmt.Errorf("failed to check for dirty_issues table: %w", err) + } + + // Table exists, no migration needed + return nil +} + // getNextID determines the next issue ID to use func getNextID(db *sql.DB) int { // Get prefix from config, default to "bd"