@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+43 -37
+39 -37
src/applications/calendar/query/PhabricatorCalendarEventQuery.php
··· 90 90 } 91 91 92 92 protected function loadPage() { 93 - $table = new PhabricatorCalendarEvent(); 94 - $conn_r = $table->establishConnection('r'); 95 - $viewer = $this->getViewer(); 96 - 97 - $data = queryfx_all( 98 - $conn_r, 99 - 'SELECT event.* FROM %T event %Q %Q %Q %Q %Q', 100 - $table->getTableName(), 101 - $this->buildJoinClause($conn_r), 102 - $this->buildWhereClause($conn_r), 103 - $this->buildGroupClause($conn_r), 104 - $this->buildOrderClause($conn_r), 105 - $this->buildLimitClause($conn_r)); 106 - 107 - $events = $table->loadAllFromArray($data); 93 + $events = $this->loadStandardPage($this->newResultObject()); 108 94 95 + $viewer = $this->getViewer(); 109 96 foreach ($events as $event) { 110 - $event->applyViewerTimezone($this->getViewer()); 97 + $event->applyViewerTimezone($viewer); 111 98 } 112 99 113 100 if (!$this->generateGhosts) { ··· 115 102 } 116 103 117 104 $enforced_end = null; 105 + $raw_limit = $this->getRawResultLimit(); 106 + 107 + if (!$raw_limit && !$this->rangeEnd) { 108 + throw new Exception( 109 + pht( 110 + 'Event queries which generate ghost events must include either a '. 111 + 'result limit or an end date, because they may otherwise generate '. 112 + 'an infinite number of results. This query has neither.')); 113 + } 118 114 119 115 foreach ($events as $key => $event) { 120 116 $sequence_start = 0; ··· 176 172 $sequence_end++; 177 173 $datetime->modify($modify_key); 178 174 $date = $datetime->format('U'); 179 - if ($sequence_end > $this->getRawResultLimit() + $sequence_start) { 175 + if ($sequence_end > $raw_limit + $sequence_start) { 180 176 break; 181 177 } 182 178 } 183 179 } else { 184 - $sequence_end = $this->getRawResultLimit() + $sequence_start; 180 + $sequence_end = $raw_limit + $sequence_start; 185 181 } 186 182 187 183 $sequence_start = max(1, $sequence_start); ··· 190 186 $events[] = $event->generateNthGhost($index, $viewer); 191 187 } 192 188 193 - if (count($events) >= $this->getRawResultLimit()) { 194 - $events = msort($events, 'getDateFrom'); 195 - $events = array_slice($events, 0, $this->getRawResultLimit(), true); 196 - $enforced_end = last($events)->getDateFrom(); 189 + // NOTE: We're slicing results every time because this makes it cheaper 190 + // to generate future ghosts. If we already have 100 events that occur 191 + // before July 1, we know we never need to generate ghosts after that 192 + // because they couldn't possibly ever appear in the result set. 193 + 194 + if ($raw_limit) { 195 + if (count($events) >= $raw_limit) { 196 + $events = msort($events, 'getDateFrom'); 197 + $events = array_slice($events, 0, $raw_limit, true); 198 + $enforced_end = last($events)->getDateFrom(); 199 + } 197 200 } 198 201 } 199 202 } ··· 251 254 return $parts; 252 255 } 253 256 254 - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { 255 - $where = array(); 257 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 258 + $where = parent::buildWhereClauseParts($conn); 256 259 257 260 if ($this->ids) { 258 261 $where[] = qsprintf( 259 - $conn_r, 262 + $conn, 260 263 'event.id IN (%Ld)', 261 264 $this->ids); 262 265 } 263 266 264 267 if ($this->phids) { 265 268 $where[] = qsprintf( 266 - $conn_r, 269 + $conn, 267 270 'event.phid IN (%Ls)', 268 271 $this->phids); 269 272 } 270 273 271 274 if ($this->rangeBegin) { 272 275 $where[] = qsprintf( 273 - $conn_r, 276 + $conn, 274 277 'event.dateTo >= %d OR event.isRecurring = 1', 275 278 $this->rangeBegin); 276 279 } 277 280 278 281 if ($this->rangeEnd) { 279 282 $where[] = qsprintf( 280 - $conn_r, 283 + $conn, 281 284 'event.dateFrom <= %d', 282 285 $this->rangeEnd); 283 286 } 284 287 285 288 if ($this->inviteePHIDs !== null) { 286 289 $where[] = qsprintf( 287 - $conn_r, 290 + $conn, 288 291 'invitee.inviteePHID IN (%Ls)', 289 292 $this->inviteePHIDs); 290 293 } 291 294 292 295 if ($this->creatorPHIDs) { 293 296 $where[] = qsprintf( 294 - $conn_r, 297 + $conn, 295 298 'event.userPHID IN (%Ls)', 296 299 $this->creatorPHIDs); 297 300 } 298 301 299 302 if ($this->isCancelled !== null) { 300 303 $where[] = qsprintf( 301 - $conn_r, 304 + $conn, 302 305 'event.isCancelled = %d', 303 306 (int)$this->isCancelled); 304 307 } 305 308 306 309 if ($this->eventsWithNoParent == true) { 307 310 $where[] = qsprintf( 308 - $conn_r, 311 + $conn, 309 312 'event.instanceOfEventPHID IS NULL'); 310 313 } 311 314 ··· 314 317 315 318 foreach ($this->instanceSequencePairs as $pair) { 316 319 $sql[] = qsprintf( 317 - $conn_r, 320 + $conn, 318 321 '(event.instanceOfEventPHID = %s AND event.sequenceIndex = %d)', 319 322 $pair[0], 320 323 $pair[1]); 321 324 } 325 + 322 326 $where[] = qsprintf( 323 - $conn_r, 327 + $conn, 324 328 '%Q', 325 329 implode(' OR ', $sql)); 326 330 } 327 331 328 - $where[] = $this->buildPagingClause($conn_r); 329 - 330 - return $this->formatWhereClause($where); 332 + return $where; 331 333 } 332 334 333 335 protected function getPrimaryTableAlias() {
+4
src/applications/people/query/PhabricatorPeopleQuery.php
··· 412 412 $min_range = PhabricatorTime::getNow(); 413 413 $max_range = $min_range + phutil_units('72 hours in seconds'); 414 414 415 + // NOTE: We don't need to generate ghosts here, because we only care if 416 + // the user is attending, and you can't attend a ghost event: RSVP'ing 417 + // to it creates a real event. 418 + 415 419 $events = id(new PhabricatorCalendarEventQuery()) 416 420 ->setViewer(PhabricatorUser::getOmnipotentUser()) 417 421 ->withInvitedPHIDs(array_keys($rebuild))