You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

issue_list.go 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. // Copyright 2017 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "fmt"
  7. "github.com/go-xorm/builder"
  8. )
  9. // IssueList defines a list of issues
  10. type IssueList []*Issue
  11. const (
  12. // default variables number on IN () in SQL
  13. defaultMaxInSize = 50
  14. )
  15. func (issues IssueList) getRepoIDs() []int64 {
  16. repoIDs := make(map[int64]struct{}, len(issues))
  17. for _, issue := range issues {
  18. if _, ok := repoIDs[issue.RepoID]; !ok {
  19. repoIDs[issue.RepoID] = struct{}{}
  20. }
  21. }
  22. return keysInt64(repoIDs)
  23. }
  24. func (issues IssueList) loadRepositories(e Engine) ([]*Repository, error) {
  25. if len(issues) == 0 {
  26. return nil, nil
  27. }
  28. repoIDs := issues.getRepoIDs()
  29. repoMaps := make(map[int64]*Repository, len(repoIDs))
  30. var left = len(repoIDs)
  31. for left > 0 {
  32. var limit = defaultMaxInSize
  33. if left < limit {
  34. limit = left
  35. }
  36. err := e.
  37. In("id", repoIDs[:limit]).
  38. Find(&repoMaps)
  39. if err != nil {
  40. return nil, fmt.Errorf("find repository: %v", err)
  41. }
  42. left = left - limit
  43. repoIDs = repoIDs[limit:]
  44. }
  45. for _, issue := range issues {
  46. issue.Repo = repoMaps[issue.RepoID]
  47. }
  48. return valuesRepository(repoMaps), nil
  49. }
  50. // LoadRepositories loads issues' all repositories
  51. func (issues IssueList) LoadRepositories() ([]*Repository, error) {
  52. return issues.loadRepositories(x)
  53. }
  54. func (issues IssueList) getPosterIDs() []int64 {
  55. posterIDs := make(map[int64]struct{}, len(issues))
  56. for _, issue := range issues {
  57. if _, ok := posterIDs[issue.PosterID]; !ok {
  58. posterIDs[issue.PosterID] = struct{}{}
  59. }
  60. }
  61. return keysInt64(posterIDs)
  62. }
  63. func (issues IssueList) loadPosters(e Engine) error {
  64. if len(issues) == 0 {
  65. return nil
  66. }
  67. posterIDs := issues.getPosterIDs()
  68. posterMaps := make(map[int64]*User, len(posterIDs))
  69. var left = len(posterIDs)
  70. for left > 0 {
  71. var limit = defaultMaxInSize
  72. if left < limit {
  73. limit = left
  74. }
  75. err := e.
  76. In("id", posterIDs[:limit]).
  77. Find(&posterMaps)
  78. if err != nil {
  79. return err
  80. }
  81. left = left - limit
  82. posterIDs = posterIDs[limit:]
  83. }
  84. for _, issue := range issues {
  85. if issue.PosterID <= 0 {
  86. continue
  87. }
  88. var ok bool
  89. if issue.Poster, ok = posterMaps[issue.PosterID]; !ok {
  90. issue.Poster = NewGhostUser()
  91. }
  92. }
  93. return nil
  94. }
  95. func (issues IssueList) getIssueIDs() []int64 {
  96. var ids = make([]int64, 0, len(issues))
  97. for _, issue := range issues {
  98. ids = append(ids, issue.ID)
  99. }
  100. return ids
  101. }
  102. func (issues IssueList) loadLabels(e Engine) error {
  103. if len(issues) == 0 {
  104. return nil
  105. }
  106. type LabelIssue struct {
  107. Label *Label `xorm:"extends"`
  108. IssueLabel *IssueLabel `xorm:"extends"`
  109. }
  110. var issueLabels = make(map[int64][]*Label, len(issues)*3)
  111. var issueIDs = issues.getIssueIDs()
  112. var left = len(issueIDs)
  113. for left > 0 {
  114. var limit = defaultMaxInSize
  115. if left < limit {
  116. limit = left
  117. }
  118. rows, err := e.Table("label").
  119. Join("LEFT", "issue_label", "issue_label.label_id = label.id").
  120. In("issue_label.issue_id", issueIDs[:limit]).
  121. Asc("label.name").
  122. Rows(new(LabelIssue))
  123. if err != nil {
  124. return err
  125. }
  126. for rows.Next() {
  127. var labelIssue LabelIssue
  128. err = rows.Scan(&labelIssue)
  129. if err != nil {
  130. rows.Close()
  131. return err
  132. }
  133. issueLabels[labelIssue.IssueLabel.IssueID] = append(issueLabels[labelIssue.IssueLabel.IssueID], labelIssue.Label)
  134. }
  135. rows.Close()
  136. left = left - limit
  137. issueIDs = issueIDs[limit:]
  138. }
  139. for _, issue := range issues {
  140. issue.Labels = issueLabels[issue.ID]
  141. }
  142. return nil
  143. }
  144. func (issues IssueList) getMilestoneIDs() []int64 {
  145. var ids = make(map[int64]struct{}, len(issues))
  146. for _, issue := range issues {
  147. if _, ok := ids[issue.MilestoneID]; !ok {
  148. ids[issue.MilestoneID] = struct{}{}
  149. }
  150. }
  151. return keysInt64(ids)
  152. }
  153. func (issues IssueList) loadMilestones(e Engine) error {
  154. milestoneIDs := issues.getMilestoneIDs()
  155. if len(milestoneIDs) == 0 {
  156. return nil
  157. }
  158. milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
  159. var left = len(milestoneIDs)
  160. for left > 0 {
  161. var limit = defaultMaxInSize
  162. if left < limit {
  163. limit = left
  164. }
  165. err := e.
  166. In("id", milestoneIDs[:limit]).
  167. Find(&milestoneMaps)
  168. if err != nil {
  169. return err
  170. }
  171. left = left - limit
  172. milestoneIDs = milestoneIDs[limit:]
  173. }
  174. for _, issue := range issues {
  175. issue.Milestone = milestoneMaps[issue.MilestoneID]
  176. }
  177. return nil
  178. }
  179. func (issues IssueList) loadAssignees(e Engine) error {
  180. if len(issues) == 0 {
  181. return nil
  182. }
  183. type AssigneeIssue struct {
  184. IssueAssignee *IssueAssignees `xorm:"extends"`
  185. Assignee *User `xorm:"extends"`
  186. }
  187. var assignees = make(map[int64][]*User, len(issues))
  188. var issueIDs = issues.getIssueIDs()
  189. var left = len(issueIDs)
  190. for left > 0 {
  191. var limit = defaultMaxInSize
  192. if left < limit {
  193. limit = left
  194. }
  195. rows, err := e.Table("issue_assignees").
  196. Join("INNER", "`user`", "`user`.id = `issue_assignees`.assignee_id").
  197. In("`issue_assignees`.issue_id", issueIDs[:limit]).
  198. Rows(new(AssigneeIssue))
  199. if err != nil {
  200. return err
  201. }
  202. for rows.Next() {
  203. var assigneeIssue AssigneeIssue
  204. err = rows.Scan(&assigneeIssue)
  205. if err != nil {
  206. rows.Close()
  207. return err
  208. }
  209. assignees[assigneeIssue.IssueAssignee.IssueID] = append(assignees[assigneeIssue.IssueAssignee.IssueID], assigneeIssue.Assignee)
  210. }
  211. rows.Close()
  212. left = left - limit
  213. issueIDs = issueIDs[limit:]
  214. }
  215. for _, issue := range issues {
  216. issue.Assignees = assignees[issue.ID]
  217. }
  218. return nil
  219. }
  220. func (issues IssueList) getPullIssueIDs() []int64 {
  221. var ids = make([]int64, 0, len(issues))
  222. for _, issue := range issues {
  223. if issue.IsPull && issue.PullRequest == nil {
  224. ids = append(ids, issue.ID)
  225. }
  226. }
  227. return ids
  228. }
  229. func (issues IssueList) loadPullRequests(e Engine) error {
  230. issuesIDs := issues.getPullIssueIDs()
  231. if len(issuesIDs) == 0 {
  232. return nil
  233. }
  234. pullRequestMaps := make(map[int64]*PullRequest, len(issuesIDs))
  235. var left = len(issuesIDs)
  236. for left > 0 {
  237. var limit = defaultMaxInSize
  238. if left < limit {
  239. limit = left
  240. }
  241. rows, err := e.
  242. In("issue_id", issuesIDs[:limit]).
  243. Rows(new(PullRequest))
  244. if err != nil {
  245. return err
  246. }
  247. for rows.Next() {
  248. var pr PullRequest
  249. err = rows.Scan(&pr)
  250. if err != nil {
  251. rows.Close()
  252. return err
  253. }
  254. pullRequestMaps[pr.IssueID] = &pr
  255. }
  256. rows.Close()
  257. left = left - limit
  258. issuesIDs = issuesIDs[limit:]
  259. }
  260. for _, issue := range issues {
  261. issue.PullRequest = pullRequestMaps[issue.ID]
  262. }
  263. return nil
  264. }
  265. func (issues IssueList) loadAttachments(e Engine) (err error) {
  266. if len(issues) == 0 {
  267. return nil
  268. }
  269. var attachments = make(map[int64][]*Attachment, len(issues))
  270. var issuesIDs = issues.getIssueIDs()
  271. var left = len(issuesIDs)
  272. for left > 0 {
  273. var limit = defaultMaxInSize
  274. if left < limit {
  275. limit = left
  276. }
  277. rows, err := e.Table("attachment").
  278. Join("INNER", "issue", "issue.id = attachment.issue_id").
  279. In("issue.id", issuesIDs[:limit]).
  280. Rows(new(Attachment))
  281. if err != nil {
  282. return err
  283. }
  284. for rows.Next() {
  285. var attachment Attachment
  286. err = rows.Scan(&attachment)
  287. if err != nil {
  288. rows.Close()
  289. return err
  290. }
  291. attachments[attachment.IssueID] = append(attachments[attachment.IssueID], &attachment)
  292. }
  293. rows.Close()
  294. left = left - limit
  295. issuesIDs = issuesIDs[limit:]
  296. }
  297. for _, issue := range issues {
  298. issue.Attachments = attachments[issue.ID]
  299. }
  300. return nil
  301. }
  302. func (issues IssueList) loadComments(e Engine, cond builder.Cond) (err error) {
  303. if len(issues) == 0 {
  304. return nil
  305. }
  306. var comments = make(map[int64][]*Comment, len(issues))
  307. var issuesIDs = issues.getIssueIDs()
  308. var left = len(issuesIDs)
  309. for left > 0 {
  310. var limit = defaultMaxInSize
  311. if left < limit {
  312. limit = left
  313. }
  314. rows, err := e.Table("comment").
  315. Join("INNER", "issue", "issue.id = comment.issue_id").
  316. In("issue.id", issuesIDs[:limit]).
  317. Where(cond).
  318. Rows(new(Comment))
  319. if err != nil {
  320. return err
  321. }
  322. for rows.Next() {
  323. var comment Comment
  324. err = rows.Scan(&comment)
  325. if err != nil {
  326. rows.Close()
  327. return err
  328. }
  329. comments[comment.IssueID] = append(comments[comment.IssueID], &comment)
  330. }
  331. rows.Close()
  332. left = left - limit
  333. issuesIDs = issuesIDs[limit:]
  334. }
  335. for _, issue := range issues {
  336. issue.Comments = comments[issue.ID]
  337. }
  338. return nil
  339. }
  340. func (issues IssueList) loadTotalTrackedTimes(e Engine) (err error) {
  341. type totalTimesByIssue struct {
  342. IssueID int64
  343. Time int64
  344. }
  345. if len(issues) == 0 {
  346. return nil
  347. }
  348. var trackedTimes = make(map[int64]int64, len(issues))
  349. var ids = make([]int64, 0, len(issues))
  350. for _, issue := range issues {
  351. if issue.Repo.IsTimetrackerEnabled() {
  352. ids = append(ids, issue.ID)
  353. }
  354. }
  355. var left = len(ids)
  356. for left > 0 {
  357. var limit = defaultMaxInSize
  358. if left < limit {
  359. limit = left
  360. }
  361. // select issue_id, sum(time) from tracked_time where issue_id in (<issue ids in current page>) group by issue_id
  362. rows, err := e.Table("tracked_time").
  363. Select("issue_id, sum(time) as time").
  364. In("issue_id", ids[:limit]).
  365. GroupBy("issue_id").
  366. Rows(new(totalTimesByIssue))
  367. if err != nil {
  368. return err
  369. }
  370. for rows.Next() {
  371. var totalTime totalTimesByIssue
  372. err = rows.Scan(&totalTime)
  373. if err != nil {
  374. rows.Close()
  375. return err
  376. }
  377. trackedTimes[totalTime.IssueID] = totalTime.Time
  378. }
  379. rows.Close()
  380. left = left - limit
  381. ids = ids[limit:]
  382. }
  383. for _, issue := range issues {
  384. issue.TotalTrackedTime = trackedTimes[issue.ID]
  385. }
  386. return nil
  387. }
  388. // loadAttributes loads all attributes, expect for attachments and comments
  389. func (issues IssueList) loadAttributes(e Engine) (err error) {
  390. if _, err = issues.loadRepositories(e); err != nil {
  391. return
  392. }
  393. if err = issues.loadPosters(e); err != nil {
  394. return
  395. }
  396. if err = issues.loadLabels(e); err != nil {
  397. return
  398. }
  399. if err = issues.loadMilestones(e); err != nil {
  400. return
  401. }
  402. if err = issues.loadAssignees(e); err != nil {
  403. return
  404. }
  405. if err = issues.loadPullRequests(e); err != nil {
  406. return
  407. }
  408. if err = issues.loadTotalTrackedTimes(e); err != nil {
  409. return
  410. }
  411. return nil
  412. }
  413. // LoadAttributes loads attributes of the issues, except for attachments and
  414. // comments
  415. func (issues IssueList) LoadAttributes() error {
  416. return issues.loadAttributes(x)
  417. }
  418. // LoadAttachments loads attachments
  419. func (issues IssueList) LoadAttachments() error {
  420. return issues.loadAttachments(x)
  421. }
  422. // LoadComments loads comments
  423. func (issues IssueList) LoadComments() error {
  424. return issues.loadComments(x, builder.NewCond())
  425. }
  426. // LoadDiscussComments loads discuss comments
  427. func (issues IssueList) LoadDiscussComments() error {
  428. return issues.loadComments(x, builder.Eq{"comment.type": CommentTypeComment})
  429. }