From 72f9050689912f5910a08d593ebe53078fe85aa4 Mon Sep 17 00:00:00 2001 From: DuckDuckWhale <28813824+DuckDuckWhale@users.noreply.github.com> Date: Tue, 28 Dec 2021 19:12:19 -0800 Subject: [PATCH] Fix: unstable sort skips/duplicates issues across pages (#18094) When viewing issues in sorted order, some issues are duplicated across pages and some are missing. This is caused by the lack of tie-breakers in database queries, making pagination inconsistent. --- models/issue.go | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/models/issue.go b/models/issue.go index ea8eb4a9a..b05c8cccd 100644 --- a/models/issue.go +++ b/models/issue.go @@ -1193,17 +1193,17 @@ type IssuesOptions struct { func sortIssuesSession(sess *xorm.Session, sortType string, priorityRepoID int64) { switch sortType { case "oldest": - sess.Asc("issue.created_unix") + sess.Asc("issue.created_unix").Asc("issue.id") case "recentupdate": - sess.Desc("issue.updated_unix") + sess.Desc("issue.updated_unix").Desc("issue.created_unix").Desc("issue.id") case "leastupdate": - sess.Asc("issue.updated_unix") + sess.Asc("issue.updated_unix").Asc("issue.created_unix").Asc("issue.id") case "mostcomment": - sess.Desc("issue.num_comments") + sess.Desc("issue.num_comments").Desc("issue.created_unix").Desc("issue.id") case "leastcomment": - sess.Asc("issue.num_comments") + sess.Asc("issue.num_comments").Desc("issue.created_unix").Desc("issue.id") case "priority": - sess.Desc("issue.priority") + sess.Desc("issue.priority").Desc("issue.created_unix").Desc("issue.id") case "nearduedate": // 253370764800 is 01/01/9999 @ 12:00am (UTC) sess.Join("LEFT", "milestone", "issue.milestone_id = milestone.id"). @@ -1211,19 +1211,27 @@ func sortIssuesSession(sess *xorm.Session, sortType string, priorityRepoID int64 "WHEN issue.deadline_unix = 0 AND (milestone.deadline_unix = 0 OR milestone.deadline_unix IS NULL) THEN 253370764800 " + "WHEN milestone.deadline_unix = 0 OR milestone.deadline_unix IS NULL THEN issue.deadline_unix " + "WHEN milestone.deadline_unix < issue.deadline_unix OR issue.deadline_unix = 0 THEN milestone.deadline_unix " + - "ELSE issue.deadline_unix END ASC") + "ELSE issue.deadline_unix END ASC"). + Desc("issue.created_unix"). + Desc("issue.id") case "farduedate": sess.Join("LEFT", "milestone", "issue.milestone_id = milestone.id"). OrderBy("CASE " + "WHEN milestone.deadline_unix IS NULL THEN issue.deadline_unix " + "WHEN milestone.deadline_unix < issue.deadline_unix OR issue.deadline_unix = 0 THEN milestone.deadline_unix " + - "ELSE issue.deadline_unix END DESC") + "ELSE issue.deadline_unix END DESC"). + Desc("issue.created_unix"). + Desc("issue.id") case "priorityrepo": - sess.OrderBy("CASE WHEN issue.repo_id = " + strconv.FormatInt(priorityRepoID, 10) + " THEN 1 ELSE 2 END, issue.created_unix DESC") + sess.OrderBy("CASE " + + "WHEN issue.repo_id = " + strconv.FormatInt(priorityRepoID, 10) + " THEN 1 " + + "ELSE 2 END ASC"). + Desc("issue.created_unix"). + Desc("issue.id") case "project-column-sorting": - sess.Asc("project_issue.sorting") + sess.Asc("project_issue.sorting").Desc("issue.created_unix").Desc("issue.id") default: - sess.Desc("issue.created_unix") + sess.Desc("issue.created_unix").Desc("issue.id") } }