mirror of
https://github.com/go-gitea/gitea
synced 2025-07-03 09:07:19 +00:00
Fix doctor deleting orphaned issues attachments (#34142)
Fix the bug when deleting orphaned issues attachments. The attachments maybe stored on other storages service rather than disk.
This commit is contained in:
@ -12,9 +12,7 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
project_model "code.gitea.io/gitea/models/project"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
system_model "code.gitea.io/gitea/models/system"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
@ -715,138 +713,13 @@ func UpdateReactionsMigrationsByType(ctx context.Context, gitServiceType api.Git
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteIssuesByRepoID deletes issues by repositories id
|
||||
func DeleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths []string, err error) {
|
||||
// MariaDB has a performance bug: https://jira.mariadb.org/browse/MDEV-16289
|
||||
// so here it uses "DELETE ... WHERE IN" with pre-queried IDs.
|
||||
sess := db.GetEngine(ctx)
|
||||
|
||||
for {
|
||||
issueIDs := make([]int64, 0, db.DefaultMaxInSize)
|
||||
|
||||
err := sess.Table(&Issue{}).Where("repo_id = ?", repoID).OrderBy("id").Limit(db.DefaultMaxInSize).Cols("id").Find(&issueIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issueIDs) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Delete content histories
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&ContentHistory{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Delete comments and attachments
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&Comment{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Dependencies for issues in this repository
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&IssueDependency{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Delete dependencies for issues in other repositories
|
||||
_, err = sess.In("dependency_id", issueIDs).Delete(&IssueDependency{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&IssueUser{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&Reaction{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&IssueWatch{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&Stopwatch{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&TrackedTime{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&project_model.ProjectIssue{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("dependent_issue_id", issueIDs).Delete(&Comment{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var attachments []*repo_model.Attachment
|
||||
err = sess.In("issue_id", issueIDs).Find(&attachments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for j := range attachments {
|
||||
attachmentPaths = append(attachmentPaths, attachments[j].RelativePath())
|
||||
}
|
||||
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&repo_model.Attachment{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("id", issueIDs).Delete(&Issue{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func GetOrphanedIssueRepoIDs(ctx context.Context) ([]int64, error) {
|
||||
var repoIDs []int64
|
||||
if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id").
|
||||
Join("LEFT", "repository", "issue.repo_id=repository.id").
|
||||
Where(builder.IsNull{"repository.id"}).
|
||||
Find(&repoIDs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return attachmentPaths, err
|
||||
}
|
||||
|
||||
// DeleteOrphanedIssues delete issues without a repo
|
||||
func DeleteOrphanedIssues(ctx context.Context) error {
|
||||
var attachmentPaths []string
|
||||
err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||
var ids []int64
|
||||
|
||||
if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id").
|
||||
Join("LEFT", "repository", "issue.repo_id=repository.id").
|
||||
Where(builder.IsNull{"repository.id"}).GroupBy("issue.repo_id").
|
||||
Find(&ids); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range ids {
|
||||
paths, err := DeleteIssuesByRepoID(ctx, ids[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attachmentPaths = append(attachmentPaths, paths...)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove issue attachment files.
|
||||
for i := range attachmentPaths {
|
||||
// FIXME: it's not right, because the attachment might not be on local filesystem
|
||||
system_model.RemoveAllWithNotice(ctx, "Delete issue attachment", attachmentPaths[i])
|
||||
}
|
||||
return nil
|
||||
return repoIDs, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user