diff options
-rw-r--r-- | RELEASE-NOTES | 3 | ||||
-rw-r--r-- | includes/Article.php | 36 | ||||
-rw-r--r-- | includes/Database.php | 2 | ||||
-rw-r--r-- | includes/Image.php | 2 | ||||
-rw-r--r-- | includes/LinkCache.php | 186 | ||||
-rw-r--r-- | includes/LinksUpdate.php | 119 | ||||
-rw-r--r-- | includes/Parser.php | 4 | ||||
-rw-r--r-- | includes/SpecialBrokenRedirects.php | 20 | ||||
-rw-r--r-- | includes/SpecialDeadendpages.php | 6 | ||||
-rw-r--r-- | includes/SpecialDisambiguations.php | 15 | ||||
-rw-r--r-- | includes/SpecialDoubleRedirects.php | 28 | ||||
-rw-r--r-- | includes/SpecialLog.php | 4 | ||||
-rw-r--r-- | includes/SpecialLonelypages.php | 17 | ||||
-rw-r--r-- | includes/SpecialRecentchangeslinked.php | 43 | ||||
-rw-r--r-- | includes/SpecialWantedpages.php | 32 | ||||
-rw-r--r-- | includes/SpecialWhatlinkshere.php | 62 | ||||
-rw-r--r-- | includes/SquidUpdate.php | 34 | ||||
-rw-r--r-- | includes/Title.php | 197 | ||||
-rw-r--r-- | maintenance/convertUtf8.php | 2 | ||||
-rw-r--r-- | maintenance/refreshLinks.inc | 5 | ||||
-rw-r--r-- | maintenance/remove-brokenlinks.php | 58 | ||||
-rw-r--r-- | maintenance/tables.sql | 21 | ||||
-rw-r--r-- | maintenance/updaters.inc | 42 |
23 files changed, 434 insertions, 504 deletions
diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 7bddb27562b2..3e183e5edafa 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 2262be271d28..b02b7679df96 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 e69e49b9e816..8b9858a4e23b 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 6b53b5cf2e2f..4882e23bc905 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 5853f16f228f..35e486bf9231 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,17 +385,21 @@ 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); } @@ -363,21 +407,6 @@ class LinkCache { } /** - * 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 47f01f50d7ad..617b2f9ea8ee 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 abd62c63a978..292776844878 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 = "<!--LINK $key-->"; $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 98fcc7c4efa9..61716eb67626 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 3329e33ac6d2..286460d812b1 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 2522e6668a4f..10a329490b62 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 9c0563e5ab8e..316984461059 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 9d91f7fe825f..bfaee33ec173 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 6f0f73f7c54f..a143f1494a93 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 86ec1b9ef744..2717d63c0064 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 1cbd8bb9fc0a..2f16efb5a95d 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 "<!-- Bad title '" . htmlspecialchars( $result->title ) . "' -->"; - } + $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 ac1dec0e1115..81e8faa6660f 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<ul>" ); - - while ( $row = $dbr->fetchObject( $res ) ) { - $nt = Title::makeTitle( $row->page_namespace, $row->page_title ); - if( !$nt ) { - continue; - } - $link = $sk->makeKnownLinkObj( $nt, '', 'redirect=no' ); - $wgOut->addHTML( "<li>{$link}" ); - - if ( $row->page_is_redirect ) { - $wgOut->addHTML( $isredir ); - wfShowIndirectLinks( 1, $row->page_id, 500 ); - } - $wgOut->addHTML( "</li>\n" ); - } - $wgOut->addHTML( "</ul>\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( '<ul>' ); while ( $row = $dbr->fetchObject( $res ) ) { $nt = Title::makeTitle( $row->page_namespace, $row->page_title ); - if( !$nt ) { - $wgOut->addHTML( '<!-- bad backlink: ' . htmlspecialchars( $row->l_from ) . " -->\n" ); - continue; - } if ( $row->page_is_redirect ) { $extra = 'redirect=no'; @@ -127,7 +97,7 @@ function wfShowIndirectLinks( $level, $lid, $limit, $offset = 0 ) { if ( $row->page_is_redirect ) { $wgOut->addHTML( $isredir ); if ( $level < 2 ) { - wfShowIndirectLinks( $level + 1, $row->page_id, 500 ); + wfShowIndirectLinks( $level + 1, $nt, 500 ); } } $wgOut->addHTML( "</li>\n" ); diff --git a/includes/SquidUpdate.php b/includes/SquidUpdate.php index 484f26dec453..91769b2a1d53 100644 --- a/includes/SquidUpdate.php +++ b/includes/SquidUpdate.php @@ -35,8 +35,13 @@ class SquidUpdate { $links = $dbr->tableName( 'links' ); $page = $dbr->tableName( 'page' ); - $sql = "SELECT page_namespace,page_title FROM $links,$page WHERE l_to={$id} and l_from=page_id" ; - $res = $dbr->query( $sql, $fname ) ; + $res = $dbr->select( array( 'links', 'page' ), + array( 'page_namespace', 'page_title' ), + array( + 'pl_namespace' => $title->getNamespace(), + 'pl_title' => $title->getDbKey(), + 'pl_from=page_id' ), + $fname ); $blurlArr = $title->getSquidURLs(); if ( $dbr->numRows( $res ) <= $this->mMaxTitles ) { while ( $BL = $dbr->fetchObject ( $res ) ) @@ -51,31 +56,6 @@ class SquidUpdate { return new SquidUpdate( $blurlArr ); } - /* static */ function newFromBrokenLinksTo( &$title ) { - $fname = 'SquidUpdate::newFromBrokenLinksTo'; - wfProfileIn( $fname ); - - # Get a list of URLs linking to this (currently non-existent) page - $dbr =& wfGetDB( DB_SLAVE ); - $brokenlinks = $dbr->tableName( 'brokenlinks' ); - $page = $dbr->tableName( 'page' ); - $encTitle = $dbr->addQuotes( $title->getPrefixedDBkey() ); - - $sql = "SELECT page_namespace,page_title FROM $brokenlinks,$cur WHERE bl_to={$encTitle} AND bl_from=page_id"; - $res = $dbr->query( $sql, $fname ); - $blurlArr = array(); - if ( $dbr->numRows( $res ) <= $this->mMaxTitles ) { - while ( $BL = $dbr->fetchObject( $res ) ) - { - $tobj = Title::makeTitle( $BL->page_namespace, $BL->page_title ); - $blurlArr[] = $tobj->getInternalURL(); - } - } - $dbr->freeResult( $res ); - wfProfileOut( $fname ); - return new SquidUpdate( $blurlArr ); - } - /* static */ function newFromTitles( &$titles, $urlArr = array() ) { foreach ( $titles as $title ) { $urlArr[] = $title->getInternalURL(); diff --git a/includes/Title.php b/includes/Title.php index a5e119f073cf..6810ee9e6795 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -1355,16 +1355,21 @@ class Title { } else { $db =& wfGetDB( DB_SLAVE ); } - $page = $db->tableName( 'page' ); - $links = $db->tableName( 'links' ); - - $sql = "SELECT page_namespace,page_title,page_id FROM $page,$links WHERE l_from=page_id AND l_to={$id} $options"; - $res = $db->query( $sql, 'Title::getLinksTo' ); + + $res = $db->select( array( 'page', 'pagelinks' ), + array( 'page_namespace', 'page_title', 'page_id' ), + array( + 'pl_from=page_id', + 'pl_namespace' => $this->getNamespace(), + 'pl_title' => $this->getDbKey() ), + 'Title::getLinksTo', + $options ); + $retVal = array(); 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; } } @@ -1374,42 +1379,6 @@ class Title { } /** - * Get an array of Title objects linking to this non-existent title. - * - Also stores the IDs in the link cache. - * - * @param string $options may be FOR UPDATE - * @return array the Title objects linking here - * @access public - */ - function getBrokenLinksTo( $options = '' ) { - global $wgLinkCache; - - if ( $options ) { - $db =& wfGetDB( DB_MASTER ); - } else { - $db =& wfGetDB( DB_SLAVE ); - } - $page = $db->tableName( 'page' ); - $brokenlinks = $db->tableName( 'brokenlinks' ); - $encTitle = $db->strencode( $this->getPrefixedDBkey() ); - - $sql = "SELECT page_namespace,page_title,page_id FROM $brokenlinks,$page " . - "WHERE bl_from=page_id AND bl_to='$encTitle' $options"; - $res = $db->query( $sql, "Title::getBrokenLinksTo" ); - $retVal = array(); - if ( $db->numRows( $res ) ) { - while ( $row = $db->fetchObject( $res ) ) { - $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title ); - $wgLinkCache->addGoodLink( $row->page_id, $titleObj->getPrefixedDBkey() ); - $retVal[] = $titleObj; - } - } - $db->freeResult( $res ); - return $retVal; - } - - - /** * Get an array of Title objects referring to non-existent articles linked from this page * * @param string $options may be FOR UPDATE @@ -1424,16 +1393,25 @@ class Title { } else { $db =& wfGetDB( DB_SLAVE ); } - $page = $db->tableName( 'page' ); - $brokenlinks = $db->tableName( 'brokenlinks' ); - $id = $this->getArticleID(); - - $sql = "SELECT bl_to FROM $brokenlinks WHERE bl_from=$id $options"; - $res = $db->query( $sql, "Title::getBrokenLinksFrom" ); + + $res = $db->safeQuery( + "SELECT pl_namespace, pl_title + FROM ! + LEFT JOIN ! + ON pl_namespace=page_namespace + AND pl_title=page_title + WHERE pl_from=? + AND page_namespace IS NULL + !", + $db->tableName( 'pagelinks' ), + $db->tableName( 'page' ), + $this->getArticleId(), + $options ); + $retVal = array(); if ( $db->numRows( $res ) ) { while ( $row = $db->fetchObject( $res ) ) { - $retVal[] = Title::newFromText( $row->bl_to ); + $retVal[] = Title::makeTitle( $row->pl_namespace, $row->pl_title ); } } $db->freeResult( $res ); @@ -1631,54 +1609,19 @@ class Title { $log = new LogPage( 'move' ); $log->addEntry( 'move_redir', $this, $reason, array( 1 => $nt->getPrefixedText() ) ); - # Swap links - - # Load titles and IDs - $linksToOld = $this->getLinksTo( 'FOR UPDATE' ); - $linksToNew = $nt->getLinksTo( 'FOR UPDATE' ); - - # Delete them all - $sql = "DELETE FROM $links WHERE l_to=$oldid OR l_to=$newid"; - $dbw->query( $sql, $fname ); - - # Reinsert - if ( count( $linksToOld ) || count( $linksToNew )) { - $sql = "INSERT INTO $links (l_from,l_to) VALUES "; - $first = true; - - # Insert links to old title - foreach ( $linksToOld as $linkTitle ) { - if ( $first ) { - $first = false; - } else { - $sql .= ','; - } - $id = $linkTitle->getArticleID(); - $sql .= "($id,$newid)"; - } - - # Insert links to new title - foreach ( $linksToNew as $linkTitle ) { - if ( $first ) { - $first = false; - } else { - $sql .= ','; - } - $id = $linkTitle->getArticleID(); - $sql .= "($id, $oldid)"; - } - - $dbw->query( $sql, $fname ); - } - # Now, we record the link from the redirect to the new title. # It should have no other outgoing links... - $dbw->delete( 'links', array( 'l_from' => $newid ) ); - $dbw->insert( 'links', array( 'l_from' => $newid, 'l_to' => $oldid ) ); + $dbw->delete( 'pagelinks', array( 'pl_from' => $newid ), $fname ); + $dbw->insert( 'pagelinks', + array( + 'pl_from' => $newid, + 'pl_namespace' => $this->getNamespace(), + 'pl_title' => $this->getDbKey() ), + $fname ); # Clear linkscc - LinkCache::linksccClearLinksTo( $oldid ); - LinkCache::linksccClearLinksTo( $newid ); + LinkCache::linksccClearLinksTo( $this ); + LinkCache::linksccClearLinksTo( $nt ); # Purge squid if ( $wgUseSquid ) { @@ -1749,17 +1692,17 @@ class Title { # Purge squid and linkscc as per article creation Article::onArticleCreate( $nt ); - # Any text links to the old title must be reassigned to the redirect - $dbw->update( 'links', array( 'l_to' => $newid ), array( 'l_to' => $oldid ), $fname ); - LinkCache::linksccClearLinksTo( $oldid ); - # Record the just-created redirect's linking to the page - $dbw->insert( 'links', array( 'l_from' => $newid, 'l_to' => $oldid ), $fname ); + $dbw->insert( 'pagelinks', + array( + 'pl_from' => $newid, + 'pl_namespace' => $this->getNamespace(), + 'pl_title' => $this->getTitle() ), + $fname ); # Non-existent target may have had broken links to it; these must - # now be removed and made into good links. - $update = new LinksUpdate( $oldid, $nt->getPrefixedDBkey() ); - $update->fixBrokenLinks(); + # now be touched to update link coloring. + $nt->touchLinks(); # Purge old title from squid # The new title, and links to the new title, are purged in Article::onArticleCreate() @@ -1849,21 +1792,13 @@ class Title { $article->updateRevisionOn( $dbw, $revision, 0 ); # Link table - if ( $dest->getArticleID() ) { - $dbw->insert( 'links', - array( - 'l_to' => $dest->getArticleID(), - 'l_from' => $newid - ), $fname - ); - } else { - $dbw->insert( 'brokenlinks', - array( - 'bl_to' => $dest->getPrefixedDBkey(), - 'bl_from' => $newid - ), $fname - ); - } + $dbw->insert( 'pagelinks', + array( + 'pl_from' => $newid, + 'pl_namespace' => $dest->getNamespace(), + 'pl_title' => $dest->getDbKey() + ), $fname + ); Article::onArticleCreate( $this ); return true; @@ -2015,5 +1950,35 @@ class Title { return ( 0 == $this->mNamespace && "" == $this->mDbkeyform ) || NS_SPECIAL == $this->mNamespace || NS_IMAGE == $this->mNamespace; } + + /** + * Update page_touched timestamps on pages linking to this title. + * In principal, this could be backgrounded and could also do squid + * purging. + */ + function touchLinks() { + $fname = 'Title::touchLinks'; + + $dbw =& wfGetDB( DB_MASTER ); + + $res = $dbw->select( 'pagelinks', + array( 'pl_from' ), + array( + 'pl_namespace' => $this->getNamespace(), + 'pl_title' => $this->getDbKey() ), + $fname ); + if ( 0 == $dbw->numRows( $res ) ) { + return; + } + + $arr = array(); + $toucharr = array(); + while( $row = $dbw->fetchObject( $res ) ) { + $toucharr[] = $row->pl_from; + } + + $dbw->update( 'page', /* SET */ array( 'page_touched' => $dbw->timestamp() ), + /* WHERE */ array( 'page_id' => $toucharr ),$fname); + } } ?> diff --git a/maintenance/convertUtf8.php b/maintenance/convertUtf8.php index 8e9c5cc804ed..14096cf1510a 100644 --- a/maintenance/convertUtf8.php +++ b/maintenance/convertUtf8.php @@ -156,7 +156,7 @@ class UtfUpdater { $this->convertTable( 'recentchanges', 'rc_id', array( 'rc_user_text', 'rc_title', 'rc_comment' ) ); - $this->convertTable( 'brokenlinks', 'bl_to' ); + $this->convertTable( 'pagelinks', 'pl_title' ); $this->convertTable( 'categorylinks', 'cl_to' ); $this->convertTable( 'imagelinks', 'il_to' ); $this->convertTable( 'watchlist', 'wl_title' ); diff --git a/maintenance/refreshLinks.inc b/maintenance/refreshLinks.inc index d788892b71da..b7e1a23420e2 100644 --- a/maintenance/refreshLinks.inc +++ b/maintenance/refreshLinks.inc @@ -14,9 +14,9 @@ function refreshLinks( $start ) { $dbw =& wfGetDB( DB_MASTER ); - $end = $dbw->selectField( 'cur', 'max(cur_id)', false ); + $end = $dbw->selectField( 'page', 'max(page_id)', false ); - print("Refreshing link table. Starting from cur_id $start of $end.\n"); + print("Refreshing link table. Starting from page_id $start of $end.\n"); # Don't generate TeX PNGs (lack of a sensible current directory causes errors anyway) $wgUser->setOption("math", 3); @@ -65,7 +65,6 @@ function refreshLinks( $start ) { $linksUpdate = new LinksUpdate( $id, $wgTitle->getPrefixedDBkey() ); $linksUpdate->doDumbUpdate(); - $linksUpdate->fixBrokenLinks(); $dbw->query("COMMIT"); } } diff --git a/maintenance/remove-brokenlinks.php b/maintenance/remove-brokenlinks.php deleted file mode 100644 index a4a139ee39c5..000000000000 --- a/maintenance/remove-brokenlinks.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -/** - * Remove spurious brokenlinks - * @package MediaWiki - * @subpackage Maintenance - */ - -/** */ -require_once( "commandLine.inc" ); -require_once( "./rebuildrecentchanges.inc" ); -$wgTitle = Title::newFromText( "Rebuild brokenlinks script" ); - -$n = 0; - -echo "Checking for broken brokenlinks...\n"; - -$dbw =& wfGetDB( DB_MASTER ); -extract( $dbw->tableNames( 'brokenlinks', 'cur', 'linkscc' ) ); - -$res = $dbw->select( 'cur', array( 'cur_namespace', 'cur_title', 'cur_id' ), false ); - -while( $s = $dbw->fetchObject( $res ) ) { - $n++; - if(($n % 500) == 0) { - echo "$n\n"; - } - $title = Title::makeTitle( $s->cur_namespace, $s->cur_title ); - if($title) { - $t = $title->getPrefixedDBKey(); - $tt = $dbw->strencode( $t ); - $any = false; - $sql2 = "SELECT bl_from,cur_id,cur_namespace,cur_title FROM $brokenlinks,$cur " . - "WHERE bl_to='$tt' AND cur_id=bl_from"; - $res2 = $dbw->query( $sql2 ); - while( $s = $dbw->fetchObject( $res2 ) ) { - $from = Title::makeTitle( $s->cur_namespace, $s->cur_title ); - $xt = $from->getPrefixedText(); - echo "Found bad brokenlink to [[$t]] from page #$s->cur_id [[$xt]]!\n"; - $any = true; - } - $dbw->freeResult( $res2 ); - if($any) { - echo "Removing brokenlinks to [[$t]]...\n"; - $sql3 = "DELETE FROM $brokenlinks WHERE bl_to='$tt'"; - $res3 = $dbw->query( $sql3 ); - #echo "-- $sql3\n"; - } - } else { - echo "Illegal title?! Namespace $s->cur_namespace, title '$s->cur_title'\n"; - } -} -echo "Done at $n.\n\n"; - -echo "Clearing linkscc table...\n"; -$sql4 = "DELETE FROM $linkscc"; -wfQuery( $sql4, DB_MASTER ); - -?> diff --git a/maintenance/tables.sql b/maintenance/tables.sql index 919b822cc67d..00416f374172 100644 --- a/maintenance/tables.sql +++ b/maintenance/tables.sql @@ -368,6 +368,27 @@ CREATE TABLE /*$wgDBprefix*/brokenlinks ( ) TYPE=InnoDB; + +-- +-- Track page-to-page hyperlinks within the wiki. +-- +CREATE TABLE /*$wgDBprefix*/pagelinks ( + -- Key to the page_id of the page containing the link. + pl_from int(8) unsigned NOT NULL default '0', + + -- Key to page_namespace/page_title of the target page. + -- The target page may or may not exist, and due to renames + -- and deletions may refer to different page records as time + -- goes by. + pl_namespace int NOT NULL default '0', + pl_title varchar(255) binary NOT NULL default '', + + UNIQUE KEY pl_from(pl_from,pl_namespace,pl_title), + KEY (pl_namespace,pl_title) + +) TYPE=InnoDB; + + -- -- Track links to images *used inline* -- We don't distinguish live from broken links here, so diff --git a/maintenance/updaters.inc b/maintenance/updaters.inc index ce2935e26e34..54abac4bf614 100644 --- a/maintenance/updaters.inc +++ b/maintenance/updaters.inc @@ -507,6 +507,46 @@ function do_namespace_size_on( $table, $prefix ) { } } +function do_pagelinks_update() { + global $wgDatabase; + if( $wgDatabase->tableExists( 'pagelinks' ) ) { + echo "...already have pagelinks table.\n"; + } else { + echo "Converting links and brokenlinks tables to pagelinks... "; + dbsource( "maintenance/archives/patch-pagelinks.sql", $wgDatabase ); + echo "ok\n"; + flush(); + + global $wgCanonicalNamespaceNames; + foreach( $wgCanonicalNamespaceNames as $ns => $name ) { + if( $ns != 0 ) { + do_pagelinks_namespace( $ns ); + } + } + } +} + +function do_pagelinks_namespace( $namespace ) { + global $wgDatabase, $wgContLang; + + $ns = IntVal( $namespace ); + echo "Cleaning up broken links for namespace $ns... "; + + $pagelinks = $wgDatabase->tableName( 'pagelinks' ); + $name = $wgContLang->getNsText( $ns ); + $prefix = $wgDatabase->strencode( $name ); + $likeprefix = str_replace( '_', '\\_', $prefix); + + $sql = "UPDATE $pagelinks + SET pl_namespace=$ns, + pl_title=TRIM(LEADING '$prefix:' FROM pl_title) + WHERE pl_namespace=0 + AND pl_title LIKE '$likeprefix:%'"; + + $wgDatabase->query( $sql, 'do_pagelinks_namespace' ); + echo "ok\n"; +} + function do_all_updates() { global $wgNewTables, $wgNewFields, $wgRenamedTables; @@ -545,6 +585,8 @@ function do_all_updates() { do_inverse_timestamp(); flush(); do_text_id(); flush(); do_namespace_size(); flush(); + + do_pagelinks_update(); flush(); initialiseMessages(); flush(); } |