X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FRevision%2FRevisionStore.php;h=29d784845512460d5efe2b459d14425e09c95873;hb=6420c79320bc099cb4ff77232beabd72040146d0;hp=2a54a9b1876682439c3b96f569e82b54796e7173;hpb=75f937543b8d3eaec8d3fe4f726c088ef028abab;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Revision/RevisionStore.php b/includes/Revision/RevisionStore.php index 2a54a9b187..29d7848455 100644 --- a/includes/Revision/RevisionStore.php +++ b/includes/Revision/RevisionStore.php @@ -278,12 +278,13 @@ class RevisionStore /** * @param int $mode DB_MASTER or DB_REPLICA + * @param array $groups * * @return IDatabase */ - private function getDBConnection( $mode ) { + private function getDBConnection( $mode, $groups = [] ) { $lb = $this->getDBLoadBalancer(); - return $lb->getConnection( $mode, [], $this->wikiId ); + return $lb->getConnection( $mode, $groups, $this->wikiId ); } /** @@ -1670,6 +1671,7 @@ class RevisionStore ) { if ( !$this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_NEW ) ) { $mainSlot = $this->emulateMainSlot_1_29( $revisionRow, $queryFlags, $title ); + // @phan-suppress-next-line PhanTypeInvalidCallableArraySize false positive $slots = new RevisionSlots( [ SlotRecord::MAIN => $mainSlot ] ); } else { // XXX: do we need the same kind of caching here @@ -1738,7 +1740,8 @@ class RevisionStore $user = User::newFromAnyId( $row->ar_user ?? null, $row->ar_user_text ?? null, - $row->ar_actor ?? null + $row->ar_actor ?? null, + $this->wikiId ); } catch ( InvalidArgumentException $ex ) { wfWarn( __METHOD__ . ': ' . $title->getPrefixedDBkey() . ': ' . $ex->getMessage() ); @@ -1792,7 +1795,8 @@ class RevisionStore $user = User::newFromAnyId( $row->rev_user ?? null, $row->rev_user_text ?? null, - $row->rev_actor ?? null + $row->rev_actor ?? null, + $this->wikiId ); } catch ( InvalidArgumentException $ex ) { wfWarn( __METHOD__ . ': ' . $title->getPrefixedDBkey() . ': ' . $ex->getMessage() ); @@ -1908,6 +1912,7 @@ class RevisionStore $this->initializeMutableRevisionFromArray( $revision, $fields ); if ( isset( $fields['content'] ) && is_array( $fields['content'] ) ) { + // @phan-suppress-next-line PhanTypeNoPropertiesForeach foreach ( $fields['content'] as $role => $content ) { $revision->setContent( $role, $content ); } @@ -1930,14 +1935,21 @@ class RevisionStore /** @var UserIdentity $user */ $user = null; - if ( isset( $fields['user'] ) && ( $fields['user'] instanceof UserIdentity ) ) { + // If a user is passed in, use it if possible. We cannot use a user from a + // remote wiki with unsuppressed ids, due to issues described in T222212. + if ( isset( $fields['user'] ) && + ( $fields['user'] instanceof UserIdentity ) && + ( $this->wikiId === false || + ( !$fields['user']->getId() && !$fields['user']->getActorId() ) ) + ) { $user = $fields['user']; } else { try { $user = User::newFromAnyId( $fields['user'] ?? null, $fields['user_text'] ?? null, - $fields['actor'] ?? null + $fields['actor'] ?? null, + $this->wikiId ); } catch ( InvalidArgumentException $ex ) { $user = null; @@ -2547,20 +2559,17 @@ class RevisionStore } /** - * Get the revision before $rev in the page's history, if any. - * Will return null for the first revision but also for deleted or unsaved revisions. - * - * MCR migration note: this replaces Revision::getPrevious - * - * @see Title::getPreviousRevisionID - * @see PageArchive::getPreviousRevision + * Implementation of getPreviousRevision and getNextRevision. * * @param RevisionRecord $rev - * @param Title|null $title if known (optional) - * + * @param int $flags + * @param string $dir 'next' or 'prev' * @return RevisionRecord|null */ - public function getPreviousRevision( RevisionRecord $rev, Title $title = null ) { + private function getRelativeRevision( RevisionRecord $rev, $flags, $dir ) { + $op = $dir === 'next' ? '>' : '<'; + $sort = $dir === 'next' ? 'ASC' : 'DESC'; + if ( !$rev->getId() || !$rev->getPageId() ) { // revision is unsaved or otherwise incomplete return null; @@ -2571,54 +2580,86 @@ class RevisionStore return null; } - if ( $title === null ) { - // this would fail for deleted revisions - $title = $this->getTitle( $rev->getPageId(), $rev->getId() ); + list( $dbType, ) = DBAccessObjectUtils::getDBOptions( $flags ); + $db = $this->getDBConnection( $dbType, [ 'contributions' ] ); + + $ts = $this->getTimestampFromId( $rev->getId(), $flags ); + if ( $ts === false ) { + // XXX Should this be moved into getTimestampFromId? + $ts = $db->selectField( 'archive', 'ar_timestamp', + [ 'ar_rev_id' => $rev->getId() ], __METHOD__ ); + if ( $ts === false ) { + // XXX Is this reachable? How can we have a page id but no timestamp? + return null; + } } + $ts = $db->addQuotes( $db->timestamp( $ts ) ); - $prev = $title->getPreviousRevisionID( $rev->getId() ); - if ( !$prev ) { + $revId = $db->selectField( 'revision', 'rev_id', + [ + 'rev_page' => $rev->getPageId(), + "rev_timestamp $op $ts OR (rev_timestamp = $ts AND rev_id $op {$rev->getId()})" + ], + __METHOD__, + [ + 'ORDER BY' => "rev_timestamp $sort, rev_id $sort", + 'IGNORE INDEX' => 'rev_timestamp', // Probably needed for T159319 + ] + ); + + if ( $revId === false ) { return null; } - return $this->getRevisionByTitle( $title, $prev ); + return $this->getRevisionById( intval( $revId ) ); } /** - * Get the revision after $rev in the page's history, if any. - * Will return null for the latest revision but also for deleted or unsaved revisions. + * Get the revision before $rev in the page's history, if any. + * Will return null for the first revision but also for deleted or unsaved revisions. * - * MCR migration note: this replaces Revision::getNext + * MCR migration note: this replaces Revision::getPrevious * - * @see Title::getNextRevisionID + * @see Title::getPreviousRevisionID + * @see PageArchive::getPreviousRevision * * @param RevisionRecord $rev - * @param Title|null $title if known (optional) + * @param int $flags (optional) $flags include: + * IDBAccessObject::READ_LATEST: Select the data from the master * * @return RevisionRecord|null */ - public function getNextRevision( RevisionRecord $rev, Title $title = null ) { - if ( !$rev->getId() || !$rev->getPageId() ) { - // revision is unsaved or otherwise incomplete - return null; - } - - if ( $rev instanceof RevisionArchiveRecord ) { - // revision is deleted, so it's not part of the page history - return null; + public function getPreviousRevision( RevisionRecord $rev, $flags = 0 ) { + if ( $flags instanceof Title ) { + // Old calling convention, we don't use Title here anymore + wfDeprecated( __METHOD__ . ' with Title', '1.34' ); + $flags = 0; } - if ( $title === null ) { - // this would fail for deleted revisions - $title = $this->getTitle( $rev->getPageId(), $rev->getId() ); - } + return $this->getRelativeRevision( $rev, $flags, 'prev' ); + } - $next = $title->getNextRevisionID( $rev->getId() ); - if ( !$next ) { - return null; + /** + * Get the revision after $rev in the page's history, if any. + * Will return null for the latest revision but also for deleted or unsaved revisions. + * + * MCR migration note: this replaces Revision::getNext + * + * @see Title::getNextRevisionID + * + * @param RevisionRecord $rev + * @param int $flags (optional) $flags include: + * IDBAccessObject::READ_LATEST: Select the data from the master + * @return RevisionRecord|null + */ + public function getNextRevision( RevisionRecord $rev, $flags = 0 ) { + if ( $flags instanceof Title ) { + // Old calling convention, we don't use Title here anymore + wfDeprecated( __METHOD__ . ' with Title', '1.34' ); + $flags = 0; } - return $this->getRevisionByTitle( $title, $next ); + return $this->getRelativeRevision( $rev, $flags, 'next' ); } /** @@ -2657,21 +2698,27 @@ class RevisionStore } /** - * Get rev_timestamp from rev_id, without loading the rest of the row + * Get rev_timestamp from rev_id, without loading the rest of the row. + * + * Historically, there was an extra Title parameter that was passed before $id. This is no + * longer needed and is deprecated in 1.34. * * MCR migration note: this replaces Revision::getTimestampFromId * - * @param Title $title * @param int $id * @param int $flags * @return string|bool False if not found */ - public function getTimestampFromId( $title, $id, $flags = 0 ) { + public function getTimestampFromId( $id, $flags = 0 ) { + if ( $id instanceof Title ) { + // Old deprecated calling convention supported for backwards compatibility + $id = $flags; + $flags = func_num_args() > 2 ? func_get_arg( 2 ) : 0; + } $db = $this->getDBConnectionRefForQueryFlags( $flags ); - $conds = [ 'rev_id' => $id ]; - $conds['rev_page'] = $title->getArticleID(); - $timestamp = $db->selectField( 'revision', 'rev_timestamp', $conds, __METHOD__ ); + $timestamp = + $db->selectField( 'revision', 'rev_timestamp', [ 'rev_id' => $id ], __METHOD__ ); return ( $timestamp !== false ) ? wfTimestamp( TS_MW, $timestamp ) : false; }