package sqlite import ( "database/sql" "fmt" ) // backfillTicketKeys assigns project codes and sequential ticket keys for rows // that predate migration 005. Safe to run repeatedly (no-ops when already set). func (db *DB) backfillTicketKeys() error { if err := db.backfillProjectCodes(); err != nil { return err } return db.backfillTicketNumbers() } func (db *DB) backfillProjectCodes() error { rows, err := db.SQL.Query(`SELECT id, name, code FROM projects`) if err != nil { return fmt.Errorf("backfill codes: list: %w", err) } defer rows.Close() type row struct { id, name, code string } var all []row used := map[string]bool{} for rows.Next() { var r row if err := rows.Scan(&r.id, &r.name, &r.code); err != nil { return err } all = append(all, r) if r.code != "" { used[r.code] = true } } if err := rows.Err(); err != nil { return err } for _, r := range all { if r.code != "" { continue } code := nextUniqueCode(r.name, used) used[code] = true if _, err := db.SQL.Exec(`UPDATE projects SET code = ? WHERE id = ?`, code, r.id); err != nil { return fmt.Errorf("backfill code %s: %w", r.id, err) } } return nil } func (db *DB) backfillTicketNumbers() error { type proj struct { id, code string seq int } var projects []proj { projRows, err := db.SQL.Query(`SELECT id, code, ticket_seq FROM projects`) if err != nil { return fmt.Errorf("backfill tickets: projects: %w", err) } for projRows.Next() { var p proj if err := projRows.Scan(&p.id, &p.code, &p.seq); err != nil { projRows.Close() return err } projects = append(projects, p) } err = projRows.Err() projRows.Close() if err != nil { return err } } for _, p := range projects { type trow struct { id, key string number sql.NullInt64 } var tickets []trow { tRows, err := db.SQL.Query(` SELECT id, number, key FROM tickets WHERE project_id = ? ORDER BY created_at ASC, id ASC`, p.id) if err != nil { return err } for tRows.Next() { var tr trow if err := tRows.Scan(&tr.id, &tr.number, &tr.key); err != nil { tRows.Close() return err } tickets = append(tickets, tr) } err = tRows.Err() tRows.Close() if err != nil { return err } } seq := p.seq for _, tr := range tickets { if tr.number.Valid && tr.key != "" { if int(tr.number.Int64) > seq { seq = int(tr.number.Int64) } continue } seq++ newKey := fmt.Sprintf("%s-%d", p.code, seq) if _, err := db.SQL.Exec(`UPDATE tickets SET number = ?, key = ? WHERE id = ?`, seq, newKey, tr.id); err != nil { return fmt.Errorf("backfill ticket %s: %w", tr.id, err) } } if seq != p.seq { if _, err := db.SQL.Exec(`UPDATE projects SET ticket_seq = ? WHERE id = ?`, seq, p.id); err != nil { return err } } } return nil }