$conds['img_timestamp'] = $dbr->timestamp( $timestamp );
}
- $row = $dbr->selectRow( 'image', self::selectFields(), $conds, __METHOD__ );
+ $fileQuery = self::getQueryInfo();
+ $row = $dbr->selectRow(
+ $fileQuery['tables'], $fileQuery['fields'], $conds, __METHOD__, [], $fileQuery['joins']
+ );
if ( $row ) {
return self::newFromRow( $row, $repo );
} else {
/**
* Fields in the image table
- * @todo Deprecate this in favor of a method that returns tables and joins
- * as well, and use CommentStore::getJoin().
+ * @deprecated since 1.31, use self::getQueryInfo() instead.
* @return array
*/
static function selectFields() {
+ wfDeprecated( __METHOD__, '1.31' );
return [
'img_name',
'img_size',
] + CommentStore::newKey( 'img_description' )->getFields();
}
+ /**
+ * Return the tables, fields, and join conditions to be selected to create
+ * a new localfile object.
+ * @since 1.31
+ * @param string[] $options
+ * - omit-lazy: Omit fields that are lazily cached.
+ * @return array With three keys:
+ * - 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()`
+ */
+ public static function getQueryInfo( array $options = [] ) {
+ $commentQuery = CommentStore::newKey( 'img_description' )->getJoin();
+ $ret = [
+ 'tables' => [ 'image' ] + $commentQuery['tables'],
+ 'fields' => [
+ 'img_name',
+ 'img_size',
+ 'img_width',
+ 'img_height',
+ 'img_metadata',
+ 'img_bits',
+ 'img_media_type',
+ 'img_major_mime',
+ 'img_minor_mime',
+ 'img_user',
+ 'img_user_text',
+ 'img_timestamp',
+ 'img_sha1',
+ ] + $commentQuery['fields'],
+ 'joins' => $commentQuery['joins'],
+ ];
+
+ if ( in_array( 'omit-nonlazy', $options, true ) ) {
+ // Internal use only for getting only the lazy fields
+ $ret['fields'] = [];
+ }
+ if ( !in_array( 'omit-lazy', $options, true ) ) {
+ // Note: Keep this in sync with self::getLazyCacheFields()
+ $ret['fields'][] = 'img_metadata';
+ }
+
+ return $ret;
+ }
+
/**
* Do not call this except from inside a repo class.
* @param Title $title
}
/**
- * @param string $prefix
+ * Returns the list of object properties that are included as-is in the cache.
+ * @param string $prefix Must be the empty string
* @return array
+ * @since 1.31 No longer accepts a non-empty $prefix
*/
- function getCacheFields( $prefix = 'img_' ) {
- static $fields = [ 'size', 'width', 'height', 'bits', 'media_type',
- 'major_mime', 'minor_mime', 'metadata', 'timestamp', 'sha1', 'user',
- 'user_text' ];
- static $results = [];
-
- if ( $prefix == '' ) {
- return array_merge( $fields, [ 'description' ] );
- }
- if ( !isset( $results[$prefix] ) ) {
- $prefixedFields = [];
- foreach ( $fields as $field ) {
- $prefixedFields[] = $prefix . $field;
- }
- $prefixedFields += CommentStore::newKey( "{$prefix}description" )->getFields();
- $results[$prefix] = $prefixedFields;
+ protected function getCacheFields( $prefix = 'img_' ) {
+ if ( $prefix !== '' ) {
+ throw new InvalidArgumentException(
+ __METHOD__ . ' with a non-empty prefix is no longer supported.'
+ );
}
- return $results[$prefix];
+ // See self::getQueryInfo() for the fetching of the data from the DB,
+ // self::loadFromRow() for the loading of the object from the DB row,
+ // and self::loadFromCache() for the caching, and self::setProps() for
+ // populating the object from an array of data.
+ return [ 'size', 'width', 'height', 'bits', 'media_type',
+ 'major_mime', 'minor_mime', 'metadata', 'timestamp', 'sha1', 'user',
+ 'user_text', 'description' ];
}
/**
- * @param string $prefix
+ * Returns the list of object properties that are included as-is in the
+ * cache, only when they're not too big, and are lazily loaded by self::loadExtraFromDB().
+ * @param string $prefix Must be the empty string
* @return array
+ * @since 1.31 No longer accepts a non-empty $prefix
*/
- function getLazyCacheFields( $prefix = 'img_' ) {
- static $fields = [ 'metadata' ];
- static $results = [];
-
- if ( $prefix == '' ) {
- return $fields;
- }
-
- if ( !isset( $results[$prefix] ) ) {
- $prefixedFields = [];
- foreach ( $fields as $field ) {
- $prefixedFields[] = $prefix . $field;
- }
- $results[$prefix] = $prefixedFields;
+ protected function getLazyCacheFields( $prefix = 'img_' ) {
+ if ( $prefix !== '' ) {
+ throw new InvalidArgumentException(
+ __METHOD__ . ' with a non-empty prefix is no longer supported.'
+ );
}
- return $results[$prefix];
+ // Keep this in sync with the omit-lazy option in self::getQueryInfo().
+ return [ 'metadata' ];
}
/**
? $this->repo->getMasterDB()
: $this->repo->getReplicaDB();
- $row = $dbr->selectRow( 'image', $this->getCacheFields( 'img_' ),
- [ 'img_name' => $this->getName() ], $fname );
+ $fileQuery = static::getQueryInfo();
+ $row = $dbr->selectRow(
+ $fileQuery['tables'],
+ $fileQuery['fields'],
+ [ 'img_name' => $this->getName() ],
+ $fname,
+ [],
+ $fileQuery['joins']
+ );
if ( $row ) {
$this->loadFromRow( $row );
# Unconditionally set loaded=true, we don't want the accessors constantly rechecking
$this->extraDataLoaded = true;
- $fieldMap = $this->loadFieldsWithTimestamp( $this->repo->getReplicaDB(), $fname );
+ $fieldMap = $this->loadExtraFieldsWithTimestamp( $this->repo->getReplicaDB(), $fname );
if ( !$fieldMap ) {
- $fieldMap = $this->loadFieldsWithTimestamp( $this->repo->getMasterDB(), $fname );
+ $fieldMap = $this->loadExtraFieldsWithTimestamp( $this->repo->getMasterDB(), $fname );
}
if ( $fieldMap ) {
* @param string $fname
* @return array|bool
*/
- private function loadFieldsWithTimestamp( $dbr, $fname ) {
+ private function loadExtraFieldsWithTimestamp( $dbr, $fname ) {
$fieldMap = false;
- $row = $dbr->selectRow( 'image', $this->getLazyCacheFields( 'img_' ), [
+ $fileQuery = self::getQueryInfo( [ 'omit-nonlazy' ] );
+ $row = $dbr->selectRow(
+ $fileQuery['tables'],
+ $fileQuery['fields'],
+ [
'img_name' => $this->getName(),
- 'img_timestamp' => $dbr->timestamp( $this->getTimestamp() )
- ], $fname );
+ 'img_timestamp' => $dbr->timestamp( $this->getTimestamp() ),
+ ],
+ $fname,
+ [],
+ $fileQuery['joins']
+ );
if ( $row ) {
$fieldMap = $this->unprefixRow( $row, 'img_' );
} else {
# File may have been uploaded over in the meantime; check the old versions
- $row = $dbr->selectRow( 'oldimage', $this->getLazyCacheFields( 'oi_' ), [
+ $fileQuery = OldLocalFile::getQueryInfo( [ 'omit-nonlazy' ] );
+ $row = $dbr->selectRow(
+ $fileQuery['tables'],
+ $fileQuery['fields'],
+ [
'oi_name' => $this->getName(),
- 'oi_timestamp' => $dbr->timestamp( $this->getTimestamp() )
- ], $fname );
+ 'oi_timestamp' => $dbr->timestamp( $this->getTimestamp() ),
+ ],
+ $fname,
+ [],
+ $fileQuery['joins']
+ );
if ( $row ) {
$fieldMap = $this->unprefixRow( $row, 'oi_' );
}
}
+ if ( isset( $fieldMap['metadata'] ) ) {
+ $fieldMap['metadata'] = $this->repo->getReplicaDB()->decodeBlob( $fieldMap['metadata'] );
+ }
+
return $fieldMap;
}
function decodeRow( $row, $prefix = 'img_' ) {
$decoded = $this->unprefixRow( $row, $prefix );
+ $decoded['description'] = CommentStore::newKey( 'description' )
+ ->getComment( (object)$decoded )->text;
+
$decoded['timestamp'] = wfTimestamp( TS_MW, $decoded['timestamp'] );
$decoded['metadata'] = $this->repo->getReplicaDB()->decodeBlob( $decoded['metadata'] );
$this->dataLoaded = true;
$this->extraDataLoaded = true;
- $this->description = CommentStore::newKey( "{$prefix}description" )
- // $row is probably using getFields() from self::getCacheFields()
- ->getCommentLegacy( wfGetDB( DB_REPLICA ), $row )->text;
-
$array = $this->decodeRow( $row, $prefix );
foreach ( $array as $name => $value ) {
*/
function getHistory( $limit = null, $start = null, $end = null, $inc = true ) {
$dbr = $this->repo->getReplicaDB();
- $tables = [ 'oldimage' ];
- $fields = OldLocalFile::selectFields();
- $conds = $opts = $join_conds = [];
+ $oldFileQuery = OldLocalFile::getQueryInfo();
+
+ $tables = $oldFileQuery['tables'];
+ $fields = $oldFileQuery['fields'];
+ $join_conds = $oldFileQuery['joins'];
+ $conds = $opts = [];
$eq = $inc ? '=' : '';
$conds[] = "oi_name = " . $dbr->addQuotes( $this->title->getDBkey() );
$dbr = $this->repo->getReplicaDB();
if ( $this->historyLine == 0 ) { // called for the first time, return line from cur
- $this->historyRes = $dbr->select( 'image',
- self::selectFields() + [
+ $fileQuery = self::getQueryInfo();
+ $this->historyRes = $dbr->select( $fileQuery['tables'],
+ $fileQuery['fields'] + [
'oi_archive_name' => $dbr->addQuotes( '' ),
'oi_deleted' => 0,
],
[ 'img_name' => $this->title->getDBkey() ],
- $fname
+ $fname,
+ [],
+ $fileQuery['joins']
);
if ( 0 == $dbr->numRows( $this->historyRes ) ) {
return false;
}
} elseif ( $this->historyLine == 1 ) {
+ $fileQuery = OldLocalFile::getQueryInfo();
$this->historyRes = $dbr->select(
- 'oldimage',
- OldLocalFile::selectFields(),
+ $fileQuery['tables'],
+ $fileQuery['fields'],
[ 'oi_name' => $this->title->getDBkey() ],
$fname,
- [ 'ORDER BY' => 'oi_timestamp DESC' ]
+ [ 'ORDER BY' => 'oi_timestamp DESC' ],
+ $fileQuery['joins']
);
}
$this->historyLine++;
}
# Invalidate cache for all pages using this file
- DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this->getTitle(), 'imagelinks' ) );
+ DeferredUpdates::addUpdate(
+ new HTMLCacheUpdate( $this->getTitle(), 'imagelinks', 'file-upload' )
+ );
return Status::newGood();
}
}
if ( count( $oldRels ) ) {
+ $fileQuery = OldLocalFile::getQueryInfo();
$res = $dbw->select(
- 'oldimage',
- OldLocalFile::selectFields(),
+ $fileQuery['tables'],
+ $fileQuery['fields'],
[
'oi_name' => $this->file->getName(),
'oi_archive_name' => array_keys( $oldRels )
],
__METHOD__,
- [ 'FOR UPDATE' ]
+ [ 'FOR UPDATE' ],
+ $fileQuery['joins']
);
$rowsInsert = [];
if ( $res->numRows() ) {
$reason = $commentStoreFaReason->createComment( $dbw, $this->reason );
foreach ( $res as $row ) {
- // Legacy from OldLocalFile::selectFields() just above
- $comment = $commentStoreOiDesc->getCommentLegacy( $dbw, $row );
+ $comment = $commentStoreOiDesc->getComment( $row );
$rowsInsert[] = [
// Deletion-specific fields
'fa_storage_group' => 'deleted',
$conditions['fa_id'] = $this->ids;
}
+ $arFileQuery = ArchivedFile::getQueryInfo();
$result = $dbw->select(
- 'filearchive',
- ArchivedFile::selectFields(),
+ $arFileQuery['tables'],
+ $arFileQuery['fields'],
$conditions,
__METHOD__,
- [ 'ORDER BY' => 'fa_timestamp DESC' ]
+ [ 'ORDER BY' => 'fa_timestamp DESC' ],
+ $arFileQuery['joins']
);
$idsPresent = [];
];
}
- // Legacy from ArchivedFile::selectFields() just above
- $comment = $commentStoreFaDesc->getCommentLegacy( $dbw, $row );
+ $comment = $commentStoreFaDesc->getComment( $row );
if ( $first && !$exists ) {
// This revision will be published as the new current version
$destRel = $this->file->getRel();
$from = self::convertSelectType( $from );
$db = wfGetDB( $from === self::READ_LATEST ? DB_MASTER : DB_REPLICA );
+ $pageQuery = self::getQueryInfo();
$row = $db->selectRow(
- 'page', self::selectFields(), [ 'page_id' => $id ], __METHOD__ );
+ $pageQuery['tables'], $pageQuery['fields'], [ 'page_id' => $id ], __METHOD__,
+ [], $pageQuery['joins']
+ );
if ( !$row ) {
return null;
}
* Return the list of revision fields that should be selected to create
* a new page.
*
+ * @deprecated since 1.31, use self::getQueryInfo() instead.
* @return array
*/
public static function selectFields() {
global $wgContentHandlerUseDB, $wgPageLanguageUseDB;
+ wfDeprecated( __METHOD__, '1.31' );
+
$fields = [
'page_id',
'page_namespace',
return $fields;
}
+ /**
+ * Return the tables, fields, and join conditions to be selected to create
+ * a new page object.
+ * @since 1.31
+ * @return array With three keys:
+ * - 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()`
+ */
+ public static function getQueryInfo() {
+ global $wgContentHandlerUseDB, $wgPageLanguageUseDB;
+
+ $ret = [
+ 'tables' => [ 'page' ],
+ 'fields' => [
+ 'page_id',
+ 'page_namespace',
+ 'page_title',
+ 'page_restrictions',
+ 'page_is_redirect',
+ 'page_is_new',
+ 'page_random',
+ 'page_touched',
+ 'page_links_updated',
+ 'page_latest',
+ 'page_len',
+ ],
+ 'joins' => [],
+ ];
+
+ if ( $wgContentHandlerUseDB ) {
+ $ret['fields'][] = 'page_content_model';
+ }
+
+ if ( $wgPageLanguageUseDB ) {
+ $ret['fields'][] = 'page_lang';
+ }
+
+ return $ret;
+ }
+
/**
* Fetch a page record with the given conditions
* @param IDatabase $dbr
* @return object|bool Database result resource, or false on failure
*/
protected function pageData( $dbr, $conditions, $options = [] ) {
- $fields = self::selectFields();
+ $pageQuery = self::getQueryInfo();
// Avoid PHP 7.1 warning of passing $this by reference
$wikiPage = $this;
- Hooks::run( 'ArticlePageDataBefore', [ &$wikiPage, &$fields ] );
+ Hooks::run( 'ArticlePageDataBefore', [
+ &$wikiPage, &$pageQuery['fields'], &$pageQuery['tables'], &$pageQuery['joins']
+ ] );
- $row = $dbr->selectRow( 'page', $fields, $conditions, __METHOD__, $options );
+ $row = $dbr->selectRow(
+ $pageQuery['tables'],
+ $pageQuery['fields'],
+ $conditions,
+ __METHOD__,
+ $options,
+ $pageQuery['joins']
+ );
Hooks::run( 'ArticlePageDataAfter', [ &$wikiPage, &$row ] );
throw new MWException( "Could not find text for current revision {$oldid}." );
}
- // @TODO: pass content object?!
- $revision = new Revision( [
- 'page' => $this->getId(),
- 'title' => $this->mTitle, // for determining the default content model
- 'comment' => $summary,
- 'minor_edit' => $meta['minor'],
- 'text' => $meta['serialized'],
- 'len' => $newsize,
- 'parent_id' => $oldid,
- 'user' => $user->getId(),
- 'user_text' => $user->getName(),
- 'timestamp' => $now,
- 'content_model' => $content->getModel(),
- 'content_format' => $meta['serialFormat'],
- ] );
-
$changed = !$content->equals( $oldContent );
$dbw = wfGetDB( DB_MASTER );
if ( $changed ) {
+ // @TODO: pass content object?!
+ $revision = new Revision( [
+ 'page' => $this->getId(),
+ 'title' => $this->mTitle, // for determining the default content model
+ 'comment' => $summary,
+ 'minor_edit' => $meta['minor'],
+ 'text' => $meta['serialized'],
+ 'len' => $newsize,
+ 'parent_id' => $oldid,
+ 'user' => $user->getId(),
+ 'user_text' => $user->getName(),
+ 'timestamp' => $now,
+ 'content_model' => $content->getModel(),
+ 'content_format' => $meta['serialFormat'],
+ ] );
+
$prepStatus = $content->prepareSave( $this, $flags, $oldid, $user );
$status->merge( $prepStatus );
if ( !$status->isOK() ) {
} else {
// T34948: revision ID must be set to page {{REVISIONID}} and
// related variables correctly. Likewise for {{REVISIONUSER}} (T135261).
- $revision->setId( $this->getLatest() );
- $revision->setUserIdAndName(
- $this->getUser( Revision::RAW ),
- $this->getUserText( Revision::RAW )
- );
+ // Since we don't insert a new revision into the database, the least
+ // error-prone way is to reuse given old revision.
+ $revision = $meta['oldRevision'];
}
if ( $changed ) {
$revCommentStore = new CommentStore( 'rev_comment' );
$arCommentStore = new CommentStore( 'ar_comment' );
- $fields = Revision::selectFields();
+ $revQuery = Revision::getQueryInfo();
$bitfield = false;
// Bitfields to further suppress the content
if ( $suppress ) {
$bitfield = Revision::SUPPRESSED_ALL;
- $fields = array_diff( $fields, [ 'rev_deleted' ] );
+ $revQuery['fields'] = array_diff( $revQuery['fields'], [ 'rev_deleted' ] );
}
// For now, shunt the revision data into the archive table.
// the rev_deleted field, which is reserved for this purpose.
// Get all of the page revisions
- $commentQuery = $revCommentStore->getJoin();
$res = $dbw->select(
- [ 'revision' ] + $commentQuery['tables'],
- $fields + $commentQuery['fields'],
+ $revQuery['tables'],
+ $revQuery['fields'],
[ 'rev_page' => $id ],
__METHOD__,
'FOR UPDATE',
- $commentQuery['joins']
+ $revQuery['joins']
);
// Build their equivalent archive rows
MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $title );
// Invalidate caches of articles which include this page
- DeferredUpdates::addUpdate( new HTMLCacheUpdate( $title, 'templatelinks' ) );
+ DeferredUpdates::addUpdate(
+ new HTMLCacheUpdate( $title, 'templatelinks', 'page-create' )
+ );
if ( $title->getNamespace() == NS_CATEGORY ) {
// Load the Category object, which will schedule a job to create
// Images
if ( $title->getNamespace() == NS_FILE ) {
- DeferredUpdates::addUpdate( new HTMLCacheUpdate( $title, 'imagelinks' ) );
+ DeferredUpdates::addUpdate(
+ new HTMLCacheUpdate( $title, 'imagelinks', 'page-delete' )
+ );
}
// User talk pages
*/
public static function onArticleEdit( Title $title, Revision $revision = null ) {
// Invalidate caches of articles which include this page
- DeferredUpdates::addUpdate( new HTMLCacheUpdate( $title, 'templatelinks' ) );
+ DeferredUpdates::addUpdate(
+ new HTMLCacheUpdate( $title, 'templatelinks', 'page-edit' )
+ );
// Invalidate the caches of all pages which redirect here
- DeferredUpdates::addUpdate( new HTMLCacheUpdate( $title, 'redirect' ) );
+ DeferredUpdates::addUpdate(
+ new HTMLCacheUpdate( $title, 'redirect', 'page-edit' )
+ );
MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $title );