1
1
mirror of https://github.com/go-gitea/gitea synced 2025-07-23 10:48:37 +00:00

Fix milestones too many SQL variables bug (#10880) (#10904)

* Fix milestones too many SQL variables bug

* Fix test

* Don't display repositories with no milestone and fix tests

* Remove unused code and add some comments
This commit is contained in:
Lunny Xiao
2020-03-31 21:40:37 +08:00
committed by GitHub
parent 596eebb2b6
commit 139fc7cfee
5 changed files with 144 additions and 108 deletions

View File

@@ -521,10 +521,12 @@ func DeleteMilestoneByRepoID(repoID, id int64) error {
return sess.Commit()
}
// CountMilestonesByRepoIDs map from repoIDs to number of milestones matching the options`
func CountMilestonesByRepoIDs(repoIDs []int64, isClosed bool) (map[int64]int64, error) {
// CountMilestones map from repo conditions to number of milestones matching the options`
func CountMilestones(repoCond builder.Cond, isClosed bool) (map[int64]int64, error) {
sess := x.Where("is_closed = ?", isClosed)
sess.In("repo_id", repoIDs)
if repoCond.IsValid() {
sess.In("repo_id", builder.Select("id").From("repository").Where(repoCond))
}
countsSlice := make([]*struct {
RepoID int64
@@ -544,11 +546,21 @@ func CountMilestonesByRepoIDs(repoIDs []int64, isClosed bool) (map[int64]int64,
return countMap, nil
}
// GetMilestonesByRepoIDs returns a list of milestones of given repositories and status.
func GetMilestonesByRepoIDs(repoIDs []int64, page int, isClosed bool, sortType string) (MilestoneList, error) {
// CountMilestonesByRepoIDs map from repoIDs to number of milestones matching the options`
func CountMilestonesByRepoIDs(repoIDs []int64, isClosed bool) (map[int64]int64, error) {
return CountMilestones(
builder.In("repo_id", repoIDs),
isClosed,
)
}
// SearchMilestones search milestones
func SearchMilestones(repoCond builder.Cond, page int, isClosed bool, sortType string) (MilestoneList, error) {
miles := make([]*Milestone, 0, setting.UI.IssuePagingNum)
sess := x.Where("is_closed = ?", isClosed)
sess.In("repo_id", repoIDs)
if repoCond.IsValid() {
sess.In("repo_id", builder.Select("id").From("repository").Where(repoCond))
}
if page > 0 {
sess = sess.Limit(setting.UI.IssuePagingNum, (page-1)*setting.UI.IssuePagingNum)
}
@@ -570,25 +582,45 @@ func GetMilestonesByRepoIDs(repoIDs []int64, page int, isClosed bool, sortType s
return miles, sess.Find(&miles)
}
// GetMilestonesByRepoIDs returns a list of milestones of given repositories and status.
func GetMilestonesByRepoIDs(repoIDs []int64, page int, isClosed bool, sortType string) (MilestoneList, error) {
return SearchMilestones(
builder.In("repo_id", repoIDs),
page,
isClosed,
sortType,
)
}
// MilestonesStats represents milestone statistic information.
type MilestonesStats struct {
OpenCount, ClosedCount int64
}
// Total returns the total counts of milestones
func (m MilestonesStats) Total() int64 {
return m.OpenCount + m.ClosedCount
}
// GetMilestonesStats returns milestone statistic information for dashboard by given conditions.
func GetMilestonesStats(userRepoIDs []int64) (*MilestonesStats, error) {
func GetMilestonesStats(repoCond builder.Cond) (*MilestonesStats, error) {
var err error
stats := &MilestonesStats{}
stats.OpenCount, err = x.Where("is_closed = ?", false).
And(builder.In("repo_id", userRepoIDs)).
Count(new(Milestone))
sess := x.Where("is_closed = ?", false)
if repoCond.IsValid() {
sess.And(builder.In("repo_id", builder.Select("id").From("repository").Where(repoCond)))
}
stats.OpenCount, err = sess.Count(new(Milestone))
if err != nil {
return nil, err
}
stats.ClosedCount, err = x.Where("is_closed = ?", true).
And(builder.In("repo_id", userRepoIDs)).
Count(new(Milestone))
sess = x.Where("is_closed = ?", true)
if repoCond.IsValid() {
sess.And(builder.In("repo_id", builder.Select("id").From("repository").Where(repoCond)))
}
stats.ClosedCount, err = sess.Count(new(Milestone))
if err != nil {
return nil, err
}

View File

@@ -11,6 +11,7 @@ import (
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
"github.com/stretchr/testify/assert"
)
@@ -370,7 +371,7 @@ func TestGetMilestonesStats(t *testing.T) {
repo1 := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
repo2 := AssertExistsAndLoadBean(t, &Repository{ID: 2}).(*Repository)
milestoneStats, err := GetMilestonesStats([]int64{repo1.ID, repo2.ID})
milestoneStats, err := GetMilestonesStats(builder.In("repo_id", []int64{repo1.ID, repo2.ID}))
assert.NoError(t, err)
assert.EqualValues(t, repo1.NumOpenMilestones+repo2.NumOpenMilestones, milestoneStats.OpenCount)
assert.EqualValues(t, repo1.NumClosedMilestones+repo2.NumClosedMilestones, milestoneStats.ClosedCount)

View File

@@ -144,6 +144,10 @@ type SearchRepoOptions struct {
TopicOnly bool
// include description in keyword search
IncludeDescription bool
// None -> include has milestones AND has no milestone
// True -> include just has milestones
// False -> include just has no milestone
HasMilestones util.OptionalBool
}
//SearchOrderBy is used to sort the result
@@ -171,12 +175,9 @@ const (
SearchOrderByForksReverse SearchOrderBy = "num_forks DESC"
)
// SearchRepository returns repositories based on search options,
// SearchRepositoryCondition returns repositories based on search options,
// it returns results in given range and number of total results.
func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
if opts.Page <= 0 {
opts.Page = 1
}
func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
var cond = builder.NewCond()
if opts.Private {
@@ -276,6 +277,29 @@ func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue})
}
switch opts.HasMilestones {
case util.OptionalBoolTrue:
cond = cond.And(builder.Gt{"num_milestones": 0})
case util.OptionalBoolFalse:
cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"}))
}
return cond
}
// SearchRepository returns repositories based on search options,
// it returns results in given range and number of total results.
func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
cond := SearchRepositoryCondition(opts)
return SearchRepositoryByCondition(opts, cond)
}
// SearchRepositoryByCondition search repositories by condition
func SearchRepositoryByCondition(opts *SearchRepoOptions, cond builder.Cond) (RepositoryList, int64, error) {
if opts.Page <= 0 {
opts.Page = 1
}
if len(opts.OrderBy) == 0 {
opts.OrderBy = SearchOrderByAlphabetically
}
@@ -296,11 +320,11 @@ func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
}
repos := make(RepositoryList, 0, opts.PageSize)
if err = sess.
Where(cond).
OrderBy(opts.OrderBy.String()).
Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).
Find(&repos); err != nil {
sess.Where(cond).OrderBy(opts.OrderBy.String())
if opts.PageSize > 0 {
sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
if err = sess.Find(&repos); err != nil {
return nil, 0, fmt.Errorf("Repo: %v", err)
}