mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 16:52:41 +01:00
Fix an issue where paginating notifications could fail a GROUP BY test
Summary: Ref T13623. When paginating notifications, we may currently construct a query which: - loads from non-unique rows; and - returns multiple results. In particular, `chronologicalKey` isn't unique across the whole table (only for a given viewer). We can get away with this because no user-facing view of notifications is truly "every notification for every viewer" today. One fix would be to implicitly force the paging query to include `withUserPHIDs(viewerPHID)`, but puruse a slightly more general fix: - Load only unique stories. - Explictly limit the pagination subquery to one result. Test Plan: - Set page size to 1, inserted duplicate notifications of all stories for another user, clicked "Next", got the GROUP BY error. - Applied the "only load unique stories" part of the change, got a "expected one row" error instead. - Applied the "limit 1" part of the change, got a second page of notifications. Maniphest Tasks: T13623 Differential Revision: https://secure.phabricator.com/D21577
This commit is contained in:
parent
10162ad43b
commit
0a3093ef9c
1 changed files with 18 additions and 6 deletions
|
@ -63,17 +63,25 @@ final class PhabricatorNotificationQuery
|
||||||
$this->buildWhereClause($conn),
|
$this->buildWhereClause($conn),
|
||||||
$this->buildLimitClause($conn));
|
$this->buildLimitClause($conn));
|
||||||
|
|
||||||
$viewed_map = ipull($data, 'hasViewed', 'chronologicalKey');
|
// See T13623. Although most queries for notifications return unique
|
||||||
|
// stories, this isn't a guarantee.
|
||||||
|
$story_map = ipull($data, null, 'chronologicalKey');
|
||||||
|
|
||||||
$stories = PhabricatorFeedStory::loadAllFromRows(
|
$stories = PhabricatorFeedStory::loadAllFromRows(
|
||||||
$data,
|
$story_map,
|
||||||
$this->getViewer());
|
$this->getViewer());
|
||||||
|
$stories = mpull($stories, null, 'getChronologicalKey');
|
||||||
|
|
||||||
foreach ($stories as $key => $story) {
|
$results = array();
|
||||||
$story->setHasViewed($viewed_map[$key]);
|
foreach ($data as $row) {
|
||||||
|
$story_key = $row['chronologicalKey'];
|
||||||
|
$has_viewed = $row['hasViewed'];
|
||||||
|
|
||||||
|
$results[] = id(clone $stories[$story_key])
|
||||||
|
->setHasViewed($has_viewed);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $stories;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||||
|
@ -145,7 +153,11 @@ final class PhabricatorNotificationQuery
|
||||||
protected function applyExternalCursorConstraintsToQuery(
|
protected function applyExternalCursorConstraintsToQuery(
|
||||||
PhabricatorCursorPagedPolicyAwareQuery $subquery,
|
PhabricatorCursorPagedPolicyAwareQuery $subquery,
|
||||||
$cursor) {
|
$cursor) {
|
||||||
$subquery->withKeys(array($cursor));
|
|
||||||
|
$subquery
|
||||||
|
->withKeys(array($cursor))
|
||||||
|
->setLimit(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function newExternalCursorStringForResult($object) {
|
protected function newExternalCursorStringForResult($object) {
|
||||||
|
|
Loading…
Reference in a new issue