* @param object[]|IResultWrapper $slotRows
* @param int $queryFlags
* @param Title $title
+ * @param array|null $slotContents a map from blobAddress to slot
+ * content blob or Content object.
*
* @return SlotRecord[]
*/
- private function constructSlotRecords( $revId, $slotRows, $queryFlags, Title $title ) {
+ private function constructSlotRecords(
+ $revId,
+ $slotRows,
+ $queryFlags,
+ Title $title,
+ $slotContents = null
+ ) {
$slots = [];
foreach ( $slotRows as $row ) {
= $this->emulateContentId( intval( $row->rev_text_id ) );
}
- $contentCallback = function ( SlotRecord $slot ) use ( $queryFlags ) {
- return $this->loadSlotContent( $slot, null, null, null, $queryFlags );
+ $contentCallback = function ( SlotRecord $slot ) use ( $slotContents, $queryFlags ) {
+ $blob = null;
+ if ( isset( $slotContents[$slot->getAddress()] ) ) {
+ $blob = $slotContents[$slot->getAddress()];
+ if ( $blob instanceof Content ) {
+ return $blob;
+ }
+ }
+ return $this->loadSlotContent( $slot, $blob, null, null, $queryFlags );
};
$slots[$row->role_name] = new SlotRecord( $row, $contentCallback );
/**
* @param object $row A database row generated from a query based on getQueryInfo()
- * @param null|object[] $slotRows Database rows generated from a query based on
- * getSlotsQueryInfo with the 'content' flag set.
+ * @param null|object[]|RevisionSlots $slots
+ * - Database rows generated from a query based on getSlotsQueryInfo
+ * with the 'content' flag set. Or
+ * - RevisionSlots instance
* @param int $queryFlags
* @param Title|null $title
* @param bool $fromCache if true, the returned RevisionRecord will ensure that no stale
* @see RevisionFactory::newRevisionFromRow
*
* MCR migration note: this replaces Revision::newFromRow
- *
*/
public function newRevisionFromRowAndSlots(
$row,
- $slotRows,
+ $slots,
$queryFlags = 0,
Title $title = null,
$fromCache = false
// Legacy because $row may have come from self::selectFields()
$comment = $this->commentStore->getCommentLegacy( $db, 'rev_comment', $row, true );
- $slots = $this->newRevisionSlots( $row->rev_id, $row, $slotRows, $queryFlags, $title );
+ if ( !( $slots instanceof RevisionSlots ) ) {
+ $slots = $this->newRevisionSlots( $row->rev_id, $row, $slots, $queryFlags, $title );
+ }
// If this is a cached row, instantiate a cache-aware revision class to avoid stale data.
if ( $fromCache ) {
* 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.
+ * preloaded.
* @param int $queryFlags
- * @param Title|null $title
+ * @param Title|null $title The title to which all the revision rows belong, if there
+ * is such a title and the caller has it handy, so we don't have to look it up again.
+ * If this parameter is given and any of the rows has a rev_page_id that is different
+ * from $title->getArticleID(), an InvalidArgumentException is thrown.
+ *
* @return StatusValue a status with a RevisionRecord[] of successfully fetched revisions
* and an array of errors for the revisions failed to fetch.
*/
$titlesByPageId = [];
foreach ( $rows as $row ) {
if ( isset( $rowsByRevId[$row->rev_id] ) ) {
- throw new InvalidArgumentException( "Duplicate rows in newRevisionsFromBatch {$row->rev_id}" );
+ $result->warning(
+ 'internalerror',
+ "Duplicate rows in newRevisionsFromBatch, rev_id {$row->rev_id}"
+ );
}
if ( $title && $row->rev_page != $title->getArticleID() ) {
throw new InvalidArgumentException(
}, $options['slots'] );
}
- // TODO: Support optional fetching of the content
- $queryInfo = self::getSlotsQueryInfo( [ 'content' ] );
+ // We need to set the `content` flag because newRevisionFromRowAndSlots requires content
+ // metadata to be loaded.
+ $slotQueryInfo = self::getSlotsQueryInfo( [ 'content' ] );
$db = $this->getDBConnectionRefForQueryFlags( $queryFlags );
$slotRows = $db->select(
- $queryInfo['tables'],
- $queryInfo['fields'],
+ $slotQueryInfo['tables'],
+ $slotQueryInfo['fields'],
$slotQueryConds,
__METHOD__,
[],
- $queryInfo['joins']
+ $slotQueryInfo['joins']
);
$slotRowsByRevId = [];
foreach ( $slotRows as $slotRow ) {
$slotRowsByRevId[$slotRow->slot_revision_id][] = $slotRow;
}
+
+ $slotContents = null;
+ if ( $options['content'] ?? false ) {
+ $blobAddresses = [];
+ foreach ( $slotRows as $slotRow ) {
+ $blobAddresses[] = $slotRow->content_address;
+ }
+ $slotContentFetchStatus = $this->blobStore
+ ->getBlobBatch( $blobAddresses, $queryFlags );
+ foreach ( $slotContentFetchStatus->getErrors() as $error ) {
+ $result->warning( $error['message'], ...$error['params'] );
+ }
+ $slotContents = $slotContentFetchStatus->getValue();
+ }
+
$result->setResult( true, array_map( function ( $row ) use
- ( $slotRowsByRevId, $queryFlags, $titlesByPageId, $result ) {
+ ( $slotRowsByRevId, $queryFlags, $titlesByPageId, $slotContents, $result ) {
if ( !isset( $slotRowsByRevId[$row->rev_id] ) ) {
$result->warning(
'internalerror',
try {
return $this->newRevisionFromRowAndSlots(
$row,
- $slotRowsByRevId[$row->rev_id],
+ new RevisionSlots(
+ $this->constructSlotRecords(
+ $row->rev_id,
+ $slotRowsByRevId[$row->rev_id],
+ $queryFlags,
+ $titlesByPageId[$row->rev_page],
+ $slotContents
+ )
+ ),
$queryFlags,
$titlesByPageId[$row->rev_page]
);