diff options
Diffstat (limited to 'models/issues/issue_update.go')
-rw-r--r-- | models/issues/issue_update.go | 390 |
1 files changed, 108 insertions, 282 deletions
diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index 7ddf7ee901..1c16817491 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -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" @@ -169,20 +167,9 @@ func CloseIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Comm return nil, err } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() - - comment, err := SetIssueAsClosed(ctx, issue, doer, false) - if err != nil { - return nil, err - } - if err := committer.Commit(); err != nil { - return nil, err - } - return comment, nil + return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) { + return SetIssueAsClosed(ctx, issue, doer, false) + }) } // ReopenIssue changes issue status to open. @@ -194,88 +181,64 @@ func ReopenIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Com return nil, err } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() - - comment, err := setIssueAsReopen(ctx, issue, doer) - if err != nil { - return nil, err - } - if err := committer.Commit(); err != nil { - return nil, err - } - return comment, nil + return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) { + return setIssueAsReopen(ctx, issue, doer) + }) } // ChangeIssueTitle changes the title of this issue, as the given user. func ChangeIssueTitle(ctx context.Context, issue *Issue, doer *user_model.User, oldTitle string) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - issue.Title = util.EllipsisDisplayString(issue.Title, 255) - if err = UpdateIssueCols(ctx, issue, "name"); err != nil { - return fmt.Errorf("updateIssueCols: %w", err) - } - - if err = issue.LoadRepo(ctx); err != nil { - return fmt.Errorf("loadRepo: %w", err) - } + return db.WithTx(ctx, func(ctx context.Context) error { + issue.Title = util.EllipsisDisplayString(issue.Title, 255) + if err = UpdateIssueCols(ctx, issue, "name"); err != nil { + return fmt.Errorf("updateIssueCols: %w", err) + } - opts := &CreateCommentOptions{ - Type: CommentTypeChangeTitle, - Doer: doer, - Repo: issue.Repo, - Issue: issue, - OldTitle: oldTitle, - NewTitle: issue.Title, - } - if _, err = CreateComment(ctx, opts); err != nil { - return fmt.Errorf("createComment: %w", err) - } - if err = issue.AddCrossReferences(ctx, doer, true); err != nil { - return err - } + if err = issue.LoadRepo(ctx); err != nil { + return fmt.Errorf("loadRepo: %w", err) + } - return committer.Commit() + opts := &CreateCommentOptions{ + Type: CommentTypeChangeTitle, + Doer: doer, + Repo: issue.Repo, + Issue: issue, + OldTitle: oldTitle, + NewTitle: issue.Title, + } + if _, err = CreateComment(ctx, opts); err != nil { + return fmt.Errorf("createComment: %w", err) + } + return issue.AddCrossReferences(ctx, doer, true) + }) } // ChangeIssueRef changes the branch of this issue, as the given user. func ChangeIssueRef(ctx context.Context, issue *Issue, doer *user_model.User, oldRef string) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - if err = UpdateIssueCols(ctx, issue, "ref"); err != nil { - return fmt.Errorf("updateIssueCols: %w", err) - } - - if err = issue.LoadRepo(ctx); err != nil { - return fmt.Errorf("loadRepo: %w", err) - } - oldRefFriendly := strings.TrimPrefix(oldRef, git.BranchPrefix) - newRefFriendly := strings.TrimPrefix(issue.Ref, git.BranchPrefix) + return db.WithTx(ctx, func(ctx context.Context) error { + if err = UpdateIssueCols(ctx, issue, "ref"); err != nil { + return fmt.Errorf("updateIssueCols: %w", err) + } - opts := &CreateCommentOptions{ - Type: CommentTypeChangeIssueRef, - Doer: doer, - Repo: issue.Repo, - Issue: issue, - OldRef: oldRefFriendly, - NewRef: newRefFriendly, - } - if _, err = CreateComment(ctx, opts); err != nil { - return fmt.Errorf("createComment: %w", err) - } + if err = issue.LoadRepo(ctx); err != nil { + return fmt.Errorf("loadRepo: %w", err) + } + oldRefFriendly := strings.TrimPrefix(oldRef, git.BranchPrefix) + newRefFriendly := strings.TrimPrefix(issue.Ref, git.BranchPrefix) - return committer.Commit() + opts := &CreateCommentOptions{ + Type: CommentTypeChangeIssueRef, + Doer: doer, + Repo: issue.Repo, + Issue: issue, + OldRef: oldRefFriendly, + NewRef: newRefFriendly, + } + if _, err = CreateComment(ctx, opts); err != nil { + return fmt.Errorf("createComment: %w", err) + } + return nil + }) } // AddDeletePRBranchComment adds delete branch comment for pull request issue @@ -297,64 +260,56 @@ func AddDeletePRBranchComment(ctx context.Context, doer *user_model.User, repo * // UpdateIssueAttachments update attachments by UUIDs for the issue func UpdateIssueAttachments(ctx context.Context, issueID int64, uuids []string) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, uuids) - if err != nil { - return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err) - } - for i := 0; i < len(attachments); i++ { - attachments[i].IssueID = issueID - if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil { - return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err) + return db.WithTx(ctx, func(ctx context.Context) error { + attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, uuids) + if err != nil { + return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err) } - } - return committer.Commit() + for i := range attachments { + attachments[i].IssueID = issueID + if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil { + return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err) + } + } + return nil + }) } // ChangeIssueContent changes issue content, as the given user. func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User, content string, contentVersion int) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - hasContentHistory, err := HasIssueContentHistory(ctx, issue.ID, 0) - if err != nil { - return fmt.Errorf("HasIssueContentHistory: %w", err) - } - if !hasContentHistory { - if err = SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0, - issue.CreatedUnix, issue.Content, true); err != nil { - return fmt.Errorf("SaveIssueContentHistory: %w", err) + return db.WithTx(ctx, func(ctx context.Context) error { + hasContentHistory, err := HasIssueContentHistory(ctx, issue.ID, 0) + if err != nil { + return fmt.Errorf("HasIssueContentHistory: %w", err) + } + if !hasContentHistory { + if err = SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0, + issue.CreatedUnix, issue.Content, true); err != nil { + return fmt.Errorf("SaveIssueContentHistory: %w", err) + } } - } - issue.Content = content - issue.ContentVersion = contentVersion + 1 + issue.Content = content + issue.ContentVersion = contentVersion + 1 - affected, err := db.GetEngine(ctx).ID(issue.ID).Cols("content", "content_version").Where("content_version = ?", contentVersion).Update(issue) - if err != nil { - return err - } - if affected == 0 { - return ErrIssueAlreadyChanged - } - - if err = SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0, - timeutil.TimeStampNow(), issue.Content, false); err != nil { - return fmt.Errorf("SaveIssueContentHistory: %w", err) - } + affected, err := db.GetEngine(ctx).ID(issue.ID).Cols("content", "content_version").Where("content_version = ?", contentVersion).Update(issue) + if err != nil { + return err + } + if affected == 0 { + return ErrIssueAlreadyChanged + } - if err = issue.AddCrossReferences(ctx, doer, true); err != nil { - return fmt.Errorf("addCrossReferences: %w", err) - } + if err = SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0, + timeutil.TimeStampNow(), issue.Content, false); err != nil { + return fmt.Errorf("SaveIssueContentHistory: %w", err) + } - return committer.Commit() + if err = issue.AddCrossReferences(ctx, doer, true); err != nil { + return fmt.Errorf("addCrossReferences: %w", err) + } + return nil + }) } // NewIssueOptions represents the options of a new issue. @@ -514,23 +469,19 @@ func UpdateIssueDeadline(ctx context.Context, issue *Issue, deadlineUnix timeuti if issue.DeadlineUnix == deadlineUnix { return nil } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - // Update the deadline - if err = UpdateIssueCols(ctx, &Issue{ID: issue.ID, DeadlineUnix: deadlineUnix}, "deadline_unix"); err != nil { - return err - } - - // Make the comment - if _, err = createDeadlineComment(ctx, doer, issue, deadlineUnix); err != nil { - return fmt.Errorf("createRemovedDueDateComment: %w", err) - } + return db.WithTx(ctx, func(ctx context.Context) error { + // Update the deadline + if err = UpdateIssueCols(ctx, &Issue{ID: issue.ID, DeadlineUnix: deadlineUnix}, "deadline_unix"); err != nil { + return err + } - return committer.Commit() + // Make the comment + if _, err = createDeadlineComment(ctx, doer, issue, deadlineUnix); err != nil { + return fmt.Errorf("createRemovedDueDateComment: %w", err) + } + return nil + }) } // FindAndUpdateIssueMentions finds users mentioned in the given content string, and saves them in the database. @@ -715,138 +666,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 - } - } - - 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]) +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 nil + return repoIDs, nil } |