X-Git-Url: http://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FLinksUpdate.php;h=0712ac807bf31fcde527b4eb4affa1afc7d48904;hb=c29fd59775f597847a57f598a76de48c63952243;hp=293c2fcdfeb843b35f2d31b9689fccc2c14facf0;hpb=b00800fa4855cf661e46f279aabecd1476411828;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/LinksUpdate.php b/includes/LinksUpdate.php index 293c2fcdfe..0712ac807b 100644 --- a/includes/LinksUpdate.php +++ b/includes/LinksUpdate.php @@ -1,6 +1,6 @@ mOptions = array(); - } else { - $this->mOptions = array( 'FOR UPDATE' ); - } - $this->mDb = wfGetDB( DB_MASTER ); - - if ( !is_object( $title ) ) { + if ( !( $title instanceof Title ) ) { throw new MWException( "The calling convention to LinksUpdate::LinksUpdate() has changed. " . "Please see Article::editUpdates() for an invocation example.\n" ); } + + if ( !( $parserOutput instanceof ParserOutput ) ) { + throw new MWException( "The calling convention to LinksUpdate::__construct() has changed. " . + "Please see WikiPage::doEditUpdates() for an invocation example.\n" ); + } + $this->mTitle = $title; $this->mId = $title->getArticleID(); + if ( !$this->mId ) { + throw new MWException( "The Title object did not provide an article ID. Perhaps the page doesn't exist?" ); + } + $this->mParserOutput = $parserOutput; $this->mLinks = $parserOutput->getLinks(); $this->mImages = $parserOutput->getImages(); $this->mTemplates = $parserOutput->getTemplates(); - $this->mDistantTemplates = $parserOutput->getDistantTemplates(); $this->mExternals = $parserOutput->getExternalLinks(); $this->mCategories = $parserOutput->getCategories(); $this->mProperties = $parserOutput->getProperties(); @@ -137,7 +143,7 @@ class LinksUpdate { # External links $existing = $this->getExistingExternals(); $this->incrTableUpdate( 'externallinks', 'el', $this->getExternalDeletions( $existing ), - $this->getExternalInsertions( $existing ) ); + $this->getExternalInsertions( $existing ) ); # Language links $existing = $this->getExistingInterlangs(); @@ -154,15 +160,6 @@ class LinksUpdate { $this->incrTableUpdate( 'templatelinks', 'tl', $this->getTemplateDeletions( $existing ), $this->getTemplateInsertions( $existing ) ); - # Distant template links - global $wgGlobalDB; - if ( $wgGlobalDB ) { - $existing = $this->getDistantExistingTemplates(); - $this->incrSharedTableUpdate( 'globaltemplatelinks', 'gtl', - $this->getDistantTemplateDeletions( $existing ), - $this->getDistantTemplateInsertions( $existing ) ); - } - # Category links $existing = $this->getExistingCategories(); @@ -252,6 +249,7 @@ class LinksUpdate { foreach ( $batches as $batch ) { list( $start, $end ) = $batch; $params = array( + 'table' => 'templatelinks', 'start' => $start, 'end' => $end, ); @@ -263,71 +261,37 @@ class LinksUpdate { } /** - * Invalidate the cache of a list of pages from a single namespace - * - * @param $namespace Integer - * @param $dbkeys Array + * @param $cats */ - function invalidatePages( $namespace, $dbkeys ) { - if ( !count( $dbkeys ) ) { - return; - } - - /** - * Determine which pages need to be updated - * This is necessary to prevent the job queue from smashing the DB with - * large numbers of concurrent invalidations of the same page - */ - $now = $this->mDb->timestamp(); - $ids = array(); - $res = $this->mDb->select( 'page', array( 'page_id' ), - array( - 'page_namespace' => $namespace, - 'page_title IN (' . $this->mDb->makeList( $dbkeys ) . ')', - 'page_touched < ' . $this->mDb->addQuotes( $now ) - ), __METHOD__ - ); - foreach ( $res as $row ) { - $ids[] = $row->page_id; - } - if ( !count( $ids ) ) { - return; - } - - /** - * Do the update - * We still need the page_touched condition, in case the row has changed since - * the non-locking select above. - */ - $this->mDb->update( 'page', array( 'page_touched' => $now ), - array( - 'page_id IN (' . $this->mDb->makeList( $ids ) . ')', - 'page_touched < ' . $this->mDb->addQuotes( $now ) - ), __METHOD__ - ); - } - function invalidateCategories( $cats ) { $this->invalidatePages( NS_CATEGORY, array_keys( $cats ) ); } /** * Update all the appropriate counts in the category table. - * @param $added associative array of category name => sort key - * @param $deleted associative array of category name => sort key + * @param $added array associative array of category name => sort key + * @param $deleted array associative array of category name => sort key */ function updateCategoryCounts( $added, $deleted ) { - $a = new Article($this->mTitle); + $a = WikiPage::factory( $this->mTitle ); $a->updateCategoryCounts( array_keys( $added ), array_keys( $deleted ) ); } + /** + * @param $images + */ function invalidateImageDescriptions( $images ) { $this->invalidatePages( NS_FILE, array_keys( $images ) ); } - function dumbTableUpdate( $table, $insertions, $fromField ) { + /** + * @param $table + * @param $insertions + * @param $fromField + */ + private function dumbTableUpdate( $table, $insertions, $fromField ) { $this->mDb->delete( $table, array( $fromField => $this->mId ), __METHOD__ ); if ( count( $insertions ) ) { # The link array was constructed without FOR UPDATE, so there may @@ -339,7 +303,10 @@ class LinksUpdate { /** * Update a table by doing a delete query then an insert query - * @private + * @param $table + * @param $prefix + * @param $deletions + * @param $insertions */ function incrTableUpdate( $table, $prefix, $deletions, $insertions ) { if ( $table == 'page_props' ) { @@ -378,57 +345,17 @@ class LinksUpdate { $this->mDb->delete( $table, $where, __METHOD__ ); } if ( count( $insertions ) ) { - if ( isset( $insertions['globaltemplatelinks'] ) ) { - $this->mDb->insert( 'globaltemplatelinks', $insertions['globaltemplatelinks'], __METHOD__, 'IGNORE' ); - } - if ( isset( $insertions['globalnamespaces'] ) ) { - $this->mDb->insert( 'globalnamespaces', $insertions['globalnamespaces'], __METHOD__, 'IGNORE' ); - } - if ( isset( $insertions['globalinterwiki'] ) ) { - $this->mDb->insert( 'globalinterwiki', $insertions['globalinterwiki'], __METHOD__, 'IGNORE' ); - } - } - } - - /** - * Update a shared table by doing a delete query then an insert query - * @private - */ - function incrSharedTableUpdate( $table, $prefix, $deletions, $insertions ) { - - global $wgWikiID; - global $wgGlobalDB; - - if ( $wgGlobalDB ) { - $dbw = wfGetDB( DB_MASTER, array(), $wgGlobalDB ); - $where = array( "{$prefix}_from_wiki" => $wgWikiID, - "{$prefix}_from_page" => $this->mId - ); - $baseKey = "{$prefix}_to_wiki"; - $middleKey = "{$prefix}_to_namespace"; - - $clause = $dbw->makeWhereFrom3d( $deletions, $baseKey, $middleKey, "{$prefix}_to_title" ); - if ( $clause ) { - $where[] = $clause; - } else { - $where = false; - } - - if ( $where ) { - $dbw->delete( $table, $where, __METHOD__ ); - } - if ( count( $insertions ) ) { - $dbw->insert( $table, $insertions, __METHOD__, 'IGNORE' ); - } + $this->mDb->insert( $table, $insertions, __METHOD__, 'IGNORE' ); } } /** * Get an array of pagelinks insertions for passing to the DB * Skips the titles specified by the 2-D array $existing - * @private + * @param $existing array + * @return array */ - function getLinkInsertions( $existing = array() ) { + private function getLinkInsertions( $existing = array() ) { $arr = array(); foreach( $this->mLinks as $ns => $dbkeys ) { $diffs = isset( $existing[$ns] ) @@ -447,9 +374,10 @@ class LinksUpdate { /** * Get an array of template insertions. Like getLinkInsertions() - * @private + * @param $existing array + * @return array */ - function getTemplateInsertions( $existing = array() ) { + private function getTemplateInsertions( $existing = array() ) { $arr = array(); foreach( $this->mTemplates as $ns => $dbkeys ) { $diffs = isset( $existing[$ns] ) ? array_diff_key( $dbkeys, $existing[$ns] ) : $dbkeys; @@ -464,51 +392,13 @@ class LinksUpdate { return $arr; } - /** - * Get an array of distant template insertions. Like getLinkInsertions() - * @private - */ - function getDistantTemplateInsertions( $existing = array() ) { - global $wgWikiID; - $arr = array(); - foreach( $this->mDistantTemplates as $wikiid => $templatesToNS ) { - foreach( $templatesToNS as $ns => $dbkeys ) { - $diffs = isset( $existing[$wikiid] ) && isset( $existing[$wikiid][$ns] ) - ? array_diff_key( $dbkeys, $existing[$wikiid][$ns] ) - : $dbkeys; - $interwiki = Interwiki::fetch( $wikiid ); - $wikiid = $interwiki->getWikiID(); - foreach ( $diffs as $dbk => $id ) { - $arr['globaltemplatelinks'][] = array( - 'gtl_from_wiki' => $wgWikiID, - 'gtl_from_page' => $this->mId, - 'gtl_from_namespace' => $this->mTitle->getNamespace(), - 'gtl_from_title' => $this->mTitle->getText(), - 'gtl_to_wiki' => $wikiid, - 'gtl_to_namespace' => $ns, - 'gtl_to_title' => $dbk - ); - $arr['globalinterwiki'][] = array( - 'giw_wikiid' => $wikiid, - 'giw_prefix' => $prefix, // FIXME: $prefix ix undefined - ); - $arr['globalnamespaces'][] = array( - 'gn_wiki' => wfWikiID( ), - 'gn_namespace' => $this->mTitle->getNamespace(), - 'gn_namespacetext' => $this->mTitle->getNsText(), - ); - } - } - } - return $arr; - } - /** * Get an array of image insertions * Skips the names specified in $existing - * @private + * @param $existing array + * @return array */ - function getImageInsertions( $existing = array() ) { + private function getImageInsertions( $existing = array() ) { $arr = array(); $diffs = array_diff_key( $this->mImages, $existing ); foreach( $diffs as $iname => $dummy ) { @@ -522,17 +412,20 @@ class LinksUpdate { /** * Get an array of externallinks insertions. Skips the names specified in $existing - * @private + * @param $existing array + * @return array */ - function getExternalInsertions( $existing = array() ) { + private function getExternalInsertions( $existing = array() ) { $arr = array(); $diffs = array_diff_key( $this->mExternals, $existing ); foreach( $diffs as $url => $dummy ) { - $arr[] = array( - 'el_from' => $this->mId, - 'el_to' => $url, - 'el_index' => wfMakeUrlIndex( $url ), - ); + foreach( wfMakeUrlIndexes( $url ) as $index ) { + $arr[] = array( + 'el_from' => $this->mId, + 'el_to' => $url, + 'el_index' => $index, + ); + } } return $arr; } @@ -540,11 +433,12 @@ class LinksUpdate { /** * Get an array of category insertions * - * @param $existing Array mapping existing category names to sort keys. If both + * @param $existing array mapping existing category names to sort keys. If both * match a link in $this, the link will be omitted from the output - * @private + * + * @return array */ - function getCategoryInsertions( $existing = array() ) { + private function getCategoryInsertions( $existing = array() ) { global $wgContLang, $wgCategoryCollation; $diffs = array_diff_assoc( $this->mCategories, $existing ); $arr = array(); @@ -584,23 +478,26 @@ class LinksUpdate { * Get an array of interlanguage link insertions * * @param $existing Array mapping existing language codes to titles - * @private + * + * @return array */ - function getInterlangInsertions( $existing = array() ) { - $diffs = array_diff_assoc( $this->mInterlangs, $existing ); - $arr = array(); - foreach( $diffs as $lang => $title ) { - $arr[] = array( - 'll_from' => $this->mId, - 'll_lang' => $lang, - 'll_title' => $title - ); - } - return $arr; + private function getInterlangInsertions( $existing = array() ) { + $diffs = array_diff_assoc( $this->mInterlangs, $existing ); + $arr = array(); + foreach( $diffs as $lang => $title ) { + $arr[] = array( + 'll_from' => $this->mId, + 'll_lang' => $lang, + 'll_title' => $title + ); + } + return $arr; } /** * Get an array of page property insertions + * @param $existing array + * @return array */ function getPropertyInsertions( $existing = array() ) { $diffs = array_diff_assoc( $this->mProperties, $existing ); @@ -618,9 +515,10 @@ class LinksUpdate { /** * Get an array of interwiki insertions for passing to the DB * Skips the titles specified by the 2-D array $existing - * @private + * @param $existing array + * @return array */ - function getInterwikiInsertions( $existing = array() ) { + private function getInterwikiInsertions( $existing = array() ) { $arr = array(); foreach( $this->mInterwikis as $prefix => $dbkeys ) { $diffs = isset( $existing[$prefix] ) ? array_diff_key( $dbkeys, $existing[$prefix] ) : $dbkeys; @@ -638,9 +536,10 @@ class LinksUpdate { /** * Given an array of existing links, returns those links which are not in $this * and thus should be deleted. - * @private + * @param $existing array + * @return array */ - function getLinkDeletions( $existing ) { + private function getLinkDeletions( $existing ) { $del = array(); foreach ( $existing as $ns => $dbkeys ) { if ( isset( $this->mLinks[$ns] ) ) { @@ -655,9 +554,10 @@ class LinksUpdate { /** * Given an array of existing templates, returns those templates which are not in $this * and thus should be deleted. - * @private + * @param $existing array + * @return array */ - function getTemplateDeletions( $existing ) { + private function getTemplateDeletions( $existing ) { $del = array(); foreach ( $existing as $ns => $dbkeys ) { if ( isset( $this->mTemplates[$ns] ) ) { @@ -669,69 +569,50 @@ class LinksUpdate { return $del; } - /** - * Given an array of existing templates, returns those templates which are not in $this - * and thus should be deleted. - * @private - */ - function getDistantTemplateDeletions( $existing ) { - $del = array(); - foreach ( $existing as $wikiid => $templatesForNS ) { - if ( isset( $this->mDistantTemplates[$wikiid] ) ) { - $del[$wikiid] = array_diff_key( $existing[$wikiid], $this->mDistantTemplates[$wikiid] ); - } else { - $del[$wikiid] = $existing[$wikiid]; - } - foreach ( $templatesForNS as $ns => $dbkeys ) { - if ( isset( $this->mDistantTemplates[$wikiid][$ns] ) ) { - $del[$wikiid][$ns] = array_diff_key( $existing[$wikiid][$ns], $this->mDistantTemplates[$wikiid][$ns] ); - } else { - $del[$wikiid][$ns] = $existing[$wikiid][$ns]; - } - } - } - return $del; - } - /** * Given an array of existing images, returns those images which are not in $this * and thus should be deleted. - * @private + * @param $existing array + * @return array */ - function getImageDeletions( $existing ) { + private function getImageDeletions( $existing ) { return array_diff_key( $existing, $this->mImages ); } /** * Given an array of existing external links, returns those links which are not * in $this and thus should be deleted. - * @private + * @param $existing array + * @return array */ - function getExternalDeletions( $existing ) { + private function getExternalDeletions( $existing ) { return array_diff_key( $existing, $this->mExternals ); } /** * Given an array of existing categories, returns those categories which are not in $this * and thus should be deleted. - * @private + * @param $existing array + * @return array */ - function getCategoryDeletions( $existing ) { + private function getCategoryDeletions( $existing ) { return array_diff_assoc( $existing, $this->mCategories ); } /** * Given an array of existing interlanguage links, returns those links which are not * in $this and thus should be deleted. - * @private + * @param $existing array + * @return array */ - function getInterlangDeletions( $existing ) { - return array_diff_assoc( $existing, $this->mInterlangs ); + private function getInterlangDeletions( $existing ) { + return array_diff_assoc( $existing, $this->mInterlangs ); } /** * Get array of properties which should be deleted. - * @private + * @param $existing array + * @return array */ function getPropertyDeletions( $existing ) { return array_diff_assoc( $existing, $this->mProperties ); @@ -740,9 +621,10 @@ class LinksUpdate { /** * Given an array of existing interwiki links, returns those links which are not in $this * and thus should be deleted. - * @private + * @param $existing array + * @return array */ - function getInterwikiDeletions( $existing ) { + private function getInterwikiDeletions( $existing ) { $del = array(); foreach ( $existing as $prefix => $dbkeys ) { if ( isset( $this->mInterwikis[$prefix] ) ) { @@ -756,9 +638,10 @@ class LinksUpdate { /** * Get an array of existing links, as a 2-D array - * @private + * + * @return array */ - function getExistingLinks() { + private function getExistingLinks() { $res = $this->mDb->select( 'pagelinks', array( 'pl_namespace', 'pl_title' ), array( 'pl_from' => $this->mId ), __METHOD__, $this->mOptions ); $arr = array(); @@ -773,9 +656,10 @@ class LinksUpdate { /** * Get an array of existing templates, as a 2-D array - * @private + * + * @return array */ - function getExistingTemplates() { + private function getExistingTemplates() { $res = $this->mDb->select( 'templatelinks', array( 'tl_namespace', 'tl_title' ), array( 'tl_from' => $this->mId ), __METHOD__, $this->mOptions ); $arr = array(); @@ -788,38 +672,12 @@ class LinksUpdate { return $arr; } - /** - * Get an array of existing distant templates, as a 3-D array - * @private - */ - function getDistantExistingTemplates() { - global $wgWikiID; - global $wgGlobalDB; - - $arr = array(); - if ( $wgGlobalDB ) { - $dbr = wfGetDB( DB_SLAVE, array(), $wgGlobalDB ); - $res = $dbr->select( 'globaltemplatelinks', array( 'gtl_to_wiki', 'gtl_to_namespace', 'gtl_to_title' ), - array( 'gtl_from_wiki' => $wgWikiID, 'gtl_from_page' => $this->mId ), __METHOD__, $this->mOptions ); - while ( $row = $dbr->fetchObject( $res ) ) { - if ( !isset( $arr[$row->gtl_to_wiki] ) ) { - $arr[$row->gtl_to_wiki] = array(); - } - if ( !isset( $arr[$row->gtl_to_wiki][$row->gtl_to_namespace] ) ) { - $arr[$row->gtl_to_wiki][$row->gtl_to_namespace] = array(); - } - $arr[$row->gtl_to_wiki][$row->gtl_to_namespace][$row->gtl_to_title] = 1; - } - $dbr->freeResult( $res ); - } - return $arr; - } - /** * Get an array of existing images, image names in the keys - * @private + * + * @return array */ - function getExistingImages() { + private function getExistingImages() { $res = $this->mDb->select( 'imagelinks', array( 'il_to' ), array( 'il_from' => $this->mId ), __METHOD__, $this->mOptions ); $arr = array(); @@ -831,9 +689,10 @@ class LinksUpdate { /** * Get an array of existing external links, URLs in the keys - * @private + * + * @return array */ - function getExistingExternals() { + private function getExistingExternals() { $res = $this->mDb->select( 'externallinks', array( 'el_to' ), array( 'el_from' => $this->mId ), __METHOD__, $this->mOptions ); $arr = array(); @@ -845,9 +704,10 @@ class LinksUpdate { /** * Get an array of existing categories, with the name in the key and sort key in the value. - * @private + * + * @return array */ - function getExistingCategories() { + private function getExistingCategories() { $res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey_prefix' ), array( 'cl_from' => $this->mId ), __METHOD__, $this->mOptions ); $arr = array(); @@ -860,9 +720,10 @@ class LinksUpdate { /** * Get an array of existing interlanguage links, with the language code in the key and the * title in the value. - * @private + * + * @return array */ - function getExistingInterlangs() { + private function getExistingInterlangs() { $res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ), array( 'll_from' => $this->mId ), __METHOD__, $this->mOptions ); $arr = array(); @@ -891,9 +752,10 @@ class LinksUpdate { /** * Get an array of existing categories, with the name in the key and sort key in the value. - * @private + * + * @return array */ - function getExistingProperties() { + private function getExistingProperties() { $res = $this->mDb->select( 'page_props', array( 'pp_propname', 'pp_value' ), array( 'pp_page' => $this->mId ), __METHOD__, $this->mOptions ); $arr = array(); @@ -903,16 +765,26 @@ class LinksUpdate { return $arr; } - /** * Return the title object of the page being updated + * @return Title */ - function getTitle() { + public function getTitle() { return $this->mTitle; } + /** + * Returns parser output + * @since 1.19 + * @return ParserOutput + */ + public function getParserOutput() { + return $this->mParserOutput; + } + /** * Return the list of images used as generated by the parser + * @return array */ public function getImages() { return $this->mImages; @@ -920,8 +792,9 @@ class LinksUpdate { /** * Invalidate any necessary link lists related to page property changes + * @param $changed */ - function invalidateProperties( $changed ) { + private function invalidateProperties( $changed ) { global $wgPagePropLinkInvalidations; foreach ( $changed as $name => $value ) { @@ -938,3 +811,74 @@ class LinksUpdate { } } } + +/** + * Update object handling the cleanup of links tables after a page was deleted. + **/ +class LinksDeletionUpdate extends SqlDataUpdate { + + protected $mPage; //!< WikiPage the wikipage that was deleted + + /** + * Constructor + * + * @param $title Title of the page we're updating + * @param $parserOutput ParserOutput: output from a full parse of this page + * @param $recursive Boolean: queue jobs for recursive updates? + */ + function __construct( WikiPage $page ) { + parent::__construct( ); + + $this->mPage = $page; + } + + /** + * Do some database updates after deletion + */ + public function doUpdate() { + $title = $this->mPage->getTitle(); + $id = $this->mPage->getId(); + + # Delete restrictions for it + $this->mDb->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ ); + + # Fix category table counts + $cats = array(); + $res = $this->mDb->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ ); + + foreach ( $res as $row ) { + $cats [] = $row->cl_to; + } + + $this->mPage->updateCategoryCounts( array(), $cats ); + + # If using cascading deletes, we can skip some explicit deletes + if ( !$this->mDb->cascadingDeletes() ) { + $this->mDb->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ ); + + # Delete outgoing links + $this->mDb->delete( 'pagelinks', array( 'pl_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'imagelinks', array( 'il_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'categorylinks', array( 'cl_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'templatelinks', array( 'tl_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'externallinks', array( 'el_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'langlinks', array( 'll_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'iwlinks', array( 'iwl_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'redirect', array( 'rd_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'page_props', array( 'pp_page' => $id ), __METHOD__ ); + } + + # If using cleanup triggers, we can skip some manual deletes + if ( !$this->mDb->cleanupTriggers() ) { + # Clean up recentchanges entries... + $this->mDb->delete( 'recentchanges', + array( 'rc_type != ' . RC_LOG, + 'rc_namespace' => $title->getNamespace(), + 'rc_title' => $title->getDBkey() ), + __METHOD__ ); + $this->mDb->delete( 'recentchanges', + array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ), + __METHOD__ ); + } + } +}