1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-20 04:20:55 +01:00

Account for raw limits properly in CalendarEventQuery

Summary:
Fixes T8613. This was pretty straightforward, I just never dug into it originally.

`rawResultLimit = 0` just means "no limit", so the fix is to only apply a limit if it is set to some nonzero value.

Also modernize a few pieces of code.

Test Plan: I'm actually not sure this can actually be hit normally? I faked `setGenerateGhosts(true)` into an unrelated query, hit the fatal, then fixed it.

Reviewers: lpriestley, chad

Reviewed By: chad

Maniphest Tasks: T8613

Differential Revision: https://secure.phabricator.com/D15653
This commit is contained in:
epriestley 2016-04-07 05:10:55 -07:00
parent 8d6488f290
commit 27104b57c8
2 changed files with 43 additions and 37 deletions

View file

@ -90,24 +90,11 @@ final class PhabricatorCalendarEventQuery
} }
protected function loadPage() { protected function loadPage() {
$table = new PhabricatorCalendarEvent(); $events = $this->loadStandardPage($this->newResultObject());
$conn_r = $table->establishConnection('r');
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$data = queryfx_all(
$conn_r,
'SELECT event.* FROM %T event %Q %Q %Q %Q %Q',
$table->getTableName(),
$this->buildJoinClause($conn_r),
$this->buildWhereClause($conn_r),
$this->buildGroupClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$events = $table->loadAllFromArray($data);
foreach ($events as $event) { foreach ($events as $event) {
$event->applyViewerTimezone($this->getViewer()); $event->applyViewerTimezone($viewer);
} }
if (!$this->generateGhosts) { if (!$this->generateGhosts) {
@ -115,6 +102,15 @@ final class PhabricatorCalendarEventQuery
} }
$enforced_end = null; $enforced_end = null;
$raw_limit = $this->getRawResultLimit();
if (!$raw_limit && !$this->rangeEnd) {
throw new Exception(
pht(
'Event queries which generate ghost events must include either a '.
'result limit or an end date, because they may otherwise generate '.
'an infinite number of results. This query has neither.'));
}
foreach ($events as $key => $event) { foreach ($events as $key => $event) {
$sequence_start = 0; $sequence_start = 0;
@ -176,12 +172,12 @@ final class PhabricatorCalendarEventQuery
$sequence_end++; $sequence_end++;
$datetime->modify($modify_key); $datetime->modify($modify_key);
$date = $datetime->format('U'); $date = $datetime->format('U');
if ($sequence_end > $this->getRawResultLimit() + $sequence_start) { if ($sequence_end > $raw_limit + $sequence_start) {
break; break;
} }
} }
} else { } else {
$sequence_end = $this->getRawResultLimit() + $sequence_start; $sequence_end = $raw_limit + $sequence_start;
} }
$sequence_start = max(1, $sequence_start); $sequence_start = max(1, $sequence_start);
@ -190,10 +186,17 @@ final class PhabricatorCalendarEventQuery
$events[] = $event->generateNthGhost($index, $viewer); $events[] = $event->generateNthGhost($index, $viewer);
} }
if (count($events) >= $this->getRawResultLimit()) { // NOTE: We're slicing results every time because this makes it cheaper
$events = msort($events, 'getDateFrom'); // to generate future ghosts. If we already have 100 events that occur
$events = array_slice($events, 0, $this->getRawResultLimit(), true); // before July 1, we know we never need to generate ghosts after that
$enforced_end = last($events)->getDateFrom(); // because they couldn't possibly ever appear in the result set.
if ($raw_limit) {
if (count($events) >= $raw_limit) {
$events = msort($events, 'getDateFrom');
$events = array_slice($events, 0, $raw_limit, true);
$enforced_end = last($events)->getDateFrom();
}
} }
} }
} }
@ -251,61 +254,61 @@ final class PhabricatorCalendarEventQuery
return $parts; return $parts;
} }
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = array(); $where = parent::buildWhereClauseParts($conn);
if ($this->ids) { if ($this->ids) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'event.id IN (%Ld)', 'event.id IN (%Ld)',
$this->ids); $this->ids);
} }
if ($this->phids) { if ($this->phids) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'event.phid IN (%Ls)', 'event.phid IN (%Ls)',
$this->phids); $this->phids);
} }
if ($this->rangeBegin) { if ($this->rangeBegin) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'event.dateTo >= %d OR event.isRecurring = 1', 'event.dateTo >= %d OR event.isRecurring = 1',
$this->rangeBegin); $this->rangeBegin);
} }
if ($this->rangeEnd) { if ($this->rangeEnd) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'event.dateFrom <= %d', 'event.dateFrom <= %d',
$this->rangeEnd); $this->rangeEnd);
} }
if ($this->inviteePHIDs !== null) { if ($this->inviteePHIDs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'invitee.inviteePHID IN (%Ls)', 'invitee.inviteePHID IN (%Ls)',
$this->inviteePHIDs); $this->inviteePHIDs);
} }
if ($this->creatorPHIDs) { if ($this->creatorPHIDs) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'event.userPHID IN (%Ls)', 'event.userPHID IN (%Ls)',
$this->creatorPHIDs); $this->creatorPHIDs);
} }
if ($this->isCancelled !== null) { if ($this->isCancelled !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'event.isCancelled = %d', 'event.isCancelled = %d',
(int)$this->isCancelled); (int)$this->isCancelled);
} }
if ($this->eventsWithNoParent == true) { if ($this->eventsWithNoParent == true) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'event.instanceOfEventPHID IS NULL'); 'event.instanceOfEventPHID IS NULL');
} }
@ -314,20 +317,19 @@ final class PhabricatorCalendarEventQuery
foreach ($this->instanceSequencePairs as $pair) { foreach ($this->instanceSequencePairs as $pair) {
$sql[] = qsprintf( $sql[] = qsprintf(
$conn_r, $conn,
'(event.instanceOfEventPHID = %s AND event.sequenceIndex = %d)', '(event.instanceOfEventPHID = %s AND event.sequenceIndex = %d)',
$pair[0], $pair[0],
$pair[1]); $pair[1]);
} }
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'%Q', '%Q',
implode(' OR ', $sql)); implode(' OR ', $sql));
} }
$where[] = $this->buildPagingClause($conn_r); return $where;
return $this->formatWhereClause($where);
} }
protected function getPrimaryTableAlias() { protected function getPrimaryTableAlias() {

View file

@ -412,6 +412,10 @@ final class PhabricatorPeopleQuery
$min_range = PhabricatorTime::getNow(); $min_range = PhabricatorTime::getNow();
$max_range = $min_range + phutil_units('72 hours in seconds'); $max_range = $min_range + phutil_units('72 hours in seconds');
// NOTE: We don't need to generate ghosts here, because we only care if
// the user is attending, and you can't attend a ghost event: RSVP'ing
// to it creates a real event.
$events = id(new PhabricatorCalendarEventQuery()) $events = id(new PhabricatorCalendarEventQuery())
->setViewer(PhabricatorUser::getOmnipotentUser()) ->setViewer(PhabricatorUser::getOmnipotentUser())
->withInvitedPHIDs(array_keys($rebuild)) ->withInvitedPHIDs(array_keys($rebuild))