Fix FOREIGN KEY constraint failed when operating on non-existent issues

Fixes #325

- Fix CloseIssue to check rows affected before inserting event
- Fix RemoveLabel to check rows affected before inserting event
- Fix UpdateIssueID to check rows affected before inserting event
- Prevent orphan events and confusing error messages

Amp-Thread-ID: https://ampcode.com/threads/T-aa765a68-5cc4-465b-a2f6-aa008933c11e
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Steve Yegge
2025-11-20 12:35:26 -05:00
parent 800b0c3fca
commit 09666b4219
3 changed files with 212 additions and 137 deletions

View File

@@ -20,11 +20,20 @@ func (s *SQLiteStorage) executeLabelOperation(
operationError string,
) error {
return s.withTx(ctx, func(tx *sql.Tx) error {
_, err := tx.ExecContext(ctx, labelSQL, labelSQLArgs...)
result, err := tx.ExecContext(ctx, labelSQL, labelSQLArgs...)
if err != nil {
return fmt.Errorf("%s: %w", operationError, err)
}
rows, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("failed to check rows affected: %w", err)
}
if rows == 0 {
// No change made (label already existed or didn't exist), so don't record event
return nil
}
_, err = tx.ExecContext(ctx, `
INSERT INTO events (issue_id, event_type, actor, comment)
VALUES (?, ?, ?, ?)

View File

@@ -711,7 +711,7 @@ func (s *SQLiteStorage) UpdateIssueID(ctx context.Context, oldID, newID string,
}
defer func() { _ = tx.Rollback() }()
_, err = tx.ExecContext(ctx, `
result, err := tx.ExecContext(ctx, `
UPDATE issues
SET id = ?, title = ?, description = ?, design = ?, acceptance_criteria = ?, notes = ?, updated_at = ?
WHERE id = ?
@@ -720,6 +720,14 @@ func (s *SQLiteStorage) UpdateIssueID(ctx context.Context, oldID, newID string,
return fmt.Errorf("failed to update issue ID: %w", err)
}
rows, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("failed to get rows affected: %w", err)
}
if rows == 0 {
return fmt.Errorf("issue not found: %s", oldID)
}
_, err = tx.ExecContext(ctx, `UPDATE dependencies SET issue_id = ? WHERE issue_id = ?`, newID, oldID)
if err != nil {
return fmt.Errorf("failed to update issue_id in dependencies: %w", err)
@@ -812,13 +820,21 @@ func (s *SQLiteStorage) CloseIssue(ctx context.Context, id string, reason string
}
defer func() { _ = tx.Rollback() }()
_, err = tx.ExecContext(ctx, `
result, err := tx.ExecContext(ctx, `
UPDATE issues SET status = ?, closed_at = ?, updated_at = ?
WHERE id = ?
`, types.StatusClosed, now, now, id)
if err != nil {
return fmt.Errorf("failed to close issue: %w", err)
}
rows, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("failed to get rows affected: %w", err)
}
if rows == 0 {
return fmt.Errorf("issue not found: %s", id)
}
_, err = tx.ExecContext(ctx, `
INSERT INTO events (issue_id, event_type, actor, comment)