* links and brokenlinks tables merged to pagelinks; this will reduce pain
authorBrion Vibber <brion@users.mediawiki.org>
Thu, 26 May 2005 10:23:36 +0000 (10:23 +0000)
committerBrion Vibber <brion@users.mediawiki.org>
Thu, 26 May 2005 10:23:36 +0000 (10:23 +0000)
  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.

23 files changed:
RELEASE-NOTES
includes/Article.php
includes/Database.php
includes/Image.php
includes/LinkCache.php
includes/LinksUpdate.php
includes/Parser.php
includes/SpecialBrokenRedirects.php
includes/SpecialDeadendpages.php
includes/SpecialDisambiguations.php
includes/SpecialDoubleRedirects.php
includes/SpecialLog.php
includes/SpecialLonelypages.php
includes/SpecialRecentchangeslinked.php
includes/SpecialWantedpages.php
includes/SpecialWhatlinkshere.php
includes/SquidUpdate.php
includes/Title.php
maintenance/convertUtf8.php
maintenance/refreshLinks.inc
maintenance/remove-brokenlinks.php [deleted file]
maintenance/tables.sql
maintenance/updaters.inc

index 7bddb27..3e183e5 100644 (file)
@@ -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 ===
 
index 2262be2..b02b767 100644 (file)
@@ -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;
                                }
                        }
                }
index e69e49b..8b9858a 100644 (file)
@@ -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' );
index 6b53b5c..4882e23 100644 (file)
@@ -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;
                                }
                        }
index 5853f16..35e486b 100644 (file)
@@ -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;
        }
 }
 
index 47f01f5..617b2f9 100644 (file)
@@ -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 );
-       }
 }
 ?>
index abd62c6..2927768 100644 (file)
@@ -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],
index 98fcc7c..61716eb 100644 (file)
@@ -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 ) {
index 3329e33..286460d 100644 (file)
@@ -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";
     }
index 2522e66..10a3294 100644 (file)
@@ -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;
        }
index 9c0563e..3169844 100644 (file)
@@ -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 );
index 9d91f7f..bfaee33 100644 (file)
@@ -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 ) );
index 6f0f73f..a143f14 100644 (file)
@@ -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';
        }
 }
 
index 86ec1b9..2717d63 100644 (file)
@@ -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 );
 
index 1cbd8bb..2f16efb 100644 (file)
@@ -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() );
index ac1dec0..81e8faa 100644 (file)
@@ -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" );
index 484f26d..91769b2 100644 (file)
@@ -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();
index a5e119f..6810ee9 100644 (file)
@@ -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;
                                }
                        }
@@ -1373,42 +1378,6 @@ class Title {
                return $retVal;
        }
 
-       /**
-        * 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
         *
@@ -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);
+       }
 }
 ?>
index 8e9c5cc..14096cf 100644 (file)
@@ -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' );
index d788892..b7e1a23 100644 (file)
@@ -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 (file)
index a4a139e..0000000
+++ /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 );
-
-?>
index 919b822..00416f3 100644 (file)
@@ -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
index ce2935e..54abac4 100644 (file)
@@ -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();
 }