X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FRevision%2FRevisionStore.php;h=735a2124d47a0b369ae27770492b07cbca3c1fd5;hb=2e3c42dbb4783cb23fdc354471602695db7a6cd5;hp=9e8dfe7e4090245bca2a00b8f13b3053272cb16a;hpb=b4c7f4c30db86ad74b068afc56a13a965c369540;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Revision/RevisionStore.php b/includes/Revision/RevisionStore.php index 9e8dfe7e40..735a2124d4 100644 --- a/includes/Revision/RevisionStore.php +++ b/includes/Revision/RevisionStore.php @@ -54,8 +54,10 @@ use Psr\Log\NullLogger; use RecentChange; use Revision; use RuntimeException; +use StatusValue; use stdClass; use Title; +use Traversable; use User; use WANObjectCache; use Wikimedia\Assert\Assert; @@ -324,10 +326,10 @@ class RevisionStore $canUseTitleNewFromId = ( $pageId !== null && $pageId > 0 && $this->dbDomain === false ); list( $dbMode, $dbOptions ) = DBAccessObjectUtils::getDBOptions( $queryFlags ); - $titleFlags = ( $dbMode == DB_MASTER ? Title::GAID_FOR_UPDATE : 0 ); // Loading by ID is best, but Title::newFromID does not support that for foreign IDs. if ( $canUseTitleNewFromId ) { + $titleFlags = ( $dbMode == DB_MASTER ? Title::READ_LATEST : 0 ); // TODO: better foreign title handling (introduce TitleFactory) $title = Title::newFromID( $pageId, $titleFlags ); if ( $title ) { @@ -1876,6 +1878,125 @@ class RevisionStore return $rev; } + /** + * Construct a RevisionRecord instance for each row in $rows, + * and return them as an associative array indexed by revision ID. + * @param Traversable|array $rows the rows to construct revision records from + * @param array $options Supports the following options: + * 'slots' - whether metadata about revision slots should be + * loaded immediately. Supports falsy or truthy value as well + * as an explicit list of slot role names. + * 'content'- whether the actual content of the slots should be + * preloaded. TODO: no supported yet. + * @param int $queryFlags + * @param Title|null $title + * @return StatusValue a status with a RevisionRecord[] of successfully fetched revisions + * and an array of errors for the revisions failed to fetch. + */ + public function newRevisionsFromBatch( + $rows, + array $options = [], + $queryFlags = 0, + Title $title = null + ) { + $result = new StatusValue(); + + $rowsByRevId = []; + $pageIds = []; + $titlesByPageId = []; + foreach ( $rows as $row ) { + if ( isset( $rowsByRevId[$row->rev_id] ) ) { + throw new InvalidArgumentException( "Duplicate rows in newRevisionsFromBatch {$row->rev_id}" ); + } + if ( $title && $row->rev_page != $title->getArticleID() ) { + throw new InvalidArgumentException( + "Revision {$row->rev_id} doesn't belong to page {$title->getArticleID()}" + ); + } + $pageIds[] = $row->rev_page; + $rowsByRevId[$row->rev_id] = $row; + } + + if ( empty( $rowsByRevId ) ) { + $result->setResult( true, [] ); + return $result; + } + + // If the title is not supplied, batch-fetch Title objects. + if ( $title ) { + $titlesByPageId[$title->getArticleID()] = $title; + } else { + $pageIds = array_unique( $pageIds ); + foreach ( Title::newFromIDs( $pageIds ) as $t ) { + $titlesByPageId[$t->getArticleID()] = $t; + } + } + + if ( !isset( $options['slots'] ) || $this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_OLD ) ) { + $result->setResult( true, + array_map( function ( $row ) use ( $queryFlags, $titlesByPageId, $result ) { + try { + return $this->newRevisionFromRow( + $row, + $queryFlags, + $titlesByPageId[$row->rev_page] + ); + } catch ( MWException $e ) { + $result->warning( 'internalerror', $e->getMessage() ); + return null; + } + }, $rowsByRevId ) + ); + return $result; + } + + $slotQueryConds = [ 'slot_revision_id' => array_keys( $rowsByRevId ) ]; + if ( is_array( $options['slots'] ) ) { + $slotQueryConds['slot_role_id'] = array_map( function ( $slot_name ) { + return $this->slotRoleStore->getId( $slot_name ); + }, $options['slots'] ); + } + + // TODO: Support optional fetching of the content + $queryInfo = self::getSlotsQueryInfo( [ 'content' ] ); + $db = $this->getDBConnectionRefForQueryFlags( $queryFlags ); + $slotRows = $db->select( + $queryInfo['tables'], + $queryInfo['fields'], + $slotQueryConds, + __METHOD__, + [], + $queryInfo['joins'] + ); + + $slotRowsByRevId = []; + foreach ( $slotRows as $slotRow ) { + $slotRowsByRevId[$slotRow->slot_revision_id][] = $slotRow; + } + $result->setResult( true, array_map( function ( $row ) use + ( $slotRowsByRevId, $queryFlags, $titlesByPageId, $result ) { + if ( !isset( $slotRowsByRevId[$row->rev_id] ) ) { + $result->warning( + 'internalerror', + "Couldn't find slots for rev {$row->rev_id}" + ); + return null; + } + try { + return $this->newRevisionFromRowAndSlots( + $row, + $slotRowsByRevId[$row->rev_id], + $queryFlags, + $titlesByPageId[$row->rev_page] + ); + } catch ( MWException $e ) { + $result->warning( 'internalerror', $e->getMessage() ); + return null; + } + }, $rowsByRevId ) ); + return $result; + } + /** * Constructs a new MutableRevisionRecord based on the given associative array following * the MW1.29 convention for the Revision constructor. @@ -2324,6 +2445,7 @@ class RevisionStore * - tables: (string[]) to include in the `$table` to `IDatabase->select()` * - fields: (string[]) to include in the `$vars` to `IDatabase->select()` * - joins: (array) to include in the `$join_conds` to `IDatabase->select()` + * @phan-return array{tables:string[],fields:string[],joins:array} */ public function getQueryInfo( $options = [] ) { $ret = [