From b6685480c28ea008bbc6f353dc229b35d1f828bd Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Fri, 20 Aug 2004 14:59:49 +0000 Subject: [PATCH] FOR UPDATE mode for Article class, and for getArticleID function of Title. Using these modes in EditPage.php to prevent odd things happening on section edits and merged conflicts --- includes/Article.php | 115 ++++++++++++++++++++++++++++-------------- includes/EditPage.php | 8 +-- includes/Title.php | 22 +++++--- 3 files changed, 96 insertions(+), 49 deletions(-) diff --git a/includes/Article.php b/includes/Article.php index 991eb5baac..0e0d866ed0 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -20,6 +20,7 @@ class Article { /* private */ var $mMinorEdit, $mRedirectedFrom; /* private */ var $mTouched, $mFileCache, $mTitle; /* private */ var $mId, $mTable; + /* private */ var $mForUpdate; function Article( &$title ) { $this->mTitle =& $title; @@ -33,6 +34,7 @@ class Article { $this->mTimestamp = $this->mComment = $this->mFileCache = ''; $this->mCountAdjustment = 0; $this->mTouched = '19700101000000'; + $this->mForUpdate = false; } # Get revision text associated with an old or archive row @@ -143,7 +145,7 @@ class Article { $index = $matches[4]; if ( $machineID == 0 ) { # Current connection - $db =& wfGetDB( DB_SLAVE ); + $db =& $this->getDB(); } else { # Alternate connection $db =& $wgLoadBalancer->getConnection( $machineID ); @@ -157,7 +159,8 @@ class Article { } if ( $db->isOpen() ) { $index = $db->strencode( $index ); - $res = $db->query( "SELECT blob_data FROM $dbName.$tblName WHERE blob_index='$index'", $fname ); + $res = $db->query( "SELECT blob_data FROM $dbName.$tblName " . + "WHERE blob_index='$index' " . $this->getSelectOptions(), $fname ); $row = $db->fetchObject( $res ); $text = $row->text_data; } @@ -332,7 +335,7 @@ class Article { function loadContent( $noredir = false ) { global $wgOut, $wgMwRedir, $wgRequest; - $dbr =& wfGetDB( DB_SLAVE ); + $dbr =& $this->getDB(); # Query variables :P $oldid = $wgRequest->getVal( 'oldid' ); $redirect = $wgRequest->getVal( 'redirect' ); @@ -358,7 +361,8 @@ class Article { $id = $this->getID(); if ( 0 == $id ) return; - $s = $dbr->getArray( 'cur', $this->getCurContentFields(), array( 'cur_id' => $id ), $fname ); + $s = $dbr->selectRow( 'cur', $this->getCurContentFields(), array( 'cur_id' => $id ), $fname, + $this->getSelectOptions() ); if ( $s === false ) { return; } @@ -382,7 +386,8 @@ class Article { } $rid = $rt->getArticleID(); if ( 0 != $rid ) { - $redirRow = $dbr->getArray( 'cur', $this->getCurContentFields(), array( 'cur_id' => $rid ), $fname ); + $redirRow = $dbr->selectRow( 'cur', $this->getCurContentFields(), + array( 'cur_id' => $rid ), $fname, $this->getSelectOptions() ); if ( $redirRow !== false ) { $this->mRedirectedFrom = $this->mTitle->getPrefixedText(); @@ -403,7 +408,8 @@ class Article { $this->mTitle->mRestrictions = explode( ',', trim( $s->cur_restrictions ) ); $this->mTitle->mRestrictionsLoaded = true; } else { # oldid set, retrieve historical version - $s = $dbr->getArray( 'old', $this->getOldContentFields(), array( 'old_id' => $oldid ), $fname ); + $s = $dbr->getArray( 'old', $this->getOldContentFields(), array( 'old_id' => $oldid ), + $fname, $this->getSelectOptions() ); if ( $s === false ) { return; } @@ -436,7 +442,7 @@ class Article { $this->mContent = false; $fname = 'Article::getContentWithout'; - $dbr =& wfGetDB( DB_SLAVE ); + $dbr =& $this->getDB(); if ( ! $oldid ) { # Retrieve current version $id = $this->getID(); @@ -444,7 +450,8 @@ class Article { return false; } - $s = $dbr->getArray( 'cur', $this->getCurContentFields(), array( 'cur_id' => $id ), $fname ); + $s = $dbr->selectRow( 'cur', $this->getCurContentFields(), array( 'cur_id' => $id ), + $fname, $this->getSelectOptions() ); if ( $s === false ) { return false; } @@ -456,7 +463,8 @@ class Article { if( $rt && $rt->getInterwiki() == '' && $rt->getNamespace() != NS_SPECIAL ) { $rid = $rt->getArticleID(); if ( 0 != $rid ) { - $redirRow = $dbr->getArray( 'cur', $this->getCurContentFields(), array( 'cur_id' => $rid ), $fname ); + $redirRow = $dbr->selectRow( 'cur', $this->getCurContentFields(), + array( 'cur_id' => $rid ), $fname, $this->getSelectOptions() ); if ( $redirRow !== false ) { $this->mRedirectedFrom = $this->mTitle->getPrefixedText(); @@ -477,7 +485,8 @@ class Article { $this->mTitle->mRestrictions = explode( ",", trim( $s->cur_restrictions ) ); $this->mTitle->mRestrictionsLoaded = true; } else { # oldid set, retrieve historical version - $s = $dbr->getArray( 'old', $this->getOldContentFields(), array( 'old_id' => $oldid ) ); + $s = $dbr->selectRow( 'old', $this->getOldContentFields(), array( 'old_id' => $oldid ), + $fname, $this->getSelectOptions() ); if ( $s === false ) { return false; } @@ -492,6 +501,33 @@ class Article { return $this->mContent; } + # Read/write accessor to select FOR UPDATE + function forUpdate( $x = NULL ) { + return wfSetVar( $this->mForUpdate, $x ); + } + + # Get the database which should be used for reads + function &getDB() { + if ( $this->mForUpdate ) { + return wfGetDB( DB_MASTER ); + } else { + return wfGetDB( DB_SLAVE ); + } + } + + # Get options for all SELECT statements + # Can pass an option array, to which the class-wide options will be appended + function getSelectOptions( $options = '' ) { + if ( $this->mForUpdate ) { + if ( $options ) { + $options[] = 'FOR UPDATE'; + } else { + $options = 'FOR UPDATE'; + } + } + return $options; + } + function getID() { if( $this->mTitle ) { return $this->mTitle->getArticleID(); @@ -503,8 +539,9 @@ class Article { function getCount() { if ( -1 == $this->mCounter ) { $id = $this->getID(); - $dbr =& wfGetDB( DB_SLAVE ); - $this->mCounter = $dbr->getField( 'cur', 'cur_counter', "cur_id={$id}" ); + $dbr =& $this->getDB(); + $this->mCounter = $dbr->selectField( 'cur', 'cur_counter', "cur_id={$id}", + 'Article::getCount', $this->getSelectOptions() ); } return $this->mCounter; } @@ -529,10 +566,10 @@ class Article { $fname = 'Article::loadLastEdit'; - $dbr =& wfGetDB( DB_SLAVE ); + $dbr =& $this->getDB(); $s = $dbr->getArray( 'cur', array( 'cur_user','cur_user_text','cur_timestamp', 'cur_comment','cur_minor_edit' ), - array( 'cur_id' => $this->getID() ), $fname ); + array( 'cur_id' => $this->getID() ), $fname, $this->getSelectOptions() ); if ( $s !== false ) { $this->mUser = $s->cur_user; @@ -575,7 +612,7 @@ class Article { $title = $this->mTitle; $contribs = array(); - $dbr =& wfGetDB( DB_SLAVE ); + $dbr =& $this->getDB(); $oldTable = $dbr->tableName( 'old' ); $userTable = $dbr->tableName( 'user' ); $encDBkey = $dbr->strencode( $title->getDBkey() ); @@ -591,9 +628,10 @@ class Article { ORDER BY timestamp DESC"; if ($limit > 0) { $sql .= ' LIMIT '.$limit; } + $sql .= ' '. $this->getSelectOptions(); $res = $dbr->query($sql, $fname); - + while ( $line = $dbr->fetchObject( $res ) ) { $contribs[] = array($line->old_user, $line->old_user_text, $line->user_real_name); } @@ -612,7 +650,6 @@ class Article { $fname = 'Article::view'; wfProfileIn( $fname ); - # Get variables from query string $oldid = $wgRequest->getVal( 'oldid' ); $diff = $wgRequest->getVal( 'diff' ); @@ -620,7 +657,6 @@ class Article { $wgOut->setArticleFlag( true ); $wgOut->setRobotpolicy( 'index,follow' ); - # If we got diff and oldid in the query, we want to see a # diff page instead of the article. @@ -635,7 +671,6 @@ class Article { } return; } - if ( empty( $oldid ) && $this->checkTouched() ) { if( $wgOut->checkLastModified( $this->mTouched ) ){ return; @@ -646,7 +681,6 @@ class Article { return; } } - # Should the parser cache be used? if ( $wgEnableParserCache && intval($wgUser->getOption( 'stubthreshold' )) == 0 && empty( $oldid ) ) { $pcache = true; @@ -660,7 +694,6 @@ class Article { $outputDone = true; } } - if ( !$outputDone ) { $text = $this->getContent( false ); # May change mTitle by following a redirect @@ -714,7 +747,6 @@ class Article { } } $wgOut->setPageTitle( $this->mTitle->getPrefixedText() ); - # If we have been passed an &rcid= parameter, we want to give the user a # chance to mark this new article as patrolled. if ( $wgUseRCPatrol && !is_null ( $rcid ) && $rcid != 0 && $wgUser->getID() != 0 && @@ -728,9 +760,10 @@ class Article { # Put link titles into the link cache $wgOut->replaceLinkHolders(); + # Add link titles as META keywords $wgOut->addMetaTags() ; - + $this->viewUpdates(); wfProfileOut( $fname ); } @@ -1294,7 +1327,7 @@ class Article { # determine whether this page has earlier revisions # and insert a warning if it does # we select the text because it might be useful below - $dbr =& wfGetDB( DB_SLAVE ); + $dbr =& $this->getDB(); $ns = $this->mTitle->getNamespace(); $title = $this->mTitle->getDBkey(); $old = $dbr->getArray( 'old', @@ -1302,7 +1335,7 @@ class Article { array( 'old_namespace' => $ns, 'old_title' => $title, - ), $fname, array( 'ORDER BY' => 'inverse_timestamp' ) + ), $fname, $this->getSelectOptions( array( 'ORDER BY' => 'inverse_timestamp' ) ) ); if( $old !== false && !$confirm ) { @@ -1317,7 +1350,7 @@ class Article { array( 'cur_namespace' => $ns, 'cur_title' => $title, - ), $fname + ), $fname, $this->getSelectOptions() ); if( $s !== false ) { @@ -1782,11 +1815,10 @@ class Article { # Loads cur_touched and returns a value indicating if it should be used function checkTouched() { $fname = 'Article::checkTouched'; - $id = $this->getID(); - $dbr =& wfGetDB( DB_SLAVE ); + $dbr =& $this->getDB(); $s = $dbr->getArray( 'cur', array( 'cur_touched', 'cur_is_redirect' ), - array( 'cur_id' => $id ), $fname ); + array( 'cur_id' => $id ), $fname, $this->getSelectOptions() ); if( $s !== false ) { $this->mTouched = wfTimestamp(TS_MW,$s->cur_touched); return !$s->cur_is_redirect; @@ -1950,7 +1982,7 @@ class Article { return; } - $dbr =& wfGetDB( DB_SLAVE ); + $dbr =& $this->getDB(); $basenamespace = $wgTitle->getNamespace() & (~1); $cur_clause = array( 'cur_title' => $wgTitle->getDBkey(), 'cur_namespace' => $basenamespace ); @@ -1961,13 +1993,14 @@ class Article { $wgOut->setSubtitle( wfMsg( "infosubtitle" )); # first, see if the page exists at all. - $exists = $dbr->selectField( 'cur', 'COUNT(*)', $cur_clause, $fname ); + $exists = $dbr->selectField( 'cur', 'COUNT(*)', $cur_clause, $fname, $this->getSelectOptions() ); if ($exists < 1) { $wgOut->addHTML( wfMsg("noarticletext") ); } else { - $numwatchers = $dbr->selectField( 'watchlist', 'COUNT(*)', $wl_clause, $fname ); + $numwatchers = $dbr->selectField( 'watchlist', 'COUNT(*)', $wl_clause, $fname, + $this->getSelectOptions() ); $wgOut->addHTML( "" ); } diff --git a/includes/EditPage.php b/includes/EditPage.php index f6a5793cc2..478fbdf6a3 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -153,7 +153,7 @@ class EditPage { } # If article is new, insert it. - $aid = $this->mTitle->getArticleID(); + $aid = $this->mTitle->getArticleID( GAID_FOR_UPDATE ); if ( 0 == $aid ) { # Don't save a new article if it's blank. if ( ( "" == $this->textbox1 ) || @@ -168,6 +168,7 @@ class EditPage { # Article exists. Check for edit conflict. $this->mArticle->clear(); # Force reload of dates, etc. + $this->mArticle->forUpdate( true ); # Lock the article if( ( $this->section != "new" ) && ($this->mArticle->getTimestamp() != $this->edittime ) ) { @@ -471,7 +472,6 @@ htmlspecialchars( $wgLang->recodeForEdit( $this->textbox1 ) ) . $wgOut->addHTML($previewhead); $wgOut->addHTML($previewHTML); } - } function blockedIPpage() @@ -484,10 +484,10 @@ htmlspecialchars( $wgLang->recodeForEdit( $this->textbox1 ) ) . $id = $wgUser->blockedBy(); $reason = $wgUser->blockedFor(); - $ip = $wgIP; + $ip = $wgIP; if ( is_numeric( $id ) ) { - $name = User::whoIs( $id ); + $name = User::whoIs( $id ); } else { $name = $id; } diff --git a/includes/Title.php b/includes/Title.php index 462bc1a2d2..2b4e562324 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -8,6 +8,9 @@ $wgTitleInterwikiCache = array(); # * Represents a title, which may contain an interwiki designation or namespace # * Can fetch various kinds of data from the database, albeit inefficiently. # + +define ( 'GAID_FOR_UPDATE', 1 ); + class Title { # All member variables should be considered private # Please use the accessor functions @@ -250,7 +253,7 @@ class Title { } $dbr =& wfGetDB( DB_SLAVE ); $res = $dbr->select( 'interwiki', array( 'iw_url', 'iw_local' ), array( 'iw_prefix' => $key ), $fname ); - if(!$res) return ""; + if(!$res) return ""; $s = $dbr->fetchObject( $res ); if(!$s) { @@ -580,12 +583,19 @@ class Title { } # Get the article ID from the link cache - # Used very heavily, e.g. in Parser::replaceInternalLinks() - function getArticleID() { + # $flags is a bit field, may be GAID_FOR_UPDATE to select for update + function getArticleID( $flags = 0 ) { global $wgLinkCache; - - if ( -1 != $this->mArticleID ) { return $this->mArticleID; } - $this->mArticleID = $wgLinkCache->addLinkObj( $this ); + + if ( $flags & GAID_FOR_UPDATE ) { + $oldUpdate = $wgLinkCache->forUpdate( true ); + $this->mArticleID = $wgLinkCache->addLinkObj( $this ); + $wgLinkCache->forUpdate( $oldUpdate ); + } else { + if ( -1 == $this->mArticleID ) { + $this->mArticleID = $wgLinkCache->addLinkObj( $this ); + } + } return $this->mArticleID; } -- 2.20.1