Merge "Reduced some master queries by adding flags to Revision functions."
authorTim Starling <tstarling@wikimedia.org>
Thu, 28 Jun 2012 05:11:04 +0000 (05:11 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 28 Jun 2012 05:11:04 +0000 (05:11 +0000)
1  2 
includes/AutoLoader.php
includes/Revision.php
includes/diff/DifferenceEngine.php
includes/filerepo/file/LocalFile.php

diff --combined includes/AutoLoader.php
@@@ -71,8 -71,7 +71,8 @@@ $wgAutoloadLocalClasses = array
        'DeferredUpdates' => 'includes/DeferredUpdates.php',
        'DeprecatedGlobal' => 'includes/DeprecatedGlobal.php',
        'DerivativeRequest' => 'includes/WebRequest.php',
 -      'DeviceDetection' => 'includes/DeviceDetection.php',
 +      'DeviceDetection' => 'includes/mobile/DeviceDetection.php',
 +      'DeviceProperties' => 'includes/mobile/DeviceDetection.php',
        'DiffHistoryBlob' => 'includes/HistoryBlob.php',
        'DoubleReplacer' => 'includes/StringUtils.php',
        'DummyLinker' => 'includes/Linker.php',
        'HttpRequest' => 'includes/HttpFunctions.old.php',
        'ICacheHelper' => 'includes/CacheHelper.php',
        'IcuCollation' => 'includes/Collation.php',
 +      'IDeviceProperties' => 'includes/mobile/DeviceDetection.php',
 +      'IDeviceDetector' => 'includes/mobile/DeviceDetection.php',
        'IdentityCollation' => 'includes/Collation.php',
        'ImageGallery' => 'includes/ImageGallery.php',
        'ImageHistoryList' => 'includes/ImagePage.php',
        'MWException' => 'includes/Exception.php',
        'MWExceptionHandler' => 'includes/Exception.php',
        'MWFunction' => 'includes/MWFunction.php',
 +      'MWHookException' => 'includes/Hooks.php',
        'MWHttpRequest' => 'includes/HttpFunctions.php',
        'MWInit' => 'includes/Init.php',
        'MWNamespace' => 'includes/Namespace.php',
        'ReplacementArray' => 'includes/StringUtils.php',
        'Replacer' => 'includes/StringUtils.php',
        'ReverseChronologicalPager' => 'includes/Pager.php',
 +      'RevisionItem' => 'includes/RevisionList.php',
        'RevisionItemBase' => 'includes/RevisionList.php',
        'RevisionListBase' => 'includes/RevisionList.php',
        'Revision' => 'includes/Revision.php',
        'TitleArrayFromResult' => 'includes/TitleArray.php',
        'ThrottledError' => 'includes/Exception.php',
        'UnlistedSpecialPage' => 'includes/SpecialPage.php',
 +      'UploadSourceAdapter' => 'includes/Import.php',
        'UppercaseCollation' => 'includes/Collation.php',
        'User' => 'includes/User.php',
        'UserArray' => 'includes/UserArray.php',
        'Xml' => 'includes/Xml.php',
        'XmlDumpWriter' => 'includes/Export.php',
        'XmlJsCode' => 'includes/Xml.php',
 +      'XMLReader2' => 'includes/Import.php',
        'XmlSelect' => 'includes/Xml.php',
        'XmlTypeCheck' => 'includes/XmlTypeCheck.php',
        'ZhClient' => 'includes/ZhClient.php',
        'ZipDirectoryReader' => 'includes/ZipDirectoryReader.php',
 +      'ZipDirectoryReaderError' => 'includes/ZipDirectoryReader.php',
  
        # includes/actions
        'CachedAction' => 'includes/actions/CachedAction.php',
        'IContextSource' => 'includes/context/IContextSource.php',
        'RequestContext' => 'includes/context/RequestContext.php',
  
+       # includes/dao
+       'IDBAccessObject' => 'includes/dao/IDBAccessObject.php',
        # includes/db
        'Blob' => 'includes/db/DatabaseUtility.php',
        'ChronologyProtector' => 'includes/db/LBFactory.php',
        'DatabaseSqlite' => 'includes/db/DatabaseSqlite.php',
        'DatabaseSqliteStandalone' => 'includes/db/DatabaseSqlite.php',
        'DatabaseType' => 'includes/db/Database.php',
 +      'DBAccessError' => 'includes/db/LBFactory.php',
        'DBConnectionError' => 'includes/db/DatabaseError.php',
        'DBError' => 'includes/db/DatabaseError.php',
        'DBObject' => 'includes/db/DatabaseUtility.php',
 +      'IORMRow' => 'includes/db/IORMRow.php',
 +      'IORMTable' => 'includes/db/IORMTable.php',
        'DBMasterPos' => 'includes/db/DatabaseUtility.php',
        'DBQueryError' => 'includes/db/DatabaseError.php',
        'DBUnexpectedError' => 'includes/db/DatabaseError.php',
        'Field' => 'includes/db/DatabaseUtility.php',
        'IBM_DB2Blob' => 'includes/db/DatabaseIbm_db2.php',
        'IBM_DB2Field' => 'includes/db/DatabaseIbm_db2.php',
 +      'IBM_DB2Helper' => 'includes/db/DatabaseIbm_db2.php',
 +      'IBM_DB2Result' => 'includes/db/DatabaseIbm_db2.php',
        'LBFactory' => 'includes/db/LBFactory.php',
 +      'LBFactory_Fake' => 'includes/db/LBFactory.php',
        'LBFactory_Multi' => 'includes/db/LBFactory_Multi.php',
        'LBFactory_Simple' => 'includes/db/LBFactory.php',
        'LBFactory_Single' => 'includes/db/LBFactory_Single.php',
        'LoadMonitor' => 'includes/db/LoadMonitor.php',
        'LoadMonitor_MySQL' => 'includes/db/LoadMonitor.php',
        'LoadMonitor_Null' => 'includes/db/LoadMonitor.php',
 +      'MssqlField' => 'includes/db/DatabaseMssql.php',
 +      'MssqlResult' => 'includes/db/DatabaseMssql.php',
        'MySQLField' => 'includes/db/DatabaseMysql.php',
        'MySQLMasterPos' => 'includes/db/DatabaseMysql.php',
        'ORAField' => 'includes/db/DatabaseOracle.php',
        'ORAResult' => 'includes/db/DatabaseOracle.php',
 +      'ORMIterator' => 'includes/db/ORMIterator.php',
        'ORMResult' => 'includes/db/ORMResult.php',
        'ORMRow' => 'includes/db/ORMRow.php',
        'ORMTable' => 'includes/db/ORMTable.php',
        'PostgresField' => 'includes/db/DatabasePostgres.php',
 +      'PostgresTransactionState' => 'includes/db/DatabasePostgres.php',
        'ResultWrapper' => 'includes/db/DatabaseUtility.php',
 +      'SavepointPostgres' => 'includes/db/DatabasePostgres.php',
        'SQLiteField' => 'includes/db/DatabaseSqlite.php',
  
        # includes/debug
        'LocalRepo' => 'includes/filerepo/LocalRepo.php',
        'NullRepo' => 'includes/filerepo/NullRepo.php',
        'RepoGroup' => 'includes/filerepo/RepoGroup.php',
 +      'TempFileRepo' => 'includes/filerepo/FileRepo.php',
  
        # includes/filerepo/file
        'ArchivedFile' => 'includes/filerepo/file/ArchivedFile.php',
        'FSLockManager' => 'includes/filerepo/backend/lockmanager/FSLockManager.php',
        'DBLockManager' => 'includes/filerepo/backend/lockmanager/DBLockManager.php',
        'LSLockManager' => 'includes/filerepo/backend/lockmanager/LSLockManager.php',
 +      'MemcLockManager' => 'includes/filerepo/backend/lockmanager/MemcLockManager.php',
 +      'QuorumLockManager' => 'includes/filerepo/backend/lockmanager/LockManager.php',
        'MySqlLockManager'=> 'includes/filerepo/backend/lockmanager/DBLockManager.php',
        'NullLockManager' => 'includes/filerepo/backend/lockmanager/LockManager.php',
        'FileOp' => 'includes/filerepo/backend/FileOp.php',
        'DoubleRedirectJob' => 'includes/job/DoubleRedirectJob.php',
        'EmaillingJob' => 'includes/job/EmaillingJob.php',
        'EnotifNotifyJob' => 'includes/job/EnotifNotifyJob.php',
 -      'Job' => 'includes/job/JobQueue.php',
 +      'Job' => 'includes/job/Job.php',
        'RefreshLinksJob' => 'includes/job/RefreshLinksJob.php',
        'RefreshLinksJob2' => 'includes/job/RefreshLinksJob.php',
        'UploadFromUrlJob' => 'includes/job/UploadFromUrlJob.php',
  
        # includes/libs
        'CSSJanus' => 'includes/libs/CSSJanus.php',
 +      'CSSJanus_Tokenizer' => 'includes/libs/CSSJanus.php',
        'CSSMin' => 'includes/libs/CSSMin.php',
        'HttpStatus' => 'includes/libs/HttpStatus.php',
        'IEContentAnalyzer' => 'includes/libs/IEContentAnalyzer.php',
        'IEUrlExtension' => 'includes/libs/IEUrlExtension.php',
        'JavaScriptMinifier' => 'includes/libs/JavaScriptMinifier.php',
 +      'JSCompilerContext' => 'includes/libs/jsminplus.php',
        'JSMinPlus' => 'includes/libs/jsminplus.php',
 +      'JSNode' => 'includes/libs/jsminplus.php',
        'JSParser' => 'includes/libs/jsminplus.php',
 +      'JSToken' => 'includes/libs/jsminplus.php',
 +      'JSTokenizer' => 'includes/libs/jsminplus.php',
  
        # includes/logging
        'DatabaseLogEntry' => 'includes/logging/LogEntry.php',
        'PNGMetadataExtractor' => 'includes/media/PNGMetadataExtractor.php',
        'SvgHandler' => 'includes/media/SVG.php',
        'SVGMetadataExtractor' => 'includes/media/SVGMetadataExtractor.php',
 +      'SVGReader' => 'includes/media/SVGMetadataExtractor.php',
        'ThumbnailImage' => 'includes/media/MediaTransformOutput.php',
        'TiffHandler' => 'includes/media/Tiff.php',
        'TransformParameterError' => 'includes/media/MediaTransformOutput.php',
        'LinkHolderArray' => 'includes/parser/LinkHolderArray.php',
        'LinkMarkerReplacer' => 'includes/parser/Parser_LinkHooks.php',
        'MWTidy' => 'includes/parser/Tidy.php',
 +      'MWTidyWrapper' => 'includes/parser/Tidy.php',
        'PPCustomFrame_DOM' => 'includes/parser/Preprocessor_DOM.php',
        'PPCustomFrame_Hash' => 'includes/parser/Preprocessor_Hash.php',
        'PPCustomFrame_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
        # includes/revisiondelete
        'RevDel_ArchivedFileItem' => 'includes/revisiondelete/RevisionDelete.php',
        'RevDel_ArchivedFileList' => 'includes/revisiondelete/RevisionDelete.php',
 +      'RevDel_ArchivedRevisionItem' => 'includes/revisiondelete/RevisionDelete.php',
        'RevDel_ArchiveItem' => 'includes/revisiondelete/RevisionDelete.php',
        'RevDel_ArchiveList' => 'includes/revisiondelete/RevisionDelete.php',
        'RevDel_FileItem' => 'includes/revisiondelete/RevisionDelete.php',
        'RevisionDeleteUser' => 'includes/revisiondelete/RevisionDeleteUser.php',
  
        # includes/search
 +      'MssqlSearchResultSet' => 'includes/search/SearchMssql.php',
        'MySQLSearchResultSet' => 'includes/search/SearchMySQL.php',
        'PostgresSearchResult' => 'includes/search/SearchPostgres.php',
        'PostgresSearchResultSet' => 'includes/search/SearchPostgres.php',
        'SearchIBM_DB2' => 'includes/search/SearchIBM_DB2.php',
        'SearchMssql' => 'includes/search/SearchMssql.php',
        'SearchMySQL' => 'includes/search/SearchMySQL.php',
 +      'SearchNearMatchResultSet' => 'includes/search/SearchEngine.php',
        'SearchOracle' => 'includes/search/SearchOracle.php',
        'SearchPostgres' => 'includes/search/SearchPostgres.php',
        'SearchResult' => 'includes/search/SearchEngine.php',
        'AncientPagesPage' => 'includes/specials/SpecialAncientpages.php',
        'BlockListPager' => 'includes/specials/SpecialBlockList.php',
        'BrokenRedirectsPage' => 'includes/specials/SpecialBrokenRedirects.php',
 +      'CategoryPager' => 'includes/specials/SpecialCategories.php',
        'ContribsPager' => 'includes/specials/SpecialContributions.php',
        'DBLockForm' => 'includes/specials/SpecialLockdb.php',
        'DBUnlockForm' => 'includes/specials/SpecialUnlockdb.php',
        'DeletedContributionsPage' => 'includes/specials/SpecialDeletedContributions.php',
        'DisambiguationsPage' => 'includes/specials/SpecialDisambiguations.php',
        'DoubleRedirectsPage' => 'includes/specials/SpecialDoubleRedirects.php',
 +      'EditWatchlistCheckboxSeriesField' => 'includes/specials/SpecialEditWatchlist.php',
 +      'EditWatchlistNormalHTMLForm' => 'includes/specials/SpecialEditWatchlist.php',
        'EmailConfirmation' => 'includes/specials/SpecialConfirmemail.php',
        'EmailInvalidation' => 'includes/specials/SpecialConfirmemail.php',
        'FewestrevisionsPage' => 'includes/specials/SpecialFewestrevisions.php',
        'FileDuplicateSearchPage' => 'includes/specials/SpecialFileDuplicateSearch.php',
        'HTMLBlockedUsersItemSelect' => 'includes/specials/SpecialBlockList.php',
 +      'ImageListPager' => 'includes/specials/SpecialListfiles.php',
        'ImportReporter' => 'includes/specials/SpecialImport.php',
        'IPBlockForm' => 'includes/specials/SpecialBlock.php',
        'LinkSearchPage' => 'includes/specials/SpecialLinkSearch.php',
        'LoginForm' => 'includes/specials/SpecialUserlogin.php',
        'LonelyPagesPage' => 'includes/specials/SpecialLonelypages.php',
        'LongPagesPage' => 'includes/specials/SpecialLongpages.php',
 +      'MergeHistoryPager' => 'includes/specials/SpecialMergeHistory.php',
        'MIMEsearchPage' => 'includes/specials/SpecialMIMEsearch.php',
        'MostcategoriesPage' => 'includes/specials/SpecialMostcategories.php',
        'MostimagesPage' => 'includes/specials/SpecialMostimages.php',
        'MostlinkedTemplatesPage' => 'includes/specials/SpecialMostlinkedtemplates.php',
        'MostrevisionsPage' => 'includes/specials/SpecialMostrevisions.php',
        'MovePageForm' => 'includes/specials/SpecialMovepage.php',
 +      'NewFilesPager' => 'includes/specials/SpecialNewimages.php',
        'NewPagesPager' => 'includes/specials/SpecialNewpages.php',
        'PageArchive' => 'includes/specials/SpecialUndelete.php',
        'PopularPagesPage' => 'includes/specials/SpecialPopularpages.php',
 +      'ProtectedPagesPager' => 'includes/specials/SpecialProtectedpages.php',
 +      'ProtectedTitlesPager' => 'includes/specials/SpecialProtectedtitles.php',
        'RandomPage' => 'includes/specials/SpecialRandompage.php',
        'ShortPagesPage' => 'includes/specials/SpecialShortpages.php',
        'SpecialActiveUsers' => 'includes/specials/SpecialActiveusers.php',
        'SpecialUnlockdb' => 'includes/specials/SpecialUnlockdb.php',
        'SpecialUpload' => 'includes/specials/SpecialUpload.php',
        'SpecialUploadStash' => 'includes/specials/SpecialUploadStash.php',
 +      'SpecialUploadStashTooLargeException' => 'includes/specials/SpecialUploadStash.php',
        'SpecialUserlogout' => 'includes/specials/SpecialUserlogout.php',
        'SpecialVersion' => 'includes/specials/SpecialVersion.php',
        'SpecialWatchlist' => 'includes/specials/SpecialWatchlist.php',
 -      'SpecialWhatlinkshere' => 'includes/specials/SpecialWhatlinkshere.php',
 +      'SpecialWhatLinksHere' => 'includes/specials/SpecialWhatlinkshere.php',
        'UncategorizedCategoriesPage' => 'includes/specials/SpecialUncategorizedcategories.php',
        'UncategorizedImagesPage' => 'includes/specials/SpecialUncategorizedimages.php',
        'UncategorizedPagesPage' => 'includes/specials/SpecialUncategorizedpages.php',
        'UnusedimagesPage' => 'includes/specials/SpecialUnusedimages.php',
        'UnusedtemplatesPage' => 'includes/specials/SpecialUnusedtemplates.php',
        'UnwatchedpagesPage' => 'includes/specials/SpecialUnwatchedpages.php',
 +      'UploadChunkFileException' => 'includes/upload/UploadFromChunks.php',
 +      'UploadChunkZeroLengthFileException' => 'includes/upload/UploadFromChunks.php',
        'UploadForm' => 'includes/specials/SpecialUpload.php',
        'UploadSourceField' => 'includes/specials/SpecialUpload.php',
        'UserrightsPage' => 'includes/specials/SpecialUserrights.php',
        'UploadStashNoSuchKeyException' => 'includes/upload/UploadStash.php',
  
        # languages
 +      'ConverterRule' => 'languages/LanguageConverter.php',
        'FakeConverter' => 'languages/Language.php',
        'Language' => 'languages/Language.php',
        'LanguageConverter' => 'languages/LanguageConverter.php',
  
        # maintenance/language
        'csvStatsOutput' => 'maintenance/language/StatOutputs.php',
 +      'extensionLanguages' => 'maintenance/language/languages.inc',
        'languages' => 'maintenance/language/languages.inc',
        'MessageWriter' => 'maintenance/language/writeMessagesArray.inc',
        'statsOutput' => 'maintenance/language/StatOutputs.php',
        'AnsiTermColorer'  => 'maintenance/term/MWTerm.php',
        'DummyTermColorer' => 'maintenance/term/MWTerm.php',
  
 +      # mw-config
 +      'InstallerOverrides' => 'mw-config/overrides.php',
 +      'MyLocalSettingsGenerator' => 'mw-config/overrides.php',
 +
        # tests
        'DbTestPreviewer' => 'tests/testHelpers.inc',
        'DbTestRecorder' => 'tests/testHelpers.inc',
 +      'DelayedParserTest' => 'tests/testHelpers.inc',
        'TestFileIterator' => 'tests/testHelpers.inc',
        'TestRecorder' => 'tests/testHelpers.inc',
  
        # tests/parser
        'ParserTest' => 'tests/parser/parserTest.inc',
        'ParserTestParserHook' => 'tests/parser/parserTestsParserHook.php',
 -      'ParserTestStaticParserHook' => 'tests/parser/parserTestsStaticParserHook.php',
  
        # tests/selenium
        'Selenium' => 'tests/selenium/Selenium.php',
        'SeleniumTestListener' => 'tests/selenium/SeleniumTestListener.php',
        'SeleniumTestSuite' => 'tests/selenium/SeleniumTestSuite.php',
        'SeleniumConfig' => 'tests/selenium/SeleniumConfig.php',
 +
 +      # skins
 +      'CologneBlueTemplate' => 'skins/CologneBlue.php',
 +      'ModernTemplate' => 'skins/Modern.php',
 +      'MonoBookTemplate' => 'skins/MonoBook.php',
 +      'NostalgiaTemplate' => 'skins/Nostalgia.php',
 +      'SkinChick' => 'skins/Chick.php',
 +      'SkinCologneBlue' => 'skins/CologneBlue.php',
 +      'SkinModern' => 'skins/Modern.php',
 +      'SkinMonoBook' => 'skins/MonoBook.php',
 +      'SkinMySkin' => 'skins/MySkin.php',
 +      'SkinNostalgia' => 'skins/Nostalgia.php',
 +      'SkinSimple' => 'skins/Simple.php',
 +      'SkinStandard' => 'skins/Standard.php',
 +      'SkinVector' => 'skins/Vector.php',
 +      'StandardTemplate' => 'skins/Standard.php',
 +      'VectorTemplate' => 'skins/Vector.php',
  );
  
  class AutoLoader {
        static function autoload( $className ) {
                global $wgAutoloadClasses, $wgAutoloadLocalClasses;
  
 +              // Workaround for PHP bug <https://bugs.php.net/bug.php?id=49143> (5.3.2. is broken, it's fixed in 5.3.6).
 +              // Strip leading backslashes from class names. When namespaces are used, leading backslashes are used to indicate
 +              // the top-level namespace, e.g. \foo\Bar. When used like this in the code, the leading backslash isn't passed to
 +              // the auto-loader ($className would be 'foo\Bar'). However, if a class is accessed using a string instead of a
 +              // class literal (e.g. $class = '\foo\Bar'; new $class()), then some versions of PHP do not strip the leading
 +              // backlash in this case, causing autoloading to fail.
 +              $className = ltrim( $className, '\\' );
 +
                if ( isset( $wgAutoloadLocalClasses[$className] ) ) {
                        $filename = $wgAutoloadLocalClasses[$className];
                } elseif ( isset( $wgAutoloadClasses[$className] ) ) {
         * Sanitizer that have define()s outside of their class definition. Of course
         * this wouldn't be necessary if everything in MediaWiki was class-based. Sigh.
         *
 +       * @param $class string
         * @return Boolean Return the results of class_exists() so we know if we were successful
         */
        static function loadClass( $class ) {
diff --combined includes/Revision.php
@@@ -23,7 -23,7 +23,7 @@@
  /**
   * @todo document
   */
- class Revision {
+ class Revision implements IDBAccessObject {
        protected $mId;
        protected $mPage;
        protected $mUserText;
@@@ -47,7 -47,7 +47,7 @@@
        const DELETED_RESTRICTED = 8;
        // Convenience field
        const SUPPRESSED_USER = 12;
-       // Audience options for Revision::getText()
+       // Audience options for accessors
        const FOR_PUBLIC = 1;
        const FOR_THIS_USER = 2;
        const RAW = 3;
         * Load a page revision from a given revision ID number.
         * Returns null if no such revision can be found.
         *
+        * $flags include:
+        *      IDBAccessObject::LATEST_READ  : Select the data from the master
+        *      IDBAccessObject::LOCKING_READ : Select & lock the data from the master
+        *      IDBAccessObject::AVOID_MASTER : Avoid master queries; data may be stale
+        *
         * @param $id Integer
+        * @param $flags Integer (optional)
         * @return Revision or null
         */
-       public static function newFromId( $id ) {
-               return Revision::newFromConds( array( 'rev_id' => intval( $id ) ) );
+       public static function newFromId( $id, $flags = 0 ) {
+               return self::newFromConds( array( 'rev_id' => intval( $id ) ), $flags );
        }
  
        /**
         * that's attached to a given title. If not attached
         * to that title, will return null.
         *
+        * $flags include:
+        *      IDBAccessObject::LATEST_READ  : Select the data from the master
+        *      IDBAccessObject::LOCKING_READ : Select & lock the data from the master
+        *      IDBAccessObject::AVOID_MASTER : Avoid master queries; data may be stale
+        *
         * @param $title Title
         * @param $id Integer (optional)
+        * @param $flags Integer Bitfield (optional)
         * @return Revision or null
         */
-       public static function newFromTitle( $title, $id = 0 ) {
+       public static function newFromTitle( $title, $id = 0, $flags = 0 ) {
                $conds = array(
                        'page_namespace' => $title->getNamespace(),
                        'page_title'     => $title->getDBkey()
@@@ -80,7 -92,7 +92,7 @@@
                if ( $id ) {
                        // Use the specified ID
                        $conds['rev_id'] = $id;
-               } elseif ( wfGetLB()->getServerCount() > 1 ) {
+               } elseif ( !( $flags & self::AVOID_MASTER ) && wfGetLB()->getServerCount() > 1 ) {
                        // Get the latest revision ID from the master
                        $dbw = wfGetDB( DB_MASTER );
                        $latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
                        // Use a join to get the latest revision
                        $conds[] = 'rev_id=page_latest';
                }
-               return Revision::newFromConds( $conds );
+               return self::newFromConds( $conds, $flags );
        }
  
        /**
         * that's attached to a given page ID.
         * Returns null if no such revision can be found.
         *
+        * $flags include:
+        *      IDBAccessObject::LATEST_READ  : Select the data from the master
+        *      IDBAccessObject::LOCKING_READ : Select & lock the data from the master
+        *      IDBAccessObject::AVOID_MASTER : Avoid master queries; data may be stale
+        *
         * @param $revId Integer
         * @param $pageId Integer (optional)
+        * @param $flags Integer Bitfield (optional)
         * @return Revision or null
         */
-       public static function newFromPageId( $pageId, $revId = 0 ) {
+       public static function newFromPageId( $pageId, $revId = 0, $flags = 0 ) {
                $conds = array( 'page_id' => $pageId );
                if ( $revId ) {
                        $conds['rev_id'] = $revId;
-               } elseif ( wfGetLB()->getServerCount() > 1 ) {
+               } elseif ( !( $flags & self::AVOID_MASTER ) && wfGetLB()->getServerCount() > 1 ) {
                        // Get the latest revision ID from the master
                        $dbw = wfGetDB( DB_MASTER );
                        $latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
                } else {
                        $conds[] = 'rev_id = page_latest';
                }
-               return Revision::newFromConds( $conds );
+               return self::newFromConds( $conds, $flags );
        }
  
        /**
         * @return Revision or null
         */
        public static function loadFromId( $db, $id ) {
-               return Revision::loadFromConds( $db, array( 'rev_id' => intval( $id ) ) );
+               return self::loadFromConds( $db, array( 'rev_id' => intval( $id ) ) );
        }
  
        /**
                } else {
                        $conds[] = 'rev_id=page_latest';
                }
-               return Revision::loadFromConds( $db, $conds );
+               return self::loadFromConds( $db, $conds );
        }
  
        /**
                } else {
                        $matchId = 'page_latest';
                }
-               return Revision::loadFromConds( $db,
+               return self::loadFromConds( $db,
                        array( "rev_id=$matchId",
                                   'page_namespace' => $title->getNamespace(),
                                   'page_title'     => $title->getDBkey() )
         * @return Revision or null
         */
        public static function loadFromTimestamp( $db, $title, $timestamp ) {
-               return Revision::loadFromConds( $db,
+               return self::loadFromConds( $db,
                        array( 'rev_timestamp'  => $db->timestamp( $timestamp ),
                                   'page_namespace' => $title->getNamespace(),
                                   'page_title'     => $title->getDBkey() )
         * Given a set of conditions, fetch a revision.
         *
         * @param $conditions Array
+        * @param $flags integer (optional)
         * @return Revision or null
         */
-       public static function newFromConds( $conditions ) {
-               $db = wfGetDB( DB_SLAVE );
-               $rev = Revision::loadFromConds( $db, $conditions );
-               if( is_null( $rev ) && wfGetLB()->getServerCount() > 1 ) {
-                       $dbw = wfGetDB( DB_MASTER );
-                       $rev = Revision::loadFromConds( $dbw, $conditions );
+       private static function newFromConds( $conditions, $flags = 0 ) {
+               $db = wfGetDB( ( $flags & self::LATEST_READ ) ? DB_MASTER : DB_SLAVE );
+               $rev = self::loadFromConds( $db, $conditions, $flags );
+               if ( is_null( $rev ) && wfGetLB()->getServerCount() > 1 ) {
+                       if ( !( $flags & self::LATEST_READ ) && !( $flags & self::AVOID_MASTER ) ) {
+                               $dbw = wfGetDB( DB_MASTER );
+                               $rev = self::loadFromConds( $dbw, $conditions, $flags );
+                       }
                }
                return $rev;
        }
         *
         * @param $db DatabaseBase
         * @param $conditions Array
+        * @param $flags integer (optional)
         * @return Revision or null
         */
-       private static function loadFromConds( $db, $conditions ) {
-               $res = Revision::fetchFromConds( $db, $conditions );
+       private static function loadFromConds( $db, $conditions, $flags = 0 ) {
+               $res = self::fetchFromConds( $db, $conditions, $flags );
                if( $res ) {
                        $row = $res->fetchObject();
                        if( $row ) {
         * @return ResultWrapper
         */
        public static function fetchRevision( $title ) {
-               return Revision::fetchFromConds(
+               return self::fetchFromConds(
                        wfGetDB( DB_SLAVE ),
                        array( 'rev_id=page_latest',
                                   'page_namespace' => $title->getNamespace(),
         *
         * @param $db DatabaseBase
         * @param $conditions Array
+        * @param $flags integer (optional)
         * @return ResultWrapper
         */
-       private static function fetchFromConds( $db, $conditions ) {
+       private static function fetchFromConds( $db, $conditions, $flags = 0 ) {
                $fields = array_merge(
                        self::selectFields(),
                        self::selectPageFields(),
                        self::selectUserFields()
                );
+               $options = array( 'LIMIT' => 1 );
+               if ( $flags & self::FOR_UPDATE ) {
+                       $options[] = 'FOR UPDATE';
+               }
                return $db->select(
                        array( 'revision', 'page', 'user' ),
                        $fields,
                        $conditions,
                        __METHOD__,
-                       array( 'LIMIT' => 1 ),
+                       $options,
                        array( 'page' => self::pageJoinCond(), 'user' => self::userJoinCond() )
                );
        }
                        'page_namespace',
                        'page_title',
                        'page_id',
 -                      'page_latest'
 +                      'page_latest',
 +                      'page_is_redirect',
 +                      'page_len',
                );
        }
  
                return array( 'user_name' );
        }
  
 +      /**
 +       * Do a batched query to get the parent revision lengths
 +       * @param $db DatabaseBase
 +       * @param $revIds Array
 +       * @return array
 +       */
 +      public static function getParentLengths( $db, array $revIds ) {
 +              $revLens = array();
 +              if ( !$revIds ) {
 +                      return $revLens; // empty
 +              }
 +              wfProfileIn( __METHOD__ );
 +              $res = $db->select( 'revision',
 +                      array( 'rev_id', 'rev_len' ),
 +                      array( 'rev_id' => $revIds ),
 +                      __METHOD__ );
 +              foreach ( $res as $row ) {
 +                      $revLens[$row->rev_id] = $row->rev_len;
 +              }
 +              wfProfileOut( __METHOD__ );
 +              return $revLens;
 +      }
 +
        /**
         * Constructor
         *
        /**
         * Get revision ID
         *
 -       * @return Integer
 +       * @return Integer|null
         */
        public function getId() {
                return $this->mId;
        /**
         * Get text row ID
         *
 -       * @return Integer
 +       * @return Integer|null
         */
        public function getTextId() {
                return $this->mTextId;
        /**
         * Returns the length of the text in this revision, or null if unknown.
         *
 -       * @return Integer
 +       * @return Integer|null
         */
        public function getSize() {
                return $this->mSize;
        /**
         * Returns the base36 sha1 of the text in this revision, or null if unknown.
         *
 -       * @return String
 +       * @return String|null
         */
        public function getSha1() {
                return $this->mSha1;
        }
  
        /**
 -       * Returns the title of the page associated with this entry.
 +       * Returns the title of the page associated with this entry or null.
 +       *
 +       * Will do a query, when title is not set and id is given.
         *
 -       * @return Title
 +       * @return Title|null
         */
        public function getTitle() {
                if( isset( $this->mTitle ) ) {
                        return $this->mTitle;
                }
 -              $dbr = wfGetDB( DB_SLAVE );
 -              $row = $dbr->selectRow(
 -                      array( 'page', 'revision' ),
 -                      self::selectPageFields(),
 -                      array( 'page_id=rev_page',
 -                                 'rev_id' => $this->mId ),
 -                      __METHOD__ );
 -              if ( $row ) {
 -                      $this->mTitle = Title::newFromRow( $row );
 +              if( !is_null( $this->mId ) ) { //rev_id is defined as NOT NULL
 +                      $dbr = wfGetDB( DB_SLAVE );
 +                      $row = $dbr->selectRow(
 +                              array( 'page', 'revision' ),
 +                              self::selectPageFields(),
 +                              array( 'page_id=rev_page',
 +                                         'rev_id' => $this->mId ),
 +                              __METHOD__ );
 +                      if ( $row ) {
 +                              $this->mTitle = Title::newFromRow( $row );
 +                      }
                }
                return $this->mTitle;
        }
        /**
         * Get the page ID
         *
 -       * @return Integer
 +       * @return Integer|null
         */
        public function getPage() {
                return $this->mPage;
         *
         * @param $audience Integer: one of:
         *      Revision::FOR_PUBLIC       to be displayed to all users
 -       *      Revision::FOR_THIS_USER    to be displayed to $wgUser
 +       *      Revision::FOR_THIS_USER    to be displayed to the given user
         *      Revision::RAW              get the ID regardless of permissions
         * @param $user User object to check for, only if FOR_THIS_USER is passed
         *              to the $audience parameter
         *
         * @param $audience Integer: one of:
         *      Revision::FOR_PUBLIC       to be displayed to all users
 -       *      Revision::FOR_THIS_USER    to be displayed to $wgUser
 +       *      Revision::FOR_THIS_USER    to be displayed to the given user
         *      Revision::RAW              get the text regardless of permissions
         * @param $user User object to check for, only if FOR_THIS_USER is passed
         *              to the $audience parameter
         *
         * @param $audience Integer: one of:
         *      Revision::FOR_PUBLIC       to be displayed to all users
 -       *      Revision::FOR_THIS_USER    to be displayed to $wgUser
 +       *      Revision::FOR_THIS_USER    to be displayed to the given user
         *      Revision::RAW              get the text regardless of permissions
         * @param $user User object to check for, only if FOR_THIS_USER is passed
         *              to the $audience parameter
         *
         * @param $audience Integer: one of:
         *      Revision::FOR_PUBLIC       to be displayed to all users
 -       *      Revision::FOR_THIS_USER    to be displayed to $wgUser
 +       *      Revision::FOR_THIS_USER    to be displayed to the given user
         *      Revision::RAW              get the text regardless of permissions
         * @param $user User object to check for, only if FOR_THIS_USER is passed
         *              to the $audience parameter
                if( $this->getTitle() ) {
                        $prev = $this->getTitle()->getPreviousRevisionID( $this->getId() );
                        if( $prev ) {
-                               return Revision::newFromTitle( $this->getTitle(), $prev );
+                               return self::newFromTitle( $this->getTitle(), $prev );
                        }
                }
                return null;
                if( $this->getTitle() ) {
                        $next = $this->getTitle()->getNextRevisionID( $this->getId() );
                        if ( $next ) {
-                               return Revision::newFromTitle( $this->getTitle(), $next );
+                               return self::newFromTitle( $this->getTitle(), $next );
                        }
                }
                return null;
                                $text = gzdeflate( $text );
                                $flags[] = 'gzip';
                        } else {
-                               wfDebug( "Revision::compressRevisionText() -- no zlib support, not compressing\n" );
+                               wfDebug( __METHOD__ . " -- no zlib support, not compressing\n" );
                        }
                }
                return implode( ',', $flags );
                wfProfileIn( __METHOD__ );
  
                $data = $this->mText;
-               $flags = Revision::compressRevisionText( $data );
+               $flags = self::compressRevisionText( $data );
  
                # Write to external storage if required
                if( $wgDefaultExternalStore ) {
                                        ? $this->getPreviousRevisionId( $dbw )
                                        : $this->mParentId,
                                'rev_sha1'       => is_null( $this->mSha1 )
-                                       ? Revision::base36Sha1( $this->mText )
+                                       ? self::base36Sha1( $this->mText )
                                        : $this->mSha1
                        ), __METHOD__
                );
  
                $current = $dbw->selectRow(
                        array( 'page', 'revision' ),
 -                      array( 'page_latest', 'rev_text_id', 'rev_len', 'rev_sha1' ),
 +                      array( 'page_latest', 'page_namespace', 'page_title',
 +                              'rev_text_id', 'rev_len', 'rev_sha1' ),
                        array(
                                'page_id' => $pageId,
                                'page_latest=rev_id',
                                'len'        => $current->rev_len,
                                'sha1'       => $current->rev_sha1
                                ) );
 +                      $revision->setTitle( Title::makeTitle( $current->page_namespace, $current->page_title ) );
                } else {
                        $revision = null;
                }
        static function countByTitle( $db, $title ) {
                $id = $title->getArticleID();
                if( $id ) {
-                       return Revision::countByPageId( $db, $id );
+                       return self::countByPageId( $db, $id );
                }
                return 0;
        }
@@@ -612,7 -612,7 +612,7 @@@ class DifferenceEngine extends ContextS
                        return false;
                }
                // Short-circuit
-               // If mOldRev is false, it means that the 
+               // If mOldRev is false, it means that the
                if ( $this->mOldRev === false || ( $this->mOldRev && $this->mNewRev
                        && $this->mOldRev->getID() == $this->mNewRev->getID() ) )
                {
                }
                if ( $wgExternalDiffEngine != 'wikidiff3' && $wgExternalDiffEngine !== false ) {
                        # Diff via the shell
 -                      global $wgTmpDirectory;
 -                      $tempName1 = tempnam( $wgTmpDirectory, 'diff_' );
 -                      $tempName2 = tempnam( $wgTmpDirectory, 'diff_' );
 +                      $tmpDir = wfTempDir();
 +                      $tempName1 = tempnam( $tmpDir, 'diff_' );
 +                      $tempName2 = tempnam( $tmpDir, 'diff_' );
  
                        $tempFile1 = fopen( $tempName1, "w" );
                        if ( !$tempFile1 ) {
                // Load the new revision object
                $this->mNewRev = $this->mNewid
                        ? Revision::newFromId( $this->mNewid )
-                       : Revision::newFromTitle( $this->getTitle() );
+                       : Revision::newFromTitle( $this->getTitle(), false, Revision::AVOID_MASTER );
  
                if ( !$this->mNewRev instanceof Revision ) {
                        return false;
@@@ -440,7 -440,6 +440,7 @@@ class LocalFile extends File 
  
                $dbw->update( 'image',
                        array(
 +                              'img_size'       => $this->size, // sanity
                                'img_width'      => $this->width,
                                'img_height'     => $this->height,
                                'img_bits'       => $this->bits,
         */
        function getDescriptionText() {
                global $wgParser;
-               $revision = Revision::newFromTitle( $this->title );
+               $revision = Revision::newFromTitle( $this->title, false, Revision::AVOID_MASTER );
                if ( !$revision ) return false;
                $text = $revision->getText();
                if ( !$text ) return false;
        /**
         * @return string
         */
 -      function getDescription() {
 +      function getDescription( $audience = self::FOR_PUBLIC, User $user = null ) {
                $this->load();
 -              return $this->description;
 +              if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_COMMENT ) ) {
 +                      return '';
 +              } elseif ( $audience == self::FOR_THIS_USER
 +                      && !$this->userCan( self::DELETED_COMMENT, $user ) )
 +              {
 +                      return '';
 +              } else {
 +                      return $this->description;
 +              }
        }
  
        /**
@@@ -1814,6 -1805,7 +1814,6 @@@ class LocalFileDeleteBatch 
         * @return FileRepoStatus
         */
        function execute() {
 -              global $wgUseSquid;
                wfProfileIn( __METHOD__ );
  
                $this->file->lock();
@@@ -2378,33 -2370,26 +2378,33 @@@ class LocalFileMoveBatch 
                $triplets = $this->getMoveTriplets();
                $triplets = $this->removeNonexistentFiles( $triplets );
  
 -              // Copy the files into their new location
 -              $statusMove = $repo->storeBatch( $triplets );
 -              wfDebugLog( 'imagemove', "Moved files for {$this->file->getName()}: {$statusMove->successCount} successes, {$statusMove->failCount} failures" );
 -              if ( !$statusMove->isGood() ) {
 -                      wfDebugLog( 'imagemove', "Error in moving files: " . $statusMove->getWikiText() );
 -                      $this->cleanupTarget( $triplets );
 -                      $statusMove->ok = false;
 -                      return $statusMove;
 -              }
 -
                $this->file->lock(); // begin
 +              // Rename the file versions metadata in the DB.
 +              // This implicitly locks the destination file, which avoids race conditions.
 +              // If we moved the files from A -> C before DB updates, another process could
 +              // move files from B -> C at this point, causing storeBatch() to fail and thus
 +              // cleanupTarget() to trigger. It would delete the C files and cause data loss.
                $statusDb = $this->doDBUpdates();
 -              wfDebugLog( 'imagemove', "Renamed {$this->file->getName()} in database: {$statusDb->successCount} successes, {$statusDb->failCount} failures" );
                if ( !$statusDb->isGood() ) {
                        $this->file->unlockAndRollback();
 -                      // Something went wrong with the DB updates, so remove the target files
 -                      $this->cleanupTarget( $triplets );
                        $statusDb->ok = false;
                        return $statusDb;
                }
 +              wfDebugLog( 'imagemove', "Renamed {$this->file->getName()} in database: {$statusDb->successCount} successes, {$statusDb->failCount} failures" );
 +
 +              // Copy the files into their new location.
 +              // If a prior process fataled copying or cleaning up files we tolerate any
 +              // of the existing files if they are identical to the ones being stored.
 +              $statusMove = $repo->storeBatch( $triplets, FileRepo::OVERWRITE_SAME );
 +              wfDebugLog( 'imagemove', "Moved files for {$this->file->getName()}: {$statusMove->successCount} successes, {$statusMove->failCount} failures" );
 +              if ( !$statusMove->isGood() ) {
 +                      // Delete any files copied over (while the destination is still locked)
 +                      $this->cleanupTarget( $triplets );
 +                      $this->file->unlockAndRollback(); // unlocks the destination
 +                      wfDebugLog( 'imagemove', "Error in moving files: " . $statusMove->getWikiText() );
 +                      $statusMove->ok = false;
 +                      return $statusMove;
 +              }
                $this->file->unlock(); // done
  
                // Everything went ok, remove the source files
                // Create dest pairs from the triplets
                $pairs = array();
                foreach ( $triplets as $triplet ) {
 +                      // $triplet: (old source virtual URL, dst zone, dest rel)
                        $pairs[] = array( $triplet[1], $triplet[2] );
                }