package database import ( "database/sql" "fmt" "log" _ "github.com/mattn/go-sqlite3" ) func InitDB(db *sql.DB) { schema := ` CREATE TABLE IF NOT EXISTS currencies ( id INTEGER PRIMARY KEY AUTOINCREMENT, code TEXT NOT NULL UNIQUE, name TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS companies ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, shares_outstanding INTEGER NOT NULL, price REAL NOT NULL, currency_id INTEGER NOT NULL, FOREIGN KEY (currency_id) REFERENCES currencies(id) ); CREATE TABLE IF NOT EXISTS periods ( id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT NOT NULL CHECK(type IN ('Q', 'H', 'Y')), year INTEGER NOT NULL, idx INTEGER NOT NULL, start_date TEXT NOT NULL, end_date TEXT NOT NULL, UNIQUE(type, year, idx) ); CREATE TABLE IF NOT EXISTS category ( id INTEGER PRIMARY KEY AUTOINCREMENT, company_id INTEGER NOT NULL, parent_id INTEGER, name TEXT NOT NULL, FOREIGN KEY (company_id) REFERENCES companies(id), FOREIGN KEY (parent_id) REFERENCES category(id), UNIQUE(company_id, name) ); CREATE TABLE IF NOT EXISTS revenue_entries ( id INTEGER PRIMARY KEY AUTOINCREMENT, company_id INTEGER NOT NULL, currency_id INTEGER NOT NULL, category_id INTEGER NOT NULL, period_id INTEGER NOT NULL, value REAL NOT NULL, FOREIGN KEY (company_id) REFERENCES companies(id), FOREIGN KEY (currency_id) REFERENCES currencies(id), FOREIGN KEY (category_id) REFERENCES category(id), FOREIGN KEY (period_id) REFERENCES periods(id), UNIQUE(company_id, category_id, period_id) );` if _, err := db.Exec(schema); err != nil { log.Fatal("Failed to create tables:", err) } fmt.Println("Tables ready") } func MigrateAddUniqueToRevenueEntries(db *sql.DB) error { steps := []string{ // 1. copy existing data into a temp table with the new constraint `CREATE TABLE IF NOT EXISTS revenue_entries_new ( id INTEGER PRIMARY KEY AUTOINCREMENT, company_id INTEGER NOT NULL, currency_id INTEGER NOT NULL, category_id INTEGER NOT NULL, period_id INTEGER NOT NULL, value REAL NOT NULL, FOREIGN KEY (company_id) REFERENCES companies(id), FOREIGN KEY (currency_id) REFERENCES currencies(id), FOREIGN KEY (category_id) REFERENCES category(id), FOREIGN KEY (period_id) REFERENCES periods(id), UNIQUE(company_id, category_id, period_id) )`, // 2. copy data over `INSERT OR IGNORE INTO revenue_entries_new SELECT id, company_id, currency_id, category_id, period_id, value FROM revenue_entries`, // 3. drop old table `DROP TABLE revenue_entries`, // 4. rename new table `ALTER TABLE revenue_entries_new RENAME TO revenue_entries`, } for _, step := range steps { if _, err := db.Exec(step); err != nil { return fmt.Errorf("migration failed: %w", err) } } return nil }