From e0ce5a322e41cf39f110fd4fa3c2c5c849964252 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 26 May 2005 10:23:36 +0000 Subject: [PATCH] * links and brokenlinks tables merged to pagelinks; this will reduce pain dealing with moves and deletes of widely-linked pages. The updaters should be fixed up to understand future versions without the tables there without breaking upgrades. --- RELEASE-NOTES | 3 + includes/Article.php | 36 ++--- includes/Database.php | 2 +- includes/Image.php | 2 +- includes/LinkCache.php | 186 +++++++++++++--------- includes/LinksUpdate.php | 119 +++----------- includes/Parser.php | 4 +- includes/SpecialBrokenRedirects.php | 20 ++- includes/SpecialDeadendpages.php | 6 +- includes/SpecialDisambiguations.php | 15 +- includes/SpecialDoubleRedirects.php | 28 ++-- includes/SpecialLog.php | 4 +- includes/SpecialLonelypages.php | 17 +- includes/SpecialRecentchangeslinked.php | 43 ++++-- includes/SpecialWantedpages.php | 32 ++-- includes/SpecialWhatlinkshere.php | 62 ++------ includes/SquidUpdate.php | 34 +--- includes/Title.php | 197 ++++++++++-------------- maintenance/convertUtf8.php | 2 +- maintenance/refreshLinks.inc | 5 +- maintenance/remove-brokenlinks.php | 58 ------- maintenance/tables.sql | 21 +++ maintenance/updaters.inc | 42 +++++ 23 files changed, 434 insertions(+), 504 deletions(-) delete mode 100644 maintenance/remove-brokenlinks.php diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 7bddb27562..3e183e5eda 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -214,6 +214,9 @@ Various bugfixes, small features, and a few experimental things: * The #p-nav id in MonoBook is now #p-navigation * Putting $4 in msg:userstatstext will now give the percentage of admnistrators out of normal users. +* links and brokenlinks tables merged to pagelinks; this will reduce pain + dealing with moves and deletes of widely-linked pages. + === Caveats === diff --git a/includes/Article.php b/includes/Article.php index 2262be271d..b02b7679df 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -1789,20 +1789,9 @@ class Article { Article::onArticleDelete( $this->mTitle ); - # Insert broken links - $brokenLinks = array(); - foreach ( $linksTo as $titleObj ) { - # Get article ID. Efficient because it was loaded into the cache by getLinksTo(). - $linkID = $titleObj->getArticleID(); - $brokenLinks[] = array( 'bl_from' => $linkID, 'bl_to' => $t ); - } - $dbw->insert( 'brokenlinks', $brokenLinks, $fname, 'IGNORE' ); - - # Delete live links - $dbw->delete( 'links', array( 'l_to' => $id ) ); - $dbw->delete( 'links', array( 'l_from' => $id ) ); + # Delete outgoing links + $dbw->delete( 'pagelinks', array( 'pl_from' => $id ) ); $dbw->delete( 'imagelinks', array( 'il_from' => $id ) ); - $dbw->delete( 'brokenlinks', array( 'bl_from' => $id ) ); $dbw->delete( 'categorylinks', array( 'cl_from' => $id ) ); # Log the deletion @@ -2195,7 +2184,8 @@ class Article { function onArticleCreate($title_obj) { global $wgUseSquid, $wgPostCommitUpdateList; - $titles = $title_obj->getBrokenLinksTo(); + $title_obj->touchLinks(); + $titles = $title_obj->getLinksTo(); # Purge squid if ( $wgUseSquid ) { @@ -2208,11 +2198,12 @@ class Article { } # Clear persistent link cache - LinkCache::linksccClearBrokenLinksTo( $title_obj->getPrefixedDBkey() ); + LinkCache::linksccClearLinksTo( $title_obj ); } function onArticleDelete($title_obj) { - LinkCache::linksccClearLinksTo( $title_obj->getArticleID() ); + $title_obj->touchLinks(); + LinkCache::linksccClearLinksTo( $title_obj ); } function onArticleEdit($title_obj) { LinkCache::linksccClearPage( $title_obj->getArticleID() ); @@ -2319,15 +2310,16 @@ class Article { $id = $this->mTitle->getArticleID(); $db =& wfGetDB( DB_SLAVE ); - $page = $db->tableName( 'page' ); - $links = $db->tableName( 'links' ); - $sql = "SELECT page_title ". - "FROM $page,$links WHERE l_to=page_id AND l_from={$id} and page_namespace=".NS_TEMPLATE; - $res = $db->query( $sql, "Article:getUsedTemplates" ); + $res = $db->select( array( 'pagelinks' ), + array( 'pl_title' ), + array( + 'pl_from' => $id, + 'pl_namespace' => NS_TEMPLATE ), + 'Article:getUsedTemplates' ); if ( false !== $res ) { if ( $db->numRows( $res ) ) { while ( $row = $db->fetchObject( $res ) ) { - $result[] = $row->page_title; + $result[] = $row->pl_title; } } } diff --git a/includes/Database.php b/includes/Database.php index e69e49b9e8..8b9858a4e2 100644 --- a/includes/Database.php +++ b/includes/Database.php @@ -430,7 +430,7 @@ class Database { * Prepare & execute an SQL statement, quoting and inserting arguments * in the appropriate places. * @param string $query - * @param string $args (default null) + * @param string $args ... */ function safeQuery( $query, $args = null ) { $prepared = $this->prepare( $query, 'Database::safeQuery' ); diff --git a/includes/Image.php b/includes/Image.php index 6b53b5cf2e..4882e23bc9 100644 --- a/includes/Image.php +++ b/includes/Image.php @@ -1369,7 +1369,7 @@ class Image if ( $db->numRows( $res ) ) { while ( $row = $db->fetchObject( $res ) ) { if ( $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title ) ) { - $wgLinkCache->addGoodLink( $row->page_id, $titleObj->getPrefixedDBkey() ); + $wgLinkCache->addGoodLinkObj( $row->page_id, $titleObj ); $retVal[] = $titleObj; } } diff --git a/includes/LinkCache.php b/includes/LinkCache.php index 5853f16f22..35e486bf92 100644 --- a/includes/LinkCache.php +++ b/includes/LinkCache.php @@ -12,6 +12,7 @@ define ('LINKCACHE_GOOD', 0); define ('LINKCACHE_BAD', 1); define ('LINKCACHE_IMAGE', 2); +define ('LINKCACHE_PAGE', 3); /** * @package MediaWiki @@ -20,8 +21,9 @@ define ('LINKCACHE_IMAGE', 2); class LinkCache { // Increment $mClassVer whenever old serialized versions of this class // becomes incompatible with the new version. - /* private */ var $mClassVer = 2; + /* private */ var $mClassVer = 3; + /* private */ var $mPageLinks; /* private */ var $mGoodLinks, $mBadLinks, $mActive; /* private */ var $mImageLinks, $mCategoryLinks; /* private */ var $mPreFilled, $mOldGoodLinks, $mOldBadLinks; @@ -36,6 +38,7 @@ class LinkCache { $this->mActive = true; $this->mPreFilled = false; $this->mForUpdate = false; + $this->mPageLinks = array(); $this->mGoodLinks = array(); $this->mBadLinks = array(); $this->mImageLinks = array(); @@ -63,15 +66,19 @@ class LinkCache { return array_key_exists( $title, $this->mBadLinks ); } - function addGoodLink( $id, $title ) { + function addGoodLinkObj( $id, $title ) { if ( $this->mActive ) { - $this->mGoodLinks[$title] = $id; + $dbkey = $title->getPrefixedDbKey(); + $this->mGoodLinks[$dbkey] = $id; + $this->mPageLinks[$dbkey] = $title; } } - function addBadLink( $title ) { - if ( $this->mActive && ( ! $this->isBadLink( $title ) ) ) { - $this->mBadLinks[$title] = 1; + function addBadLinkObj( $title ) { + $dbkey = $title->getPrefixedDbKey(); + if ( $this->mActive && ( ! $this->isBadLink( $dbkey ) ) ) { + $this->mBadLinks[$dbkey] = 1; + $this->mPageLinks[$dbkey] = $title; } } @@ -104,6 +111,7 @@ class LinkCache { function suspend() { $this->mActive = false; } function resume() { $this->mActive = true; } + function getPageLinks() { return $this->mPageLinks; } function getGoodLinks() { return $this->mGoodLinks; } function getBadLinks() { return array_keys( $this->mBadLinks ); } function getImageLinks() { return $this->mImageLinks; } @@ -156,18 +164,24 @@ class LinkCache { $wgMemc->add( $key, $id, 3600*24 ); } - if ( 0 == $id ) { $this->addBadLink( $title ); } - else { $this->addGoodLink( $id, $title ); } + if( 0 == $id ) { + $this->addBadLinkObj( $nt ); + } else { + $this->addGoodLinkObj( $id, $nt ); + } wfProfileOut( $fname ); return $id; } + /** + * Bulk-check the pagelinks and page arrays for existence info. + * @param Title $fromtitle + */ function preFill( &$fromtitle ) { global $wgEnablePersistentLC; $fname = 'LinkCache::preFill'; wfProfileIn( $fname ); - # Note -- $fromtitle is a Title *object* $this->suspend(); $id = $fromtitle->getArticleID(); @@ -194,24 +208,24 @@ class LinkCache { } $page = $db->tableName( 'page' ); - $links = $db->tableName( 'links' ); + $pagelinks = $db->tableName( 'pagelinks' ); - $sql = "SELECT page_id,page_namespace,page_title - FROM $page,$links - WHERE page_id=l_to AND l_from=$id $options"; + $sql = "SELECT page_id,pl_namespace,pl_title + FROM $pagelinks + LEFT JOIN $page + ON pl_namespace=page_namespace AND pl_title=page_title + WHERE pl_from=$id $options"; $res = $db->query( $sql, $fname ); while( $s = $db->fetchObject( $res ) ) { - $this->addGoodLink( $s->page_id, - Title::makeName( $s->page_namespace, $s->page_title ) - ); - } - - $res = $db->select( 'brokenlinks', array( 'bl_to' ), array( 'bl_from' => $id ), $fname, array( $options ) ); - while( $s = $db->fetchObject( $res ) ) { - $this->addBadLink( $s->bl_to ); + $title = Title::makeTitle( $s->pl_namespace, $s->pl_title ); + if( $s->page_id ) { + $this->addGoodLinkObj( $s->page_id, $title ); + } else { + $this->addBadLinkObj( $title ); + } } - - $this->mOldBadLinks = $this->mBadLinks; + $this->mOldPageLinks = $this->mPageLinks; + $this->mOldBadLinks = $this->mBadLinks; $this->mOldGoodLinks = $this->mGoodLinks; $this->mPreFilled = true; @@ -246,6 +260,24 @@ class LinkCache { function getImageDeletions() { return array_diff_assoc( $this->mOldImageLinks, $this->mImageLinks ); } + + function getPageAdditions() { + $set = array_diff( array_keys( $this->mPageLinks ), array_keys( $this->mOldPageLinks ) ); + $out = array(); + foreach( $set as $key ) { + $out[$key] = $this->mPageLinks[$key]; + } + return $out; + } + + function getPageDeletions() { + $set = array_diff( array_keys( $this->mOldPageLinks ), array_keys( $this->mPageLinks ) ); + $out = array(); + foreach( $set as $key ) { + $out[$key] = $this->mOldPageLinks[$key]; + } + return $out; + } /** * Parameters: @@ -275,6 +307,12 @@ class LinkCache { $del = $this->getBadDeletions(); $add = $this->getBadAdditions(); break; + case LINKCACHE_PAGE: + $old =& $this->mOldPageLinks; + $cur =& $this->mPageLinks; + $del = $this->getPageDeletions(); + $add = $this->getPageAdditions(); + break; default: # LINKCACHE_IMAGE return false; } @@ -286,6 +324,7 @@ class LinkCache { * Clears cache but leaves old preFill copies alone */ function clear() { + $this->mPageLinks = array(); $this->mGoodLinks = array(); $this->mBadLinks = array(); $this->mImageLinks = array(); @@ -319,6 +358,7 @@ class LinkCache { } $cc = @unserialize( $cacheobj ); if( isset( $cc->mClassVer ) and ($cc->mClassVer == $this->mClassVer ) ){ + $this->mOldPageLinks = $this->mPageLinks = $cc->mPageLinks; $this->mOldGoodLinks = $this->mGoodLinks = $cc->mGoodLinks; $this->mOldBadLinks = $this->mBadLinks = $cc->mBadLinks; $this->mPreFilled = true; @@ -345,38 +385,27 @@ class LinkCache { /** * Delete linkscc rows which link to here - * @param $pid is a page id + * @param $title The page linked to * @static */ - function linksccClearLinksTo( $pid ){ + function linksccClearLinksTo( $title ){ global $wgEnablePersistentLC; if ( $wgEnablePersistentLC ) { $fname = 'LinkCache::linksccClearLinksTo'; $pid = intval( $pid ); $dbw =& wfGetDB( DB_MASTER ); # Delete linkscc rows which link to here - $dbw->deleteJoin( 'linkscc', 'links', 'lcc_pageid', 'l_from', array( 'l_to' => $pid ), $fname ); + $dbw->deleteJoin( 'linkscc', 'pagelinks', 'lcc_pageid', 'pl_from', + array( + 'pl_namespace' => $title->getNamespace(), + 'pl-title' => $title->getDbKey() ), + $fname ); # Delete linkscc row representing this page $dbw->delete( 'linkscc', array( 'lcc_pageid' => $pid ), $fname); } } - /** - * Delete linkscc rows with broken links to here - * @param $title is a prefixed db title for example like Title->getPrefixedDBkey() returns. - * @static - */ - function linksccClearBrokenLinksTo( $title ){ - global $wgEnablePersistentLC; - $fname = 'LinkCache::linksccClearBrokenLinksTo'; - - if ( $wgEnablePersistentLC ) { - $dbw =& wfGetDB( DB_MASTER ); - $dbw->deleteJoin( 'linkscc', 'brokenlinks', 'lcc_pageid', 'bl_from', array( 'bl_to' => $title ), $fname ); - } - } - /** * @param $pid is a page id * @static @@ -404,6 +433,12 @@ class LinkBatch { */ var $data = array(); + function LinkBatch( $arr = array() ) { + foreach( $arr as $item ) { + $this->addObj( $item ); + } + } + function addObj( $title ) { $this->add( $title->getNamespace(), $title->getDBkey() ); } @@ -433,9 +468,45 @@ class LinkBatch { // This is very similar to Parser::replaceLinkHolders $dbr = wfGetDB( DB_SLAVE ); $page = $dbr->tableName( 'page' ); - $sql = "SELECT page_id, page_namespace, page_title FROM $page WHERE "; - $first = true; + $sql = "SELECT page_id, page_namespace, page_title FROM $page WHERE " + . $this->constructSet( 'page', $dbr ); + // Do query + $res = $dbr->query( $sql, $fname ); + + // Process results + // For each returned entry, add it to the list of good links, and remove it from $remaining + + $remaining = $this->data; + while ( $row = $dbr->fetchObject( $res ) ) { + $title = Title::makeTitle( $row->page_namespace, $row->page_title ); + $cache->addGoodLinkObj( $row->page_id, $title ); + unset( $remaining[$row->page_namespace][$row->page_title] ); + } + $dbr->freeResult( $res ); + + // The remaining links in $data are bad links, register them as such + foreach ( $remaining as $ns => $dbkeys ) { + foreach ( $dbkeys as $dbkey => $nothing ) { + $title = Title::makeTitle( $ns, $dbkey ); + $cache->addBadLinkObj( $title ); + } + } + + wfProfileOut( $fname ); + } + + /** + * Construct a WHERE clause which will match all the given titles. + * Give the appropriate table's field name prefix ('page', 'pl', etc). + * + * @param string $prefix + * @return string + * @access public + */ + function constructSet( $prefix, $db ) { + $first = true; + $sql = ''; foreach ( $this->data as $ns => $dbkeys ) { if ( !count( $dbkeys ) ) { continue; @@ -446,7 +517,7 @@ class LinkBatch { } else { $sql .= ' OR '; } - $sql .= "(page_namespace=$ns AND page_title IN ("; + $sql .= "({$prefix}_namespace=$ns AND {$prefix}_title IN ("; $firstTitle = true; foreach( $dbkeys as $dbkey => $nothing ) { @@ -455,35 +526,12 @@ class LinkBatch { } else { $sql .= ','; } - $sql .= $dbr->addQuotes( $dbkey ); + $sql .= $db->addQuotes( $dbkey ); } $sql .= '))'; } - - // Do query - $res = $dbr->query( $sql, $fname ); - - // Process results - // For each returned entry, add it to the list of good links, and remove it from $remaining - - $remaining = $this->data; - while ( $row = $dbr->fetchObject( $res ) ) { - $title = Title::makeTitle( $row->page_namespace, $row->page_title ); - $cache->addGoodLink( $row->page_id, $title->getPrefixedDBkey() ); - unset( $remaining[$row->page_namespace][$row->page_title] ); - } - $dbr->freeResult( $res ); - - // The remaining links in $data are bad links, register them as such - foreach ( $remaining as $ns => $dbkeys ) { - foreach ( $dbkeys as $dbkey => $nothing ) { - $title = Title::makeTitle( $ns, $dbkey ); - $cache->addBadLink( $title->getPrefixedText() ); - } - } - - wfProfileOut( $fname ); + return $sql; } } diff --git a/includes/LinksUpdate.php b/includes/LinksUpdate.php index 47f01f50d7..617b2f9ea8 100644 --- a/includes/LinksUpdate.php +++ b/includes/LinksUpdate.php @@ -43,66 +43,43 @@ class LinksUpdate { $add = array(); $dbw =& wfGetDB( DB_MASTER ); - $links = $dbw->tableName( 'links' ); - $brokenlinks = $dbw->tableName( 'brokenlinks' ); + $pagelinks = $dbw->tableName( 'pagelinks' ); $imagelinks = $dbw->tableName( 'imagelinks' ); $categorylinks = $dbw->tableName( 'categorylinks' ); #------------------------------------------------------------------------------ # Good links - if ( $wgLinkCache->incrementalSetup( LINKCACHE_GOOD, $del, $add ) ) { + if ( $wgLinkCache->incrementalSetup( LINKCACHE_PAGE, $del, $add ) ) { # Delete where necessary if ( count( $del ) ) { - $dbw->delete('links',array('l_from'=>$this->mId, 'l_to'=> $del),$fname); + $batch = new LinkBatch( $del ); + $set = $batch->constructSet( 'pl', $dbw ); + $sql = "DELETE FROM $pagelinks WHERE pl_from={$this->mId} AND ($set)"; + $dbw->query( $sql, $fname ); } } else { # Delete everything - $dbw->delete( 'links', array( 'l_from' => $this->mId ), $fname ); + $dbw->delete( 'pagelinks', array( 'pl_from' => $this->mId ), $fname ); # Get the addition list $add = $wgLinkCache->getGoodLinks(); } # Do the insertion - if ( 0 != count( $add ) ) { - $arr=array(); - foreach($add as $lt=>$lid) + if( 0 != count( $add ) ) { + $arr = array(); + foreach( $add as $lt => $target ) { array_push( $arr, array( - 'l_from' => $this->mId, - 'l_to' => $lid ) ); + 'pl_from' => $this->mId, + 'pl_namespace' => $target->getNamespace(), + 'pl_title' => $target->getDbKey() ) ); + } + # The link cache was constructed without FOR UPDATE, so there may be collisions # Ignoring for now, I'm not sure if that causes problems or not, but I'm fairly # sure it's better than without IGNORE - $dbw->insert( 'links', $arr, $fname, array( 'IGNORE' ) ); - } - - #------------------------------------------------------------------------------ - # Bad links - - if ( $wgLinkCache->incrementalSetup( LINKCACHE_BAD, $del, $add ) ) { - # Delete where necessary - if ( count( $del ) ) { - $dbw->delete('brokenlinks',array('bl_from'=>$this->mId, 'bl_to'=> $del),$fname); - } - } else { - # Delete all - $dbw->delete( 'brokenlinks', array( 'bl_from' => $this->mId ),$fname ); - - # Get addition list - $add = $wgLinkCache->getBadLinks(); - } - - # Do additions - $sql = ''; - if ( 0 != count ( $add ) ) { - $arr = array(); - foreach( $add as $blt ) { - array_push( $arr, array( - 'bl_from' => $this->mId, - 'bl_to' => $blt ) ); - } - $dbw->insert( 'brokenlinks', $arr, $fname, array( 'IGNORE' ) ); + $dbw->insert( 'pagelinks', $arr, $fname, array( 'IGNORE' ) ); } #------------------------------------------------------------------------------ @@ -196,8 +173,6 @@ class LinksUpdate { } } - $this->fixBrokenLinks(); - wfProfileOut( $fname ); } @@ -213,37 +188,24 @@ class LinksUpdate { $dbw =& wfGetDB( DB_MASTER ); - $links = $dbw->tableName( 'links' ); - $brokenlinks = $dbw->tableName( 'brokenlinks' ); + $pagelinks = $dbw->tableName( 'pagelinks' ); $imagelinks = $dbw->tableName( 'imagelinks' ); $categorylinks = $dbw->tableName( 'categorylinks' ); - $dbw->delete('links', array('l_from'=>$this->mId),$fname); + $dbw->delete('pagelinks', array('pl_from'=>$this->mId),$fname); - $a = $wgLinkCache->getGoodLinks(); + $a = $wgLinkCache->getPageLinks(); if ( 0 != count( $a ) ) { $arr = array(); - foreach( $a as $lt => $lid ) { + foreach( $a as $lt => $target ) { array_push( $arr, array( - 'l_from' => $this->mId, - 'l_to' => $lid ) ); + 'pl_from' => $this->mId, + 'pl_namespace' => $target->getNamespace(), + 'pl_title' => $target->getTitle() ) ); } - $dbw->insert( 'links', $arr, $fname, array( 'IGNORE' ) ); + $dbw->insert( 'pagelinks', $arr, $fname, array( 'IGNORE' ) ); } - $dbw->delete('brokenlinks', array('bl_from'=>$this->mId),$fname); - - $a = $wgLinkCache->getBadLinks(); - if ( 0 != count ( $a ) ) { - $arr = array(); - foreach( $a as $blt ) { - array_push($arr,array( - 'bl_from' => $this->mId, - 'bl_to' => $blt)); - } - $dbw->insert( 'brokenlinks', $arr, $fname, array( 'IGNORE' ) ); - } - $dbw->delete('imagelinks', array('il_from'=>$this->mId),$fname); $a = $wgLinkCache->getImageLinks(); @@ -280,40 +242,7 @@ class LinksUpdate { $dbw->insert( 'categorylinks', $arr, $fname, array( 'IGNORE' ) ); } } - $this->fixBrokenLinks(); wfProfileOut( $fname ); } - - /** - * Update any brokenlinks *to* this page - * Call for a newly created page, or just to make sure state is consistent - */ - function fixBrokenLinks() { - $fname = 'LinksUpdate::fixBrokenLinks'; - - $dbw =& wfGetDB( DB_MASTER ); - $page = $dbw->tableName( 'page' ); - $links = $dbw->tableName( 'links' ); - - $res = $dbw->select( 'brokenlinks', array( 'bl_from' ), array( 'bl_to' => $this->mTitle ), - $fname, 'FOR UPDATE' ); - if ( 0 == $dbw->numRows( $res ) ) { return; } - - $arr=array(); - $toucharr=array(); - while ( $row = $dbw->fetchObject( $res ) ) { - array_push( $arr, array( - 'l_from' => $row->bl_from, - 'l_to' => $this->mId ) ); - $toucharr[]=$row->bl_from; - } - - # Ignore errors. If a link existed in both the brokenlinks table and the links - # table, that's an error which can be fixed at this stage by simply ignoring collisions - $dbw->insert( 'links', $arr, $fname, array( 'IGNORE' ) ); - $dbw->update( 'page', /* SET */ array( 'page_touched' => $dbw->timestamp() ), - /* WHERE */ array( 'page_id' => $toucharr ),$fname); - $dbw->delete( 'brokenlinks', array( 'bl_to' => $this->mTitle ), $fname ); - } } ?> diff --git a/includes/Parser.php b/includes/Parser.php index abd62c63a9..2927768448 100644 --- a/includes/Parser.php +++ b/includes/Parser.php @@ -2911,7 +2911,7 @@ class Parser while ( $s = $dbr->fetchObject($res) ) { $title = Title::makeTitle( $s->page_namespace, $s->page_title ); $pdbk = $title->getPrefixedDBkey(); - $wgLinkCache->addGoodLink( $s->page_id, $pdbk ); + $wgLinkCache->addGoodLinkObj( $s->page_id, $title ); if ( $threshold > 0 ) { $size = $s->page_len; @@ -2935,7 +2935,7 @@ class Parser $searchkey = ""; $title = $this->mLinkHolders['titles'][$key]; if ( empty( $colours[$pdbk] ) ) { - $wgLinkCache->addBadLink( $pdbk ); + $wgLinkCache->addBadLinkObj( $title ); $colours[$pdbk] = 0; $wgOutputReplace[$searchkey] = $sk->makeBrokenLinkObj( $title, $this->mLinkHolders['texts'][$key], diff --git a/includes/SpecialBrokenRedirects.php b/includes/SpecialBrokenRedirects.php index 98fcc7c4ef..61716eb676 100644 --- a/includes/SpecialBrokenRedirects.php +++ b/includes/SpecialBrokenRedirects.php @@ -31,11 +31,19 @@ class BrokenRedirectsPage extends PageQueryPage { function getSQL() { $dbr =& wfGetDB( DB_SLAVE ); - extract( $dbr->tableNames( 'page', 'brokenlinks' ) ); + extract( $dbr->tableNames( 'page', 'pagelinks' ) ); - $sql = "SELECT 'BrokenRedirects' as type, page_namespace as namespace," . - "page_title as title, bl_to FROM $brokenlinks,$page " . - 'WHERE page_is_redirect=1 AND bl_from=page_id '; + $sql = "SELECT 'BrokenRedirects' AS type, + p1.page_namespace AS namespace, + p1.page_title AS title, + pl_namespace, + pl_title + FROM $pagelinks, $page AS p1 + LEFT JOIN $page AS p2 + ON pl_namespace=p2.page_namespace AND pl_title=p2.page_title + WHERE p1.page_is_redirect=1 + AND pl_from=p1.page_id + AND p2.page_namespace IS NULL"; return $sql; } @@ -45,8 +53,8 @@ class BrokenRedirectsPage extends PageQueryPage { function formatResult( $skin, $result ) { $fromObj = Title::makeTitle( $result->namespace, $result->title ); - if ( isset( $result->bl_to ) ) { - $toObj = Title::newFromText( $result->bl_to ); + if ( isset( $result->pl_title ) ) { + $toObj = Title::makeTitle( $result->pl_namespace, $result->pl_title ); } else { $blinks = $fromObj->getBrokenLinksFrom(); if ( $blinks ) { diff --git a/includes/SpecialDeadendpages.php b/includes/SpecialDeadendpages.php index 3329e33ac6..286460d812 100644 --- a/includes/SpecialDeadendpages.php +++ b/includes/SpecialDeadendpages.php @@ -44,10 +44,10 @@ class DeadendPagesPage extends PageQueryPage { */ function getSQL() { $dbr =& wfGetDB( DB_SLAVE ); - extract( $dbr->tableNames( 'page', 'links' ) ); + extract( $dbr->tableNames( 'page', 'pagelinks' ) ); return "SELECT 'Deadendpages' as type, page_namespace AS namespace, page_title as title, page_title AS value " . - "FROM $page LEFT JOIN $links ON page_id = l_from " . - "WHERE l_from IS NULL " . + "FROM $page LEFT JOIN $pagelinks ON page_id = pl_from " . + "WHERE pl_from IS NULL " . "AND page_namespace = 0 " . "AND page_is_redirect = 0"; } diff --git a/includes/SpecialDisambiguations.php b/includes/SpecialDisambiguations.php index 2522e6668a..10a329490b 100644 --- a/includes/SpecialDisambiguations.php +++ b/includes/SpecialDisambiguations.php @@ -34,20 +34,19 @@ class DisambiguationsPage extends PageQueryPage { function getSQL() { $dbr =& wfGetDB( DB_SLAVE ); - extract( $dbr->tableNames( 'page', 'links' ) ); + extract( $dbr->tableNames( 'page', 'pagelinks' ) ); $dp = Title::newFromText(wfMsgForContent("disambiguationspage")); + $id = $dp->getArticleId(); $dns = $dp->getNamespace(); $dtitle = $dbr->addQuotes( $dp->getDBkey() ); $sql = "SELECT 'Disambiguations' as type," - . " pa.page_namespace AS namespace, pa.page_title AS title" - . " FROM {$links} as la, {$links} as lb, {$page} as pa, {$page} as pb" - . " WHERE pb.page_namespace = $dns" - . " AND pb.page_title = $dtitle" - . " AND la.l_from = lb.l_to" - . " AND pa.page_id = lb.l_from" - . " AND pb.page_id = lb.l_to" ; + . " pl_namespace AS namespace, pl_title AS title" + . " FROM {$pagelinks}, {$page}" + . " WHERE page_namespace = $dns" + . " AND page_title = $dtitle" + . " AND pl_from=page_id"; return $sql; } diff --git a/includes/SpecialDoubleRedirects.php b/includes/SpecialDoubleRedirects.php index 9c0563e5ab..3169844610 100644 --- a/includes/SpecialDoubleRedirects.php +++ b/includes/SpecialDoubleRedirects.php @@ -31,18 +31,20 @@ class DoubleRedirectsPage extends PageQueryPage { function getSQL() { $dbr =& wfGetDB( DB_SLAVE ); - extract( $dbr->tableNames( 'page', 'links' ) ); + extract( $dbr->tableNames( 'page', 'pagelinks' ) ); $sql = "SELECT 'DoubleRedirects' as type," . " pa.page_namespace as namespace, pa.page_title as title," . " pb.page_namespace as nsb, pb.page_title as tb," . " pc.page_namespace as nsc, pc.page_title as tc" . - " FROM $links AS la, $links AS lb, $page AS pa, $page AS pb, $page AS pc" . + " FROM $pagelinks AS la, $pagelinks AS lb, $page AS pa, $page AS pb, $page AS pc" . " WHERE pa.page_is_redirect=1 AND pb.page_is_redirect=1" . - " AND la.l_from=pa.page_id" . - " AND la.l_to=pb.page_id" . - " AND lb.l_from=pb.page_id" . - " AND lb.l_to=pc.page_id"; + " AND la.pl_from=pa.page_id" . + " AND la.pl_namespace=pb.page_namespace" . + " AND la.pl_title=pb.page_title" . + " AND lb.pl_from=pb.page_id" . + " AND lb.pl_namespace=pc.page_namespace" . + " AND lb.pl_title=pc.page_title"; return $sql; } @@ -56,18 +58,20 @@ class DoubleRedirectsPage extends PageQueryPage { if ( $result && !isset( $result->nsb ) ) { $dbr =& wfGetDB( DB_SLAVE ); - extract( $dbr->tableNames( 'page', 'links' ) ); + extract( $dbr->tableNames( 'page', 'pagelinks' ) ); $encTitle = $dbr->addQuotes( $result->title ); $sql = "SELECT pa.page_namespace as namespace, pa.page_title as title," . " pb.page_namespace as nsb, pb.page_title as tb," . " pc.page_namespace as nsc, pc.page_title as tc" . - " FROM $links AS la, $links AS lb, $page AS pa, $page AS pb, $page AS pc" . + " FROM $pagelinks AS la, $pagelinks AS lb, $page AS pa, $page AS pb, $page AS pc" . " WHERE pa.page_is_redirect=1 AND pb.page_is_redirect=1" . - " AND la.l_from=pa.page_id" . - " AND la.l_to=pb.page_id" . - " AND lb.l_from=pb.page_id" . - " AND lb.l_to=pc.page_id" . + " AND la.pl_from=pa.page_id" . + " AND la.pl_namespace=pb.page_namespace" . + " AND la.pl_title=pb.page_title" . + " AND lb.pl_from=pb.page_id" . + " AND lb.pl_namespace=pc.page_namespace" . + " AND lb.pl_title=pc.page_title" . " AND pa.page_namespace={$result->namespace}" . " AND pa.page_title=$encTitle"; $res = $dbr->query( $sql, $fname ); diff --git a/includes/SpecialLog.php b/includes/SpecialLog.php index 9d91f7fe82..bfaee33ec1 100644 --- a/includes/SpecialLog.php +++ b/includes/SpecialLog.php @@ -300,9 +300,9 @@ class LogViewer { // Enter the existence or non-existence of this page into the link cache, // for faster makeLinkObj() in LogPage::actionText() if( $s->page_id ) { - $wgLinkCache->addGoodLink( $s->page_id, $title->getPrefixedText() ); + $wgLinkCache->addGoodLinkObj( $s->page_id, $title ); } else { - $wgLinkCache->addBadLink( $title->getPrefixedText() ); + $wgLinkCache->addBadLinkObj( $title ); } $userLink = $this->skin->makeLinkObj( $user, htmlspecialchars( $s->user_name ) ); diff --git a/includes/SpecialLonelypages.php b/includes/SpecialLonelypages.php index 6f0f73f7c5..a143f1494a 100644 --- a/includes/SpecialLonelypages.php +++ b/includes/SpecialLonelypages.php @@ -32,11 +32,20 @@ class LonelyPagesPage extends PageQueryPage { function getSQL() { $dbr =& wfGetDB( DB_SLAVE ); - extract( $dbr->tableNames( 'page', 'links' ) ); + extract( $dbr->tableNames( 'page', 'pagelinks' ) ); + + return + "SELECT 'Lonelypages' AS type, + page_namespace AS namespace, + page_title AS title, + page_title AS value + FROM $page + LEFT JOIN $pagelinks + ON page_namespace=pl_namespace AND page_title=pl_title + WHERE pl_namespace IS NULL + AND page_namespace=".NS_MAIN." + AND page_is_redirect=0"; - return "SELECT 'Lonelypages' as type, page_namespace AS namespace, page_title AS title, page_title AS value " . - "FROM $page LEFT JOIN $links ON page_id=l_to ". - 'WHERE l_to IS NULL AND page_namespace='.NS_MAIN.' AND page_is_redirect=0'; } } diff --git a/includes/SpecialRecentchangeslinked.php b/includes/SpecialRecentchangeslinked.php index 86ec1b9ef7..2717d63c00 100644 --- a/includes/SpecialRecentchangeslinked.php +++ b/includes/SpecialRecentchangeslinked.php @@ -62,22 +62,43 @@ function wfSpecialRecentchangeslinked( $par = NULL ) { $cmq = 'AND rev_minor_edit=0'; } else { $cmq = ''; } - extract( $dbr->tableNames( 'categorylinks', 'links', 'revision', 'page' ) ); + extract( $dbr->tableNames( 'categorylinks', 'pagelinks', 'revision', 'page' ) ); // If target is a Category, use categorylinks and invert from and to if( $nt->getNamespace() == NS_CATEGORY ) { $catkey = $dbr->addQuotes( $nt->getDBKey() ); - $sql = "SELECT page_id,page_namespace,page_title,rev_user,rev_comment," . - "rev_user_text,rev_timestamp,rev_minor_edit,page_is_new FROM $categorylinks, $revision, $page " . - "WHERE rev_timestamp > '{$cutoff}' {$cmq} AND cl_from=page_id AND cl_to=$catkey " . - "GROUP BY page_id,page_namespace,page_title,rev_user,rev_comment,rev_user_text," . - "rev_timestamp,rev_minor_edit,page_is_new ORDER BY rev_timestamp DESC LIMIT {$limit}"; + $sql = + "SELECT page_id,page_namespace,page_title,rev_user,rev_comment, + rev_user_text,rev_timestamp,rev_minor_edit, + page_is_new + FROM $categorylinks, $revision, $page + WHERE rev_timestamp > '{$cutoff}' + {$cmq} + AND rev_page=page_id + AND cl_from=page_id + AND cl_to=$catkey +GROUP BY page_id,page_namespace,page_title, + rev_user,rev_comment,rev_user_text,rev_timestamp,rev_minor_edit, + page_is_new +ORDER BY rev_timestamp DESC + LIMIT {$limit}"; } else { - $sql = "SELECT page_id,page_namespace,page_title,rev_user,rev_comment," . - "rev_user_text,rev_timestamp,rev_minor_edit,page_is_new FROM $links, $revision, $page " . - "WHERE rev_timestamp > '{$cutoff}' {$cmq} AND l_to=page_id AND l_from=$id " . - "GROUP BY page_id,page_namespace,page_title,rev_user,rev_comment,rev_user_text," . - "rev_timestamp,rev_minor_edit,page_is_new ORDER BY rev_timestamp DESC LIMIT {$limit}"; + $sql = + "SELECT page_id,page_namespace,page_title, + rev_user,rev_comment,rev_user_text,rev_timestamp,rev_minor_edit, + page_is_new + FROM $pagelinks, $revision, $page + WHERE rev_timestamp > '{$cutoff}' + {$cmq} + AND rev_page=page_id + AND pl_namespace=page_namespace + AND pl_title=page_title + AND pl_from=$id +GROUP BY page_id,page_namespace,page_title, + rev_user,rev_comment,rev_user_text,rev_timestamp,rev_minor_edit, + page_is_new +ORDER BY rev_timestamp DESC + LIMIT {$limit}"; } $res = $dbr->query( $sql, $fname ); diff --git a/includes/SpecialWantedpages.php b/includes/SpecialWantedpages.php index 1cbd8bb9fc..2f16efb5a9 100644 --- a/includes/SpecialWantedpages.php +++ b/includes/SpecialWantedpages.php @@ -28,30 +28,28 @@ class WantedPagesPage extends QueryPage { function getSQL() { $dbr =& wfGetDB( DB_SLAVE ); - $brokenlinks = $dbr->tableName( 'brokenlinks' ); - - # We cheat and return the full-text from bl_to in the title. - # In the future, a pre-parsed name will be available. - $agrvalue=$dbr->aggregateValue('COUNT(DISTINCT bl_from)'); + $pagelinks = $dbr->tableName( 'pagelinks' ); + $page = $dbr->tableName( 'page' ); return - "SELECT 'Wantedpages' as type, - 0 as namespace, - bl_to as title, - COUNT(DISTINCT bl_from) as value - FROM $brokenlinks - GROUP BY bl_to - HAVING $agrvalue > 1"; + "SELECT 'Wantedpages' AS type, + pl_namespace AS namespace, + pl_title AS title, + COUNT(*) AS value + FROM $pagelinks + LEFT JOIN $page + ON pl_namespace=page_namespace AND pl_title=page_title + WHERE page_namespace IS NULL + GROUP BY pl_namespace,pl_title + HAVING COUNT(*) > 1"; } function formatResult( $skin, $result ) { global $wgContLang; - $nt = Title::newFromDBkey( $result->title ); - $text = $wgContLang->convert( $result->title ); - if( is_null( $nt ) ) { - return ""; - } + $nt = Title::makeTitle( $result->namespace, $result->title ); + $text = $wgContLang->convert( $nt->getPrefixedText() ); $plink = $skin->makeBrokenLink( $nt->getPrefixedText(), $text ); + $nl = wfMsg( "nlinks", $result->value ); $nlink = $skin->makeKnownLink( $wgContLang->specialPage( "Whatlinkshere" ), $nl, "target=" . $nt->getPrefixedURL() ); diff --git a/includes/SpecialWhatlinkshere.php b/includes/SpecialWhatlinkshere.php index ac1dec0e11..81e8faa666 100644 --- a/includes/SpecialWhatlinkshere.php +++ b/includes/SpecialWhatlinkshere.php @@ -29,7 +29,6 @@ function wfSpecialWhatlinkshere($par = NULL) { $wgOut->setPagetitle( $nt->getPrefixedText() ); $wgOut->setSubtitle( wfMsg( 'linklistsub' ) ); - $id = $nt->getArticleID(); $sk = $wgUser->getSkin(); $isredir = ' (' . wfMsg( 'isredirect' ) . ")\n"; @@ -38,53 +37,22 @@ function wfSpecialWhatlinkshere($par = NULL) { $specialTitle = Title::makeTitle( NS_SPECIAL, 'Whatlinkshere' ); $wgOut->addHTML( wfViewPrevNext( $offset, $limit, $specialTitle, 'target=' . urlencode( $target ) ) ); - $dbr =& wfGetDB( DB_SLAVE ); - extract( $dbr->tableNames( 'page', 'brokenlinks', 'links' ) ); - - if ( 0 == $id ) { - $sql = "SELECT page_id,page_namespace,page_title,page_is_redirect FROM $brokenlinks,$page WHERE bl_to='" . - $dbr->strencode( $nt->getPrefixedDBkey() ) . "' AND bl_from=page_id " . - $dbr->limitResult( $limit, $offset ); - $res = $dbr->query( $sql, $fname ); - - if ( 0 == $dbr->numRows( $res ) ) { - $wgOut->addHTML( wfMsg( 'nolinkshere' ) ); - } else { - $wgOut->addHTML( wfMsg( 'linkshere' ) ); - $wgOut->addHTML( "\n\n" ); - $dbr->freeResult( $res ); - } - } else { - wfShowIndirectLinks( 0, $id, $limit, $offset ); - } + wfShowIndirectLinks( 0, $nt, $limit, $offset ); $wgOut->addHTML( wfViewPrevNext( $offset, $limit, $specialTitle, 'target=' . urlencode( $target ) ) ); } /** - * + * @param int $level + * @param Title $target + * @param int $limit + * @param int $offset + * @access private */ -function wfShowIndirectLinks( $level, $lid, $limit, $offset = 0 ) { +function wfShowIndirectLinks( $level, $target, $limit, $offset = 0 ) { global $wgOut, $wgUser; $fname = 'wfShowIndirectLinks'; $dbr =& wfGetDB( DB_READ ); - extract( $dbr->tableNames( 'links','page' ) ); if ( $level == 0 ) { $limitSql = $dbr->limitResult( $limit, $offset ); @@ -92,8 +60,14 @@ function wfShowIndirectLinks( $level, $lid, $limit, $offset = 0 ) { $limitSql = "LIMIT $limit"; } - $sql = "SELECT page_id,page_namespace,page_title,page_is_redirect FROM $links,$page WHERE l_to={$lid} AND l_from=page_id $limitSql"; - $res = $dbr->query( $sql, $fname ); + $res = $dbr->select( array( 'pagelinks', 'page' ), + array( 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ), + array( + 'pl_from=page_id', + 'pl_namespace' => $target->getNamespace(), + 'pl_title' => $target->getDbKey() ), + $fname, + $limitSql ); if ( 0 == $dbr->numRows( $res ) ) { if ( 0 == $level ) { @@ -110,10 +84,6 @@ function wfShowIndirectLinks( $level, $lid, $limit, $offset = 0 ) { $wgOut->addHTML( '