Skip to content

Commit 968c04c

Browse files
GiteaBotlunny
andauthored
Fix issue comment number (#30556) (#33055)
Backport #30556 by @lunny Fix #22419 Only comments with types `CommentTypeComment` and `CommentTypeReview` will be counted as conversations of the pull request. `CommentTypeReview` was missed in the previous implementation. Co-authored-by: Lunny Xiao <[email protected]>
1 parent 27de603 commit 968c04c

File tree

5 files changed

+89
-19
lines changed

5 files changed

+89
-19
lines changed

models/issues/comment.go

+33-5
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,20 @@ func (t CommentType) HasMailReplySupport() bool {
197197
return false
198198
}
199199

200+
func (t CommentType) CountedAsConversation() bool {
201+
for _, ct := range ConversationCountedCommentType() {
202+
if t == ct {
203+
return true
204+
}
205+
}
206+
return false
207+
}
208+
209+
// ConversationCountedCommentType returns the comment types that are counted as a conversation
210+
func ConversationCountedCommentType() []CommentType {
211+
return []CommentType{CommentTypeComment, CommentTypeReview}
212+
}
213+
200214
// RoleInRepo presents the user's participation in the repo
201215
type RoleInRepo string
202216

@@ -893,7 +907,7 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment
893907
}
894908
fallthrough
895909
case CommentTypeComment:
896-
if _, err = db.Exec(ctx, "UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID); err != nil {
910+
if err := UpdateIssueNumComments(ctx, opts.Issue.ID); err != nil {
897911
return err
898912
}
899913
fallthrough
@@ -1182,8 +1196,8 @@ func DeleteComment(ctx context.Context, comment *Comment) error {
11821196
return err
11831197
}
11841198

1185-
if comment.Type == CommentTypeComment {
1186-
if _, err := e.ID(comment.IssueID).Decr("num_comments").Update(new(Issue)); err != nil {
1199+
if comment.Type.CountedAsConversation() {
1200+
if err := UpdateIssueNumComments(ctx, comment.IssueID); err != nil {
11871201
return err
11881202
}
11891203
}
@@ -1300,6 +1314,21 @@ func (c *Comment) HasOriginalAuthor() bool {
13001314
return c.OriginalAuthor != "" && c.OriginalAuthorID != 0
13011315
}
13021316

1317+
func UpdateIssueNumCommentsBuilder(issueID int64) *builder.Builder {
1318+
subQuery := builder.Select("COUNT(*)").From("`comment`").Where(
1319+
builder.Eq{"issue_id": issueID}.And(
1320+
builder.In("`type`", ConversationCountedCommentType()),
1321+
))
1322+
1323+
return builder.Update(builder.Eq{"num_comments": subQuery}).
1324+
From("`issue`").Where(builder.Eq{"id": issueID})
1325+
}
1326+
1327+
func UpdateIssueNumComments(ctx context.Context, issueID int64) error {
1328+
_, err := db.GetEngine(ctx).Exec(UpdateIssueNumCommentsBuilder(issueID))
1329+
return err
1330+
}
1331+
13031332
// InsertIssueComments inserts many comments of issues.
13041333
func InsertIssueComments(ctx context.Context, comments []*Comment) error {
13051334
if len(comments) == 0 {
@@ -1332,8 +1361,7 @@ func InsertIssueComments(ctx context.Context, comments []*Comment) error {
13321361
}
13331362

13341363
for _, issueID := range issueIDs {
1335-
if _, err := db.Exec(ctx, "UPDATE issue set num_comments = (SELECT count(*) FROM comment WHERE issue_id = ? AND `type`=?) WHERE id = ?",
1336-
issueID, CommentTypeComment, issueID); err != nil {
1364+
if err := UpdateIssueNumComments(ctx, issueID); err != nil {
13371365
return err
13381366
}
13391367
}

models/issues/comment_test.go

+9
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,12 @@ func TestMigrate_InsertIssueComments(t *testing.T) {
9797

9898
unittest.CheckConsistencyFor(t, &issues_model.Issue{})
9999
}
100+
101+
func Test_UpdateIssueNumComments(t *testing.T) {
102+
assert.NoError(t, unittest.PrepareTestDatabase())
103+
issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
104+
105+
assert.NoError(t, issues_model.UpdateIssueNumComments(db.DefaultContext, issue2.ID))
106+
issue2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
107+
assert.EqualValues(t, 1, issue2.NumComments)
108+
}

models/issues/review.go

+4
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,10 @@ func InsertReviews(ctx context.Context, reviews []*Review) error {
639639
return err
640640
}
641641
}
642+
643+
if err := UpdateIssueNumComments(ctx, review.IssueID); err != nil {
644+
return err
645+
}
642646
}
643647

644648
return committer.Commit()

models/repo.go

+29-14
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import (
1919
"code.gitea.io/gitea/models/unit"
2020
user_model "code.gitea.io/gitea/models/user"
2121
"code.gitea.io/gitea/modules/log"
22+
23+
"xorm.io/builder"
2224
)
2325

2426
// Init initialize model
@@ -27,7 +29,7 @@ func Init(ctx context.Context) error {
2729
}
2830

2931
type repoChecker struct {
30-
querySQL func(ctx context.Context) ([]map[string][]byte, error)
32+
querySQL func(ctx context.Context) ([]int64, error)
3133
correctSQL func(ctx context.Context, id int64) error
3234
desc string
3335
}
@@ -38,8 +40,7 @@ func repoStatsCheck(ctx context.Context, checker *repoChecker) {
3840
log.Error("Select %s: %v", checker.desc, err)
3941
return
4042
}
41-
for _, result := range results {
42-
id, _ := strconv.ParseInt(string(result["id"]), 10, 64)
43+
for _, id := range results {
4344
select {
4445
case <-ctx.Done():
4546
log.Warn("CheckRepoStats: Cancelled before checking %s for with id=%d", checker.desc, id)
@@ -54,21 +55,23 @@ func repoStatsCheck(ctx context.Context, checker *repoChecker) {
5455
}
5556
}
5657

57-
func StatsCorrectSQL(ctx context.Context, sql string, id int64) error {
58-
_, err := db.GetEngine(ctx).Exec(sql, id, id)
58+
func StatsCorrectSQL(ctx context.Context, sql any, ids ...any) error {
59+
args := []any{sql}
60+
args = append(args, ids...)
61+
_, err := db.GetEngine(ctx).Exec(args...)
5962
return err
6063
}
6164

6265
func repoStatsCorrectNumWatches(ctx context.Context, id int64) error {
63-
return StatsCorrectSQL(ctx, "UPDATE `repository` SET num_watches=(SELECT COUNT(*) FROM `watch` WHERE repo_id=? AND mode<>2) WHERE id=?", id)
66+
return StatsCorrectSQL(ctx, "UPDATE `repository` SET num_watches=(SELECT COUNT(*) FROM `watch` WHERE repo_id=? AND mode<>2) WHERE id=?", id, id)
6467
}
6568

6669
func repoStatsCorrectNumStars(ctx context.Context, id int64) error {
67-
return StatsCorrectSQL(ctx, "UPDATE `repository` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE repo_id=?) WHERE id=?", id)
70+
return StatsCorrectSQL(ctx, "UPDATE `repository` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE repo_id=?) WHERE id=?", id, id)
6871
}
6972

7073
func labelStatsCorrectNumIssues(ctx context.Context, id int64) error {
71-
return StatsCorrectSQL(ctx, "UPDATE `label` SET num_issues=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=?) WHERE id=?", id)
74+
return StatsCorrectSQL(ctx, "UPDATE `label` SET num_issues=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=?) WHERE id=?", id, id)
7275
}
7376

7477
func labelStatsCorrectNumIssuesRepo(ctx context.Context, id int64) error {
@@ -105,11 +108,11 @@ func milestoneStatsCorrectNumIssuesRepo(ctx context.Context, id int64) error {
105108
}
106109

107110
func userStatsCorrectNumRepos(ctx context.Context, id int64) error {
108-
return StatsCorrectSQL(ctx, "UPDATE `user` SET num_repos=(SELECT COUNT(*) FROM `repository` WHERE owner_id=?) WHERE id=?", id)
111+
return StatsCorrectSQL(ctx, "UPDATE `user` SET num_repos=(SELECT COUNT(*) FROM `repository` WHERE owner_id=?) WHERE id=?", id, id)
109112
}
110113

111114
func repoStatsCorrectIssueNumComments(ctx context.Context, id int64) error {
112-
return StatsCorrectSQL(ctx, "UPDATE `issue` SET num_comments=(SELECT COUNT(*) FROM `comment` WHERE issue_id=? AND type=0) WHERE id=?", id)
115+
return StatsCorrectSQL(ctx, issues_model.UpdateIssueNumCommentsBuilder(id))
113116
}
114117

115118
func repoStatsCorrectNumIssues(ctx context.Context, id int64) error {
@@ -128,9 +131,12 @@ func repoStatsCorrectNumClosedPulls(ctx context.Context, id int64) error {
128131
return repo_model.UpdateRepoIssueNumbers(ctx, id, true, true)
129132
}
130133

131-
func statsQuery(args ...any) func(context.Context) ([]map[string][]byte, error) {
132-
return func(ctx context.Context) ([]map[string][]byte, error) {
133-
return db.GetEngine(ctx).Query(args...)
134+
// statsQuery returns a function that queries the database for a list of IDs
135+
// sql could be a string or a *builder.Builder
136+
func statsQuery(sql any, args ...any) func(context.Context) ([]int64, error) {
137+
return func(ctx context.Context) ([]int64, error) {
138+
var ids []int64
139+
return ids, db.GetEngine(ctx).SQL(sql, args...).Find(&ids)
134140
}
135141
}
136142

@@ -201,7 +207,16 @@ func CheckRepoStats(ctx context.Context) error {
201207
},
202208
// Issue.NumComments
203209
{
204-
statsQuery("SELECT `issue`.id FROM `issue` WHERE `issue`.num_comments!=(SELECT COUNT(*) FROM `comment` WHERE issue_id=`issue`.id AND type=0)"),
210+
statsQuery(builder.Select("`issue`.id").From("`issue`").Where(
211+
builder.Neq{
212+
"`issue`.num_comments": builder.Select("COUNT(*)").From("`comment`").Where(
213+
builder.Expr("issue_id = `issue`.id").And(
214+
builder.In("type", issues_model.ConversationCountedCommentType()),
215+
),
216+
),
217+
},
218+
),
219+
),
205220
repoStatsCorrectIssueNumComments,
206221
"issue count 'num_comments'",
207222
},

models/repo_test.go

+14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"testing"
88

99
"code.gitea.io/gitea/models/db"
10+
issues_model "code.gitea.io/gitea/models/issues"
1011
"code.gitea.io/gitea/models/unittest"
1112

1213
"github.com/stretchr/testify/assert"
@@ -22,3 +23,16 @@ func TestDoctorUserStarNum(t *testing.T) {
2223

2324
assert.NoError(t, DoctorUserStarNum(db.DefaultContext))
2425
}
26+
27+
func Test_repoStatsCorrectIssueNumComments(t *testing.T) {
28+
assert.NoError(t, unittest.PrepareTestDatabase())
29+
30+
issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
31+
assert.NotNil(t, issue2)
32+
assert.EqualValues(t, 0, issue2.NumComments) // the fixture data is wrong, but we don't fix it here
33+
34+
assert.NoError(t, repoStatsCorrectIssueNumComments(db.DefaultContext, 2))
35+
// reload the issue
36+
issue2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
37+
assert.EqualValues(t, 1, issue2.NumComments)
38+
}

0 commit comments

Comments
 (0)