* Ensure topics added using the API are added to the repository Fix #12426 Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>tags/v1.15.0-dev
@@ -248,6 +248,8 @@ var migrations = []Migration{ | |||
NewMigration("add changed_protected_files column for pull_request table", addChangedProtectedFilesPullRequestColumn), | |||
// v156 -> v157 | |||
NewMigration("fix publisher ID for tag releases", fixPublisherIDforTagReleases), | |||
// v157 -> v158 | |||
NewMigration("ensure repo topics are up-to-date", fixRepoTopics), | |||
} | |||
// GetCurrentDBVersion returns the current db version | |||
@@ -0,0 +1,68 @@ | |||
// Copyright 2020 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package migrations | |||
import ( | |||
"xorm.io/xorm" | |||
) | |||
func fixRepoTopics(x *xorm.Engine) error { | |||
type Topic struct { | |||
ID int64 `xorm:"pk autoincr"` | |||
Name string `xorm:"UNIQUE VARCHAR(25)"` | |||
RepoCount int | |||
} | |||
type RepoTopic struct { | |||
RepoID int64 `xorm:"pk"` | |||
TopicID int64 `xorm:"pk"` | |||
} | |||
type Repository struct { | |||
ID int64 `xorm:"pk autoincr"` | |||
Topics []string `xorm:"TEXT JSON"` | |||
} | |||
const batchSize = 100 | |||
sess := x.NewSession() | |||
defer sess.Close() | |||
repos := make([]*Repository, 0, batchSize) | |||
topics := make([]string, 0, batchSize) | |||
for start := 0; ; start += batchSize { | |||
repos = repos[:0] | |||
if err := sess.Begin(); err != nil { | |||
return err | |||
} | |||
if err := sess.Limit(batchSize, start).Find(&repos); err != nil { | |||
return err | |||
} | |||
if len(repos) == 0 { | |||
break | |||
} | |||
for _, repo := range repos { | |||
topics = topics[:0] | |||
if err := sess.Select("name").Table("topic"). | |||
Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id"). | |||
Where("repo_topic.repo_id = ?", repo.ID).Desc("topic.repo_count").Find(&topics); err != nil { | |||
return err | |||
} | |||
repo.Topics = topics | |||
if _, err := sess.ID(repo.ID).Cols("topics").Update(repo); err != nil { | |||
return err | |||
} | |||
} | |||
if err := sess.Commit(); err != nil { | |||
return err | |||
} | |||
} | |||
return nil | |||
} |
@@ -197,10 +197,13 @@ func FindTopics(opts *FindTopicOptions) (topics []*Topic, err error) { | |||
// GetRepoTopicByName retrives topic from name for a repo if it exist | |||
func GetRepoTopicByName(repoID int64, topicName string) (*Topic, error) { | |||
return getRepoTopicByName(x, repoID, topicName) | |||
} | |||
func getRepoTopicByName(e Engine, repoID int64, topicName string) (*Topic, error) { | |||
var cond = builder.NewCond() | |||
var topic Topic | |||
cond = cond.And(builder.Eq{"repo_topic.repo_id": repoID}).And(builder.Eq{"topic.name": topicName}) | |||
sess := x.Table("topic").Where(cond) | |||
sess := e.Table("topic").Where(cond) | |||
sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id") | |||
has, err := sess.Get(&topic) | |||
if has { | |||
@@ -211,7 +214,13 @@ func GetRepoTopicByName(repoID int64, topicName string) (*Topic, error) { | |||
// AddTopic adds a topic name to a repository (if it does not already have it) | |||
func AddTopic(repoID int64, topicName string) (*Topic, error) { | |||
topic, err := GetRepoTopicByName(repoID, topicName) | |||
sess := x.NewSession() | |||
defer sess.Close() | |||
if err := sess.Begin(); err != nil { | |||
return nil, err | |||
} | |||
topic, err := getRepoTopicByName(sess, repoID, topicName) | |||
if err != nil { | |||
return nil, err | |||
} | |||
@@ -220,7 +229,25 @@ func AddTopic(repoID int64, topicName string) (*Topic, error) { | |||
return topic, nil | |||
} | |||
return addTopicByNameToRepo(x, repoID, topicName) | |||
topic, err = addTopicByNameToRepo(sess, repoID, topicName) | |||
if err != nil { | |||
return nil, err | |||
} | |||
topicNames := make([]string, 0, 25) | |||
if err := sess.Select("name").Table("topic"). | |||
Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id"). | |||
Where("repo_topic.repo_id = ?", repoID).Desc("topic.repo_count").Find(&topicNames); err != nil { | |||
return nil, err | |||
} | |||
if _, err := sess.ID(repoID).Cols("topics").Update(&Repository{ | |||
Topics: topicNames, | |||
}); err != nil { | |||
return nil, err | |||
} | |||
return topic, sess.Commit() | |||
} | |||
// DeleteTopic removes a topic name from a repository (if it has it) | |||